【问题标题】:HTML Canvas performance on resize调整大小时的 HTML Canvas 性能
【发布时间】:2018-03-08 18:47:11
【问题描述】:

我设法将一个旧的 OO 处理草图翻译成 HTML5 Canvas: https://gist.github.com/sindiploma/ce1bb3b8424c32fb8d9e094f8944a789

我添加了mr.Doob的Stats library来记录画布表现。

它在文档加载时按预期执行,但在尝试调整窗口大小时。帧率急剧下降。

关于如何提高 resize 事件的性能的任何提示?

您也可以查看此现场演示:

(function(f,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"===typeof define&&define.amd?define(e):f.Stats=e()})(this,function(){var f=function(){function e(a){c.appendChild(a.dom);return a}function u(a){for(var d=0;d<c.children.length;d++)c.children[d].style.display=d===a?"block":"none";l=a}var l=0,c=document.createElement("div");c.style.cssText="position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:10000";c.addEventListener("click",function(a){a.preventDefault();
u(++l%c.children.length)},!1);var k=(performance||Date).now(),g=k,a=0,r=e(new f.Panel("FPS","#0ff","#002")),h=e(new f.Panel("MS","#0f0","#020"));if(self.performance&&self.performance.memory)var t=e(new f.Panel("MB","#f08","#201"));u(0);return{REVISION:16,dom:c,addPanel:e,showPanel:u,begin:function(){k=(performance||Date).now()},end:function(){a++;var c=(performance||Date).now();h.update(c-k,200);if(c>g+1E3&&(r.update(1E3*a/(c-g),100),g=c,a=0,t)){var d=performance.memory;t.update(d.usedJSHeapSize/
1048576,d.jsHeapSizeLimit/1048576)}return c},update:function(){k=this.end()},domElement:c,setMode:u}};f.Panel=function(e,f,l){var c=Infinity,k=0,g=Math.round,a=g(window.devicePixelRatio||1),r=80*a,h=48*a,t=3*a,v=2*a,d=3*a,m=15*a,n=74*a,p=30*a,q=document.createElement("canvas");q.width=r;q.height=h;q.style.cssText="width:80px;height:48px";var b=q.getContext("2d");b.font="bold "+9*a+"px Helvetica,Arial,sans-serif";b.textBaseline="top";b.fillStyle=l;b.fillRect(0,0,r,h);b.fillStyle=f;b.fillText(e,t,v);
b.fillRect(d,m,n,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d,m,n,p);return{dom:q,update:function(h,w){c=Math.min(c,h);k=Math.max(k,h);b.fillStyle=l;b.globalAlpha=1;b.fillRect(0,0,r,m);b.fillStyle=f;b.fillText(g(h)+" "+e+" ("+g(c)+"-"+g(k)+")",t,v);b.drawImage(q,d+a,m,n-a,p,d,m,n-a,p);b.fillRect(d+n-a,m,a,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d+n-a,m,a,g((1-h/w)*p))}}};return f});

var canvas = document.getElementById("inner_heading-canvas");

if (canvas.getContext) {
  var context = canvas.getContext("2d");
}

window.addEventListener("load", init, false);
window.addEventListener(
  "resize",
  function() {
    clearTimeout(init);
    setTimeout(init, 500);
  },
  false
);

// Init
function init() {
  var net = undefined;

  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;

  var nodesLength = Math.floor(canvas.width * canvas.height / 2000);

  // Nodes
  net = new Net();
  net.populate(nodesLength);

  window.requestAnimationFrame(render);

  function render() {
    net.update();
    net.draw();
    net.connect(50);
    window.requestAnimationFrame(render);
  }
}

// Net
class Net {
  constructor() {
    this.nodes = [];
    this.length = undefined;
  }

  populate(length) {
    this.length = length;

    for (var i = 0; i < length; i++) {
      var xPos = Math.floor(getRandom(0, canvas.width));
      var yPos = Math.floor(getRandom(0, canvas.height));
      this.nodes.push(new Node(xPos, yPos));
    }
  }

  update() {
    for (var i = 0; i < this.length; i++) {
      this.nodes[i].update();
    }
  }

  draw() {
    context.fillStyle = "#000000";
    context.fillRect(0, 0, canvas.width, canvas.height);

    for (var i = 0; i < this.length; i++) {
      this.nodes[i].draw();
    }
  }

