Холст с использованием высокой загрузки процессора в Chrome - PullRequest
0 голосов
/ 07 февраля 2019

Я использую демонстрацию Codepen, но после проверки использования ЦП в Chrome, он использует около 100% ЦП.После усердных попыток я не могу понять проблему, так как я не являюсь экспертом в javascript и canvas.Какие модификации мне нужно, чтобы он использовал меньше ресурсов процессора. Codepen Link Насколько я понимаю, проблема в анимировании частиц или, возможно, я ошибаюсь.

// Global Animation Setting
window.requestAnimFrame = 
  window.requestAnimationFrame ||
  window.webkitRequestAnimationFrame ||
  window.mozRequestAnimationFrame ||
  window.oRequestAnimationFrame ||
  window.msRequestAnimationFrame ||
  function(callback) {
    window.setTimeout(callback, 1000/60);
};

// Global Canvas Setting
var canvas = document.getElementById('particle');
var ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;


// Particles Around the Parent
function Particle(x, y, distance) {
  this.angle = Math.random() * 2 * Math.PI;
  this.radius = Math.random() ; 
  this.opacity =  (Math.random()*5 + 2)/10;
  this.distance = (1/this.opacity)*distance;
  this.speed = this.distance*0.00003;
  
  this.position = {
    x: x + this.distance * Math.cos(this.angle),
    y: y + this.distance * Math.sin(this.angle)
  };
  
  this.draw = function() {
    ctx.fillStyle = "rgba(255,255,255," + this.opacity + ")";
    ctx.beginPath();
    ctx.arc(this.position.x, this.position.y, this.radius, 0, Math.PI*2, false);
    ctx.fill();
    ctx.closePath();
  }
  this.update = function() {
    this.angle += this.speed; 
    this.position = {
      x: x + this.distance * Math.cos(this.angle),
      y: y + this.distance * Math.sin(this.angle)
    };
    this.draw();
  }
}

function Emitter(x, y) {
  this.position = { x: x, y: y};
  this.radius = 30;
  this.count = 3000;
  this.particles = [];
  
  for(var i=0; i< this.count; i ++ ){
    this.particles.push(new Particle(this.position.x, this.position.y, this.radius));
  }
}


Emitter.prototype = {
  draw: function() {
    ctx.fillStyle = "rgba(0,0,0,1)";
    ctx.beginPath();
    ctx.arc(this.position.x, this.position.y, this.radius, 0, Math.PI*2, false);
    ctx.fill();
    ctx.closePath();    
  },
  update: function() {  
   for(var i=0; i< this.count; i++) {
     this.particles[i].update();
   }
    this.draw(); 
  }
}


var emitter = new Emitter(canvas.width/2, canvas.height/2);

function loop() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  emitter.update();
  requestAnimFrame(loop);
}

loop();
body{background:#000;}
 <canvas id="particle"></canvas>

enter image description here

1 Ответ

0 голосов
/ 08 февраля 2019

Избегайте, насколько это возможно, полупрозрачности.

Рисование с помощью альфа-канала убивает процессор, избегайте максимально возможного смешивания с использованием сплошных цветов:

// Global Animation Setting
window.requestAnimFrame = 
  window.requestAnimationFrame ||
  window.webkitRequestAnimationFrame ||
  window.mozRequestAnimationFrame ||
  window.oRequestAnimationFrame ||
  window.msRequestAnimationFrame ||
  function(callback) {
    window.setTimeout(callback, 1000/60);
};

// Global Canvas Setting
var canvas = document.getElementById('particle');
var ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;


// Particles Around the Parent
function Particle(x, y, distance) {
  this.angle = Math.random() * 2 * Math.PI;
  this.radius = Math.random() ; 
  this.opacity =  (Math.random()*5 + 2)/10;
  // convert to solid color '#nnnnnn'
  this.color = '#' + Math.floor((this.opacity * 255)).toString(16).padStart(2, 0).repeat(3);
  this.distance = (1/this.opacity)*distance;
  this.speed = this.distance*0.00003;
  
  this.position = {
    x: x + this.distance * Math.cos(this.angle),
    y: y + this.distance * Math.sin(this.angle)
  };
  
  this.draw = function() {
    ctx.fillStyle = this.color;
    ctx.beginPath();
    ctx.arc(this.position.x, this.position.y, this.radius, 0, Math.PI*2, false);
    ctx.fill();
    ctx.closePath();
  }
  this.update = function() {
    this.angle += this.speed; 
    this.position = {
      x: x + this.distance * Math.cos(this.angle),
      y: y + this.distance * Math.sin(this.angle)
    };
    this.draw();
  }
}

function Emitter(x, y) {
  this.position = { x: x, y: y};
  this.radius = 30;
  this.count = 3000;
  this.particles = [];
  
  for(var i=0; i< this.count; i ++ ){
    this.particles.push(new Particle(this.position.x, this.position.y, this.radius));
  }
}


Emitter.prototype = {
  draw: function() {
    ctx.fillStyle = "rgba(0,0,0,1)";
    ctx.beginPath();
    ctx.arc(this.position.x, this.position.y, this.radius, 0, Math.PI*2, false);
    ctx.fill();
    ctx.closePath();    
  },
  update: function() {  
   for(var i=0; i< this.count; i++) {
     this.particles[i].update();
   }
    this.draw(); 
  }
}


var emitter = new Emitter(canvas.width/2, canvas.height/2);

function loop() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  emitter.update();
  requestAnimFrame(loop);
}

