如果多次应用,带alpha的rgba fillStyle不会完全不透明

时间:2022-11-04 15:34:15

I stubled upon a weird problem. Tthe following code results in making the image fade away because it's overdrawn by a semi-opaque rect over and over again.

我发现了一个奇怪的问题。以下代码导致图像逐渐消失,因为它一遍又一遍地被半透明的矩形透支。

But at least at the 10th iteration of draw(); the image should be completely overdrawn, because the rect should be fully opaque by then, right? But it actually never disappears completely.

但至少在draw()的第10次迭代中;图像应该完全透支,因为那时rect应该是完全不透明的,对吧?但它实际上永远不会完全消失。

This effect is worse on Chrome than it is on Firefox. But beware: bad screens may hide this faulty behaviour =)

这种效果在Chrome上比在Firefox上更糟糕。但要注意:糟糕的屏幕可能隐藏这种错误的行为=)

I also made a demo on jsFiddle.

我还在jsFiddle上做了一个演示。

$(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);

}    
});

The effect one may want to achieve is that, say an object is moving all over the canvas, and the already drawn positions get overdrawn only slightly so after-glow of after-fade effect. But this result is just fugly.

人们可能希望实现的效果是,一个物体在整个画布上移动,并且已经绘制的位置仅稍微透支,因此后褪色效果的余辉。但这个结果真是太难看了。

So is there any solution to this?

那有什么解决方案吗?

3 个解决方案

#1


4  

Since the rectangle is only 10% opaque, the result of drawing it over the image is a composite of 90% of the image and 10% white. Each time you draw it you lose 10% of the previous iteration of the image; the rectangle itself does not become more opaque. (To get that effect, you would need to position another object over the image and animate its opacity.) So after 10 iterations you still have (0.9^10) or about 35% of the original image. Note that rounding errors will probably set in after about 30 iterations.

由于矩形只有10%不透明,因此在图像上绘制它的结果是90%的图像和10%的白色的复合。每次绘制它都会丢失前一次图像迭代的10%;矩形本身不会变得更不透明。 (要获得该效果,您需要在图像上放置另一个对象并为其不透明度设置动画。)因此,在10次迭代后,您仍然拥有(0.9 ^ 10)或大约35%的原始图像。请注意,舍入错误可能会在大约30次迭代后设置。

#2


3  

I know this is old but I don't think the previously accepted answer is correct. I think this is happening as a result of pixel values being truncated from float to byte. In Windows 7 running Chrome version 39.0.2171.95m, after running your fiddle for a while, the image is still visible but only lightly, and doesn't appear to be changing any more. If I take a screenshot I see the following pixel values on the image:

我知道这已经过时了,但我不认为以前接受的答案是正确的。我认为这是由于像素值从浮点到字节被截断而发生的。在运行Chrome版本39.0.2171.95m的Windows 7中,在运行您的小提琴一段时间之后,图像仍然可见,但只是轻微的,并且似乎不再发生变化。如果我截取屏幕截图,我会在图像上看到以下像素值:

(246, 246, 246)

(246,246,246)

When you draw a rectangle over it with rgba of:

当你使用rgba在其上绘制一个矩形时:

(255, 255, 255, 0.1)

(255,255,255,0.1)

and apply alpha blending using the default compositing mode of source-over, before converting to a byte you get:

并使用source-over的默认合成模式应用alpha混合,然后转换为您获得的字节:

(255 * 0.1 + 246 * 0.9) = 246.9

(255 * 0.1 + 246 * 0.9)= 246.9

So you can see that, assuming the browser simply truncates the floating point value to a byte, it will write out a value of 246, and every time you repeat the drawing operation you'll always end up with the same value.

所以你可以看到,假设浏览器只是将浮点值截断为一个字节,它会写出一个246的值,每次重复绘图操作时,你总会得到相同的值。

There is a big discussion on the issue at this blog post here.

这篇博客文章对此问题进行了大量讨论。

As a workaround you could continually clear the canvas and redraw the image with a decreasing globalAlpha value. For example:

作为一种解决方法,您可以不断清除画布并使用递减的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);

Fiddle is here.

小提琴就在这里。

#3


0  

