E
E
Electro-max2021-12-16 19:41:05
JavaScript
Electro-max, 2021-12-16 19:41:05

Switching between Canvas layers causes a freeze. What makes the computer so busy?

I'm making a game and decided to place independent locations on Canvas layers. Turning on one layer turns off the previous one. So I expected to minimize the load on the computer (the code serving only one page always works), but in practice, when moving from one location to another, it starts to slow down and eventually the code freezes (if you click back and forth). For readability, I simplified everything and posted an example without animation and pictures, but with a long switch, even it freezes, and when the page is refreshed, everything starts working well (instantly). To create layers, I use the CanvasStack-2v01.js code.
Where are resources spent and how to apply layers correctly?
HTML Code

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="../Game/CSS/style.css">
    <title>Document game</title>
</head>
<body>
<canvas id="canvas" width = 400 height = 200></canvas>
    <script src="../Game/JS/CanvasStack-2v01.js"></script>
    <script src="../Game/JS/layerOne.js"></script>
    <script src="../Game/JS/layerTwo.js"></script>
</body>
</html>

File with the first layer LayerOne.js
let launchLayerOne = function() {
let canvas_stack = new CanvasStack('canvas');
    const layer1 = canvas_stack.createLayer();
    const layer1_canvas = document.getElementById(layer1);
    const layer1_ctx = layer1_canvas.getContext('2d');
    layer1_canvas.style.background = 'black';

    game();
    
    function game() {
    render();
    requestAnimationFrame(game);
    };

    function render() {
    layer1_ctx.rect(50, 50, 100, 100);
    layer1_ctx.fillStyle = 'rgba(247, 7, 7, 0.8)';
    layer1_ctx.fill();
    };

    layer1_canvas.onmousedown = function(e) {
    let rect = this.getBoundingClientRect();
    x = e.clientX - rect.left;
    y = e.clientY - rect.top;
    let detect
    layer1_ctx.isPointInPath(x, y) ? detect = 1 : detect = 0;
    if(detect === 1) {
        launchLayerTwo();
        canvas_stack.deleteLayer(layer1);
    }
    };
};
launchLayerOne()

File with the second layer LayerTwo.js
let launchLayerTwo = function() {
    let canvas_stack = new CanvasStack('canvas');
        const layer2 = canvas_stack.createLayer();
        const layer2_canvas = document.getElementById(layer2);
        const layer2_ctx = layer2_canvas.getContext('2d');
        layer2_canvas.style.background = 'grey';
        game();
        
        function game() {
        render();
        requestAnimationFrame(game);
        };
    
        function render() {
        layer2_ctx.rect(250, 50, 100, 100);
        layer2_ctx.fillStyle = 'rgba(13, 171, 10, 0.8)';
        layer2_ctx.fill();
        };
    
        layer2_canvas.onmousedown = function(e) {
        let rect = this.getBoundingClientRect();
        x = e.clientX - rect.left;
        y = e.clientY - rect.top;
        let detect
        layer2_ctx.isPointInPath(x, y) ? detect = 1 : detect = 0;
        if(detect === 1) {
            launchLayerOne();
            canvas_stack.deleteLayer(layer2);
        }
        };
    };

File with code for creating layers CanvasStack-2v01.js
var CanvasStack;

