如何解决为什么在 HTML5 Canvas 上反复绘制半透明填充不能完全覆盖它?
这到底是怎么回事?
为什么混合会在 255,214,255
处突然停止?这是预期的行为吗?
我以为它最终会命中 255,255,255
,但它没有。它甚至没有接近。一开始的红色矩形永远不会消失成白色,它永远保持颜色255,255
。
const ctx = document.querySelector('canvas').getContext('2d');
// draw "red"
ctx.fillStyle = 'red';
ctx.fillRect(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,0.01)';
ctx.fillRect(0,200);
// schedule next iteration
requestAnimationFrame(loop);
}
loop(); // start loop
function getCurrentColor() {
return ctx.getImageData(0,1,1).data.join(',');
}
<!DOCTYPE html>
<html>
<head></head>
<body>
<canvas width="300" height="200"></canvas>
</body>
</html>
非常感谢解释和解决方案! :P
解决方法
HTML/JS 画布的底层图形缓冲区是位图,它只允许从 Integer
到 0
的 255
颜色值。
这意味着每个渲染操作的输出都必须四舍五入!
假设您从颜色值 214
开始。如果你然后混合 214 * 0.99 + 255 * 0.01
,理论上你会得到 214.41
。但是这然后被四舍五入到 214
以存储在画布位图中!!
因此,基本上,只要您的步数小于 214
xD
不幸的是,没有办法使用普通的 JavaScript 来规避这一点,因为您无法更改图形缓冲区架构或混合/合成操作。
但是,如果你真的想,你当然可以破解它:P
看看WebGL API
。它允许您实现自定义渲染操作和图形缓冲区。例如,您可以在内部使用 0.5
图形缓冲区。
最后不是完全白色的原因很简单:您在某物之上添加了一个非完全透明的覆盖层。不管你多久这样做一次,你永远不会达到 100%,因为它是一种渐近线行为。只需图像以下
我有一张 100% 的红纸。我会用 50% 透明的白色玻璃覆盖它。 然后你会看到 100% => 50% 的 50%。 如果我再这样做,你的逻辑会说我有 50% + 50% = 100% 但这不是它的工作原理。 你有 50% 的红色和 50% 的白色玻璃,并用另一个 50% 的白色玻璃覆盖它。这使得玻璃呈现 25% 的红色和 75% 的白色。
如果重复几次,红色的量会下降,但永远不会达到 0%。
解决方案是再次绘制红色矩形,但不透明度更高。如果您随后到达 ctx.fillStyle = 'rgba(255,255,1.00)';
,它将完全覆盖红色。
示例代码如下:
const ctx = document.querySelector('canvas').getContext('2d');
// draw "red"
ctx.fillStyle = 'red';
ctx.fillRect(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,200);
ctx.fillStyle = 'rgba(255,'+percentDone+')';
ctx.fillRect(0,200);
requestAnimationFrame(render);
}
function getCurrentColor() {
return ctx.getImageData(0,1,1).data.join(',');
}
<canvas width="300" height="200"></canvas>
外部:https://jsfiddle.net/Holger50/51h0yLm6/3/
€dit 1:
将要添加透明度的颜色与该位置的颜色进行比较,计算时仅考虑颜色差异,并基于整数除法。如果计算出的值小于 1,则颜色不会改变,这就是示例中颜色强度 214 时发生的情况。
我尝试了几种方法来找出为什么这会停在一个奇怪的步骤上。也许以下有助于理解问题:如果您将初始填充颜色设置为 ctx.fillStyle = 'black';
并发出以下 ctx.fillStyle = 'rgba(255,100,150,0.01)';
,则停止输出为 207,43,129,255
。
似乎是基于现有值以某种方式计算了具有透明度的新颜色。如果差异太小,则不会添加颜色。
例如:如果您使用 ctx.fillStyle = 'rgba(10,0.01)
,红色部分永远不会增长,因为内部 10*0.01 小于 1,因此不会被考虑在内(它被四舍五入到下一个整数)。您还可以在颜色值中看到这一点。红色增加 100。控制台输出每一步增加 1。蓝色增加 150,控制台增加 2,直到它达到值 44。之后,它只会随着步骤 1 增长,直到它完全停止在 129。
我附上了第二个小提琴,你可以在那里看到效果。
const ctx = document.querySelector('canvas').getContext('2d');
// draw "red"
ctx.fillStyle = 'black';
ctx.fillRect(0,200);
// draw "transparent white" repeatedly
let lastColor;
render();
function render() {
if (getCurrentColor() !== lastColor)
console.log(lastColor = getCurrentColor());
ctx.fillStyle = 'rgba(10,0.01)';
ctx.fillRect(0,');
}
<canvas width="300" height="200"></canvas>
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。