我发现了一个奇怪的问题。下面的代码导致图像逐渐消失,因为它一遍又一遍地被半透明的矩形透支。
但至少在draw();
的第10次迭代时,图像应该完全透支,因为那时rect应该是完全不透明的,对吧?但它实际上永远不会完全消失。
此效果在Chrome上比在Firefox上更糟糕。但要注意:糟糕的屏幕可能会隐藏这种错误的行为=)
我还在jsFiddle上发了demo。
$(function () {
var canvas = $("#mycanvas"),
ctx = canvas[0].getContext("2d"),
imgUrl = "http://it-runde.de/dateien/2009/august/14/25.png";
var image = new Image();
image.src = imgUrl ;
$(image).load(function() {
ctx.drawImage(image, 0, 0, canvas.width(), canvas.height());
draw();
});
function draw() {
ctx.fillStyle = "rgba(255, 255, 255, 0.1)";
ctx.fillRect(0,0,canvas.width(),canvas.height());
setTimeout(draw, 100);
}
});
人们可能希望实现的效果是,一个物体在整个画布上移动,并且已经绘制的位置仅略微透支,因此后褪色效果的余辉。但这个结果真是太可怕了。
那么有什么解决方案吗?
答案 0 :(得分:5)
我知道这已经过时但我不认为以前接受的答案是正确的。我认为这是由于像素值从浮点到字节被截断而发生的。在运行Chrome版本39.0.2171.95m的Windows 7中,在运行您的小提琴一段时间后,图像仍然可见,但只是轻微,并且似乎不再发生变化。如果我截取屏幕截图,我会在图像上看到以下像素值:
(246,246,246)
使用rgba:
在其上绘制一个矩形时(255,255,255,0.1)
并使用source-over的默认合成模式应用alpha混合,然后转换为您获得的字节:
(255 * 0.1 + 246 * 0.9)= 246.9
所以你可以看到,假设浏览器只是将浮点值截断为一个字节,它会写出一个246的值,每次重复绘图操作时,你总会得到相同的值。
此博客文章here对此问题进行了大量讨论。
作为一种解决方法,您可以不断清除画布并使用递减的globalAlpha值重绘图像。例如:
// Clear the canvas
ctx.globalAlpha = 1.0;
ctx.fillStyle = "rgb(255, 255, 255)";
ctx.fillRect(0,0,canvas.width(),canvas.height());
// Decrement the alpha and draw the image
alpha -= 0.1;
if (alpha < 0) alpha = 0;
ctx.globalAlpha = alpha;
console.log(alpha);
ctx.drawImage(image, 0, 0, 256, 256);
setTimeout(draw, 100);
小提琴是here。
答案 1 :(得分:4)
由于矩形只有10%不透明,因此在图像上绘制的结果是图像的90%和10%的白色的复合。每次绘制它都会丢失前一次图像迭代的10%;矩形本身不会变得更不透明。 (要获得该效果,您需要在图像上放置另一个对象并为其不透明度设置动画。)因此,在10次迭代后,您仍然拥有(0.9^10)
或原始图像的约35%。请注意,舍入错误可能会在大约30次迭代后设置。
答案 2 :(得分:0)
之前完全说明了原因。如果没有清除它并且像@Sam已经说过那样重新绘制它是不可能摆脱它。
你能做些什么来补偿它是设置globalCompositeOperation
。
有各种各样的操作有所帮助。根据我的测试,我可以说hard-light
最适合深色背景,lighter
最适合明亮背景。但这取决于你的场景。
在&#34;附近&#34;黑
ctx.globalCompositeOperation = 'hard-light'
ctx.fillStyle = 'rgba(20,20,20,0.2)' // The closer to black the better
ctx.fillRect(0, 0, width, height)
ctx.globalCompositeOperation = 'source-over' // reset to default value
答案 3 :(得分:0)
解决方案是使用ctx.getImageData和ctx.putImageData处理像素数据。
不要将ctx.fillRect与半透明的fillStyle一起使用,而是将每个像素略微设置为每帧的背景色。就我而言,它是黑色的,这使事情变得更简单。
使用这种解决方案,如果考虑浮点精度,您的足迹可以随心所欲。
function postProcess(){
const fadeAmount = 1-1/256;
const imageData = ctx.getImageData(0, 0, w, h);
for (let x = 0; x < w; x++) {
for (let y = 0; y < h; y++) {
const i = (x + y * w) * 4;
imageData.data[i] = Math.floor(imageData.data[i]*fadeAmount);
imageData.data[i + 1] = Math.floor(imageData.data[i + 1]*fadeAmount);
imageData.data[i + 2] = Math.floor(imageData.data[i + 2]*fadeAmount);
imageData.data[i + 3] = 255;
}
}
ctx.putImageData(imageData, 0, 0);
}
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const w = window.innerWidth;
const h = window.innerHeight;
canvas.width = w;
canvas.height = h;
const cs = createCs(50);
let frame = 0;
function init(){
ctx.strokeStyle = '#FFFFFF';
ctx.fillStyle = '#000000';
ctx.fillRect(0, 0, w, h)
loop();
}
function createCs(n){
const cs = [];
for(let i = 0; i < n; i++){
cs.push({
x: Math.random()*w,
y: Math.random()*h,
r: Math.random()*5+1
});
}
return cs;
}
function draw(frame){
//no longer need these:
//ctx.fillStyle = 'rgba(0,0,0,0.02)'
//ctx.fillRect(0, 0, w, h)
ctx.beginPath();
cs.forEach(({x,y,r}, i) => {
cs[i].x += 0.5;
if(cs[i].x > w) cs[i].x = -r;
ctx.moveTo(x+r+Math.cos((frame+i*4)/30)*r, y+Math.sin((frame+i*4)/30)*r);
ctx.arc(x+Math.cos((frame+i*4)/30)*r,y+Math.sin((frame+i*4)/30)*r,r,0,Math.PI*2);
});
ctx.closePath();
ctx.stroke();
//only fade every 4 frames
if(frame % 4 === 0) postProcess(0,0,w,h*0.5);
//fade every frame
postProcess(0,h*0.5,w,h*0.5);
}
//fades canvas to black
function postProcess(sx,sy,dw,dh){
sx = Math.round(sx);
sy = Math.round(sy);
dw = Math.round(dw);
dh = Math.round(dh);
const fadeAmount = 1-4/256;
const imageData = ctx.getImageData(sx, sy, dw, dh);
for (let x = 0; x < w; x++) {
for (let y = 0; y < h; y++) {
const i = (x + y * w) * 4;
imageData.data[i] = Math.floor(imageData.data[i]*fadeAmount);
imageData.data[i + 1] = Math.floor(imageData.data[i + 1]*fadeAmount);
imageData.data[i + 2] = Math.floor(imageData.data[i + 2]*fadeAmount);
imageData.data[i + 3] = 255;
}
}
ctx.putImageData(imageData, sx, sy);
}
function loop(){
draw(frame);
frame ++;
requestAnimationFrame(loop);
}
init();
canvas {
width: 100%;
height: 100%;
}
<canvas id="canvas"/>