简体   繁体   English

透明HTML5 canvas todataurl仅在移动设备上呈现透明背景

[英]Transparent HTML5 canvas todataurl only rendering transparent background on mobile devices

EDITED, see end of question. 编辑,请参阅问题结尾。

In my application I have two canvas elements. 在我的应用程序中,我有两个canvas元素。 One shows layered, transparent pngs, the other one gets an image from a file input and masks it. 一个显示分层透明的png,另一个从文件输入中获取图像并对其进行遮罩。 The chosen image is transparent where it is not masked. 所选图像在未被遮盖的地方是透明的。 This image is then converted to a dataUrl, transformed to fit into the first canvas and added as the top layer of the first canvas. 然后将此图像转换为dataUrl,进行转换以适合第一个画布,并添加为第一个画布的顶层。

Everything works as expected on desktop browsers: Chrome OSX, Safari OSX. 一切都能在桌面浏览器上正常运行:Chrome OSX,Safari OSX。 I only add it in on load, so I made sure no race conditions can occur. 我只在加载时添加它,所以我确保不会发生竞争情况。

On Android Chrome and Safari iOS the canvas converted todataURL is rendered transparent. 在Android Chrome和Safari iOS上,转换为dataURL的画布呈现为透明。 If I add a non-transparent image to the second canvas, the rendered image will show even on mobile devices. 如果我将非透明图像添加到第二个画布,则渲染的图像甚至将在移动设备上显示。

To check I added the supposedly transparent canvas to the body. 为了检查,我在身体上添加了所谓的透明画布。 It shows correctly on desktop, but is transparent on mobile Browsers. 它在桌面上正确显示,但在移动浏览器上透明。 Here the simplified JS. 这里是简化的JS。 I am using fabric.js for convenience, but the problem is the same without the lib. 为了方便起见,我使用fabric.js,但是没有lib的问题是相同的。 I even once added a background color. 我什至一次添加了背景色。 Then only the color will show. 然后将仅显示颜色。 Any ideas why todataurl on mobile browsers renders only transparent pixels? 有什么想法为什么在移动浏览器中的todataurl只呈现透明像素?

<body>
<canvas id="canv"></canvas>
<script src="fabric.js"></script>
<script>
// main canvas
var c = new fabric.Canvas('canv');
c.setWidth(200);
c.setHeight(200);

var i = document.createElement('img');
i.src = 'dummy.jpg';
// i.src = 'dummy1.png';
i.onload = function(e) {
    //document.body.appendChild(i);

    scale = 1; // resizes the image
    var ci = new fabric.Image(i);

    ci.set({
        left: 0,
        top: 0,
        scaleX: scale,
        scaleY: scale,
        originX: 'left',
        originY: 'top'
    }).setCoords();

    // temporary canvas, will be converted to dataurl, contains transformed image
    var tmpCanvas = new fabric.Canvas();
    tmpCanvas.setWidth(100);
    tmpCanvas.setHeight(100);
    ci.scaleToWidth(100);
    tmpCanvas.add(ci);
    tmpCanvas.renderAll();

    // create image from temporary canvas
    var customImage = new fabric.Image.fromURL(tmpCanvas.toDataURL({ format: 'png' }), function (cImg) {
        // add it to original canvas
        c.clear();
        c.add(cImg);
        c.renderAll();
        data = c.toDataURL({ format: 'png' });

        // resized image 
        var newc = new fabric.StaticCanvas().setWidth(300).setHeight(300);
        var newImg = new fabric.Image.fromURL(data, function (c1Img) {

            newc.add(c1Img);
            newc.renderAll();

            // append to body to check if canvas is rendered correctly
            document.body.appendChild(newc.lowerCanvasEl);
        });
    });
}
</script>

EDIT: I solved the problem, but could not find the problem on the Javascript side. 编辑:我解决了问题,但找不到Java脚本方面的问题。

The problem was that I copied a temporary canvas onto another canvas. 问题是我将一个临时画布复制到了另一个画布上。 The scale and position of the added canvas was computed by finding the bounding box of non transparent pixels in a png, which was generated exactly for this purpose. 通过在png中找到非透明像素的边界框来计算添加的画布的比例和位置,该边界框正是为此目的而生成的。 A mask in short. 简短地说是面具。

The bounding box was calculated in another temporary canvas at the start of the app (based on this answer). 边界框是在应用程序启动时在另一个临时画布中计算的(基于此答案)。 Although all sizes of the mask and its canvas were set correctly and the canvas was never added to the DOM, when loaded on a small screen the results of the bounding box differed from from the full screen results. 尽管正确设置了蒙版及其画布的所有大小,并且从未将画布添加到DOM,但是在小屏幕上加载时,边框的结果与全屏结果有所不同。 After much testing i found this was true on Desktop too. 经过大量测试后,我发现台式机也是如此。

Because I already spent so much time on the problem, I decided to try to calculate the bounds in PHP and put it into a data attribute. 因为我已经在这个问题上花了很多时间,所以我决定尝试在PHP中计算边界并将其放入data属性。 Which worked great! 哪个很棒!

For those interested in the PHP solution: 对于那些对PHP解决方案感兴趣的人:

function get_bounding_box($imgPath) {

$img = imagecreatefrompng($imgPath);
$w = imagesx($img);
$h = imagesy($img);

$bounds = [
    'left' => $w,
    'right' => 0,
    'top' => $h,
    'bottom' => 0
];
// get alpha of every pixel, if it is not fully transparent, write it to bounds
for ($yPos = 0; $yPos < $h; $yPos++) {
    for ($xPos = 0; $xPos < $w; $xPos++) {
        // Check, ob Pixel nicht vollständig transparent ist
        $rgb = imagecolorat($img, $xPos, $yPos);
        if (imagecolorsforindex($img, $rgb)['alpha']  < 127) {
            if ($xPos < $bounds['left']) {
                $bounds['left'] = $xPos;
            }

            if ($xPos > $bounds['right']) {
                $bounds['right'] = $xPos;
            }

            if ($yPos < $bounds['top']) {
                $bounds['top'] = $yPos;
            }

            if ($yPos > $bounds['bottom']) {
                $bounds['bottom'] = $yPos;
            }
        }
    }
}
return $bounds;

} }

The problem was that I copied a temporary canvas onto another canvas. 问题是我将一个临时画布复制到了另一个画布上。 The scale and position of the added canvas was computed by finding the bounding box of non transparent pixels in a png, which was generated exactly for this purpose. 通过在png中找到非透明像素的边界框来计算添加的画布的比例和位置,该边界框正是为此目的而生成的。 A mask in short. 简短地说是面具。

The bounding box was calculated in another temporary canvas at the start of the app (based on this answer). 边界框是在应用程序启动时在另一个临时画布中计算的(基于此答案)。 Although all sizes of the mask and its canvas were set correctly and the canvas was never added to the DOM, when loaded on a small screen the results of the bounding box differed from from the full screen results. 尽管正确设置了蒙版及其画布的所有大小,并且从未将画布添加到DOM,但是在小屏幕上加载时,边框的结果与全屏结果有所不同。 After much testing i found this was true on Desktop too. 经过大量测试后,我发现台式机也是如此。

Because I already spent so much time on the problem, I decided to try to calculate the bounds in PHP and put it into a data attribute. 因为我已经在这个问题上花了很多时间,所以我决定尝试在PHP中计算边界并将其放入data属性。 Which worked great! 哪个很棒!

For those interested in the PHP solution: 对于那些对PHP解决方案感兴趣的人:

function get_bounding_box($imgPath) {

$img = imagecreatefrompng($imgPath);
$w = imagesx($img);
$h = imagesy($img);

$bounds = [
    'left' => $w,
    'right' => 0,
    'top' => $h,
    'bottom' => 0
];
// get alpha of every pixel, if it is not fully transparent, write it to bounds
for ($yPos = 0; $yPos < $h; $yPos++) {
    for ($xPos = 0; $xPos < $w; $xPos++) {
        // Check, ob Pixel nicht vollständig transparent ist
        $rgb = imagecolorat($img, $xPos, $yPos);
        if (imagecolorsforindex($img, $rgb)['alpha']  < 127) {
            if ($xPos < $bounds['left']) {
                $bounds['left'] = $xPos;
            }

            if ($xPos > $bounds['right']) {
                $bounds['right'] = $xPos;
            }

            if ($yPos < $bounds['top']) {
                $bounds['top'] = $yPos;
            }

            if ($yPos > $bounds['bottom']) {
                $bounds['bottom'] = $yPos;
            }
        }
    }
}
return $bounds;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM