【问题标题】:HTML5 Canvas transparency weirdness with pngs带有png的HTML5 Canvas透明度怪异
【发布时间】:2015-12-10 15:00:35
【问题描述】:

我正在尝试对从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 徽标的小提琴:http://jsfiddle.net/97c0eetr/

为什么这种不透明度算法不适用于 jsfiddle 徽标(以及我在 http://localhost 上测试的其他透明 png)?

编辑:FF42 Ubuntu,如果它有所作为的话。

【问题讨论】:

    标签: javascript html canvas transparency


    【解决方案1】:

    您遇到了大端/小端问题:在当今的大多数计算机/设备上,字节序是小端,这意味着对于 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;
    }
    

    这里更新了小提琴: http://jsfiddle.net/97c0eetr/5/

    最好的 100% 解决方案当然是根据浏览器的 endinannes 选择两个不透明度函数。但是通过押小端,您可以覆盖 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 - 它将以正确的不透明度写入 - 然后在目标画布上 drawImage 它,以便在绘制时考虑不透明度。

    由于您已经有两个画布,因此更改很容易,请在此处查看更新的小提琴: http://jsfiddle.net/97c0eetr/4/

    【讨论】:

    • 大吃一惊。但是,为什么在更新的小提琴中,背景不是红色的?
    猜你喜欢
    • 2014-12-21
    • 1970-01-01
    • 2015-06-26
    • 1970-01-01
    • 2012-07-21
    • 2013-10-08
    • 1970-01-01
    • 2011-03-14
    • 2016-06-09
    相关资源
    最近更新 更多