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)。