黑色调整大小的画布不会随着时间的推移将绘图完全褪色为黑色
Black resized canvas not completely fading drawings to black over time
我有一个黑色画布,里面画着一些东西。我希望随着时间的推移,里面画的东西按照绘制顺序(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);
问题分为两部分:
-
当使用低alpha值绘制时,会出现(相当已知的)舍入误差。浏览器将永远无法获得等于0的颜色和alpha通道的结果混合,因为混合后的结果浮点值将在绘制时转换为整数,这意味着该值永远不会低于1。下一次混合时(值1,因为alpha内部是0到255之间的值)将再次使用该值,并将其四舍五入到1,然后永远保持不变。
-
为什么当你有一个调整大小的画布时它会起作用——在这种情况下,这是因为你只画了大画布的一半到小画布,这导致像素被插值。由于该值非常低,这意味着在这种情况下;黑色";(完全透明),因为周围像素之间的平均值将导致该值四舍五入为0——与#1相反。
为了解决这个问题,你必须手动清除预期为黑色的规格。这将涉及到您自己跟踪每个粒子/规格,或者使用直接像素操作更改alpha。
更新:
关键是要使用跟踪。您可以通过将每个等级库创建为一个自更新点来实现这一点,该点可以跟踪alpha并清除。
在线演示
一个简单的规范对象可以是这样的:
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
}
正如您所看到的,性能不是一个问题,也没有残留。希望这能有所帮助。
我不知道我是否能很好地理解你,但看着你,我认为,对于你想要的东西,你需要在循环的任何迭代中提供画布的大小。如果没有,那么你只是取初始值:
编辑
如果将阈值过滤器应用于画布,则可以执行此操作。你可以每秒钟运行一次过滤器,只是为了让前缀不会受到太大的打击。
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/
为了避免舍入问题,您可以使用更长的刷新间隔和更大的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();
Fiddle演示:http://jsfiddle.net/6VvbQ/37/
- Canvas Html5绘图应用程序,移动画布会导致重大问题
- 使用jQuery以红色和黑色闪烁文本
- HTML画布在绘图后立即擦除矩形
- 如何在JS Leatflet绘图插件中获取圆的坐标
- 将画布转换为pdf:黑色背景
- 在一个页面中具有多个捕捉绘图和捕捉内容
- DT数据表背景颜色为黑色,但仍有悬停和选择颜色
- SVG手写字母绘图动画
- 绘图悬停文本未显示
- 三js黑色材质,透明
- 为画布绘图添加鼠标事件
- 黑色三件套帆布背景
- 如何强制绘图将x轴编号显示为类别
- 在Firefox中导出高图表时出现黑色图像错误
- Three.js-Skybox显示黑色
- 绘图应用程序-单击工具更改光标
- 重置transform:rotate(),方法是移除并追加在追加和重绘图表后未显示数据的画布
- 为什么我的犀牛书;s的SVG时钟为黑色圆圈
- Highcharts多个Y轴绘图带碰撞
- 黑色调整大小的画布不会随着时间的推移将绘图完全褪色为黑色