我正在尝试对从 canvas
检索的一些 ImageData
应用透明度。很简单,对吧?只需为每四个数据点设置一个适当的值...仅,它似乎只适用于没有预先存在的 alpa 值的像素。
//create 2 canvases
var canvas = document.createElement("canvas");
document.body.appendChild(canvas);
var ctx = canvas.getContext("2d");
canvas.id = "tempCanvas";
var canvas2 = document.createElement("canvas");
document.body.appendChild(canvas2);
var ctx2 = canvas2.getContext("2d");
canvas2.id = "tempCanvas2";
//draw the png in the first canvas
var testimg = document.getElementById("testimg");
ctx.drawImage(testimg, 0, 0, 200, 200);
//helper function to opacity to the pixels
opacity = function(pixels, value){
var d = pixels.data;
for(var i=0;i<d.length;i+=4){
d[i+3] = value*255; //scale by 255
if(d[i+3] > 255) d[i+3] = 255; //clamp
}
return pixels;
}
//make the first canvas' image data opaque
var data = ctx.getImageData(0,0,200,200);
data = opacity(data, 0.5);
//draw to second canvas
ctx2.fillStyle="#900";
ctx2.fillRect(0,0,200,200);
//should get a jsfiddle logo overlayed on red background
//instead, get yucky grey background
ctx2.putImageData(data, 0, 0);
(由于同源策略,重现有点困难,但这里有一个使用 png jsfiddle Logo 的 fiddle : http://jsfiddle.net/97c0eetr/ )
为什么这种不透明度算法不适用于 jsfiddle Logo (以及我在 http://localhost 上测试的其他透明 png)?
编辑:FF42 Ubuntu,如果有影响的话。
最佳答案
您遇到了大端/小端问题:在当今的大多数计算机/设备上,端序是小端,这意味着对于 32 位值,字节顺序是 3-2-1-0。
因此,不透明度,即第 4 个字节(索引 3)将在 pixelIndex * 4 + 0
处找到。 ... == pixelIndex * 4
.
只需在代码中将不透明度函数更改为:
opacity = function(pixels, value){
var d = pixels.data;
for(var i=0;i<d.length;i+=4){
d[i] = value*255; //scale by 255
if(d[i] > 255) d[i] = 255; //clamp
}
return pixels;
}
这里更新了 fiddle : http://jsfiddle.net/97c0eetr/5/
最好的 100% 解决方案当然是有两个不透明度函数,您可以根据浏览器的结尾来选择。但通过押注于小端,您可以覆盖大约 99% 的设备。
如果您想了解字节顺序,我曾经做了一个小要点来了解它,请在这里找到它: https://gist.github.com/TooTallNate/4750953
代码如下:
function endianness () {
var b = new ArrayBuffer(4);
var a = new Uint32Array(b);
var c = new Uint8Array(b);
a[0] = 0xdeadbeef;
if (c[0] == 0xef) return 'LE';
if (c[0] == 0xde) return 'BE';
throw new Error('unknown endianness');
}
顺便说一下,你的函数可以简化为
opacity = function(pixels, value){
value = Math.floor(value * 255);
if (value>255) value = 255;
var d = pixels.data;
for(var i=0;i<d.length;i+=4){
d[i] = value;
}
return pixels;
}
<小时/>
编辑:那么为什么背景不是红色的? -->> 使用 putImageData
时,您正在使用原始 1 像素进行 1 像素复制方法。所以一切都会被替换,无论如何,例如 globalCompositeOperation
或任何设置。所以红色背景刚刚被新数据覆盖。
解决方案是putImageData
在临时 Canvas 上 - 将以正确的不透明度写入 -,然后写入 drawImage
它在您的目标 Canvas 上,以便在绘制时考虑不透明度。
由于您已经有两个 Canvas ,因此更改很容易,请在此处查看更新的 fiddle : http://jsfiddle.net/97c0eetr/4/
关于javascript - HTML5 Canvas 透明度与 png 的怪异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34205157/