  connect(distanceMax) {
    for (var i = 0; i < this.length - 1; i++) {
      this.nodes[i].connections = [];

      for (var j = 0; j < this.length - 1; j++) {
        var a = this.nodes[j].x - this.nodes[i].x;
        var b = this.nodes[j].y - this.nodes[i].y;
        var c = Math.sqrt(a * a + b * b);

        if (c < distanceMax) {
          this.nodes[i].connections.push(j);
        }
      }

      for (var k = 0; k < this.nodes[i].connections.length; k++) {
        context.beginPath();
        context.moveTo(this.nodes[i].x, this.nodes[i].y);
        context.lineTo(
          this.nodes[this.nodes[i].connections[k]].x,
          this.nodes[this.nodes[i].connections[k]].y
        );
        context.strokeStyle = "rgba(255,255,255,.15)";
        context.stroke();
      }
    }
  }
}

// Node
class Node {
  constructor(_x, _y) {
    this.x = _x;
    this.y = _y;
    this.radius = 2;
    this.depth = Math.floor(getRandom(1, 10)) / 10;
  }

  update() {
    var velocity = (1 - this.depth) / 10;
    this.x = this.x + velocity;

    if (this.x > canvas.width || this.x < 0) {
      this.x = 0;
    }
  }

  draw() {
    var alpha = 1 - this.depth;
    context.beginPath();
    context.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
    context.fillStyle = "rgba(255,255,255," + alpha + ")";
    context.fill();
  }
}

// Helpers
function getRandom(min, max) {
  return Math.random() * (max - min) + min;
}

// Stats
var stats = new Stats();
stats.showPanel(0);
document.body.appendChild(stats.dom);

function animate() {
  stats.begin();
  stats.end();

  requestAnimationFrame(animate);
}

requestAnimationFrame(animate);
body {
  margin: 0;
}
<html>
    <head></head>
    <body>
        <canvas id="inner_heading-canvas" width="600" height="600"></canvas>
    </body>
</html>

