如何避免html5画布上的永久粒子轨迹?

时间:2016-12-10 15:47:47

标签: javascript html5 canvas graphics colors

HTML5画布上有数以千计的移动粒子,我的目标是在每个画布后面绘制一条短的渐弱轨迹。一个很好而快速的方法是不是每帧完全清除画布,而是用半透明颜色覆盖它。这是一个只有一个粒子的例子:



var canvas = document.getElementById('display');
var ctx = canvas.getContext('2d');
var displayHeight = canvas.height;

var backgroundColor = '#000000';
var overlayOpacity = 0.05;

var testParticle = {
	pos: 0,
  size: 3
};

function render(ctx, particle) {
  ctx.globalAlpha = overlayOpacity;
  ctx.fillStyle = backgroundColor;
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  ctx.globalAlpha = 1.0;
  
  ctx.fillStyle = '#FFF';
  ctx.fillRect(particle.pos, displayHeight / 2, particle.size, particle.size);
}

function update(particle) {
	particle.pos += 1;
}

// Fill with initial color
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);

function mainLoop() {
  update(testParticle);
  render(ctx, testParticle);
  requestAnimationFrame(mainLoop);
}

mainLoop();

<canvas id="display" width="320" height="240"></canvas>
&#13;
&#13;
&#13;

存在一个明显的问题:低不透明度值时,踪迹永远不会完全消失。您可以在我的单粒子示例中看到(几乎)不会消失的水平线。我理解为什么会这样。由半透明ColorB覆盖的ColorA基本上是线性插值,如果我们反复执行以下操作,ColorA永远不会完全收敛到ColorB:

ColorA = lerp(ColorA, ColorB, opacityOfB)

我的问题是,我该怎样做才能使它融合到背景色中,这样小径就不会永远留在那里?手动使用WebGL或绘图轨迹不是有效选项(分别由于兼容性和性能原因)。一种可能性是遍历所有画布像素并手动将低亮度像素设置为背景色,尽管对于大型画布可能会变得昂贵。我想知道是否有更好的解决方案。

2 个答案:

答案 0 :(得分:2)

在某些情况下可以使用的解决方法是将overlayOpacity设置为0.1(此值收敛),但是每x次绘制一次,而不是每次渲染调用。 因此,当每隔一段时间绘制它时,它或多或少保持相同的路径长度。

var renderCount = 0;
var overlayOpacity = 0.1;

function render(ctx, particle) {

    if((renderCount++)%2 == 0) {
        ctx.globalAlpha = overlayOpacity;
        ctx.fillStyle = backgroundColor;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
    }

    ctx.globalAlpha = 1.0;
    ctx.fillStyle = '#FFF';
    ctx.fillRect(particle.pos, displayHeight / 2, particle.size, particle.size);
}

显然,缺点是它看起来更猛烈,或许在你的情况下这可能是不可接受的。

答案 1 :(得分:0)

最佳解决方案是使用复合操作“目标输出”并淡入透明背景。适用于衰落率低至globalAlpha = 0.01且事件略低于0.006,但在此之下可能会很麻烦。然后,如果您需要更慢的淡入淡出,则每隔2帧或3帧进行淡入淡出。

ctx.globalAlpha = 0.01;           // fade rate
ctx.globalCompositeOperation = "destination-out"  // fade out destination pixels
ctx.fillRect(0,0,w,h)
ctx.globalCompositeOperation = "source-over"
ctx.globalAlpha = 1;           // reset alpha

如果您想要彩色背景,则需要在屏幕外的画布上渲染动画,并在每帧的屏幕画布上渲染动画。或者使画布背景成为您想要的颜色。