用 HTML5 Canvas 做一个简单的 2d 物理引擎(碰撞,绘图)。我想要一个带有标题导航栏的全屏 Canvas 。如何创建此布局并正确处理调整大小?
我尝试了几种解决方案:
- 一个涉及以编程方式调整 Canvas 大小以填充其容器 onload() 和 onresize()。 Canvas 内容保持不变。
- 另一个涉及带有百分比的响应式 Canvas ,其内容随着 Canvas 缩小而缩小。
谁能帮助我们走向 chalice ?最重要的问题是您对调整 Canvas 大小最佳实践的看法(我们应该这样做吗?)。如果是这样,关于调整 Canvas 像素大小和媒体查询与 flex/percents 与 javascript 容器测量等之间的争论呢?
示例/尝试:
示例 1: 这是我在模型的 v1 中使用的 Javascript 代码。相应的 HTML 只是一个带有标题和 100% 容器的基本文档,容器内有 Canvas 并被设置为填充容器。
window.onload = function(){
init();
};
window.addEventListener("resize", init);
function init(){
console.log("init");
initCanvas();
drawCircle();
}
function initCanvas() {
var content = document.querySelector(".content");
var canvas = document.querySelector(".myCanvas");
canvas.width = content.clientWidth;
canvas.height = content.clientHeight;
}
示例 2: 这个 CodePen 是我制作的调整大小 Canvas 的一个例子。不过,在极端调整大小时,它仍然会退回到导航栏下方。
最佳答案
调整大小
这将取决于您的渲染方式。
requestAnimationFrame
是最佳实践。
通常,对 DOM 进行任何更改的最佳做法是使用 requestAnimationFrame
来确保更改与显示硬件的刷新同步呈现。 requestAnimationFrame
还确保只有当页面可见时才会进行更改。即(如果客户端将选项卡切换到另一个选项卡,您的选项卡将不会触发任何 requestAnimationFrame
事件)
最好将 Canvas 分辨率保持得尽可能低。将 Canvas 保持在高于显示器的分辨率意味着您将在屏幕外的部分进行大量不必要的渲染,或者如果您通过 CSS 缩放 Canvas ,则向下采样或向上采样可能会导致各种不需要的伪像。
调整大小事件的问题。
resize 事件由多种来源、更改窗口大小的操作系统事件、鼠标事件或来自 Javascript 触发。这些事件都没有同步到显示,并且一些调整大小事件可以以非常高的速率触发(鼠标驱动的调整大小每秒可以触发 100 多次)
因为调整 Canvas 大小也会清除图像数据并重置上下文状态,所以每次调整大小都需要重新渲染内容。调整大小事件的快速触发率可能会使线程重载工作,您将开始丢失事件,页面会感觉滞后,并且您可以获取未及时为下一个显示帧更新的页面部分。
调整大小时,您应该尽量避免在不需要时调整大小时。因此,调整大小的最佳时间是通过 requestAnimationFrame
回调。
实时渲染
如果您正在实时渲染,那么调整大小的最佳方法是在每个渲染帧开始时将 Canvas 大小与容器或窗口大小进行比较。如果大小不匹配,则调整 Canvas 大小。
// no need for a resize event listener.
function renderLoop(){
// innerWidth / height or containor size
if(canvas.width !== innerWidth || canvas.height !== innerHeight){
canvas.width = innerWidth;
canvas.height = innerHeight;
}
// your code
requestAnimationFrame(renderLoop);
}
requestAnimationFrame(renderLoop);
静态渲染
如果您不经常渲染或根据需要渲染。那么您可能最好将 Canvas 保持在屏幕外的标准分辨率,并在屏幕上使用可调整大小的 Canvas 来呈现屏幕外 Canvas 的 View 。
在这种情况下,您将保持主循环处于事件状态,该循环将检查指示需要更新 View 的信号量。任何改变 Canvas 的东西都会将标志设置为 true。
const mainCanvas = document.createElement("canvas");
const mCtx ...
const canvas = document.getElementById("displayCanvas");
const ctx ...
// when updating content
function renderContent(){
mCtx.drawStuff...
...
updateView = true; // flag the change
}
// the resize event only need flag that there is a change
window.addEventListener("resize",()=> updateView = true );
var updateView = true;
function renderLoop(){
if(updateView){
updateView = false; // clear the flag
// is there a need to change display canvas resolution.
if(canvas.width !== innerWidth || canvas.height !== innerHeight){
canvas.width = innerWidth;
canvas.height = innerHeight;
}
// draw the mainCanvas onto the display canvas.
ctx.drawImage(mainCanvas, viewOrigin.x, viewOrigin.y);
}
requestAnimationFrame(renderLoop);
}
requestAnimationFrame(renderLoop);
在您的情况下,使用上述方法(即使是实时的)可以让您更好地控制看到 Canvas 内容的哪一部分。
CSS
有些人认为 CSS 是唯一应该更改任何类型的视觉内容的地方。 Canvas 的问题是 CSS 无法设置 Canvas 分辨率,因此如果您使用 CSS,您仍然必须设置 Canvas 分辨率。
对于 Canvas ,我没有设置任何 CSS 大小,而是让 Canvas 分辨率属性设置显示大小 canvas.width=1920; canvas.height=1080;
我看不出在不需要时必须设置 CSS 宽度和高度的意义
注意:如果您不使用 requestAnimationFrame
,您将需要设置 CSS 大小,因为您不能保证 Canvas 分辨率会在下一次刷新时及时设置,而自动 CSS 更新(例如canvas.style.width="100%"
) 将与显示设备同步变化。
关于调整大小的 HTML Canvas 最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46637707/