调整HTML5画布大小以适应窗口

如何自动缩放HTML5 <canvas>元素以适应页面?

例如,我可以通过将heightwidth属性设置为100%来让<div>缩放,但<canvas>不能缩放,不是吗?

649329 次浏览

如果你的div完全填满了网页,那么你可以填充这个div,这样就有一个画布来填充这个div。

你可能会发现这很有趣,因为你可能需要使用css来使用百分比,但是,这取决于你使用的浏览器,以及它在多大程度上符合规范: http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#the-canvas-element < / p >

画布的固有尺寸 元素的大小相等 用数字来坐标空间 用CSS像素解释。然而, 元素的大小可以任意设置 通过样式表。在呈现期间, 图像按比例缩放以适应此布局 大小。< / p >

您可能需要获取div的offsetWidth和height,或者获取窗口的高度/宽度并将其设置为像素值。

除非你想让画布自动提升你的图像数据(这就是James Black的答案,但它看起来不漂亮),你必须自己调整它的大小并重新绘制图像。画布居中

我相信我已经找到了一个优雅的解决方案:

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;
}

到目前为止,还没有对我的表现产生任何大的负面影响。

基本上你要做的是将onresize事件绑定到你的body,一旦你捕捉到事件,你只需要使用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>

根据浏览器客户机的尺寸设置画布坐标空间的宽度和高度需要您在浏览器调整大小时重新调整大小和绘制。

一个不那么复杂的解决方案是在Javascript变量中维护可绘制的尺寸,但根据屏幕设置画布尺寸。宽度、屏幕。高度尺寸。使用CSS来适应:

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

浏览器窗口通常不会比屏幕本身大(除非屏幕分辨率被误报,因为它可能是不匹配的双显示器),所以背景不会显示,像素比例也不会变化。画布像素将与屏幕分辨率成正比,除非你使用CSS缩放画布。

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);

我认为这是我们应该做的: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);
(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();
});


}());

一个纯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对应 Canvas像素位置,你需要变换坐标 系统。< / p >

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();
});

我使用的是sketch.js,所以在我为画布运行init命令后,我用jquery改变宽度和高度。它将维度基于父元素。

$ (' # DrawCanvas ') .sketch () .attr(“高度”,$ (' # DrawCanvas ') .parent () .height ()) .attr(“宽度”,$ (' # DrawCanvas ') .parent () .width ());

下面的解决方案对我最有效。因为我对编码相对陌生,所以我喜欢用视觉来确认某些东西是否按照我期望的方式工作。我在以下地点找到了它: http://htmlcheats.com/html/resize-the-html5-canvas-dyamically/ < / p >

代码如下:

(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();
}
})();
html,
body {
width: 100%;
height: 100%;
margin: 0px;
border: 0;
overflow: hidden;
/*  Disable scrollbars */
display: block;
/* No floating content on sides */
}
<canvas id='c' style='position:absolute; left:0px; top:0px;'></canvas>

The blue border shows you the edge of the resizing canvas, and is always along the edge of the window, visible on all 4 sides, which was NOT the case for some of the other above answers. Hope it helps.

如果你对保留纵横比感兴趣,并在纯CSS中这样做(给定纵横比),你可以像下面这样做。关键字是::content元素上的padding-bottom,用于调整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>

使用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;">

下面是一个小而完整的代码片段,它结合了所有的答案。按:“运行代码片段”;然后按“整页”键;并调整窗口大小以查看它的运行情况:

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>

这个片段使用画布。clientWidth和canvas。clientHeight而不是window。innerWidth和window。innerHeight使代码段在复杂布局中正确运行。然而,如果你只是把它放在一个使用全窗口的div中,它也适用于全窗口。这样更灵活。

代码片段使用了新窗口。请求animationframe重复调整画布的大小每帧。如果不能使用此方法,请使用setTimeout代替。而且,这是低效的。为了提高效率,存储clientWidth和clienttheight,只有当clientWidth和clienttheight改变时才重新计算和绘制。

“参考”的概念;分辨率允许您使用一个分辨率编写所有的绘制命令…并且它会自动调整到客户端大小,而无需更改绘图代码。

这个代码片段是不言自明的,但如果你喜欢用英文解释:https://medium.com/@doomgoober/resizing-canvas-vector-graphics-without-aliasing-7a1f9e684e4d

这对我很管用。 伪代码:< / p >
// screen width and height
scr = {w:document.documentElement.clientWidth,h:document.documentElement.clientHeight}
canvas.width = scr.w
canvas.height = scr.h

另外,就像devyn说的,你可以替换“;document. documentelement .client”;与“inner"对于宽度和高度:

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

它仍然有效。

设置初始大小。

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);

2022的答案

在2022年检查元素是否调整大小的推荐方法是使用ResizeObserver

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

它比window.addEventListener('resize', myResizeTheCanvasFn)onresize = myResizeTheCanvasFn更好,因为它处理画布调整大小的每一种情况,即使它与窗口调整大小无关。

同样,使用window.innerWidthwindow.innerHeight也没有意义。你想要画布本身的大小,而不是窗口的大小。这样,无论你把画布放在哪里,你都会得到正确的大小,而不必重写你的尺寸代码。

至于用画布填满窗户

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

你需要display: block的原因是因为默认情况下画布是inline,这意味着它在结尾包含额外的空间。如果没有display: block,你将得到一个滚动条。许多人通过在文档正文中添加overflow: hidden来修复滚动条问题,但这只是隐藏了画布的CSS设置不正确的事实。最好是修复bug(将canvas设置为display: block而不是用overflow: hidden隐藏bug

完整的示例

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');


const observer = new ResizeObserver((entries) => {
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
});
observer.observe(canvas)


// not import but draw something just to showcase


const hsla = (h, s, l, a) => `hsla(${h * 360}, ${s * 100}%, ${l * 100}%, ${a})`;


function render(time) {
const {width, height} = canvas;


ctx.clearRect(0, 0, width, height);
ctx.save();
ctx.translate(width / 2, height / 2);
ctx.rotate(time * 0.0001);
  

const range = Math.max(width, height) * 0.8;
const size = 64 + Math.sin(time * 0.001) * 50;
for (let i = 0; i < range; i += size) {
ctx.fillStyle = hsla(i / range * 0.3 + time * 0.0001, 1, 0.5, 1);
ctx.fillRect( i, -range, size, range * 2);
ctx.fillRect(-i, -range, size, range * 2);
}
  

ctx.restore();


requestAnimationFrame(render)
}
requestAnimationFrame(render)
html, body {
height: 100%;
margin: 0;
}
canvas {
width: 100%;
height: 100%;
display: block;
}
<canvas></canvas>

Note: there are other issues related to resizing the canvas. Specifically if you want to deal with different devicePixelRatio settings. See this article for more.

一个最小的设置

超文本标记语言

<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);

的挑战

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显示。