随着时间的推移,黑色调整大小的画布不会使图纸完全褪色

时间:2013-11-26 08:27:18

标签: javascript html5 canvas html5-canvas

我有一块黑色的画布,里面画着东西。随着时间的推移,我希望内部绘制的东西按照它们绘制的顺序(FIFO)淡化为黑色。如果我使用尚未调整大小的画布,则此方法有效。调整画布大小后,元素会淡化为灰白色。

问题:当画布调整大小后,为什么白色斑点不会完全淡化为黑色?如何在没有调整画布大小的情况下将它们淡化为黑色?

这是一些演示的代码。 http://jsfiddle.net/6VvbQ/35/

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
context.fillRect(0, 0, 300, 150);

// Comment this out and it works as intended, why?
canvas.width = canvas.height = 300;

window.draw = function () {
    context.fillStyle = 'rgba(255,255,255,1)';
    context.fillRect(
        Math.floor(Math.random() * 300),
        Math.floor(Math.random() * 150),
        2, 2);
    context.fillStyle = 'rgba(0,0,0,.02)';
    context.fillRect(0, 0, 300, 150);

    setTimeout('draw()', 1000 / 20);
}
setTimeout('draw()', 1000 / 20);

3 个答案:

答案 0 :(得分:2)

我不知道我是不是很好,但是看着你的小提琴我认为,对于你正在寻找的东西,你需要在循环的任何迭代中提供画布的大小。如果没有,那么你只是采取初始值:

修改

如果将阈值过滤器应用于画布,则可以执行此操作。你可以每秒运行一次过滤器,这样就不会对prefromanece造成太大的打击。

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
context.fillRect(0,0,300,150);
//context.globalAlpha=1;
//context.globalCompositeOperation = "source-over";


var canvas2 = document.getElementById('canvas2');
var context2 = canvas2.getContext('2d');

canvas2.width=canvas2.height=canvas.width;

window.draw = function(){
    var W = canvas2.width;
    var H = canvas2.height;
    context2.fillStyle='rgba(255,255,255,1)';
    context2.fillRect(
        Math.floor(Math.random()*W),
        Math.floor(Math.random()*H),
        2,2);
    context2.fillStyle='rgba(0,0,0,.02)';
    context2.fillRect(0,0,W,H);

    context.fillStyle='rgba(0,0,0,1)';
    context.fillRect(0,0,300,150);
    context.drawImage(canvas2,0,0,300,150);

    setTimeout('draw()', 1000/20);
}
setTimeout('draw()', 1000/20);

window.thresholdFilter = function () {
    var W = canvas2.width;
    var H = canvas2.height;
    var i, j, threshold = 30, rgb = []
    ,   imgData=context2.getImageData(0,0,W,H), Npixels = imgData.data.length;

    for (i = 0; i < Npixels; i += 4) {
        rgb[0] = imgData.data[i];
        rgb[1] = imgData.data[i+1];
        rgb[2] = imgData.data[i+2];
        if (    rgb[0] < threshold &&
                rgb[1] < threshold &&
                rgb[2] < threshold
        ) {
           imgData.data[i] = 0;
           imgData.data[i+1] = 0;
           imgData.data[i+2] = 0;
        }
    }

    context2.putImageData(imgData,0,0);
};

setInterval("thresholdFilter()", 1000);

这是小提琴:http://jsfiddle.net/siliconball/2VaLb/4/

答案 1 :(得分:2)

问题分为两部分:

  1. 使用低alpha值绘制时会出现(rather known)舍入误差。浏览器将永远无法得到颜色和alpha通道的结果混合等于0,因为混合的结果浮点值将在绘制时转换为整数,这意味着该值永远不会低于1。时间它混合它(值1,因为alpha内部是0到255之间的值)将再次使用此值并再次舍入到1,并且永远它会去。

  2. 当你有一个调整大小的画布时它为什么会起作用 - 在这种情况下,这是因为你只绘制了一半大画布到较小的画布,导致像素被插值。由于该值非常低,这意味着在这种情况下,像素将变为“黑色”(完全透明),因为周围像素之间的平均值将导致值被舍入为0 - 与#1相反。

  3. 要解决此问题,您需要手动清除规格,以确定它是黑色的。这将涉及跟踪每个粒子/规格或使用直接像素操作更改alpha。

    <强>更新

    关键是使用跟踪。您可以通过将每个规范创建为自动更新点来跟踪alpha和清除来实现此目的。

    Online demo here

    一个简单的spec对象可能如下所示:

    function Spec(ctx, speed) {
    
        var me = this;
    
        reset();                             /// initialize object
    
        this.update = function() {
    
            ctx.clearRect(me.x, me.y, 1, 1); /// clear previous drawing
    
            this.alpha -= speed;             /// update alpha
    
            if (this.alpha <= 0) reset();    /// if black then reset again
    
            /// draw the spec
            ctx.fillStyle = 'rgba(255,255,255,' + me.alpha + ')';
            ctx.fillRect(me.x, me.y, 1, 1);
        }
    
        function reset() {
    
            me.x = (ctx.canvas.width * Math.random())|0;  /// random x rounded to int
            me.y = (ctx.canvas.height * Math.random())|0; /// random y rounded to int
    
            if (me.alpha) {                               /// reset alpha
                me.alpha = 1.0;                           /// set to 1 if existed
            } else {
                me.alpha = Math.random();                 /// use random if not
            }
        }
    }
    

    当我们需要清除规范时,将x和y舍入为整数值可以节省一些时间,因为我们不会遇到子像素。否则你也需要清除规范周围的区域。

    接下来的步骤是生成许多要点:

    /// create 100 specs with random speed
    var i = 100, specs = [];
    
    while(i--) {
        specs.push(new Spec(ctx, Math.random() * 0.015 + 0.005));
    }
    

    您只需使用可根据规格单独设置的速度,而不是弄乱FPS。

    现在只需更新循环中的每个对象:

    function loop() {
    
        /// iterate each object
        var i = specs.length - 1;    
        while(i--) {
            specs[i].update();        /// update each object
        }
    
        requestAnimationFrame(loop);  /// loop synced to monitor
    }
    

    正如您所看到的,性能不是问题,并且没有残留物。希望这会有所帮助。

答案 2 :(得分:1)

为了避免舍入问题,您可以使用更长的刷新间隔和更大的alpha值将淡入淡出效果提取到具有自己的计时器的单独函数。

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
context.fillRect(0, 0, 300, 150);

// Comment this out and it works as intended, why?
canvas.width = canvas.height = 300;

window.draw = function () {
    context.fillStyle = 'rgba(255,255,255,1)';
    context.fillRect(
        Math.floor(Math.random() * 300),
        Math.floor(Math.random() * 300),
        2, 2);
    setTimeout('draw()', 1000 / 20);
}

window.fadeToBlack = function () {
    context.fillStyle = 'rgba(0,0,0,.1)';
    context.fillRect(0, 0, 300, 300);
    setTimeout('fadeToBlack()', 1000 / 4);    
}

draw();
fadeToBlack();

小提琴演示:http://jsfiddle.net/6VvbQ/37/