loop();
body{background:#000;}
<canvas id="particle"></canvas>

Но этого по-прежнему недостаточно,

Избегайте рисовать как можно больше.

Операции рисования выполняютсяочень медленно на холсте (по сравнению с неокрашенными) и его следует избегать как можно больше.Чтобы сделать это, вы можете отсортировать частицы по цвету и нарисовать их по стеку отдельных объектов Path, но для этого необходимо немного округлить значение opacity (это делается, когда затвердевает цвет).

// Global Animation Setting
window.requestAnimFrame = 
  window.requestAnimationFrame ||
  window.webkitRequestAnimationFrame ||
  window.mozRequestAnimationFrame ||
  window.oRequestAnimationFrame ||
  window.msRequestAnimationFrame ||
  function(callback) {
    window.setTimeout(callback, 1000/60);
};

// Global Canvas Setting
var canvas = document.getElementById('particle');
var ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;


// Particles Around the Parent
function Particle(x, y, distance) {
  this.angle = Math.random() * 2 * Math.PI;
  this.radius = Math.random() ; 
  this.opacity =  (Math.random()*5 + 2)/10;
  // convert to solid color '#nnnnnn'
  this.color = '#' + Math.floor((this.opacity * 255)).toString(16).padStart(2, 0).repeat(3);
  this.distance = (1/this.opacity)*distance;
  this.speed = this.distance*0.00003;
  
  this.position = {
    x: x + this.distance * Math.cos(this.angle),
    y: y + this.distance * Math.sin(this.angle)
  };
  
  this.draw = function() {
    // here we remove everything but the 'arc' operation and a moveTo
    // no paint
    ctx.moveTo(this.position.x + this.radius, this.position.y);
    ctx.arc(this.position.x, this.position.y, this.radius, 0, Math.PI*2, false);
  }
  this.update = function() {
    this.angle += this.speed; 
    this.position = {
      x: x + this.distance * Math.cos(this.angle),
      y: y + this.distance * Math.sin(this.angle)
    };
    // 'update' should not 'draw'
//    this.draw();
  }
}

function Emitter(x, y) {
  this.position = { x: x, y: y};
  this.radius = 30;
  this.count = 3000;
  this.particles = [];
  
  for(var i=0; i< this.count; i ++ ){
    this.particles.push(new Particle(this.position.x, this.position.y, this.radius));
  }
  // sort our particles by color (opacity = color)
  this.particles.sort(function(a, b) {
    return a.opacity - b.opacity;
  });
}


Emitter.prototype = {
  draw: function() {
    ctx.fillStyle = "rgba(0,0,0,1)";
    ctx.beginPath();
    ctx.arc(this.position.x, this.position.y, this.radius, 0, Math.PI*2, false);
    ctx.fill();
    // draw our particles in batches
    var particle, color;
    ctx.beginPath();
    for(var i=0; i<this.count; i++) {
      particle = this.particles[i];
      if(color !== particle.color) {
        ctx.fill();
        ctx.beginPath();
        ctx.fillStyle = color = particle.color;
      }
      particle.draw();
    }
    ctx.fill(); // fill the last batch
    
    
  },
  update: function() {  
   for(var i=0; i< this.count; i++) {
     this.particles[i].update();
   }
    this.draw(); 
  }
}


var emitter = new Emitter(canvas.width/2, canvas.height/2);

function loop() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  emitter.update();
  requestAnimFrame(loop);
}

