简体   繁体   English

调整大小的 HTML 画布最佳实践

[英]HTML canvas best practices for resizing

Doing a simple 2d physics engine with HTML5 Canvas (collisions,graphing).使用 HTML5 Canvas(碰撞、绘图)做一个简单的 2d 物理引擎。 I want a full screen canvas with a header navbar.我想要一个带有标题导航栏的全屏画布。 How can I create this layout and handle resizing correctly?如何创建此布局并正确处理调整大小?

I have tried several solutions:我尝试了几种解决方案:

  1. One involved programatically resizing the canvas to fill its container onload() and onresize() .一种涉及以编程方式调整画布大小以填充其容器onload()onresize() Canvas contents stay the same.画布内容保持不变。
  2. Another involved a responsive canvas with percents whose contents shrunk as the canvas shrunk.另一个涉及带有百分比的响应式画布,其内容随着画布缩小而缩小。

Can anyone help lead us to the holy grail?谁能帮助我们引领我们走向圣杯? The most important question is your opinion about canvas resizing best practices (should we do it?).最重要的问题是您对调整画布大小最佳实践的看法(我们应该这样做吗?)。 If so, what about the debate between resizing the canvas pixels and media queries vs flex/percents vs javascript container measuring, etc.如果是这样,那么在调整画布像素大小和媒体查询、flex/percents 和 javascript 容器测量等之间的争论呢?


Example/Attempts:示例/尝试:

Example-1: Here is the Javascript code which I used in v1 of my mock up.示例 1:这是我在模型的 v1 中使用的 Javascript 代码。 The corresponding HTML was just a basic document with a header and a 100% container with the canvas inside the container and being set to fill the container.相应的 HTML 只是一个带有标题和 100% 容器的基本文档,容器内有画布,并被设置为填充容器。

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

Example-2: This CodePen is an example of the resizing canvas that I made.示例 2:此 CodePen 是我制作的调整大小画布的示例。 It still retreats up under the navbar during extreme resizing though.尽管如此,在极端调整大小期间,它仍然会在导航栏下方撤退。

Resizing调整大小

It will depend on how you are rendering.这将取决于您的渲染方式。

requestAnimationFrame is best practice. requestAnimationFrame是最佳实践。

Generally best practice to make any changes to the DOM is to use requestAnimationFrame to ensure that changes are presented in sync with the display hardware's refresh.通常,对 DOM 进行任何更改的最佳实践是使用requestAnimationFrame来确保更改与显示硬件的刷新同步呈现。 requestAnimationFrame also ensure that only when the page is visible will the changes be made. requestAnimationFrame还确保只有当页面可见时才会进行更改。 ie (if the client switches tabs to another tab, your tab will not fire any requestAnimationFrame events)即(如果客户端将选项卡切换到另一个选项卡,您的选项卡将不会触发任何requestAnimationFrame事件)

It is also best to keep the canvas resolution as low as possible.最好保持画布分辨率尽可能低。 Keeping the canvas at a resolution higher than the display means you will be doing a lot of needless rendering on portions that are off screen, or if you are scaling the canvas via CSS the down or upsampling can result in a variety of unwanted artifacts.将画布的分辨率保持在高于显示器的分辨率意味着您将对屏幕外的部分进行大量不必要的渲染,或者如果您通过 CSS 缩放画布,向下或向上采样可能会导致各种不需要的工件。

The problem with the resize event.调整大小事件的问题。

The resize event is triggered by a variety of sources, OS events that change the window size, mouse events, or from Javascript.调整大小事件由各种来源、更改窗口大小的操作系统事件、鼠标事件或来自 Javascript 触发。 None of these events are synced to the display, and some resize events can fire at very high rates (mouse driven resize can fire 100+ times a second)这些事件都不会同步到显示器,并且一些调整大小事件可以以非常高的速率触发(鼠标驱动的调整大小可以每秒触发 100 多次)

Because resizing the canvas also clears the image data and resets the context state, each resize requires a re-rendering of the content.因为调整画布大小也会清除图像数据并重置上下文状态,所以每次调整大小都需要重新渲染内容。 The rapid firing rate of the resize event can overwork the thread and you will start to lose events , the page will feel laggy and you can get parts of the page that are not updated in time for the next display frame.调整大小事件的快速触发率会使线程过度工作,您将开始丢失事件,页面会感觉滞后,并且您可能会获得未及时更新以用于下一个显示帧的页面部分。

When resizing you should try to avoid resizing when not needed.调整大小时,应尽量避免在不需要时调整大小。 Thus the best time to resize is via a requestAnimationFrame callback.因此调整大小的最佳时间是通过requestAnimationFrame回调。

Realtime rendering实时渲染

If you are rendering in realtime then the best way to resize is to compare the canvas size to the container's or window size at the start of every render frame.如果您正在实时渲染,那么调整大小的最佳方法是在每个渲染帧开始时将画布大小与容器或窗口大小进行比较。 If the sizes do not match then resize the 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);

Static renders静态渲染

If you are rendering infrequently or as needed.如果您不经常渲染或根据需要进行渲染。 Then you may be best off to keep a canvas at a standard resolution offscreen and use a resizable canvas on screen to render a view of the offscreen canvas.那么您最好将画布保持在屏幕外的标准分辨率,并在屏幕上使用可调整大小的画布来渲染屏幕外画布的视图。

In that case you keep a main loop alive that will check a semaphore that indicates that there is a need to update the view.在这种情况下,您保持主循环处于活动状态,它将检查指示需要更新视图的信号量。 Anything that changes the canvas will then just set the flag to true.任何更改画布的内容都会将标志设置为 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);

In your case using the above method (even for realtime) gives you better control over what part of the canvas content is seen.在您的情况下,使用上述方法(即使是实时的)可以让您更好地控制可以看到画布内容的哪一部分。

CSS CSS

Some poeple consider that CSS is the only place any type of visual content should be changed.有些人认为 CSS 是唯一应该改变任何类型的视觉内容的地方。 The problem with the canvas is that CSS can not set the canvas resolution so if you use CSS you still have to set the canvas resolution.画布的问题在于 CSS 无法设置画布分辨率,因此如果您使用 CSS,您仍然必须设置画布分辨率。

For the canvas I do not set any CSS sizes and let the canvas resolution properties set the display size canvas.width=1920; canvas.height=1080;对于画布,我没有设置任何 CSS 大小,而是让画布分辨率属性设置显示大小canvas.width=1920; canvas.height=1080; canvas.width=1920; canvas.height=1080; I can not see the point of having having to set the CSS width and height when there is no need to我看不出在不需要时必须设置 CSS 宽度和高度的意义

Note: If you do not use requestAnimationFrame you will need to set the CSS size as you can not guarantee the canvas resolution will be set in time for the next refresh, while auto CSS updates (eg canvas.style.width="100%" ) will change in sync with the display device.注意:如果您不使用requestAnimationFrame您将需要设置 CSS 大小,因为您无法保证在下一次刷新时及时设置画布分辨率,而自动 CSS 更新(例如canvas.style.width="100%" ) 将与显示设备同步变化。

It works, you could check it with element inspect.它有效,您可以使用元素检查来检查它。 And I was thinking you wanted change the style width/height of canvas, not the width or height of canvas, they are quite different.我在想你想改变画布的样式宽度/高度,而不是画布的宽度或高度,它们是完全不同的。 The width or height of canvas would just affect the ratio of things you draw on the canvas.画布的宽度或高度只会影响您在画布上绘制的内容的比例。 And the CSS style width or height could change the display size of the canvas.并且 CSS 样式的宽度或高度可以改变画布的显示大小。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM