画布中的绘画随着时间的推移逐渐消失 奇怪的alpha分层行为

Wil*_*eur 5 javascript drawing alpha canvas

我正在绘制一幅未被清除的画布,并使其随着时间的推移逐渐消失为纯色,或者在显示背后层的alpha中消失.

我的第一直觉是简单地在图纸上填充一个矩形,每个框架都有一个低的alpha,这样填充颜色就会逐渐淡出画面.

但我发现了一些奇怪的行为(至少对我而言,我确信这是有原因的).填充颜​​色永远不会完全累积.结果的变化取决于油漆和填充颜色相互之间的颜色更浅/更暗.

我发现这个问题,有人和我一样:绘制画布后淡出线条?

最佳答案看起来不错,而且和我试过的一样.但它只适用于白色的黑色.这是同一个不同颜色的小提琴的另一个版本,你会看到绘画永远不会消失,它会留下一个幽灵:http://jsfiddle.net/R4V97/92/

var canvas = document.getElementById("canvas"),
    ctx = canvas.getContext("2d"),
    painting = false,
    lastX = 0,
    lastY = 0;

canvas.width = canvas.height = 600;

canvas.onmousedown = function (e) {
    if (!painting) {
        painting = true;
    } else {
        painting = false;
    }

    lastX = e.pageX - this.offsetLeft;
    lastY = e.pageY - this.offsetTop;
};

canvas.onmousemove = function (e) {
    if (painting) {
        mouseX = e.pageX - this.offsetLeft;
        mouseY = e.pageY - this.offsetTop;

        ctx.strokeStyle = "rgba(255,255,255,1)";
        ctx.beginPath();
        ctx.moveTo(lastX, lastY);
        ctx.lineTo(mouseX, mouseY);
        ctx.stroke();

        lastX = mouseX;
        lastY = mouseY;
    }
}

function fadeOut() {
    ctx.fillStyle = "rgba(60,30,50,0.2)";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    setTimeout(fadeOut,100);
}

fadeOut();
Run Code Online (Sandbox Code Playgroud)

此外,如果您将填充不透明度更改为0.01,并将时间更改为20毫秒,它甚至不会填充正确的颜色,使其保持灰色.

我尝试过的其他事情都受到同样的根本问题的困扰.我尝试在两个画布之间弹跳,画布A并用画布B缩小alpha到画布B,然后将画布B画回画布A - 同样的问题,它有一个不会消失的阈值.

作为测试,我甚至尝试了获取图像数据的超慢速,循环遍历所有像素alpha通道并在将数据放回之前乘以0.95.它仍然留下一个鬼,我必须在循环中做这样的事情(由于某种原因它甚至从未低于10):

if (alpha<25) {
    alpha = 0;
}
Run Code Online (Sandbox Code Playgroud)

我想我可以将画布划分为一个或多个网格,并且每帧一个单元格执行imageData事件,低衰落时间可能不会引人注意.

但是,如果有人知道一个更好的方式,或者我没有得到的核心事情,我将非常感激!

  • 哦,还应该注意,我让它在画布上光栅化,因为我正在用粒子/算法绘画,所以我不是在寻找解决方案,这意味着我不断刷新并重新绘制相同的点.谢谢!

Bli*_*n67 9

RGB 和 8 位整数数学!

您需要避免接触 RGB 通道,因为当您对 8 位值进行数学计算时,结果将出现巨大错误。例如(8 位整数数学) 14 * 0.1 = 1, 8 * 0.1 = 1 因此,当您在现有像素上绘制时,您将得到一个舍入误差,该误差对于每个通道来说都是不同的,具体取决于您在顶部绘制的颜色。

没有完美的解决方案,但您可以通过使用全局复合操作“destination-out”来避免颜色通道并仅淡出 alpha 通道,这将通过减少像素 alpha 来淡出渲染。

对于淡入率低至 globalAlpha = 0.01 甚至稍低一点的 0.006 效果很好,但低于该值可能会很麻烦。然后,如果您需要更慢的淡入淡出,只需每隔第二或第三帧进行淡入淡出即可。

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
Run Code Online (Sandbox Code Playgroud)

请注意,这会将画布淡化为透明。如果您希望淡入淡出过程向特定颜色发展,则需要将淡入淡出画布保留为单独的屏幕外画布,并将其绘制在具有所需淡入淡出背景的画布上。

