【问题标题】:HTML canvas best practices for resizing调整大小的 HTML 画布最佳实践
【发布时间】:2020-03-08 13:06:20
【问题描述】:

用 HTML5 Canvas 做一个简单的 2D 物理引擎(碰撞、绘图)。我想要一个带有标题导航栏的全屏画布。如何创建此布局并正确处理大小调整?

我尝试了几种解决方案:

  1. 其中一个涉及以编程方式调整画布大小以填充其容器 onload() 和 onresize()。画布内容保持不变。
  2. 另一个涉及带有百分比的响应式画布,其内容随着画布的缩小而缩小。

谁能帮助我们到达圣杯?最重要的问题是您对调整画布大小最佳实践的看法(我们应该这样做吗?)。如果是这样,那么调整画布像素大小和媒体查询与 flex/percents 与 javascript 容器测量等之间的争论呢?


示例/尝试:

示例 1: 这是我在我的模型 v1 中使用的 Javascript 代码。相应的 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; 
}

示例 2: 这个 CodePen 是我制作的调整画布大小的示例。但在极端调整大小时,它仍然会在导航栏下方缩回。

【问题讨论】:

    标签: html css layout html5-canvas


    【解决方案1】:

    调整大小

    这将取决于您的渲染方式。

    requestAnimationFrame 是最佳做法。

    通常,对 DOM 进行任何更改的最佳做法是使用 requestAnimationFrame 以确保更改与显示硬件的刷新同步呈现。 requestAnimationFrame 还确保只有当页面可见时才会进行更改。即(如果客户端将选项卡切换到另一个选项卡,您的选项卡将不会触发任何requestAnimationFrame 事件)

    最好将画布分辨率保持在尽可能低的水平。将画布保持在高于显示的分辨率意味着您将在屏幕外的部分进行大量不必要的渲染,或者如果您通过 CSS 缩放画布,则向下或向上采样可能会导致各种不需要的伪影。

    resize 事件的问题。

    调整大小事件由多种来源、改变窗口大小的操作系统事件、鼠标事件或 JavaScript 触发。这些事件都不会同步到显示器,并且某些调整大小事件可以以非常高的速率触发(鼠标驱动的调整大小可以每秒触发 100 多次)

    因为调整画布大小也会清除图像数据并重置上下文状态,因此每次调整大小都需要重新渲染内容。 resize 事件的快速触发率会使线程过度工作,您将开始丢失事件,页面会感觉滞后,并且您可以获取页面的某些部分没有及时更新到下一个显示帧。

    在调整大小时,您应该尽量避免在不需要时调整大小。因此,调整大小的最佳时间是通过requestAnimationFrame 回调。

    实时渲染

    如果您是实时渲染,那么调整大小的最佳方法是在每个渲染帧开始时将画布大小与容器或窗口大小进行比较。如果大小不匹配,则调整画布大小。

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

    静态渲染

    如果您不经常渲染或根据需要渲染。那么您最好将画布保持在屏幕外的标准分辨率,并在屏幕上使用可调整大小的画布来呈现屏幕外画布的视图。

    在这种情况下,您保持一个主循环处于活动状态,它将检查指示需要更新视图的信号量。任何改变画布的东西都会将标志设置为 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);
    

    在您的情况下,使用上述方法(即使是实时方法)可以让您更好地控制看到的画布内容的哪一部分。

    CSS

    有些人认为 CSS 是唯一应该更改任何类型的视觉内容的地方。画布的问题是CSS不能设置画布分辨率,所以如果你使用CSS,你仍然需要设置画布分辨率。

    对于画布我没有设置任何 CSS 大小,而是让画布分辨率属性设置显示大小canvas.width=1920; canvas.height=1080; 我看不出在不需要时必须设置 CSS 宽度和高度的意义

    注意:如果您不使用requestAnimationFrame,您将需要设置CSS 大小,因为您不能保证画布分辨率会在下次刷新时及时设置,而自动CSS 更新(例如canvas.style.width="100%")会与显示设备同步变化。

    【讨论】:

    • 好帖子,你错过了提到另一个黑色野兽组合resize+orientationchange,...每个浏览器都以不同的方式解释...
    【解决方案2】:

    它有效,您可以使用元素检查来检查它。 我在想你想改变画布的样式宽度/高度,而不是画布的宽度或高度,它们是完全不同的。 画布的宽度或高度只会影响你在画布上绘制的东西的比例。 CSS 样式的宽度或高度可以改变画布的显示大小。

    【讨论】:

      猜你喜欢
      • 2017-05-06
      • 2016-01-02
      • 2012-05-16
      • 1970-01-01
      • 1970-01-01
      • 2015-11-29
      • 1970-01-01
      • 1970-01-01
      • 2021-11-27
      相关资源
      最近更新 更多