The reason was perfectly stated before. It is not possible to get rid of it without clearing it and redrawing it like @Sam already said.

原因之前已经完美地说明了。如果没有清除它并重新绘制它就不可能摆脱它,就像@Sam已经说过的那样。

What you can you do to compensate it a bit is to set globalCompositeOperation.

您可以做些什么来补偿它是设置globalCompositeOperation。

There are various operations that help. From my tests I can say that hard-light works best for dark backgrounds and lighter work best for bright backgrounds. But this very depends on your scene.

有各种各样的操作有所帮助。根据我的测试,我可以说硬灯最适合深色背景,而较轻的工作最适合明亮的背景。但这很大程度上取决于你的场景。

An example making trails on "near" black

在“近”黑色上制作小道的示例

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

#1


4  

Since the rectangle is only 10% opaque, the result of drawing it over the image is a composite of 90% of the image and 10% white. Each time you draw it you lose 10% of the previous iteration of the image; the rectangle itself does not become more opaque. (To get that effect, you would need to position another object over the image and animate its opacity.) So after 10 iterations you still have (0.9^10) or about 35% of the original image. Note that rounding errors will probably set in after about 30 iterations.

由于矩形只有10%不透明,因此在图像上绘制它的结果是90%的图像和10%的白色的复合。每次绘制它都会丢失前一次图像迭代的10%;矩形本身不会变得更不透明。 (要获得该效果,您需要在图像上放置另一个对象并为其不透明度设置动画。)因此,在10次迭代后,您仍然拥有(0.9 ^ 10)或大约35%的原始图像。请注意,舍入错误可能会在大约30次迭代后设置。

#2


3  

I know this is old but I don't think the previously accepted answer is correct. I think this is happening as a result of pixel values being truncated from float to byte. In Windows 7 running Chrome version 39.0.2171.95m, after running your fiddle for a while, the image is still visible but only lightly, and doesn't appear to be changing any more. If I take a screenshot I see the following pixel values on the image:

我知道这已经过时了,但我不认为以前接受的答案是正确的。我认为这是由于像素值从浮点到字节被截断而发生的。在运行Chrome版本39.0.2171.95m的Windows 7中,在运行您的小提琴一段时间之后,图像仍然可见,但只是轻微的,并且似乎不再发生变化。如果我截取屏幕截图,我会在图像上看到以下像素值:

(246, 246, 246)

(246,246,246)

When you draw a rectangle over it with rgba of:

当你使用rgba在其上绘制一个矩形时:

(255, 255, 255, 0.1)

(255,255,255,0.1)

and apply alpha blending using the default compositing mode of source-over, before converting to a byte you get:

并使用source-over的默认合成模式应用alpha混合,然后转换为您获得的字节:

(255 * 0.1 + 246 * 0.9) = 246.9

(255 * 0.1 + 246 * 0.9)= 246.9

So you can see that, assuming the browser simply truncates the floating point value to a byte, it will write out a value of 246, and every time you repeat the drawing operation you'll always end up with the same value.

所以你可以看到,假设浏览器只是将浮点值截断为一个字节,它会写出一个246的值,每次重复绘图操作时,你总会得到相同的值。

There is a big discussion on the issue at this blog post here.

这篇博客文章对此问题进行了大量讨论。

As a workaround you could continually clear the canvas and redraw the image with a decreasing globalAlpha value. For example:

作为一种解决方法,您可以不断清除画布并使用递减的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);

Fiddle is here.

小提琴就在这里。

#3


0  

The reason was perfectly stated before. It is not possible to get rid of it without clearing it and redrawing it like @Sam already said.

原因之前已经完美地说明了。如果没有清除它并重新绘制它就不可能摆脱它,就像@Sam已经说过的那样。

What you can you do to compensate it a bit is to set globalCompositeOperation.

您可以做些什么来补偿它是设置globalCompositeOperation。

There are various operations that help. From my tests I can say that hard-light works best for dark backgrounds and lighter work best for bright backgrounds. But this very depends on your scene.

有各种各样的操作有所帮助。根据我的测试,我可以说硬灯最适合深色背景,而较轻的工作最适合明亮的背景。但这很大程度上取决于你的场景。

An example making trails on "near" black

在“近”黑色上制作小道的示例

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