Изменение размера HTML5-холста по размеру окна

Как я могу автоматически масштабировать HTML5 <canvas> элемент, чтобы соответствовать странице?

Например, я могу получить <div> масштабировать, установив height а также width свойства до 100%, но <canvas> не будет масштабироваться, не так ли?

19 ответов

Решение

Я считаю, что нашел элегантное решение для этого:

JavaScript

/* important! for alignment, you should make things
 * relative to the canvas' current width/height.
 */
function draw() {
  var ctx = (a canvas context);
  ctx.canvas.width  = window.innerWidth;
  ctx.canvas.height = window.innerHeight;
  //...drawing code...
}

CSS

html, body {
  width:  100%;
  height: 100%;
  margin: 0;
}

До сих пор не оказал большого негативного влияния на производительность.

Следующее решение сработало для меня лучше всего. Поскольку я относительно новичок в программировании, мне нравится получать визуальное подтверждение того, что что-то работает так, как я ожидаю. Я нашел его на следующем сайте: http://htmlcheats.com/html/resize-the-html5-canvas-dyamically/

Вот код:

<!DOCTYPE html>

<head>
    <meta charset="utf-8">
    <title>Resize HTML5 canvas dynamically | www.htmlcheats.com</title>
    <style>
    html, body {
      width: 100%;
      height: 100%;
      margin: 0px;
      border: 0;
      overflow: hidden; /*  Disable scrollbars */
      display: block;  /* No floating content on sides */
    }
    </style>
</head>

<body>
    <canvas id='c' style='position:absolute; left:0px; top:0px;'>
    </canvas>

    <script>
    (function() {
        var
        // Obtain a reference to the canvas element using its id.
        htmlCanvas = document.getElementById('c'),
        // Obtain a graphics context on the canvas element for drawing.
        context = htmlCanvas.getContext('2d');

       // Start listening to resize events and draw canvas.
       initialize();

       function initialize() {
           // Register an event listener to call the resizeCanvas() function 
           // each time the window is resized.
           window.addEventListener('resize', resizeCanvas, false);
           // Draw canvas border for the first time.
           resizeCanvas();
        }

        // Display custom canvas. In this case it's a blue, 5 pixel 
        // border that resizes along with the browser window.
        function redraw() {
           context.strokeStyle = 'blue';
           context.lineWidth = '5';
           context.strokeRect(0, 0, window.innerWidth, window.innerHeight);
        }

        // Runs each time the DOM window resize event fires.
        // Resets the canvas dimensions to match window,
        // then draws the new borders accordingly.
        function resizeCanvas() {
            htmlCanvas.width = window.innerWidth;
            htmlCanvas.height = window.innerHeight;
            redraw();
        }
    })();

    </script>
</body> 
</html>

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

По сути, вам нужно привязать событие onresize к своему телу. После того, как вы поймаете событие, вам просто нужно изменить размер холста, используя window.innerWidth и window.innerHeight.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Canvas Resize</title>

    <script type="text/javascript">
        function resize_canvas(){
            canvas = document.getElementById("canvas");
            if (canvas.width  < window.innerWidth)
            {
                canvas.width  = window.innerWidth;
            }

            if (canvas.height < window.innerHeight)
            {
                canvas.height = window.innerHeight;
            }
        }
    </script>
</head>

<body onresize="resize_canvas()">
        <canvas id="canvas">Your browser doesn't support canvas</canvas>
</body>
</html>

2022 ответ

Рекомендуемый способ в 2022 году проверить, является ли размер элемента измененным, чтобы использоватьResizeObserver

      const observer = new ResizeObserver(myResizeTheCanvasFn);
observer.observe(someCanvasElement);

Это лучше, чемwindow.addEventListener('resize', myResizeTheCanvasFn)илиonresize = myResizeTheCanvasFnпотому что он обрабатывает КАЖДЫЙ случай изменения размера холста, даже если это не связано с изменением размера окна.

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

