为什么在 HTML5 Canvas 上重复绘制半透明填充不能完全覆盖它?
Why does repeatedly drawing translucent fills on HTML5 Canvas not cover it completely?
这到底是怎么回事?
为什么混合在 255,214,214,255
处突然停止?这是预期的行为吗?
我以为它最终会达到 255,255,255,255
,但事实并非如此。它甚至没有接近。从一开始的红色矩形永远不会消失成白色,它永远保持颜色 255,214,214,255
。
const ctx = document.querySelector('canvas').getContext('2d');
// draw "red"
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, 300, 200);
function loop() {
// log new colors
if (getCurrentColor() !== loop.lastColor) {
console.log(loop.lastColor = getCurrentColor());
}
// draw "transparent white" repeatedly
ctx.fillStyle = 'rgba(255,255,255,0.01)';
ctx.fillRect(0, 0, 300, 200);
// schedule next iteration
requestAnimationFrame(loop);
}
loop(); // start loop
function getCurrentColor() {
return ctx.getImageData(0, 0, 1, 1).data.join(',');
}
<!DOCTYPE html>
<html>
<head></head>
<body>
<canvas width="300" height="200"></canvas>
</body>
</html>
非常感谢解释和解决方案! :P
最后这不是完全白色的原因很简单:您在某些东西之上添加了一个非完全透明的覆盖层。无论您多久这样做一次,您都永远不会达到 100%,因为这是一种渐近线行为。只需图像如下
我有一张 100% 的红纸。我会用 50% 透明的白色玻璃覆盖它。
然后你会看到 100% 的 50% => 50%。
如果我再这样做,你的逻辑会说我有 50% + 50% = 100% 但事实并非如此。
你有 50% 的红色和 50% 的白色玻璃,并用另一个 50% 的白色玻璃覆盖它。这使得玻璃呈现 25% 的红色和 75% 的白色。
如果重复几次,红色的数量会减少但永远不会达到 0%。
解决方法是再次绘制红色矩形,但使用更高的不透明度。如果你到达 ctx.fillStyle = 'rgba(255,255,255,1.00)';
它将完全覆盖红色。
示例代码如下:
const ctx = document.querySelector('canvas').getContext('2d');
// draw "red"
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, 300, 200);
// draw "transparent white" repeatedly
let lastColor;
let percentDone=0;
render();
function render() {
if (getCurrentColor() !== lastColor)
console.log(lastColor = getCurrentColor());
percentDone += .005;
if (percentDone == 1) {percentDone = 1;}
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, 300, 200);
ctx.fillStyle = 'rgba(255,255,255,'+percentDone+')';
ctx.fillRect(0, 0, 300, 200);
requestAnimationFrame(render);
}
function getCurrentColor() {
return ctx.getImageData(0, 0, 1, 1).data.join(',');
}
<canvas width="300" height="200"></canvas>
外部:https://jsfiddle.net/Holger50/51h0yLm6/3/
€编辑 1:
将要添加的透明度颜色与该位置的颜色进行比较,仅考虑颜色差异进行计算,并基于整数除法。如果计算值低于 1,则颜色不会改变,这就是您示例中颜色强度 214 时发生的情况。
我已经尝试了几种方法来找出为什么这会在一个奇怪的步骤上停止。也许以下内容有助于理解问题:如果将初始填充颜色设置为 ctx.fillStyle = 'black';
并发出以下 ctx.fillStyle = 'rgba(255,100,150,0.01)';
停止输出为 207,43,129,255
.
似乎新的透明度颜色是根据已经存在的值以某种方式计算出来的。如果差异太低,则不会添加颜色。
例如:如果您使用 ctx.fillStyle = 'rgba(10,100,150,0.01)
,红色部分将永远不会增长,因为内部 10*0.01 小于 1,因此不被考虑(四舍五入为下一个整数)。您还可以在颜色值中看到这一点。红色提高 100。控制台输出每一步提高 1。蓝色提高150,控制台提高2,直到达到值44。之后,它只会以步骤1增长,直到完全停止在129。
我附上了第二个fiddle,你可以看到效果。
const ctx = document.querySelector('canvas').getContext('2d');
// draw "red"
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, 300, 200);
// draw "transparent white" repeatedly
let lastColor;
render();
function render() {
if (getCurrentColor() !== lastColor)
console.log(lastColor = getCurrentColor());
ctx.fillStyle = 'rgba(10,100,150,0.01)';
ctx.fillRect(0, 0, 300, 200);
requestAnimationFrame(render);
}
function getCurrentColor() {
return ctx.getImageData(0, 0, 1, 1).data.join(',');
}
<canvas width="300" height="200"></canvas>
HTML/JS 画布的底层 图形缓冲区 是 位图 ,它只允许 Integer
颜色值从 0
到 255
。这意味着每个渲染操作的输出都必须 rounded!
假设您从颜色值 214
开始。如果你然后混合 214 * 0.99 + 255 * 0.01
,理论上你会得到 214.41
。但这随后被舍入到 214
以存储在 canvas 位图中!
所以,基本上,你被困在 214
(或与此相关的任何其他值,只要你的步数小于 0.5
)xD
不幸的是,没有办法用简单的 JavaScript 来规避这个问题,因为你不能改变图形缓冲区架构或 blending/compositing 操作。
但我敢肯定,如果你真的愿意,你完全可以破解它。通过 WebGL API
或类似的东西实现 Float32
图形缓冲区。
这到底是怎么回事?
为什么混合在 255,214,214,255
处突然停止?这是预期的行为吗?
我以为它最终会达到 255,255,255,255
,但事实并非如此。它甚至没有接近。从一开始的红色矩形永远不会消失成白色,它永远保持颜色 255,214,214,255
。
const ctx = document.querySelector('canvas').getContext('2d');
// draw "red"
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, 300, 200);
function loop() {
// log new colors
if (getCurrentColor() !== loop.lastColor) {
console.log(loop.lastColor = getCurrentColor());
}
// draw "transparent white" repeatedly
ctx.fillStyle = 'rgba(255,255,255,0.01)';
ctx.fillRect(0, 0, 300, 200);
// schedule next iteration
requestAnimationFrame(loop);
}
loop(); // start loop
function getCurrentColor() {
return ctx.getImageData(0, 0, 1, 1).data.join(',');
}
<!DOCTYPE html>
<html>
<head></head>
<body>
<canvas width="300" height="200"></canvas>
</body>
</html>
非常感谢解释和解决方案! :P
最后这不是完全白色的原因很简单:您在某些东西之上添加了一个非完全透明的覆盖层。无论您多久这样做一次,您都永远不会达到 100%,因为这是一种渐近线行为。只需图像如下
我有一张 100% 的红纸。我会用 50% 透明的白色玻璃覆盖它。 然后你会看到 100% 的 50% => 50%。 如果我再这样做,你的逻辑会说我有 50% + 50% = 100% 但事实并非如此。 你有 50% 的红色和 50% 的白色玻璃,并用另一个 50% 的白色玻璃覆盖它。这使得玻璃呈现 25% 的红色和 75% 的白色。
如果重复几次,红色的数量会减少但永远不会达到 0%。
解决方法是再次绘制红色矩形,但使用更高的不透明度。如果你到达 ctx.fillStyle = 'rgba(255,255,255,1.00)';
它将完全覆盖红色。
示例代码如下:
const ctx = document.querySelector('canvas').getContext('2d');
// draw "red"
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, 300, 200);
// draw "transparent white" repeatedly
let lastColor;
let percentDone=0;
render();
function render() {
if (getCurrentColor() !== lastColor)
console.log(lastColor = getCurrentColor());
percentDone += .005;
if (percentDone == 1) {percentDone = 1;}
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, 300, 200);
ctx.fillStyle = 'rgba(255,255,255,'+percentDone+')';
ctx.fillRect(0, 0, 300, 200);
requestAnimationFrame(render);
}
function getCurrentColor() {
return ctx.getImageData(0, 0, 1, 1).data.join(',');
}
<canvas width="300" height="200"></canvas>
外部:https://jsfiddle.net/Holger50/51h0yLm6/3/
€编辑 1:
将要添加的透明度颜色与该位置的颜色进行比较,仅考虑颜色差异进行计算,并基于整数除法。如果计算值低于 1,则颜色不会改变,这就是您示例中颜色强度 214 时发生的情况。
我已经尝试了几种方法来找出为什么这会在一个奇怪的步骤上停止。也许以下内容有助于理解问题:如果将初始填充颜色设置为 ctx.fillStyle = 'black';
并发出以下 ctx.fillStyle = 'rgba(255,100,150,0.01)';
停止输出为 207,43,129,255
.
似乎新的透明度颜色是根据已经存在的值以某种方式计算出来的。如果差异太低,则不会添加颜色。
例如:如果您使用 ctx.fillStyle = 'rgba(10,100,150,0.01)
,红色部分将永远不会增长,因为内部 10*0.01 小于 1,因此不被考虑(四舍五入为下一个整数)。您还可以在颜色值中看到这一点。红色提高 100。控制台输出每一步提高 1。蓝色提高150,控制台提高2,直到达到值44。之后,它只会以步骤1增长,直到完全停止在129。
我附上了第二个fiddle,你可以看到效果。
const ctx = document.querySelector('canvas').getContext('2d');
// draw "red"
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, 300, 200);
// draw "transparent white" repeatedly
let lastColor;
render();
function render() {
if (getCurrentColor() !== lastColor)
console.log(lastColor = getCurrentColor());
ctx.fillStyle = 'rgba(10,100,150,0.01)';
ctx.fillRect(0, 0, 300, 200);
requestAnimationFrame(render);
}
function getCurrentColor() {
return ctx.getImageData(0, 0, 1, 1).data.join(',');
}
<canvas width="300" height="200"></canvas>
HTML/JS 画布的底层 图形缓冲区 是 位图 ,它只允许 Integer
颜色值从 0
到 255
。这意味着每个渲染操作的输出都必须 rounded!
假设您从颜色值 214
开始。如果你然后混合 214 * 0.99 + 255 * 0.01
,理论上你会得到 214.41
。但这随后被舍入到 214
以存储在 canvas 位图中!
所以,基本上,你被困在 214
(或与此相关的任何其他值,只要你的步数小于 0.5
)xD
不幸的是,没有办法用简单的 JavaScript 来规避这个问题,因为你不能改变图形缓冲区架构或 blending/compositing 操作。
但我敢肯定,如果你真的愿意,你完全可以破解它。通过 WebGL API
或类似的东西实现 Float32
图形缓冲区。