优化画布
该 <canvas>
元素是 Web 上渲染 2D 图形的应用最广泛的工具之一。但是,当网站和应用程序将 Canvas API 推到其极限时,性能就会开始下降。本文提供了优化画布元素使用的建议,以确保图形性能良好。
性能技巧
以下是提高画布性能的一些技巧。
在离屏画布上预渲染类似的图元或重复对象
如果您发现自己在每个动画帧上重复一些相同的绘图操作,请考虑将它们卸载到离屏画布。然后,您可以根据需要将离屏图像渲染到主画布,而无需不必要地重复生成它的步骤。
myCanvas.offscreenCanvas = document.createElement("canvas");
myCanvas.offscreenCanvas.width = myCanvas.width;
myCanvas.offscreenCanvas.height = myCanvas.height;
myCanvas.getContext("2d").drawImage(myCanvas.offScreenCanvas, 0, 0);
避免使用浮点坐标,而使用整数
当您在画布上渲染没有整数的对象时,就会发生子像素渲染。
ctx.drawImage(myImage, 0.3, 0.5);
这会迫使浏览器进行额外的计算以创建抗锯齿效果。为了避免这种情况,请确保使用 Math.floor()
对 drawImage()
的所有坐标进行舍入,例如。
不要在 drawImage
中缩放图像
在加载时将图像的不同尺寸缓存到离屏画布上,而不是在 drawImage()
中不断缩放它们。
对复杂的场景使用多层画布
在您的应用程序中,您可能会发现某些对象需要频繁移动或更改,而其他对象则保持相对静态。在这种情况下,一种可能的优化方法是使用多个 <canvas>
元素分层您的项目。
例如,假设您有一个游戏,顶部有 UI,中间有游戏玩法动作,底部有静态背景。在这种情况下,您可以将游戏分成三个 <canvas>
层。UI 仅在用户输入时更改,游戏玩法层在每个新帧时更改,而背景通常保持不变。
<div id="stage">
<canvas id="ui-layer" width="480" height="320"></canvas>
<canvas id="game-layer" width="480" height="320"></canvas>
<canvas id="background-layer" width="480" height="320"></canvas>
</div>
<style>
#stage {
width: 480px;
height: 320px;
position: relative;
border: 2px solid black;
}
canvas {
position: absolute;
}
#ui-layer {
z-index: 3;
}
#game-layer {
z-index: 2;
}
#background-layer {
z-index: 1;
}
</style>
对大型背景图像使用纯 CSS
如果您有一个静态背景图像,您可以使用 CSS background
属性将其绘制到一个普通的 <div>
元素上,并将其放置在画布下方。这将无需在每次滴答时都将背景渲染到画布上。
使用 CSS 变换缩放画布
CSS 变换 更快,因为它们使用 GPU。最佳情况是不缩放画布,或者使用较小的画布并放大,而不是使用较大的画布并缩小。
const scaleX = window.innerWidth / canvas.width;
const scaleY = window.innerHeight / canvas.height;
const scaleToFit = Math.min(scaleX, scaleY);
const scaleToCover = Math.max(scaleX, scaleY);
stage.style.transformOrigin = "0 0"; //scale from top left
stage.style.transform = `scale(${scaleToFit})`;
关闭透明度
如果您的应用程序使用画布并且不需要透明背景,请在使用 HTMLCanvasElement.getContext()
创建绘图上下文时将 alpha
选项设置为 false
。浏览器可以使用此信息在内部优化渲染。
const ctx = canvas.getContext("2d", { alpha: false });
缩放以适应高分辨率显示器
您可能会发现画布项目在更高分辨率的显示器上看起来模糊。虽然可能存在许多解决方案,但一个简单的第一步是同时使用其属性、样式及其上下文的比例来放大和缩小画布大小。
// Get the DPR and size of the canvas
const dpr = window.devicePixelRatio;
const rect = canvas.getBoundingClientRect();
// Set the "actual" size of the canvas
canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
// Scale the context to ensure correct drawing operations
ctx.scale(dpr, dpr);
// Set the "drawn" size of the canvas
canvas.style.width = `${rect.width}px`;
canvas.style.height = `${rect.height}px`;
更多技巧
- 将画布调用一起批处理。例如,绘制一条多段线而不是多条单独的线。
- 避免不必要的画布状态更改。
- 仅渲染屏幕差异,而不是整个新状态。
- 尽可能避免使用
shadowBlur
属性。 - 尽可能避免 文本渲染。
- 尝试不同的方法来清除画布 (
clearRect()
与fillRect()
与调整画布大小)。 - 对于动画,使用
window.requestAnimationFrame()
而不是setInterval()
。 - 小心使用重量级的物理库。