Answer the question
In order to leave comments, you need to log in
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>
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()
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);
}
};
};
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
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question