(function()
{
"use strict";

class Layer
{
    constructor(canvasID, canvasElement)
    {
    this.id = canvasID;
    this.cElem = canvasElement;
    this.dragObjects = [];
    }
}

CanvasStack = class{
    constructor(cvsID, stackLimit){
    const savThis = this;

    function setResizeHandler(resizeLayers, timeout){
        let timer_id = undefined;
        window.addEventListener("resize", ()=>{
        if(timer_id != undefined) 
        {
            clearTimeout(timer_id);
            timer_id = undefined;
        }
        timer_id = setTimeout(()=>{
            timer_id = undefined;
            resizeLayers();
            savThis.bkgCanvas.resizeFns.forEach((currFn)=>currFn());
            }, timeout);
        });
    }
            
    function resizeLayers(){
        const t = savThis.bkgCanvas.offsetTop + savThis.bkgCanvas.clientTop,
            l = savThis.bkgCanvas.offsetLeft + savThis.bkgCanvas.clientLeft,
            w = savThis.bkgCanvas.offsetWidth,
            h = savThis.bkgCanvas.offsetHeight;

        // check if canvas size changed when window resized, allow some rounding error in layout calcs
        if ((Math.abs(w - savThis.rawWidth)/w < 0.01) && (Math.abs(h - savThis.rawHeight)/h < 0.01))
        {
          // canvas size didn't change so return
        return;
        }
        // canvas has been resized so resize all the overlay canvases
        for (let j=1; j<savThis.bkgCanvas.layers.length; j++)  // bkg is layer[0]
        {
        let ovl = savThis.bkgCanvas.layers[j].cElem;
          if (ovl)  // may have been deleted so empty slot
        {
            ovl.style.top = t+'px';
            ovl.style.left = l+'px';
            ovl.style.width = w+'px';
            ovl.style.height = h+'px';
            ovl.width = w;    // reset canvas attribute to pixel width
            ovl.height = h;  
        }
    }
    }

      // check if this is a context for an overlay
    if (cvsID.indexOf("_ovl_") !== -1)
    {
        console.error("CanvasStack: canvas must be a background canvas not an overlay");
        return {};
    }
    
    this.cId = cvsID;
    this.stackLimit = stackLimit || 6;
    this.bkgCanvas = document.getElementById(cvsID);
    this.rawWidth = this.bkgCanvas.offsetWidth;   
    this.rawHeight = this.bkgCanvas.offsetHeight;
    this.bkgCanvas.resizeFns = [];

    if (!this.bkgCanvas.hasOwnProperty('layers'))
    {
        // create an array to hold all the overlay canvases for this canvas
        this.bkgCanvas.layers = [];
        // make a Layer object for the bkgCanvas
        let bkgL = new Layer(this.cId, this.bkgCanvas);   // bkgCanvas is always layer[0]
        this.bkgCanvas.layers[0] = bkgL;
        // make sure the overlay canvases always match the bkgCanvas size
        setResizeHandler(resizeLayers, 250);
    }
    if (!this.bkgCanvas.hasOwnProperty('unique'))
    {
        this.bkgCanvas.unique = 0;
    }
    }

    createLayer(){
    const w = this.rawWidth,
            h = this.rawHeight,
            nLyrs = this.bkgCanvas.layers.length;  // bkg is layer 0 so at least 1

      // check background canvas is still there
    if (!(this.bkgCanvas && this.bkgCanvas.layers))
    {
        console.log("CanvasStack: missing background canvas");
        return;
    } 
    if (this.bkgCanvas.layers.length >= this.stackLimit)
    {
        console.error("CanvasStack: too many layers");
        return;
    }
      this.bkgCanvas.unique += 1;     // a private static variable
    const uniqueTag = this.bkgCanvas.unique.toString();
    const ovlId = this.cId+"_ovl_"+uniqueTag;
    const ovlHTML = "<canvas id='"+ovlId+"' style='position:absolute' width='"+w+"' height='"+h+"'></canvas>";
    const topCvs = this.bkgCanvas.layers[nLyrs-1].cElem; 
    topCvs.insertAdjacentHTML('afterend', ovlHTML);
    const newCvs = document.getElementById(ovlId);
    newCvs.style.backgroundColor = "transparent";
    newCvs.style.left = (this.bkgCanvas.offsetLeft+this.bkgCanvas.clientLeft)+'px';
    newCvs.style.top = (this.bkgCanvas.offsetTop+this.bkgCanvas.clientTop)+'px';
      // make it the same size as the background canvas
    newCvs.style.width = this.bkgCanvas.offsetWidth+'px';
    newCvs.style.height = this.bkgCanvas.offsetHeight+'px';
    let newL = new Layer(ovlId, newCvs);
      // save the ID in an array to facilitate removal
    this.bkgCanvas.layers.push(newL);
    
      return ovlId;    // return the new canvas id 
    }

    deleteLayer(ovlyId){
      // check background canvas is still there
    if (!(this.bkgCanvas && this.bkgCanvas.layers))
    {
        console.log("CanvasStack: missing background canvas");
        return;
    } 
    for (let i=1; i<this.bkgCanvas.layers.length; i++)
    {
        if (this.bkgCanvas.layers[i].id === ovlyId)
        {
        let ovlNode = this.bkgCanvas.layers[i].cElem;
        if (ovlNode)
        {
            ovlNode.parentNode.removeChild(ovlNode);
        }
          // now delete layers array element
          this.bkgCanvas.layers.splice(i,1);   // delete the Layer object
        }
    }
    }

    deleteAllLayers(){
      // check background canvas is still there
    if (!(this.bkgCanvas && this.bkgCanvas.layers))
    {
        console.log("CanvasStack: missing background canvas");
        return;
    } 
      for (let i=this.bkgCanvas.layers.length-1; i>0; i--)   // don't delete layers[0] its the bakg canavs
    {
        let ovlNode = this.bkgCanvas.layers[i].cElem;
        if (ovlNode)
        {
        let orphan = ovlNode.parentNode.removeChild(ovlNode);
        orphan = null;
        }
        // now delete layers array element
        this.bkgCanvas.layers.splice(i,1);
    }
      // clear any resize callbacks, the layers are gone
      this.bkgCanvas.resizeFns.length = 0;   // remove any added handlers, leave the basic
    }

    addResizeCallback(callbackFn){
    this.bkgCanvas.resizeFns.push(callbackFn);
    }
};
}());

Answer the question

In order to leave comments, you need to log in

1 answer(s)
S
SagePtr, 2021-12-17
@SagePtr

What does the browser profiler say?

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question