演示彩色背景上的彩色粒子和淡入淡出。

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
Run Code Online (Sandbox Code Playgroud)

演示在带有淡入淡出的彩色背景上绘图。

单击拖动鼠标进行绘制。

var canvas = document.createElement("canvas");
canvas.width = 1024;
canvas.height = 1024;
var ctx = canvas.getContext("2d");
var w = canvas.width;
var h = canvas.height;
document.body.appendChild(canvas);

var fadCan = document.createElement("canvas");
fadCan.width = canvas.width;
fadCan.height = canvas.height;
var fCtx = fadCan.getContext("2d");

var cw = w / 2;  // center 
var ch = h / 2;
var globalTime;

function randColour(){
    return "hsl("+(Math.floor(Math.random()*360))+",100%,50%)";
}
var pps = [];
for(var i = 0; i < 100; i ++){
    pps.push({
        x : Math.random() * canvas.width,
        y : Math.random() * canvas.height,
        d : Math.random() * Math.PI * 2,
        sp : Math.random() * 2 + 0.41,
        col : randColour(),
        s : Math.random() * 5 + 2,
        t : (Math.random() * 6 -3)/10,
        
    });
}
function doDots(){
    for(var i = 0; i < 100; i ++){
        var d = pps[i];
        d.d += d.t * Math.sin(globalTime / (d.t+d.sp+d.s)*1000);
        d.x += Math.cos(d.d) * d.sp;
        d.y += Math.sin(d.d) * d.sp;
        d.x = (d.x + w)%w;
        d.y = (d.y + w)%w;
        fCtx.fillStyle = d.col;
        fCtx.beginPath();
        fCtx.arc(d.x,d.y,d.s,0,Math.PI * 2);
        fCtx.fill();
        
    }
}


var frameCount = 0;
// main update function
function update(timer){
    globalTime = timer;
    frameCount += 1;
    ctx.setTransform(1,0,0,1,0,0); // reset transform
    ctx.globalAlpha = 1;           // reset alpha
    ctx.fillStyle = "hsl("+(Math.floor((timer/50000)*360))+",100%,50%)";
    ctx.fillRect(0,0,w,h);
    doDots();
    if(frameCount%2){
        fCtx.globalCompositeOperation = "destination-out";
        fCtx.fillStyle = "black";
        var r = Math.random() * 0.04
        fCtx.globalAlpha = (frameCount & 2 ? 0.16:0.08)+r;
        fCtx.fillRect(0,0,w,h);
        fCtx.globalAlpha = 1;
        fCtx.globalCompositeOperation = "source-over"
    }
    ctx.drawImage(fadCan,0,0)
    requestAnimationFrame(update);
}
requestAnimationFrame(update);
Run Code Online (Sandbox Code Playgroud)


Wil*_*eur 5

用我最终的结果回答我自己的问题 - 感谢回复,在了解到核心问题是一个舍入问题后,我认为向淡入淡出量添加一些随机噪声可以帮助确保它并不总是四舍五入到相同的数字,有点像在它卡住时摇晃它。

这是修改过的相同 jsfiddle:http : //jsfiddle.net/R4V97/97/

var canvas = document.getElementById("canvas"),
    ctx = canvas.getContext("2d"),
    painting = false,
    lastX = 0,
    lastY = 0;

canvas.width = canvas.height = 600;

canvas.onmousedown = function (e) {
    if (!painting) {
        painting = true;
    } else {
        painting = false;
    }

    lastX = e.pageX - this.offsetLeft;
    lastY = e.pageY - this.offsetTop;
};

canvas.onmousemove = function (e) {
    if (painting) {
        mouseX = e.pageX - this.offsetLeft;
        mouseY = e.pageY - this.offsetTop;

        ctx.strokeStyle = "rgba(255,255,255,1)";
        ctx.beginPath();
        ctx.moveTo(lastX, lastY);
        ctx.lineTo(mouseX, mouseY);
        ctx.stroke();

        lastX = mouseX;
        lastY = mouseY;
    }
}

function fadeOut() {
    var r = 0.3 + (Math.random()*0.1);
    ctx.fillStyle = "rgba(60,30,50,"+r+")";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    setTimeout(fadeOut,100);
}

fadeOut();
Run Code Online (Sandbox Code Playgroud)

这会略微影响淡入淡出的平滑度,但与鬼迹相比,它的显着性/侵入性要小得多。