javascript - HTML5 Canvas 透明度与 png 的怪异

标签 javascript html canvas transparency

我正在尝试对从 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/

相关文章:

javascript - 圈子上的共享地点

javascript - 如何使元素在视口(viewport)中可见? jQuery

javascript - 更改依赖于域的 css 并将 "?"添加到域中

javascript - Bootstrap下拉事件

php - 失去焦点时禁用页面刷新

javascript - 浏览隐藏在网页上的链接(可访问性问题)

javascript - WordPress 定制器预览脚本在初始页面加载时执行实时预览,而不是在我从预览窗口导航内页时执行实时预览

javascript - 通过 js 检测浏览器窗口/选项卡关闭事件

android - 如何使用 itext pdf 在 pdf Canvas 上绘制路径?

javascript - 使用 todataurl 时输入错误