在下面的代码中,我点击块时开始/停止粒子火焰。它工作正常,但是,我需要有大约10-20个火焰能够单独启动/停止并跟踪它们。一位朋友建议我将每个火焰放在一个小画布上,然后单独绘制每个画布,我认为这将是一个渲染矫枉过正,同时运行10-20 draw()。我该怎么办呢?
有一点需要注意的是,火焰的位置是在粒子()中给出的,然后会产生一个粒子阵列,它基本上代表火焰。
// init canvas
var canvas = $('canvas'),
ctx = canvas[0].getContext('2d') // world
,
ctx2 = canvas[1].getContext('2d') // fog
,
context = canvas[2].getContext('2d') // flame
,
mDown = false,
r1 = 100,
r2 = 300,
density = .4,
hideOnMove = true,
hideFill = 'rgba( 0, 0, 0, 1 )'
,
overlay = 'rgba( 0, 0, 0, 1 )',
particles = [],
particle_count = 100;
// init flame
for (var i = 0; i < particle_count; i++) {
particles.push(new particle());
}
if (!hideOnMove) {
// shouldn't be done like this, but this is a demo
canvas.get(1).remove();
}
// black out the canvas
ctx.fillStyle = overlay;
ctx.fillRect(0, 0, 1280, 800);
// set up our "eraser"
ctx.globalCompositeOperation = 'destination-out';
canvas.last()
.on('mousemove', function (ev, ev2) {
ev2 && (ev = ev2);
var pX = ev.pageX,
pY = ev.pageY;
// reveal wherever we drag
var radGrd = ctx.createRadialGradient(pX, pY, r1, pX, pY, r2);
radGrd.addColorStop(0, 'rgba( 0, 0, 0, 1 )');
radGrd.addColorStop(density, 'rgba( 0, 0, 0, .1 )');
radGrd.addColorStop(1, 'rgba( 0, 0, 0, 0 )');
ctx.fillStyle = radGrd;
ctx.fillRect(pX - r2, pY - r2, r2 * 2, r2 * 2);
// partially hide the entire map and re-reval where we are now
ctx2.globalCompositeOperation = 'source-over';
ctx2.clearRect(0, 0, 1280, 800);
ctx2.fillStyle = hideFill;
ctx2.fillRect(0, 0, 1280, 800);
var radGrd = ctx.createRadialGradient(pX, pY, r1, pX, pY, r2);
radGrd.addColorStop(0, 'rgba( 0, 0, 0, 1 )');
radGrd.addColorStop(.8, 'rgba( 0, 0, 0, .1 )');
radGrd.addColorStop(1, 'rgba( 0, 0, 0, 0 )');
ctx2.globalCompositeOperation = 'destination-out';
ctx2.fillStyle = radGrd;
ctx2.fillRect(pX - r2, pY - r2, r2 * 2, r2 * 2);
})
.trigger('mousemove', {
pageX: 150,
pageY: 150
});
function drawing() {
// clear canvas
context.clearRect(0, 0, 1280, 800);
context.globalCompositeOperation = "lighter";
for (var i = 0; i < particles.length; i++) {
var p = particles[i];
context.beginPath();
//changing opacity according to the life.
//opacity goes to 0 at the end of life of a particle
p.opacity = Math.round(p.remaining_life / p.life * 100) / 100
//a gradient instead of white fill
var gradient = context.createRadialGradient(p.location.x, p.location.y, 0, p.location.x, p.location.y, p.radius);
gradient.addColorStop(0, "rgba(" + p.r + ", " + p.g + ", " + p.b + ", " + p.opacity + ")");
gradient.addColorStop(0.5, "rgba(" + p.r + ", " + p.g + ", " + p.b + ", " + p.opacity + ")");
gradient.addColorStop(1, "rgba(" + p.r + ", " + p.g + ", " + p.b + ", 0)");
context.fillStyle = gradient;
context.arc(p.location.x, p.location.y, p.radius, Math.PI * 2, false);
context.fill();
//lets move the particles
p.remaining_life--;
p.radius--;
p.location.x += p.speed.x;
p.location.y += p.speed.y;
//regenerate particles
if (p.remaining_life < 0 || p.radius < 0) {
//a brand new particle replacing the dead one
particles[i] = new particle();
}
}
}
// set flame on/off
var myVar = 0;
var on = 0;
$('.c').css({
left: "610px",
top: "500px"
});
$('.c').click(function () {
if (on == 0) {
myVar = setInterval(drawing, 33);
on = 1;
} else {
clearInterval(myVar);
context.clearRect(0, 0, 1280, 800);
on = 0;
}
});
function particle() {
//speed, life, location, life, colors
//speed.x range = -2.5 to 2.5
//speed.y range = -15 to -5 to make it move upwards
//lets change the Y speed to make it look like a flame
this.speed = {
x: -2.5 + Math.random() * 5,
y: -15 + Math.random() * 10
};
//flame location
this.location = {
x: 640,
y: 520
};
//radius range = 10-30
this.radius = 10 + Math.random() * 20;
//life range = 20-30
this.life = 20 + Math.random() * 10;
this.remaining_life = this.life;
//colors
this.r = Math.round(Math.random() * 255);
this.g = Math.round(Math.random() * 255);
this.b = Math.round(Math.random() * 255);
}
请在此处查看完整网页http://codepen.io/anon/pen/hxrat
答案 0 :(得分:0)
如果您在一堆小画布中预先生成粒子,则可能会提高性能。基本上生成具有不同颜色和大小的粒子图像列表,然后将其用于所有粒子。绘制粒子图像时仍可应用不透明度。在具有给定不透明度的给定位置绘制小画布应该更快,而不是绘制具有径向渐变的路径。
以下是一个示例,似乎至少使性能翻倍:http://codepen.io/anon/pen/Izqwu
我不预先生成粒子画布。相反,我编写了一个函数getParticleCanvas()
,它采用一种颜色并返回一个32 * 32像素的画布(如果它不存在则创建一次),然后在drawing()中使用。然后以正确的大小和不透明度绘制粒子画布。为了表现,该位置四舍五入到最近的像素。
此外,为了减少可能的粒子画布数量,每个通道的随机颜色四舍五入到8个不同的步骤:
this.r = Math.round(Math.random() * 8)*32;
this.g = Math.round(Math.random() * 8)*32;
this.b = Math.round(Math.random() * 8)*32;
你可以将getParticleCanvas()中的半径从16减少到8而不会引起注意。
答案 1 :(得分:0)
我得到了一些外界支持,并最终这样做,我认为这是保持绩效的最佳方式:
在内存中创建一个小虚拟画布以运行火焰的draw()
var flameCanvas = document.createElement('canvas');
flameCanvas.width = 100;
flameCanvas.height = 400;
var context = flameCanvas.getContext('2d');
使用大画布在任何需要的地方显示虚拟画布的副本。
ctx3.drawImage(flameCanvas, 420, -37, 50, 200);
ctx3.drawImage(flameCanvas, 325, -47, 60, 240);
...
渲染在虚拟画布中运行,图像只是全部复制,这样可以提高性能。唯一的缺点是所有的火焰都是一样的。
为了跟踪它们,我用if子句包围每个
if (centerFlame_on) {
ctx3.drawImage(flameCanvas, 590, 165);
}
我根据鼠标点击将centerFlame_on更新为true / false。