【问题标题】:How to avoid permanent particle trails on html5 canvas?如何避免 html5 画布上的永久粒子痕迹?
【发布时间】:2017-04-25 22:17:33
【问题描述】:

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>

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

ColorA = lerp(ColorA, ColorB, opacityOfB)

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

【问题讨论】:

    标签: javascript html canvas graphics colors


    【解决方案1】:

    在某些情况下可行的解决方法是将 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);
    }
    

    显然缺点是它看起来更猛烈,也许这在您的情况下可能无法接受。

    【讨论】:

    • 这是一个有趣的想法!问题是,0.1 的不透明度仍然会留下带有其他背景颜色的痕迹(例如 #100030)。 0.2 效果更好,但为了保持相同的轨迹长度,应该每 4 帧绘制一次,这开始看起来很紧张。
    【解决方案2】:

    最好的解决方案是使用复合操作“destination-out”并淡入透明背景。适用于淡化率下降到 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
    

    如果您想要彩色背景,则需要在屏幕外画布上渲染动画,并在每一帧都将其渲染到屏幕画布上。或者将画布背景设置为您想要的颜色。

    【讨论】:

      猜你喜欢
      • 2023-04-05
      • 1970-01-01
      • 2013-12-14
      • 1970-01-01
      • 1970-01-01
      • 2012-05-11
      • 2019-04-16
      • 1970-01-01
      • 2019-06-09
      相关资源
      最近更新 更多