[英]Improving performance of particles in canvas
我一直在嘗試用canvas和Javascript重新創建這個項目 。 我無法破譯原始代碼,所以我從頭開始。 不同的是,我的項目開始滯后於大約2500顆粒,而上面的項目工作時有30 000顆。
我將在下面粘貼我的整個代碼,但這些是相關部分:
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 )
}
我正在為我的方塊的每個'距離'像素創建一個新粒子,並將它們全部推入一個數組中。 當我的鼠標要接近其中一個時,粒子將開始遠離鼠標移動,直到距離鼠標90-95px。
從這條線看,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;
}
但該項目不會遇到與I相同的性能問題。
我的完整代碼供參考,(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()
學習使用開發工具中的“性能”選項卡,您可以看到哪些功能花費的時間最多。 在這種情況下,我認為你會看到它是ctx.fill
。 您發布的示例是將像素寫入ImageData
緩沖區,這將比繪制和填充弧更快。 在示例中還有許多其他小優化,但這將是最重要的一個,繪圖通常比更新慢得多。
要獲得更好的渲染效果,您需要將渲染對象添加到同一路徑。 創建路徑后,您可以在一次調用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()
//}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.