loop();
body{background:#000;}
<canvas id="particle"></canvas>

Это лучше, но еще не идеально ...

Наконец, будьте умны в отношении ВАШЕЙ анимации.

В вашей анимации непрозрачность определяет расстояние.То есть частицы, находящиеся дальше от центра, являются наиболее прозрачными.Это точно определяет, что такое радиальный градиент .

Таким образом, мы можем сократить количество операций рисования до двух.Да, только две краски для 3000 частиц, используя радиальный градиент и немного компоновки, мы можем сначала нарисовать все частиц в одном кадре, а затем применить градиент в качестве маски, которая будет применять егоцвет только там, где уже было что то нарисовано.Мы даже можем сохранить прозрачность.

// Global Animation Setting
window.requestAnimFrame = 
  window.requestAnimationFrame ||
  window.webkitRequestAnimationFrame ||
  window.mozRequestAnimationFrame ||
  window.oRequestAnimationFrame ||
  window.msRequestAnimationFrame ||
  function(callback) {
    window.setTimeout(callback, 1000/60);
};

// Global Canvas Setting
var canvas = document.getElementById('particle');
var ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;


// Particles Around the Parent
function Particle(x, y, distance) {
  this.angle = Math.random() * 2 * Math.PI;
  this.radius = Math.random() ; 
  this.opacity =  (Math.random()*5 + 2)/10;
  this.distance = (1/this.opacity)*distance;
  this.speed = this.distance*0.00003;
  
  this.position = {
    x: x + this.distance * Math.cos(this.angle),
    y: y + this.distance * Math.sin(this.angle)
  };
  
  this.draw = function() {
    // still no paint here
    ctx.moveTo(this.position.x + this.radius, this.position.y);
    ctx.arc(this.position.x, this.position.y, this.radius, 0, Math.PI*2, false);
  }
  this.update = function() {
    this.angle += this.speed; 
    this.position = {
      x: x + this.distance * Math.cos(this.angle),
      y: y + this.distance * Math.sin(this.angle)
    };
    this.draw();
  }
}

function Emitter(x, y) {
  this.position = { x: x, y: y};
  this.radius = 30;
  this.count = 3000;
  this.particles = [];
  
  for(var i=0; i< this.count; i ++ ){
    this.particles.push(new Particle(this.position.x, this.position.y, this.radius));
  }
  // a radial gradient that we will use as mask
  // in particle.constructor
  // opacities go from 0.2 to 0.7
  // with a distance range of [radius, 1 / 0.2 * this.radius]
  this.grad = ctx.createRadialGradient(x, y, this.radius, x, y, 1 / 0.2 * this.radius);
  this.grad.addColorStop(0, 'rgba(255,255,255,0.7)');
  this.grad.addColorStop(1, 'rgba(255,255,255,0.2)');
}


Emitter.prototype = {
  draw: function() {
    ctx.fillStyle = "rgba(0,0,0,1)";
    ctx.beginPath();
    ctx.arc(this.position.x, this.position.y, this.radius, 0, Math.PI*2, false);
    ctx.fill();
    ctx.closePath();    
  },
  update: function() {
   ctx.beginPath(); // one Path
   ctx.fillStyle = 'black'; // a solid color
   for(var i=0; i< this.count; i++) {
     this.particles[i].update();
   }
   ctx.fill(); // one paint
   // prepare the composite operation
   ctx.globalCompositeOperation = 'source-in';
   ctx.fillStyle = this.grad; // our gradient
   ctx.fillRect(0,0,canvas.width, canvas.height); // cover the whole canvas
   // reset for next paints (center arc and next frame's clearRect)
   ctx.globalCompositeOperation = 'source-over';
   this.draw(); 
  }
}

var emitter = new Emitter(canvas.width/2, canvas.height/2);

function loop() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  emitter.update();
  requestAnimFrame(loop);
}

loop();
body{background:#000;}
<canvas id="particle"></canvas>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...