Что касается холста, чтобы заполнить окно

      html, body {
  height: 100%;
}
canvas {
  width: 100%;
  height: 100%;
  display: block;   /* this is IMPORTANT! */
}

Причина, по которой вам это нужно, заключается в том, что по умолчанию холстinlineчто означает, что он включает дополнительный пробел в конце. Без вы получите полосу прокрутки. Многие люди решают проблему с полосой прокрутки, добавляя ее в тело документа, но это просто скрывает тот факт, что CSS холста был установлен неправильно. Лучше исправить ошибку (установить холст наdisplay: blockчем скрыть ошибку сoverflow: hidden

Полный пример

Примечание. Существуют и другие проблемы, связанные с изменением размера холста. В частности, если вы хотите иметь дело с различными настройками devicePixelRatio. См. эту статью для получения дополнительной информации.

Установка ширины и высоты координатного полотна на основе размеров клиента браузера требует изменения размера и перерисовки при каждом изменении размера браузера.

Менее запутанное решение состоит в том, чтобы поддерживать прорисовываемые размеры в переменных Javascript, но устанавливать размеры холста на основе размеров screen.width, screen.height. Используйте CSS, чтобы соответствовать:

#containingDiv { 
  overflow: hidden;
}
#myCanvas {
  position: absolute; 
  top: 0px;
  left: 0px;
} 

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

Чистый CSS подход, добавляющий к решению @jerseyboy выше.
Работает в Firefox (тестирование в v29), Chrome (тестирование в v34) и Internet Explorer (тестирование в v11).

<!DOCTYPE html>
<html>
    <head>
    <style>
        html,
        body {
            width: 100%;
            height: 100%;
            margin: 0;
        }
        canvas {
            background-color: #ccc;
            display: block;
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            width: 100%;
            height: 100%;
        }
    </style>
</head>
<body>
    <canvas id="canvas" width="500" height="500"></canvas>
    <script>
        var canvas = document.getElementById('canvas');
        if (canvas.getContext) {
            var ctx = canvas.getContext('2d');
            ctx.fillRect(25,25,100,100);
            ctx.clearRect(45,45,60,60);
            ctx.strokeRect(50,50,50,50);
        }
    </script>
</body>
</html>

Ссылка на пример: http://temporaer.net/open/so/140502_canvas-fit-to-window.html

Но будьте осторожны, как пишет @jerseyboy в своем комментарии:

Изменение масштаба холста с помощью CSS проблематично. По крайней мере, в Chrome и Safari позиции событий мыши / касания не будут соответствовать 1:1 с позициями пикселей холста, и вам придется трансформировать системы координат.

function resize() {

    var canvas = document.getElementById('game');
    var canvasRatio = canvas.height / canvas.width;
    var windowRatio = window.innerHeight / window.innerWidth;
    var width;
    var height;

    if (windowRatio < canvasRatio) {
        height = window.innerHeight;
        width = height / canvasRatio;
    } else {
        width = window.innerWidth;
        height = width * canvasRatio;
    }

    canvas.style.width = width + 'px';
    canvas.style.height = height + 'px';
};

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

Установите начальный размер.

const canvas = document.getElementById('canvas');

canvas.width  = window.innerWidth;
canvas.height = window.innerHeight;

Размер обновления при изменении размера окна.

function windowResize() {
  canvas.width  = window.innerWidth;
  canvas.height = window.innerHeight;
};

window.addEventListener('resize', windowResize);

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

Если ваш div полностью заполнил веб-страницу, то вы можете заполнить этот div и получить холст, который заполняет div.

Это может показаться вам интересным, так как вам может понадобиться использовать CSS для использования процентов, но это зависит от того, какой браузер вы используете, и насколько он соответствует спецификации: http://www.whatwg.org/ функции / веб-приложения / текущие работы / многостраничные /-холст-element.html # холст-элемент

Внутренние размеры элемента canvas равны размеру координатного пространства, а числа интерпретируются в CSS-пикселях. Тем не менее, размер элемента может быть выбран произвольно с помощью таблицы стилей. Во время рендеринга изображение масштабируется для соответствия размеру макета.

Вам может понадобиться получить offsetWidth и высоту div, или получить высоту / ширину окна и установить его в качестве значения пикселя.

Если вы заинтересованы в сохранении пропорций и в чистом CSS (учитывая соотношение сторон), вы можете сделать что-то вроде ниже. Ключ является padding-bottom на ::content элемент, который определяет размеры container элемент. Это размер относительно ширины его родителя, который является 100% по умолчанию. Указанное здесь соотношение должно соответствовать соотношению размеров на canvas элемент.

// Javascript

var canvas = document.querySelector('canvas'),
    context = canvas.getContext('2d');

context.fillStyle = '#ff0000';
context.fillRect(500, 200, 200, 200);

context.fillStyle = '#000000';
context.font = '30px serif';
context.fillText('This is some text that should not be distorted, just scaled', 10, 40);
/*CSS*/

.container {
  position: relative; 
  background-color: green;
}

.container::after {
  content: ' ';
  display: block;
  padding: 0 0 50%;
}

.wrapper {
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  bottom: 0;
}

canvas {
  width: 100%;
  height: 100%;
}
<!-- HTML -->

<div class=container>
  <div class=wrapper>
    <canvas width=1200 height=600></canvas>  
  </div>
</div>

CSS

body { margin: 0; } 
canvas { display: block; } 

JavaScript

window.addEventListener("load", function()
{
    var canvas = document.createElement('canvas'); document.body.appendChild(canvas);
    var context = canvas.getContext('2d');

    function draw()
    {
        context.clearRect(0, 0, canvas.width, canvas.height);
        context.beginPath();
        context.moveTo(0, 0); context.lineTo(canvas.width, canvas.height); 
        context.moveTo(canvas.width, 0); context.lineTo(0, canvas.height); 
        context.stroke();
    }
    function resize()
    {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
        draw();
    }
    window.addEventListener("resize", resize);
    resize();
});

Используя jQuery, вы можете отслеживать изменение размеров окна и изменять ширину холста, используя также jQuery.

Что-то вроде того

$( window ).resize(function() {
   $("#myCanvas").width($( window ).width())
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="myCanvas" width="200" height="100" style="border:1px solid #000000;">

Минимальная настройка

HTML

      <canvas></canvas>

CSS

      html,
body {
  height: 100%;
  margin: 0;
}

canvas {
  width: 100%;
  height: 100%;
  display: block;
}

JavaScript

      const canvas = document.querySelector('canvas');

const resizeObserver = new ResizeObserver(() => {
  canvas.width = Math.round(canvas.clientWidth * devicePixelRatio);
  canvas.height = Math.round(canvas.clientHeight * devicePixelRatio);
});

resizeObserver.observe(canvas);

Для WebGL

      const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');

const resizeObserver = new ResizeObserver(() => {
  canvas.width = Math.round(canvas.clientWidth * devicePixelRatio);
  canvas.height = Math.round(canvas.clientHeight * devicePixelRatio);
  gl.viewport(0, 0, canvas.width, canvas.height);
});

resizeObserver.observe(canvas);

Обратите внимание, что мы должны учитывать соотношение пикселей устройства, особенно для отображения HD-DPI.

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

function refresh(referenceWidth, referenceHeight, drawFunction) {
  const myCanvas = document.getElementById("myCanvas");
  myCanvas.width = myCanvas.clientWidth;
  myCanvas.height = myCanvas.clientHeight;

  const ratio = Math.min(
    myCanvas.width / referenceWidth,
    myCanvas.height / referenceHeight
  );
  const ctx = myCanvas.getContext("2d");
  ctx.scale(ratio, ratio);

  drawFunction(ctx, ratio);
  window.requestAnimationFrame(() => {
    refresh(referenceWidth, referenceHeight, drawFunction);
  });
}

//100, 100 is the "reference" size. Choose whatever you want.
refresh(100, 100, (ctx, ratio) => {
  //Custom drawing code! Draw whatever you want here.
  const referenceLineWidth = 1;
  ctx.lineWidth = referenceLineWidth / ratio;
  ctx.beginPath();
  ctx.strokeStyle = "blue";
  ctx.arc(50, 50, 49, 0, 2 * Math.PI);
  ctx.stroke();
});
div {
  width: 90vw;
  height: 90vh;
}

canvas {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
<div>
  <canvas id="myCanvas"></canvas>
</div>

В этом фрагменте кода используются canvas.clientWidth и canvas.clientHeight, а не window.innerWidth и window.innerHeight, чтобы фрагмент работал правильно внутри сложного макета. Однако он работает и для полного окна, если вы просто поместите его в div, который использует полное окно. Такой способ более гибкий.

Фрагмент использует новое окно window.requestAnimationFrame для многократного изменения размера холста в каждом кадре. Если вы не можете использовать это, используйте вместо этого setTimeout. Кроме того, это неэффективно. Чтобы сделать его более эффективным, сохраните clientWidth и clientHeight и пересчитывайте и перерисовывайте только при изменении clientWidth и clientHeight.

Идея "эталонного" разрешения позволяет вам писать все ваши команды рисования, используя одно разрешение... и оно автоматически подстраивается под размер клиента без необходимости изменения кода рисования.

Фрагмент не требует пояснений, но, если вы предпочитаете, объяснение на английском языке: https://medium.com/@doomgoober/resizing-canvas-vector-graphics-without-aliasing-7a1f9e684e4d

(function() {

    // get viewport size
    getViewportSize = function() {
        return {
            height: window.innerHeight,
            width:  window.innerWidth
        };
    };

    // update canvas size
    updateSizes = function() {
        var viewportSize = getViewportSize();
        $('#myCanvas').width(viewportSize.width).height(viewportSize.height);
        $('#myCanvas').attr('width', viewportSize.width).attr('height', viewportSize.height);
    };

    // run on load
    updateSizes();

    // handle window resizing
    $(window).on('resize', function() {
        updateSizes();
    });

}());

Я думаю, что именно это мы и должны делать: http://www.html5rocks.com/en/tutorials/casestudies/gopherwoord-studios-resizing-html5-games/

function resizeGame() {
    var gameArea = document.getElementById('gameArea');
    var widthToHeight = 4 / 3;
    var newWidth = window.innerWidth;
    var newHeight = window.innerHeight;
    var newWidthToHeight = newWidth / newHeight;

    if (newWidthToHeight > widthToHeight) {
        newWidth = newHeight * widthToHeight;
        gameArea.style.height = newHeight + 'px';
        gameArea.style.width = newWidth + 'px';
    } else {
        newHeight = newWidth / widthToHeight;
        gameArea.style.width = newWidth + 'px';
        gameArea.style.height = newHeight + 'px';
    }

    gameArea.style.marginTop = (-newHeight / 2) + 'px';
    gameArea.style.marginLeft = (-newWidth / 2) + 'px';

    var gameCanvas = document.getElementById('gameCanvas');
    gameCanvas.width = newWidth;
    gameCanvas.height = newHeight;
}

window.addEventListener('resize', resizeGame, false);
window.addEventListener('orientationchange', resizeGame, false);

Это сработало для меня. Псевдокод:

// screen width and height
scr = {w:document.documentElement.clientWidth,h:document.documentElement.clientHeight}
canvas.width = scr.w
canvas.height = scr.h

Кроме того, как сказала Девин, вы можете заменить "document.documentElement.client" на "inner" как для ширины, так и для высоты:

**document.documentElement.client**Width
**inner**Width
**document.documentElement.client**Height
**inner**Height

и он все еще работает.

Я использую sketch.js, поэтому после запуска команды init для canvas я изменяю ширину и высоту с помощью jquery. Размеры основываются на родительском элементе.

$ ('#DrawCanvas'). Эскиз (). Attr ('высота',$('#DrawCanvas'). Родитель (). Высота ()). Attr ('ширина', $ (. Родительским '#DrawCanvas') ().width());

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