[英]Canvas particles, collisions and performance
我正在創建一個Web應用程序,它具有交互式背景,粒子在彈跳。 在任何時候屏幕上都有大約200個圓形顆粒,最多約800個顆粒。 正在為粒子運行的一些碰撞和效果是以下原型。 我想知道我是否可以通過使用網絡工作人員來進行這些計算來提高性能?
/**
* Particles
*/
Jarvis.prototype.genForegroundParticles = function(options, count){
count = count || this.logoParticlesNum;
for (var i = 0; i < count; i++) {
this.logoParticles.push(new Particle());
}
}
Jarvis.prototype.genBackgroundParticles = function(options, count){
count = count || this.backgroundParticlesNum;
for (var i = 0; i < count; i++) {
this.backgroundParticles.push(new Particle(options));
}
}
Jarvis.prototype.motion = {
linear : function(particle, pIndex, particles){
particle.x += particle.vx
particle.y += particle.vy
},
normalizeVelocity : function(particle, pIndex, particles){
if (particle.vx - particle.vxInitial > 1) {
particle.vx -= 0.05;
} else if (particle.vx - particle.vxInitial < -1) {
particle.vx += 0.05;
}
if (particle.vy - particle.vyInitial > 1) {
particle.vy -= 0.05;
} else if (particle.vx - particle.vxInitial < -1) {
particle.vy += 0.05;
}
},
explode : function(particle, pIndex, particles) {
if (particle.isBottomOut()) {
particles.splice(pIndex, 1);
} else {
particle.x += particle.vx;
particle.y += particle.vy;
particle.vy += 0.1;
}
if (particles.length === 0){
particles.motion.removeMotion("explode");
this.allowMenu = true;
}
}
}
Jarvis.prototype.collision = {
boundingBox: function(particle, pIndex, particles){
if (particle.y > (this.HEIGHT - particle.radius) || particle.y < particle.radius) {
particle.vy *= -1;
}
if(particle.x > (this.WIDTH - particle.radius) || particle.x < particle.radius) {
particle.vx *= -1;
}
},
boundingBoxGravity: function(particle, pIndex, particles){
// TODO: FIX GRAVITY TO WORK PROPERLY IN COMBINATION WITH FX AND MOTION
if (particle.y > (this.HEIGHT - particle.radius) || particle.y < particle.radius) {
particle.vy *= -1;
particle.vy += 5;
}
if(particle.x > (this.WIDTH - particle.radius) || particle.x < particle.radius) {
particle.vx *= -1;
particle.vx += 5;
}
},
infinity: function(particle, pIndex, particles){
if (particle.x > this.WIDTH){
particle.x = 0;
}
if (particle.x < 0){
particle.x = this.WIDTH;
}
if (particle.y > this.HEIGHT){
particle.y = 0;
}
if (particle.y < 0) {
particle.y = this.HEIGHT;
}
}
}
Jarvis.prototype.fx = {
link : function(particle, pIndex, particles){
for(var j = pIndex + 1; j < particles.length; j++) {
var p1 = particle;
var p2 = particles[j];
var particleDistance = getDistance(p1, p2);
if (particleDistance <= this.particleMinLinkDistance) {
this.backgroundCtx.beginPath();
this.backgroundCtx.strokeStyle = "rgba("+p1.red+", "+p1.green+", "+p1.blue+","+ (p1.opacity - particleDistance / this.particleMinLinkDistance) +")";
this.backgroundCtx.moveTo(p1.x, p1.y);
this.backgroundCtx.lineTo(p2.x, p2.y);
this.backgroundCtx.stroke();
this.backgroundCtx.closePath();
}
}
},
shake : function(particle, pIndex, particles){
if (particle.xInitial - particle.x >= this.shakeAreaThreshold){
particle.xOper = (randBtwn(this.shakeFactorMin, this.shakeFactorMax) * 2) % (this.WIDTH);
} else if (particle.xInitial - particle.x <= -this.shakeAreaThreshold) {
particle.xOper = (randBtwn(-this.shakeFactorMax, this.shakeFactorMin) * 2) % (this.WIDTH);
}
if (particle.yInitial - particle.y >= this.shakeAreaThreshold){
particle.yOper = (randBtwn(this.shakeFactorMin, this.shakeFactorMax) * 2) % (this.HEIGHT);
} else if (particle.yInitial - particle.y <= -this.shakeAreaThreshold) {
particle.yOper = (randBtwn(-this.shakeFactorMax, this.shakeFactorMin) * 2) % (this.HEIGHT);
}
particle.x += particle.xOper;
particle.y += particle.yOper;
},
radialWave : function(particle, pIndex, particles){
var distance = getDistance(particle, this.center);
if (particle.radius >= (this.dim * 0.0085)) {
particle.radiusOper = -0.02;
} else if (particle.radius <= 1) {
particle.radiusOper = 0.02;
}
particle.radius += particle.radiusOper * particle.radius;
},
responsive : function(particle, pIndex, particles){
var newPosX = (this.logoParticles.logoOffsetX + this.logoParticles.particleRadius) + (this.logoParticles.particleDistance + this.logoParticles.particleRadius) * particle.arrPos.x;
var newPosY = (this.logoParticles.logoOffsetY + this.logoParticles.particleRadius) + (this.logoParticles.particleDistance + this.logoParticles.particleRadius) * particle.arrPos.y;
if (particle.xInitial !== newPosX || particle.yInitial !== newPosY){
particle.xInitial = newPosX;
particle.yInitial = newPosY;
particle.x = particle.xInitial;
particle.y = particle.yInitial;
}
},
motionDetect : function(particle, pIndex, particles){
var isClose = false;
var distance = null;
for (var i = 0; i < this.touches.length; i++) {
var t = this.touches[i];
var point = {
x : t.clientX,
y : t.clientY
}
var d = getDistance(point, particle);
if (d <= this.blackhole) {
isClose = true;
if (d <= distance || distance === null) {
distance = d;
}
}
}
if (isClose){
if (particle.radius < (this.dim * 0.0085)) {
particle.radius += 0.25;
}
if (particle.green >= 0 && particle.blue >= 0) {
particle.green -= 10;
particle.blue -= 10;
}
} else {
if (particle.radius > particle.initialRadius) {
particle.radius -= 0.25;
}
if (particle.green <= 255 && particle.blue <= 255) {
particle.green += 10;
particle.blue += 10;
}
}
},
reverseBlackhole : function(particle, pIndex, particles){
for (var i = 0; i < this.touches.length; i++) {
var t = this.touches[i];
var point = {
x : t.clientX,
y : t.clientY
}
var distance = getDistance(point, particle);
if (distance <= this.blackhole){
var diff = getPointsDifference(point, particle);
particle.vx += -diff.x / distance;
particle.vy += -diff.y / distance;
}
}
}
}
此外,萬一有人想知道我有3個畫布層,我將為所有畫布層添加粒子渲染功能和清晰功能
繪制全屏徑向漸變和粒子的背景
菜單畫布
菜單按鈕覆蓋選擇器(顯示哪個菜單處於活動狀態等)
Jarvis.prototype.backgroundDraw = function() {
// particles
var that = this;
this.logoParticles.forEach(function(particle, i){
particle.draw(that.backgroundCtx);
that.logoParticles.motion.forEach(function(motionType, motionIndex){
that.motion[motionType].call(that, particle, i, that.logoParticles, "foregroundParticles");
});
that.logoParticles.fx.forEach(function(fxType, fxIndex){
that.fx[fxType].call(that, particle, i, that.logoParticles, "foregroundParticles");
});
that.logoParticles.collision.forEach(function(collisionType, collisionIndex){
that.collision[collisionType].call(that, particle, i, that.logoParticles, "foregroundParticles");
});
});
this.backgroundParticles.forEach(function(particle, i){
particle.draw(that.backgroundCtx);
that.backgroundParticles.motion.forEach(function(motionType, motionIndex){
that.motion[motionType].call(that, particle, i, that.backgroundParticles, "backgroundParticles");
});
that.backgroundParticles.fx.forEach(function(fxType, fxIndex){
that.fx[fxType].call(that, particle, i, that.backgroundParticles, "backgroundParticles");
});
that.backgroundParticles.collision.forEach(function(collisionType, collisionIndex){
that.collision[collisionType].call(that, particle, i, that.backgroundParticles, "backgroundParticles");
});
});
}
Jarvis.prototype.clearCanvas = function() {
switch(this.background.type){
case "radial_gradient":
this.setBackgroundRadialGradient(this.background.color1, this.background.color2);
break;
case "plane_color":
this.setBackgroundColor(this.background.red, this.background.green, this.background.blue, this.background.opacity);
break;
default:
this.setBackgroundColor(142, 214, 255, 1);
}
this.foregroundCtx.clearRect(this.clearStartX, this.clearStartY, this.clearDistance, this.clearDistance);
this.middlegroundCtx.clearRect(this.clearStartX, this.clearStartY, this.clearDistance, this.clearDistance);
}
Jarvis.prototype.mainLoop = function() {
this.clearCanvas();
this.backgroundDraw();
this.drawMenu();
window.requestAnimFrame(this.mainLoop.bind(this));
}
任何其他優化技巧將不勝感激。 我已經閱讀了幾篇文章,但我不確定如何進一步優化此代碼。
您可以使用FabricJS Canvas Library。 FabricJS默認支持交互性,當您創建新對象(圓形,矩形等)時,您可以通過鼠標或觸摸屏對其進行操作。
var canvas = new fabric.Canvas('c');
var rect = new fabric.Rect({
width: 10, height: 20,
left: 100, top: 100,
fill: 'yellow',
angle: 30
});
canvas.add(rect);
看,我們以面向對象的方式在那里工作。
除了切換到使用硬件加速的技術之外,我不知道你可以做些什么重大改進。
我希望這會有所幫助,但正如問題所述,WebGL會更快。 如果你不知道從哪里開始,這里是一個很好的: webglacademy
我還是看到了一些小東西:
radialWave : function(particle, pIndex, particles){
// As you don't use distance here remove this line
// it's a really greedy calculus that involves square root
// always avoid if you don't have to use it
// var distance = getDistance(particle, this.center);
if (particle.radius >= (this.dim * 0.0085)) {
particle.radiusOper = -0.02;
} else if (particle.radius <= 1) {
particle.radiusOper = 0.02;
}
particle.radius += particle.radiusOper * particle.radius;
},
另一件小事:
Jarvis.prototype.backgroundDraw = function() {
// particles
var that = this;
// Declare callbacks outside of forEach calls
// it will save you a function declaration each time you loop
// Do this for logo particles
var logoMotionCallback = function(motionType, motionIndex){
// Another improvement may be to use a direct function that does not use 'this'
// and instead pass this with a parameter called currentParticle for example
// call and apply are known to be pretty heavy -> see if you can avoid this
that.motion[motionType].call(that, particle, i, that.logoParticles, "foregroundParticles");
};
var logoFxCallback = function(fxType, fxIndex){
that.fx[fxType].call(that, particle, i, that.logoParticles, "foregroundParticles");
};
var logoCollisionCallback = function(collisionType, collisionIndex){
that.collision[collisionType].call(that, particle, i, that.logoParticles, "foregroundParticles");
};
this.logoParticles.forEach(function(particle, i){
particle.draw(that.backgroundCtx);
that.logoParticles.motion.forEach(motionCallback);
that.logoParticles.fx.forEach(fxCallback);
that.logoParticles.collision.forEach(collisionCallback);
});
// Now do the same for background particles
var bgMotionCallback = function(motionType, motionIndex){
that.motion[motionType].call(that, particle, i, that.backgroundParticles, "backgroundParticles");
};
var bgFxCallback = function(fxType, fxIndex){
that.fx[fxType].call(that, particle, i, that.backgroundParticles, "backgroundParticles");
};
var bgCollisionCallback = function(collisionType, collisionIndex){
that.collision[collisionType].call(that, particle, i, that.backgroundParticles, "backgroundParticles");
};
this.backgroundParticles.forEach(function(particle, i){
particle.draw(that.backgroundCtx);
that.backgroundParticles.motion.forEach(bgMotionCallback);
that.backgroundParticles.fx.forEach(bgFxCallback);
that.backgroundParticles.collision.forEach(bgCollisionCallback);
});
}
如果您希望加速代碼,這里有一些微優化:
for(var i = 0, l = bla.length; i < l; i++) { ... }
而不是bla.forEach(...)
radius <= distance
慢, radius*radius <= distanceSquared
很快。 reverseBlackhole : function(particle, pIndex, particles) { var blackholeSqr = this.blackhole * this.blackhole, touches = this.touches, fnSqrt = Math.sqrt, t, diffX, diffY, dstSqr; for (var i = 0, l = touches.length; i < l; i++) { t = touches[i]; diffX = particle.x - t.clientX; diffY = particle.y - t.clientY; distSqr = (diffX * diffX + diffY * diffY); // comparing distance without a SQRT needed if (dstSqr <= blackholeSqr){ var dist = Math.sqrt(dstSqr); particle.vx -= diffX / dist; particle.vy -= diffY / dist; } } }
為了加快繪圖速度(或在繪圖過程中減少繪圖):
而對於整個動畫:
this.backgroundParticles.forEach(..)
:在200個粒子的情況下,這樣做
this.backgroundParticles.forEach(
)
that.backgroundParticles.motion.forEach
) that.backgroundParticles.fx.forEach
) that.backgroundParticles.collision.forEach
) this.foregroundparticles.forEach(..)
為了獲得更高的性能,刪除OOP的東西,並只在有意義的地方尋找丑陋的意大利面條代碼。
可以通過不對每個粒子彼此進行測試來優化碰撞檢測。 只需查看四叉樹。 並不難實現,並且可以使用它的基礎來提出自定義解決方案。
由於你正在做一些矢量數學,試試glmatrix庫 。 優化的矢量數學:-)
我想您可能會發現webworker支持與WebGL支持相同:
WebGL支持: http : //caniuse.com/#search=webgl
WebWorker支持: http : //caniuse.com/#search=webworker
從表面上看,它們看起來可能不同,但實際上並非如此。 你唯一能獲得的是暫時的IE10支持。 IE11的市場份額已經超過IE10,並且這一鴻溝將繼續增長。 唯一需要注意的是webgl支持似乎也基於更新的顯卡驅動程序。
當然,我不知道你的具體需求,所以這可能不起作用。
許多圖書館都這樣做。 畫布應該可以使用,只是有點酷。 WebGL通常具有所有很酷的粒子特征。
您可能需要使用延遲庫或創建一個系統來確定所有Web工作者何時完成並擁有工作線程池。
一些警告:
未經證實的謠言:我聽說您可以通過網絡工作人員消息傳遞的數據量有限。 您應該對此進行測試,因為它似乎直接適用於您的用例。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.