简体   繁体   English

提高画布中颗粒的性能

[英]Improving performance of particles in canvas

I've been trying to recreate this Project in canvas and Javascript. 我一直在尝试用canvas和Javascript重新创建这个项目 I wasn't able to decipher the original code so I did it from scratch. 我无法破译原始代码,所以我从头开始。 The difference is that my projects starts to lag at about 2500 particles while the project above works with 30 000. 不同的是,我的项目开始滞后于大约2500颗粒,而上面的项目工作时有30 000颗。

I'll paste my entire code below but these are the relevant parts: 我将在下面粘贴我的整个代码,但这些是相关部分:

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 -> distance between mouse and particle) (c - >鼠标与粒子之间的距离)

I'm creating a new Particle every 'distance' pixels of my square and pushing all of them into an array. 我正在为我的方块的每个'距离'像素创建一个新粒子,并将它们全部推入一个数组中。 when My mouse is to close to one of them the particle will start moving away from the mouse until it is 90-95px away from the Mouse. 当我的鼠标要接近其中一个时,粒子将开始远离鼠标移动,直到距离鼠标90-95px。

30 000 pixels seems to work in a similar fashion judging from this line 从这条线看,30 000像素似乎以类似的方式工作

  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;
  }

but that project doesn't run into the same case of performance Issues as I. 但该项目不会遇到与I相同的性能问题。

my full code for reference, (html is just a canvas): 我的完整代码供参考,(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()

Learn to use the Performance tab in dev tools and you can see which functions are taking the most time. 学习使用开发工具中的“性能”选项卡,您可以看到哪些功能花费的时间最多。 In this case I think you'll see that it's ctx.fill . 在这种情况下,我认为你会看到它是ctx.fill The example you posted is writing pixels into an ImageData buffer which will be much faster than drawing and filling arcs. 您发布的示例是将像素写入ImageData缓冲区,这将比绘制和填充弧更快。 There are a lot of other small optimisations in the example but that's going to be the most important one, drawing is usually much slower than updating. 在示例中还有许多其他小优化,但这将是最重要的一个,绘图通常比更新慢得多。

To get better rendering you need to add render objects to the same path. 要获得更好的渲染效果,您需要将渲染对象添加到同一路径。 Once the path is created then you can draw them in one call to ctx.fill 创建路径后,您可以在一次调用ctx.fill绘制它们

Try to limit accessing innerWidth and innerHeight as they are very slow DOM objects that may cause reflows just by accessing them. 尝试限制访问innerWidthinnerHeight因为它们是非常慢的DOM对象,只有通过访问它们才可能导致回流。

Further improvements can be made by using object pools and preallocation but that is beyond the scope of a single answer. 通过使用对象池和预分配可以进一步改进,但这超出了单个答案的范围。

Make the following changes to your animate function. 对您的动画功能进行以下更改。

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()
   //}
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM