简体   繁体   中英

HTML canvas best practices for resizing

Doing a simple 2d physics engine with HTML5 Canvas (collisions,graphing). 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() . 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.


Example/Attempts:

Example-1: Here is the Javascript code which I used in v1 of my mock up. 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.

 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. It still retreats up under the navbar during extreme resizing though.

Resizing

It will depend on how you are rendering.

requestAnimationFrame is best practice.

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. requestAnimationFrame also ensure that only when the page is visible will the changes be made. ie (if the client switches tabs to another tab, your tab will not fire any requestAnimationFrame events)

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.

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

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.

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.

 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

Some poeple consider that CSS is the only place any type of visual content should be changed. 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.

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

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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