Canvas on resize - сохранить максимальную ширину или высоту без растяжения / заполнения

У меня вопрос к html5-игре с использованием canvas в javascript.

Мне бы хотелось, чтобы показанный игроку холст содержал либо 1920 холстов. Ширины, либо 1080 холстов. Высоты, чтобы не видеть отступы. Это означает, что я не хочу сохранять (оптимальное) игровое соотношение 16/9, если игрок изменяет размер своего экрана с другим соотношением, но я хочу сохранить пропорцию окна просмотра 16/9, чтобы избежать растяжения (используя canvas.style.***)

Позвольте мне привести пример игрока с изменяемым размером экрана (window.innerWidth = 500 и window.innerHeight = 1600), при 1600/500 > 1920 / 1080, я хотел бы, чтобы игрок мог видеть 1920 ширины игры и " меньше чем 1080" высоты игры, предотвращая растяжение области просмотра.

Agar.io является еще одним хорошим примером.

Большое спасибо!

1 ответ

Решение

Подходит, но оставит пустые области по бокам или сверху внизу, если аспекты не совпадают.

var scale = Math.min(innerWidth / canvas.width, innerHeight / canvas.height);

или заполнить, но обрежет холст, если аспекты не совпадают

var scale = Math.max(innerWidth / canvas.width, innerHeight / canvas.height);

для отображения 1600 на 500 и холст 1920 на 1080

1600 / 1920 = 0.8333;
500 / 1080 = 0.463;

таким образом, чтобы соответствовать холст будет 0.463 * (1920,1080) = (889,500) пустой по бокам

и заполнить холст будет 0.8333 * (1920,1080) = (1600,900) обрезается сверху и снизу.

Более подробную информацию о масштабе и подгонке можно найти ниже.

Если вы масштабируете, чтобы заполнить холст, вам нужно будет учитывать обрезанную область и найти смещение к верхнему левому углу холста (это будет за пределами страницы).

var leftOffset = 0;
var topOffset = 0;
var canW = scale * canvas.width;
var canH = scale * canvas.height;
if(canW > innerWidth ){
    leftOffset = ((canW - innerWidth) / canW) * 1920 / 2;
}else
if(canH > innerHeight ){
    topOffset = ((canH - innerHeight) / canH) * 1080 / 2;
}

Ваш холст заполнит страницу innerWidth и innerHeight, но вам нужно будет компенсировать весь рендеринг. Это можно сделать, установив преобразование на правильные смещения

ctx.setTransform(1,0,0,1,-leftOffset, -topOffset);

Размер отображения холста будет

canvas.style.width = innerWidth + "px"; 
canvas.style.height = innerHeight + "px";

и разрешение холста будет

canvas.width = 1920 - (leftOffset * 2);
canvas.height = 1080 - (topOffset * 2);

Ниже приведен пример кода в действии. Очень ленивое кодирование, просто чтобы показать, что оно работает, на самом деле не нужно создавать и уничтожать canvas при изменении размеров. Также как скрипка, потому что у него есть изменяемые размеры панелей для тестирования.

 var canvas;
var createCanvas = function(){
    if(canvas !== undefined){
         document.body.removeChild(canvas);
    
    }
    var canWidth = 1920;
    var canHeight = 1080;

    var scale = Math.max(innerWidth /canWidth, innerHeight / canHeight);
    var leftOffset = 0;
    var topOffset = 0;
    var canW = scale * canWidth;
    var canH = scale * canHeight;
    if(canW > innerWidth ){
        leftOffset = ((canW - innerWidth) / canW) * canWidth / 2;
    }else
    if(canH > innerHeight ){
        topOffset = ((canH - innerHeight) / canH) * canHeight / 2;
    }

    canvas = document.createElement("canvas");
    canvas.style.position = "absolute";
    canvas.style.top = "0px";
    canvas.style.left = "0px";
    canvas.style.width = innerWidth + "px"; 
    canvas.style.height = innerHeight + "px";
    canvas.width = canWidth - (leftOffset * 2);
    canvas.height = canHeight - (topOffset * 2);
    document.body.appendChild(canvas);
    var ctx = canvas.getContext("2d");


    ctx.setTransform(1,0,0,1,-leftOffset, -topOffset);
    ctx.beginPath();
    ctx.arc(canWidth / 2, canHeight/2, 400,0,Math.PI * 2);
    ctx.stroke();
}

window.addEventListener('resize',createCanvas);
createCanvas();


Масштабирование, чтобы соответствовать

Означает, что все изображение будет видимым, но может быть некоторое пустое пространство по бокам или сверху и снизу, если изображение не совпадает с аспектом холста. В примере показано изображение, масштабированное для соответствия. Синий цвет по бокам связан с тем, что изображение отличается от холста.

Масштабирование для заполнения

Означает, что изображение масштабируется так, что все пиксели холста будут покрыты изображением. Если аспект изображения не совпадает с холстом, то некоторые части изображения будут обрезаны. В примере показано изображение, масштабированное для заполнения. Обратите внимание, что верх и низ изображения больше не видны.

Пример Масштаб, чтобы соответствовать

var image = new Image();
image.src = "imgURL";
image.onload = function(){
    scaleToFit(this);
}

function scaleToFit(img){
    // get the scale
    var scale = Math.min(canvas.width / img.width, canvas.height / img.height);
    // get the top left position of the image
    var x = (canvas.width / 2) - (img.width / 2) * scale;
    var y = (canvas.height / 2) - (img.height / 2) * scale;
    ctx.drawImage(img, x, y, img.width * scale, img.height * scale);
}

Пример шкалы для заполнения

var image = new Image();
image.src = "imgURL";
image.onload = function(){
    scaleToFill(this);
}

function scaleToFill(img){
    // get the scale
    var scale = Math.max(canvas.width / img.width, canvas.height / img.height);
    // get the top left position of the image
    var x = (canvas.width / 2) - (img.width / 2) * scale;
    var y = (canvas.height / 2) - (img.height / 2) * scale;
    ctx.drawImage(img, x, y, img.width * scale, img.height * scale);
}

Единственное различие между этими двумя функциями - получение шкалы. Подгонка использует минимальную шкалу подгонки, если заполнение использует максимальную шкалу подгонки.

Другие вопросы по тегам