Как изменить размер <canvas>в зависимости от ширины / высоты окна и сохранить соотношение сторон? - PullRequest
0 голосов
/ 06 января 2020

Я хочу использовать холст, максимальная 1000px x 1000px. Но если экран меньше или окно становится меньше, чтобы автоматически настроить ширину / высоту на основе этого при сохранении исходного соотношения сторон. Например: если ширина = 500px, то canvas = 500x500, а не 500x1000. если высота = 100, то холст = 100х100. Я не против использовать javascript, css, html для этого. спасибо.

1 Ответ

0 голосов
/ 07 января 2020

Масштабирование в соответствии с фиксированным соотношением сторон.

Вы можете использовать CanvasRenderingContext2D.setTransform , чтобы масштабировать и позиционировать преобразование, чтобы соответствовать и центрировать содержимое.

Вам потребуется эталонное разрешение, которое определяет исходную шкалу координат 1 и аспект. Например ...

const refRes = {width: 1000, height: 1000}; 

Масштабирование по размеру

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

// Get the scale to fit content to the canvas
const scale = Math.min(canvas.width / refRes.width, canvas.height / refRes.height);

// set the origin so that the scaled content is centered on the canvas
const origin = {
    x: (canvas.width - refRes.width * scale) / 2,
    y: (canvas.height - refRes.height * scale) / 2
};

// Set the transform to scale and center on canvas
ctx.setTransform(scale, 0, 0, scale, origin.x, origin.y);

// Then render your content using the original coordinates.
ctx.fillRect(0, 0, 1000, 1000); // will fit any sized canvas

При сохранении аспекта могут быть неиспользованные пиксели слева и справа или выше и ниже в зависимости от аспекта холста.

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

Вы можете масштабировать, чтобы заполнить, что обрезает содержимое, но гарантирует, что все пиксели используются. Просто используйте максимальный масштабированный размер. Например ...

// Use max res to scale to fill
const scale = Math.max(canvas.width / refRes.width, canvas.height / refRes.height);

Демонстрация

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

requestAnimationFrame(mainLoop);
const ctx = canvas.getContext("2d");
const size = 1000;
Math.TAU = Math.PI * 2;
Math.randI = (m, M) => Math.random() * (M - m) + m | 0; // for unsigned int32 
Math.nearZero = val => Math.abs(val) < 1e-3;
const refRes = {width: size, height: size}; 
renderContent();

// State for canvas size changes
var xRes = canvas.width, yRes = canvas.height;
var xResC = xRes, yResC = yRes; // current resolution
var xResD = 0, yResD = 0;  // resolution delta change
const rate = 0.2; // rate of canvas size change
                  // WARNING there is no bounds checking for canvas size. 
                  // If  rate < 0 || rate > 0.5 you MUST check that canvas size
                  // is safe before setting its width and height
                  

function scaleToFit() {
    const scale = Math.min(canvas.width / refRes.width, canvas.height / refRes.height);
    ctx.setTransform(
        scale, 0, 0, scale, 
        (canvas.width - refRes.width * scale) / 2, 
        (canvas.height - refRes.height * scale) / 2
    );
}

function mainLoop() {
    xResC += (xResD = (xResD += (xRes - xResC) * rate) * rate);
    yResC += (yResD = (yResD += (yRes - yResC) * rate) * rate);
    const w = xResC | 0;
    const h = yResC | 0;
    if (w !== canvas.width || h !== canvas.height) {
        canvas.width = w;
        canvas.height = h;
        renderContent();
    }
    if(Math.nearZero(xResD) && Math.nearZero(yResD)) {
        xRes = Math.randI(30, 300);
        yRes = Math.randI(30, 200);
    }
    requestAnimationFrame(mainLoop);
}

function renderContent() {
    scaleToFit();
    ctx.fillStyle = "#Faa";
    ctx.fillRect(0,0,size,size);

    ctx.fillStyle = "#8aF";
    ctx.beginPath();
    ctx.arc(size / 2, size / 2, size / 2 - 4, 0, Math.TAU);
    ctx.fill();

    ctx.fillStyle = "#FF8";
    ctx.fillRect(
        (size - size * Math.SQRT1_2) / 2, (size - size * Math.SQRT1_2) / 2, 
        size * Math.SQRT1_2, size * Math.SQRT1_2
    );

    ctx.lineWidth = 10;
    ctx.beginPath();
    ctx.arc(size / 2, size / 2, size / 2 - 4, 0, Math.TAU);
    ctx.rect(
        (size - size * Math.SQRT1_2) / 2, (size - size * Math.SQRT1_2) / 2, 
        size * Math.SQRT1_2, size * Math.SQRT1_2
    );
    ctx.stroke();
}
canvas {
    border: 1px solid black;
}
<canvas id="canvas"></canvas>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...