【问题讨论】:

    标签: html performance canvas window-resize frame-rate


    【解决方案1】:

    清除 requestAnimationFrame 最重要的提升:

    var rafID = null; //scoping requestAnimationFrame request-ID
    //...
    //inside resize-callback
    window.cancelAnimationFrame(rafID);
    ...
    //inside init() + inside render()
    rafID = window.requestAnimationFrame(render); 
    

    通过这种方式,我们确保浏览器不会仍在尝试更新旧实例。我将它添加到下面的代码 sn-p 中,我还确保超时有效(对于debouncing)。

    (function(f,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"===typeof define&&define.amd?define(e):f.Stats=e()})(this,function(){var f=function(){function e(a){c.appendChild(a.dom);return a}function u(a){for(var d=0;d<c.children.length;d++)c.children[d].style.display=d===a?"block":"none";l=a}var l=0,c=document.createElement("div");c.style.cssText="position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:10000";c.addEventListener("click",function(a){a.preventDefault();
    u(++l%c.children.length)},!1);var k=(performance||Date).now(),g=k,a=0,r=e(new f.Panel("FPS","#0ff","#002")),h=e(new f.Panel("MS","#0f0","#020"));if(self.performance&&self.performance.memory)var t=e(new f.Panel("MB","#f08","#201"));u(0);return{REVISION:16,dom:c,addPanel:e,showPanel:u,begin:function(){k=(performance||Date).now()},end:function(){a++;var c=(performance||Date).now();h.update(c-k,200);if(c>g+1E3&&(r.update(1E3*a/(c-g),100),g=c,a=0,t)){var d=performance.memory;t.update(d.usedJSHeapSize/
    1048576,d.jsHeapSizeLimit/1048576)}return c},update:function(){k=this.end()},domElement:c,setMode:u}};f.Panel=function(e,f,l){var c=Infinity,k=0,g=Math.round,a=g(window.devicePixelRatio||1),r=80*a,h=48*a,t=3*a,v=2*a,d=3*a,m=15*a,n=74*a,p=30*a,q=document.createElement("canvas");q.width=r;q.height=h;q.style.cssText="width:80px;height:48px";var b=q.getContext("2d");b.font="bold "+9*a+"px Helvetica,Arial,sans-serif";b.textBaseline="top";b.fillStyle=l;b.fillRect(0,0,r,h);b.fillStyle=f;b.fillText(e,t,v);
    b.fillRect(d,m,n,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d,m,n,p);return{dom:q,update:function(h,w){c=Math.min(c,h);k=Math.max(k,h);b.fillStyle=l;b.globalAlpha=1;b.fillRect(0,0,r,m);b.fillStyle=f;b.fillText(g(h)+" "+e+" ("+g(c)+"-"+g(k)+")",t,v);b.drawImage(q,d+a,m,n-a,p,d,m,n-a,p);b.fillRect(d+n-a,m,a,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d+n-a,m,a,g((1-h/w)*p))}}};return f});
    
    var canvas = document.getElementById("inner_heading-canvas");
    
    var timeoutID = null; 
    var rafID = null;
    
    if (canvas.getContext) {
      var context = canvas.getContext("2d");
    }
    
    window.addEventListener("load", init, false);
    window.addEventListener(
      "resize",
      function() {
        clearTimeout(timeoutID);
        window.cancelAnimationFrame(rafID);
        timeoutID = setTimeout(init, 500);
      },
      false
    );
    
    // Init
    function init() {
      var net = undefined;
    
      canvas.width = window.innerWidth;
      canvas.height = window.innerHeight;
    
      var nodesLength = Math.floor(canvas.width * canvas.height / 2000);
    
      // Nodes
      net = new Net();
      net.populate(nodesLength);
    
      rafID = window.requestAnimationFrame(render);
    
      function render() {
        net.update();
        net.draw();
        net.connect(50);
        rafID = window.requestAnimationFrame(render);
      }
    }
    
    // Net
    class Net {
      constructor() {
        this.nodes = [];
        this.length = undefined;
      }
    
      populate(length) {
        this.length = length;
    
        for (var i = 0; i < length; i++) {
          var xPos = Math.floor(getRandom(0, canvas.width));
          var yPos = Math.floor(getRandom(0, canvas.height));
          this.nodes.push(new Node(xPos, yPos));
        }
      }
    
      update() {
        for (var i = 0; i < this.length; i++) {
          this.nodes[i].update();
        }
      }
    
      draw() {
        context.fillStyle = "#000000";
        context.fillRect(0, 0, canvas.width, canvas.height);
    
        for (var i = 0; i < this.length; i++) {
          this.nodes[i].draw();
        }
      }
    
      connect(distanceMax) {
        for (var i = 0; i < this.length - 1; i++) {
          this.nodes[i].connections = [];
    
          for (var j = 0; j < this.length - 1; j++) {
            var a = this.nodes[j].x - this.nodes[i].x;
            var b = this.nodes[j].y - this.nodes[i].y;
            var c = Math.sqrt(a * a + b * b);
    
            if (c < distanceMax) {
              this.nodes[i].connections.push(j);
            }
          }
    
          for (var k = 0; k < this.nodes[i].connections.length; k++) {
            context.beginPath();
            context.moveTo(this.nodes[i].x, this.nodes[i].y);
            context.lineTo(
              this.nodes[this.nodes[i].connections[k]].x,
              this.nodes[this.nodes[i].connections[k]].y
            );
            context.strokeStyle = "rgba(255,255,255,.15)";
            context.stroke();
          }
        }
      }
    }
    
    // Node
    class Node {
      constructor(_x, _y) {
        this.x = _x;
        this.y = _y;
        this.radius = 2;
        this.depth = Math.floor(getRandom(1, 10)) / 10;
      }
    
      update() {
        var velocity = (1 - this.depth) / 10;
        this.x = this.x + velocity;
    
        if (this.x > canvas.width || this.x < 0) {
          this.x = 0;
        }
      }
    
      draw() {
        var alpha = 1 - this.depth;
        context.beginPath();
        context.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
        context.fillStyle = "rgba(255,255,255," + alpha + ")";
        context.fill();
      }
    }
    
    // Helpers
    function getRandom(min, max) {
      return Math.random() * (max - min) + min;
    }
    
    // Stats
    var stats = new Stats();
    stats.showPanel(0);
    document.body.appendChild(stats.dom);
    
    function animate() {
      stats.begin();
      stats.end();
    
      requestAnimationFrame(animate);
    }
    
    requestAnimationFrame(animate);
    body {
      margin: 0;
    }
    <html>
        <head></head>
        <body>
            <canvas id="inner_heading-canvas" width="600" height="600"></canvas>
        </body>
    </html>

    除了这个快速修复之外,还有许多其他通用方法可以更好地调整大小。通过在 stackoverflow 上搜索可以学到一些技巧:

    【讨论】:

      猜你喜欢
      • 2021-01-16
      • 1970-01-01
      • 1970-01-01
      • 2017-06-25
      • 2011-01-23
      • 2012-04-23
      • 2023-03-22
      • 2014-05-29
      • 1970-01-01
      相关资源
      最近更新 更多