代码之家  ›  专栏  ›  技术社区  ›  cubefox

改善帆布中颗粒的性能

  •  4
  • cubefox  · 技术社区  · 6 年前

    我一直在尝试重建 this Project 在画布和JavaScript中。我无法破译原始代码,所以我从头开始。不同的是,我的项目开始滞后于大约2500个粒子,而上面的项目使用30000个粒子。

    我将把整个代码粘贴到下面,但这些是相关的部分:

    var particleContainer = []
    var distance = 10
    
    
    for(let i = 0; i< square.height/distance; i++){
        for(let j = 0; j< square.height/distance; j++){
        particleContainer.push( new Particle(square.x + i*distance,square.y + j*distance) )
    }  
    }
    
    if(  c < 90  ){
                i.xVelocity = a/c * -20
                i.yVelocity = b/c * -20
            }else if(90 < c && c < 95){
                i.xVelocity = a/c * -1
                i.yVelocity = b/c * -1
            }else if(c2 !== 0){
                i.xVelocity =( a2/c2 )
                i.yVelocity = (b2/c2 )
            }
    
    • (C->鼠标和粒子之间的距离)

    我正在创建一个新的粒子,每“距离”我的正方形像素,并将它们全部推到一个数组中。当我的鼠标靠近其中一个时,粒子将开始远离鼠标,直到它距离鼠标90-95px。

    从这条线来看,30000像素的工作方式类似

      for ( i = 0; i < NUM_PARTICLES; i++ ) {
    
        p = Object.create( particle );
        p.x = p.ox = MARGIN + SPACING * ( i % COLS );
        p.y = p.oy = MARGIN + SPACING * Math.floor( i / COLS );
    
        list[i] = p;
      }
    

    但该项目不会遇到与我相同的性能问题。

    我的完整代码供参考(HTML只是一个画布):

    var canvas = document.querySelector("canvas")
    var c = canvas.getContext('2d')
    
    
    
    
    function getMousePos(canvas, evt) {
        // var rect = canvas.getBoundingClientRect();
        return {
          x: evt.clientX,
          y: evt.clientY
        };
      }
    
      document.addEventListener('mousemove', function(evt) {
        var mousePos = getMousePos(canvas, evt);
        mouse.x= mousePos.x; 
        mouse.y= mousePos.y;
      }, false);
    
      var mouse = {
        x:0,
        y:0
      }
    
    function Particle(x,y){
        this.x = x;
        this.y = y;
        this.xFixed = x;
        this.yFixed = y;
        this.radius = 1
        this.xVelocity = 0
        this.yVelocity = 0
        this.color = 'white'
    }
    
    Particle.prototype.draw = function(){
        c.save()
        c.beginPath()
        c.arc(this.x, this.y, this.radius,0,Math.PI*2,false)
        c.fillStyle = this.color
        c.fill()
    }
    
    Particle.prototype.update = function(){
        this.draw()
        this.x += this.xVelocity
        this.y += this.yVelocity
    }
    
    var square = {
        x: 500,
        y: 150,
        height: 500,
        width: 500,
        color: 'white'
    }
    
    var particleContainer = []
    var distance = 10
    
    
    for(let i = 0; i< square.height/distance; i++){
        for(let j = 0; j< square.height/distance; j++){
        particleContainer.push( new Particle(square.x + i*distance,square.y + j*distance) )
    }
    
    }
    
    
    
    
    
    function animate(){
        requestAnimationFrame(animate);
        c.clearRect(0,0,window.innerWidth,window.innerHeight)
    
      canvas.width = window.innerWidth
    canvas.height = window.innerHeight
    
        for(i of particleContainer){
            let a = mouse.x - i.x
            let b = mouse.y - i.y
            let c = Math.sqrt(Math.pow(b,2) + Math.pow(a,2))
    
            let a2 = i.xFixed - i.x
            let b2 = i.yFixed - i.y
            let c2 = Math.sqrt(Math.pow(b2,2) + Math.pow(a2,2))
    
            if(  c < 90  ){
                i.xVelocity = a/c * -20
                i.yVelocity = b/c * -20
            }else if(90 < c && c < 95){
                i.xVelocity = a/c * -1
                i.yVelocity = b/c * -1
            }else if(c2 !== 0){
                i.xVelocity =( a2/c2 )
                i.yVelocity = (b2/c2 )
            }
    
    
        }
    
       for(i of particleContainer){
           i.update()
       }
    }
    
    animate()
    
    2 回复  |  直到 6 年前
        1
  •  1
  •   Blindman67    6 年前

    要获得更好的渲染效果,需要将渲染对象添加到同一路径。一旦创建了路径,您就可以在一次调用中 ctx.fill

    尝试限制访问 innerWidth innerHeight 因为它们是非常慢的DOM对象,仅通过访问它们就可能导致回流。

    可以通过使用对象池和预分配进行进一步的改进,但这超出了单个答案的范围。

    对动画功能进行以下更改。

    var W = 1, H = 1;
    function animate() {
        requestAnimationFrame(animate);
        c.clearRect(0 ,0, W, H)
        if (H !== innerHeight || W !== innerWidth) {
            W = canvas.width = innerWidth;
            H = canvas.height = innerHeight;
        }
        c.beginPath(); // start a new path
        c.fillStyle = "white";
        for (i of particleContainer) {  // update and draw all particles in one pass
            const a = mouse.x - i.x, b = mouse.y - i.y
            const c = (b * b + a * a) ** 0.5;
            const a2 = i.xFixed - i.x, b2 = i.yFixed - i.y
            const c2 = (b2 * b2 + a2 * a2) ** 0.5; 
            if(  c < 90  ){
                i.x += a/c * -20
                i.y += b/c * -20
            }else if(90 < c && c < 95){
                i.x += a/c * -1
                i.y += b/c * -1
            }else if(c2 !== 0){
                i.x +=( a2/c2 )
                i.y += (b2/c2 )
            }
            c.rect(i.x, i.y, 1,1);
        }
        c.fill();  // path complete render it.
       //for(i of particleContainer){  // no longer needed
       //    i.update()
       //}
    }
    
        2
  •  1
  •   Ben West    6 年前

    学习使用dev工具中的性能选项卡,您可以看到哪些函数花费的时间最多。在这种情况下,我认为您会看到它的 ctx.fill . 您发布的示例是将像素写入 ImageData 缓冲区将比绘制和填充弧快得多。在这个例子中还有很多其他的小优化,但最重要的是,绘图通常比更新慢得多。