Search code examples
javascripthtml5-canvasresponsiveness

How to stop the flickering scrollbars and still get max screen sized canvas


I'm trying to get my canvas element to take up the whole width/height of the document body, and have it resize when the body size changes (such as a cell phone going from portrait to landscape).

The canvas resizes as expected, but for some reason causes the scroll bars to "flicker" off and on.

let dimensions = {
  w: document.body.clientWidth,
  h: document.body.clientHeight,
};

const log = console.log.bind(console);
const $ = document.querySelector.bind(document);
let canvas;
let ctx;
let screenSizeObserver;
let initialized = false;

function initialize() {
  if (document.visibilityState === "visible" && initialized === false) {
    initialized = true;

    let c = document.createElement("canvas");
    c.id = "canvas";
    document.body.append(c);

    canvas = $("#canvas");
    ctx = canvas.getContext("2d");

    dimensions.w = document.body.clientWidth;
    dimensions.h = document.body.clientHeight;
    canvas.width = dimensions.w;
    canvas.height = dimensions.h;
  }
}

document.addEventListener("DOMContentLoaded", () => {
  document.addEventListener("visibilitychange", initialize);
  initialize();
});

screenSizeObserver = new ResizeObserver(() => {
  //log(document.body.clientWidth);

  dimensions.w = document.body.clientWidth;
  dimensions.h = document.body.clientHeight;
  canvas.width = dimensions.w;
  canvas.height = dimensions.h;
});

screenSizeObserver.observe(document.body);
html,
body {
    margin: 0;
    padding: 0;
    width: 100%;
    height: 100%;
    box-sizing: border-box;
    background:
        linear-gradient(180deg,
            rgba(0, 191, 254, 1) 10%,
            rgba(252, 252, 252, 1) 100%);
}

canvas {
    box-sizing: border-box;
    position: relative;
    padding: 0;
    margin: 0;
    background: rgba(0, 0, 0, .5);
}


Solution

  • Looks like changing the canvas to a Block level element instead of an Inline element will fix the flickering issue. It allows exact setting of the canvas element width/height to the body element width/height, removing the scrollbars entirely and still covering the entire viewable area.

    let dimensions = {
      w: document.body.clientWidth,
      h: document.body.clientHeight,
    };
    
    const log = console.log.bind(console);
    const $ = document.querySelector.bind(document);
    let canvas;
    let ctx;
    let screenSizeObserver;
    let initialized = false;
    
    function initialize() {
      if (document.visibilityState === "visible" && initialized === false) {
        initialized = true;
    
        let c = document.createElement("canvas");
        c.id = "fui_canvas";
        c.style.display = "block";
        // css style display property MUST be set to "block" or it will cause an annoying flickering of the scroll bars off and on;
        // also, the width/height will be 17px over/under, and not the maximum exact pixel size of the body;
        document.body.append(c);
    
        canvas = $("#fui_canvas");
        ctx = canvas.getContext("2d");
    
        dimensions.w = canvas.parentElement.clientWidth;
        dimensions.h = canvas.parentElement.clientHeight;
        canvas.width = dimensions.w;
        canvas.height = dimensions.h;
    
        screenSizeObserver = new ResizeObserver(() => {
          dimensions.w = canvas.parentElement.clientWidth;
          dimensions.h = canvas.parentElement.clientHeight;
          canvas.width = dimensions.w;
          canvas.height = dimensions.h;
        });
    
        screenSizeObserver.observe(document.body);
      }
    }
    
    document.addEventListener("DOMContentLoaded", () => {
      document.addEventListener("visibilitychange", initialize);
      initialize();
    });
    html,
    body {
        margin: 0;
        padding: 0px;
        width: 100%;
        height: 100%;
        box-sizing: border-box;
        background:
            linear-gradient(180deg,
                rgba(0, 191, 254, 1) 10%,
                rgba(252, 252, 252, 1) 100%);
    }
    
    canvas {
        background: rgba(0, 0, 0, .25);
    }