@Electro-max

Переключение между слоями Canvas приводит к зависанию. Что так нагружает компьютер?

Делаю игру и решил на слоях Canvas размещать независимые локации. При включении одного слоя отключается предыдущий. Так я рассчитывал минимизировать нагрузку на компьютер (всегда работает код обслуживающий только одну страницу), но на практике переходя с одной локации на другую начинает притормаживать и в итоге зависает код (если щелкать туда обратно). Для читабельности упростил все и выложил пример без анимации и картинок, но при долгом переключении даже он зависает, а при обнавлении страници все начинает работать хорошо (моментально). Для создания слоев использую код CanvasStack-2v01.js
Куда тратятся ресурсы и как правильно применять слои?
Код HTML
<!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>

Файл с первым слоем 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()

Файл со вторым слоем 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);
        }
        };
    };

Файл с кодом для создания слоев 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);
    }
};
}());
  • Вопрос задан
  • 130 просмотров
Пригласить эксперта
Ответы на вопрос 1
SagePtr
@SagePtr
Еда - это святое
А профайлер в браузере что говорит?
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы