我试图在画布上绘制与计算样式相同的颜色。
对于这样的颜色很好:
rgb(0,255,255)"
但是像
这样的东西rgba(0,0,0,0)
在画布上是黑色的,但在浏览器中是白色的
答案 0 :(得分:1)
红色,绿色,蓝色和Alpha通道
在大多数情况下,您在浏览器中看到的所有图形都包含4个通道。前3个是颜色通道,代表每种成分颜色的强度,红色,绿色和蓝色。第四个通道是Alpha通道,表示像素的透明度。存储在存储器中的每个通道都是8位宽,允许256个离散值。对于颜色通道0表示无贡献,255表示全强度。 alpha通道还有256个可能的值,从0完全透明到255完全不透明。但传统的alpha表示为从0到1的单位值。
我们可以使用CSS颜色字符串rgba(red,green,blue,alpha)
来表示像素颜色。
来源混合
当像素的α值<1时1(字节值<255)它与下面的像素混合(这由硬件完成),屏幕上的结果像素是两个像素的混合。
混合像素的标准公式基于1984年Porter-Duff compositing
的论文在最简单的形式中,当将一个像素绘制在另一个像素之上时,使用所有通道的字节值,使用以下过程,并将其称为“source-over”。 ref W3C Simple alpha compositing
// the destination is the pixel being drawn over
var destination = {r : 255, g : 0, b : 0, a : 255}; // red opaque
// source is the pixel being put on top
var source = {r : 0, g : 255, b : 0, a : 127}; // green about half transparent
// normalised means brought to a unit value ranging between 0-1 inclusive
var ad = destination.a / 255; // normalise the destination alpha
var as = source.a / 255; // and source
// get the normalised alpha value for the resulting pixel
var ar = as + ad * (1 - as);
// the resulting pixel
var result = {};
// calculate the colour channels.
result.r = (source.r * as + destination.r * ad * (1 - as)) / ar;
result.g = (source.g * as + destination.g * ad * (1 - as)) / ar;
result.b = (source.b * as + destination.b * ad * (1 - as)) / ar;
// calculate the alpha channel
result.a = ar * 255; // bring alpha back to the byte value
// Though it may seem silly to convert to 8 bit range
// it is important to do so because there is a
// considerable loss of precision in all these
// calculations
// convert to a pixel value a used in 2D context getImageData
var pixel = new Uint8ClampedArray([
result.r,
result.g,
result.b,
result.a,
]);
将它放入一个将执行相同操作的函数中,再加上两个辅助函数。
function blendSourceOver(s,d){
var ad = d.a / 255; // normalise the destination alpha
var as = s.a / 255; // and source
var ar = as + ad * (1 - as);
var r = {};
r.r = Math.round((s.r * as + d.r * ad * (1 - as)) / ar);
r.g = Math.round((s.g * as + d.g * ad * (1 - as)) / ar);
r.b = Math.round((s.b * as + d.b * ad * (1 - as)) / ar);
r.a = Math.round(ar * 255);
return r;
}
function rgbaToColour(col){
col = col.replace("rgba(","").replace(")","").split(",");
var r = {};
r.r = Number(col[0]);
r.g = Number(col[1]);
r.b = Number(col[2]);
r.a = Math.round(Number(col[3]) * 255);
return r;
}
function colourTorgba(col){
return `rgba(${col.r},${col.g},${col.b},${col.a / 255})`;
}
在画布上匹配DOM结果。
问题是让画布与DOM匹配。让我们考虑两个元素,一个在另一个元素上。第一个div是红色,第二个是蓝色,alpha为0.5。
DOM颜色混合示例
.exm { width : 100px; height: 30px; color: white; text-align: center;}
<div style = "background : rgba(255, 0, 0, 1);" class = "exm">
<div style = "background : rgba(0, 0, 255, 0.5); position : relative; top : 0px; left : 0px;" class = "exm">
Red + Blue
</div>
</div>
产生的颜色是什么,未知?
现在说我们希望向画布渲染产生的颜色。据我所知,没有直接的方法来对颜色进行采样,因此我们必须从已知的方法中创建颜色。
有两种方法可以做到这一点。
通过渲染复制
首先是复制DOM上发生的事情。添加红色,然后在其上面绘制蓝色。
通过复制渲染步骤匹配DOM颜色的示例
var ctx = can.getContext("2d");
ctx.fillStyle = "rgba(255, 0, 0, 1)";
ctx.fillRect(0, 0, 100, 30);
ctx.fillStyle = "rgba(0, 0, 255, 0.5)";
ctx.fillRect(0, 0, 100, 30);
ctx.font = "18px arial";
ctx.fillStyle = "white";
ctx.textAlign = "center";
ctx.fillText("Canvas", 50, 22);
var dat = ctx.getImageData(1,1,1,1).data;
colResult.textContent = `Resulting colour rgba(${dat[0]},${dat[1]},${dat[2]},${dat[3]/255})`;
.exm { width : 100px; height: 30px; color: white; text-align: center; font : 18px arial;}
.text {color : black; font-size: xx-small;}
<p class="exm text"> Match DOM and Canvas colours via rendering replication</p>
<div style = "background : rgba(255, 0, 0, 1);" class = "exm">
<div style = "background : rgba(0, 0, 255, 0.5); position : relative; top : 0px; left : 0px;" class = "exm"> DOM
</div>
</div>
<canvas id = "can" width = "100" height = "30" class = "exm"></canvas>
<p class="exm text" id="colResult"></p>
按计算
第二种是使用source-over混合函数计算颜色。
使用Porter-Duff "source-over"
混合计算颜色的示例。
function blendSourceOver(s,d){
var ad = d.a / 255; // normalise the destination alpha
var as = s.a / 255; // and source
var ar = as + ad * (1 - as);
var r = {};
r.r = Math.round((s.r * as + d.r * ad * (1 - as)) / ar);
r.g = Math.round((s.g * as + d.g * ad * (1 - as)) / ar);
r.b = Math.round((s.b * as + d.b * ad * (1 - as)) / ar);
r.a = Math.round(ar * 255);
return r;
}
function rgbaToColour(col){
col = col.replace("rgba(","").replace(")","").split(",");
var r = {};
r.r = Number(col[0]);
r.g = Number(col[1]);
r.b = Number(col[2]);
r.a = Math.round(Number(col[3]) * 255);
return r;
}
function colourTorgba(col){
return `rgba(${col.r},${col.g},${col.b},${col.a / 255})`;
}
var colour = colourTorgba(
blendSourceOver(
rgbaToColour("rgba(0, 0, 255, 0.5)"), // source
rgbaToColour("rgba(255, 0, 0, 1)") // destination
)
);
var ctx = can.getContext("2d");
ctx.fillStyle = colour;
ctx.fillRect(0, 0, 100, 30);
ctx.font = "18px arial";
ctx.fillStyle = "white";
ctx.textAlign = "center";
ctx.fillText("Canvas", 50, 22);
colResult.textContent = "Resulting colour "+colour;
.exm { width : 100px; height: 30px; color: white; text-align: center; font : 18px arial;}
.text {color : black; font-size: xx-small;}
<p class="exm text"> Match DOM and Canvas colours by calculation</p>
<div style = "background : rgba(255, 0, 0, 1);" class = "exm">
<div style = "background : rgba(0, 0, 255, 0.5); position : relative; top : 0px; left : 0px;" class = "exm"> DOM
</div>
</div>
<canvas id = "can" width = "100" height = "30" class = "exm"></canvas>
<p class="exm text" id="colResult"></p>
检查alpha = 1
现在必须小心,因为你所做的所有计算必须总是以alpha值为1的颜色结束。如果没有,你会遗漏一些信息。所有DOM颜色混合的最终结果是Alpha = 1(背景),任何进一步的透明度都超出了DOM的上下文,并且是浏览器窗口的一部分。
计算顺序
如果您希望计算两种以上的颜色,则必须按照DOM中的顺序进行计算。如果订单不正确,那么结果颜色也将不正确。
例如,假设你有一个红色背景,然后是一个0.5 alpha的蓝色div,然后是一个0.5 alpha的绿色div。计算顺序是自下而上。
background = rgbaToColour("rgba(255,0,0,1)");
div1 = rgbaToColour("rgba(0,0,255,0.5)");
div2 = rgbaToColour("rgba(0,255,0,0.5)");
首先,背景和div1然后将其结果与div2
混合var temp = blendSourceOver(div1,background); // source then destination
var result = blendSourceOver(div2,temp); // source then temp destination
console.log(colourTorgb(result)); // => "rgba(63,128,64,1)"
以另一种方式进行操作会产生完全不同的颜色。
var temp = blendSourceOver(div1,background); // source then destination
var result = blendSourceOver(div1,temp); // source then temp destination
console.log(colourTorgb(result)); // => "rgba(63,64,128,1)"
进一步阅读
对于所有你需要知道的W3C(悬疑填充)Compositing and Blending Level 2以更详细的方式介绍这个主题。