如何避免在HTML5画布上的永久粒子步道



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>

有一个明显的问题:不透明度值低,步道永远不会完全消失。您可以看到(几乎)在我的单粒子示例中不会褪色的水平线。我知道为什么会发生这种情况。通过半透明的ColorB覆盖的Colora基本上是线性插值,如果我们反复执行以下操作,Colora永远不会完全收敛到ColorB:

ColorA = lerp(ColorA, ColorB, opacityOfB)

我的问题是,我该怎么做才能使其融合到背景颜色,以免踪迹永远留在那里?使用WebGL或手动图形跟踪不是有效的选项(分别是兼容性和性能原因)。一种可能性是在所有帆布像素上循环循环,并手动将亮度较低的像素设置为背景颜色,尽管对于大帆布来说可能会变得昂贵。我想知道是否有更好的解决方案。

作为一种解决方法,在某些情况下可能有效的是将覆盖性设置为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);
}

显然,缺点是它看起来更加混乱,也许在您的情况下这是无法接受的。

最佳解决方案是使用复合操作" destination-out",并淡入透明背景。淡入淡化率= 0.01的淡入率效果很好,并且事件略低于0.006,但在此以下可能会很麻烦。然后,如果您需要较慢的褪色,则每2或第三帧即可淡入淡出。

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

如果您想要彩色背景,则需要在屏幕上的画布上渲染动画,并在每个框架上呈现在屏幕上的画布上。或使画布背景成为您想要的颜色。

如果有人为此挣扎,这是对我有用的解决方法:

// Do this instead of ctx.fillStyle some alpha value and ctx.fillRect  
if(Math.random() > 0.8){
  ctx.fillStyle = 'rgba(255, 255, 255, '+getRandomNumber(0.1,0.001)+')';
  ctx.fillRect(0, 0, canvas.width, canvas.height);
}
// Define this helper function somewhere in your code
function getRandomNumber(minValue, maxValue) {
  return Math.random() * (maxValue - minValue) + minValue;
}

它也适用于不同的彩色背景。通过使用Math.random()&gt来调整步道长度。0.8和getrandomnumber(0.1,0.001)。

相关内容

  • 没有找到相关文章

最新更新