我有一块黑色的画布,里面画着东西。随着时间的推移,我希望内部绘制的东西按照它们绘制的顺序(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);
答案 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);
答案 1 :(得分:2)
问题分为两部分:
使用低alpha值绘制时会出现(rather known)舍入误差。浏览器将永远无法得到颜色和alpha通道的结果混合等于0,因为混合的结果浮点值将在绘制时转换为整数,这意味着该值永远不会低于1。时间它混合它(值1,因为alpha内部是0到255之间的值)将再次使用此值并再次舍入到1,并且永远它会去。
当你有一个调整大小的画布时它为什么会起作用 - 在这种情况下,这是因为你只绘制了一半大画布到较小的画布,导致像素被插值。由于该值非常低,这意味着在这种情况下,像素将变为“黑色”(完全透明),因为周围像素之间的平均值将导致值被舍入为0 - 与#1相反。
要解决此问题,您需要手动清除规格,以确定它是黑色的。这将涉及跟踪每个粒子/规格或使用直接像素操作更改alpha。
<强>更新强>:
关键是使用跟踪。您可以通过将每个规范创建为自动更新点来跟踪alpha和清除来实现此目的。
一个简单的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();