简体   繁体   English

javascript canvas drawimage 是否适用于 iPhone?

[英]Does javascript canvas drawimage work on iPhones?

I have a web page that loads an image from an input and does a resize and rotate so it is always portrait and displays it to the screen.我有一个 web 页面,它从输入加载图像并调整大小和旋转,因此它始终是纵向的并将其显示到屏幕上。 This works on desktop browsers and on android, but not on my friends iPhone.这适用于桌面浏览器和 android,但不适用于我朋友的 iPhone。

function showImage(){
var input = document.getElementById("card");
    var filesToUpload = input.files;
    var file = filesToUpload[0];

    var img = document.createElement("img");
    var reader = new FileReader();

    reader.onload = function (e) {
        img.src = e.target.result
    }

    reader.onloadend = function () {
        var canvas = document.createElement("canvas");

        var width = img.width;
        var height = img.height;

        var ctx = canvas.getContext("2d");

        canvas.width = width;
        canvas.height = height;

        ctx.drawImage(img, 0, 0, width, height);

        var imgloc = document.getElementById("cardloc");

        imgloc.appendChild(canvas);

    }
    reader.readAsDataURL(file);}


<input id="card" name="card" type="file" accept="image/*" capture/>
<button type=button id="chooseButton" name="chooseButton" onclick="showImage()">
    Choose A card
</button>
<div id="cardloc"></div>

This is a cut down version of the code without the resize and rotate to demonstrate the problem.这是代码的缩减版本,没有调整大小和旋转来演示问题。 When working you can see the img.src is populated with the encoded image and also the canvas.toDataURL() is also returning the encoded image.工作时,您可以看到 img.src 填充了编码图像,并且 canvas.toDataURL() 也返回编码图像。 On the iphone img.src is populated but canvas.toDataURL() is not, just 6 chars long denoting no image loaded.在 iphone 上 img.src 已填充,但 canvas.toDataURL() 未填充,只有 6 个字符长表示未加载图像。

I am all out of ideas, can anyone help?我完全没有想法,有人可以帮忙吗?

It certainly does (though I don't have an iPhone at hand).确实如此(尽管我手头没有 iPhone)。

The problem here is that loading an Image is an asynchronous task (it is performed in parallel of JavaScript execution), even when the source is a data:URL, and that you can't draw an image that is not yet loaded on a canvas.这里的问题是加载图像是一个异步任务(它与 JavaScript 执行并行执行),即使源是数据:URL,并且您无法绘制尚未加载到 ZFCC75150C72DA8DCF61 上的图像.

Now why does it work on some browsers?现在为什么它可以在某些浏览器上运行? Because you are actually executing the drawing part asynchronously too, but in an unrelated event.因为您实际上也在异步执行绘图部分,但是在一个不相关的事件中。

The underlying is a bit complex but basically a FileReader always fire two different events when it's done reading the Blob: onerror and onloadended or onload and onloadend , the onloadend firing right after the previous one.底层有点复杂,但基本上 FileReader 总是在读取 Blob 时触发两个不同的事件: onerroronloadendedonloadonloadendonloadend在前一个事件之后立即触发。

Here you are setting the src of your <img> in the onload event, at that moment the browser will start loading the image.在这里,您在onload事件中设置<img>src ,此时浏览器将开始加载图像。 Since it's from a data: URL, it doesn't have much to do (no network request), so when the second event ( onloadend ) fires, it may actually already have loaded the image, and may thus be able to draw it on the canvas.由于它来自数据:URL,它没有太多事情要做(没有网络请求),所以当第二个事件( onloadend )触发时,它实际上可能已经加载了图像,因此可以绘制它canvas。
However, this is actually a fluck that it works.但是,这实际上是一种侥幸。

The correct way to handle it is to wait for your image's onload event.处理它的正确方法是等待图像的onload事件。

reader.onload = function (e) {
  img.onload = function () {

    var canvas = document.createElement("canvas");

    var width = img.width;
    var height = img.height;

    var ctx = canvas.getContext("2d");

    canvas.width = width;
    canvas.height = height;

    ctx.drawImage(img, 0, 0, width, height);

    var imgloc = document.getElementById("cardloc");

    imgloc.appendChild(canvas);

  };
  img.src = e.target.result
}
reader.readAsDataURL(file);

But you don't even need a FileReader here, you'd be better using a blob: URL which is just a pointer to the file on disk and thus avoids wasting memory, and also avoids one level of callback hell:但是你在这里甚至不需要 FileReader,你最好使用一个blob:URL ,它只是一个指向磁盘上文件的指针,从而避免浪费 memory,并且还避免了一级回调地狱:

img.onload = function () {

  var canvas = document.createElement("canvas");

  var width = img.width;
  var height = img.height;

  var ctx = canvas.getContext("2d");

  canvas.width = width;
  canvas.height = height;

  ctx.drawImage(img, 0, 0, width, height);

  var imgloc = document.getElementById("cardloc");

  imgloc.appendChild(canvas);

}
img.src = URL.createObjectURL(file);

Ps: since you said you also do cropping, if you still have problems with Safari after this fix, then I invite you to read this Q/A about an actual bug in this browser (that I can't be sure you are facing with what you gave to us here). Ps:既然你说你也做裁剪,如果你在这个修复后仍然对 Safari 有问题,那么我邀请你阅读这个关于这个浏览器中实际错误的Q/A (我不能确定你正面临你在这里给我们的东西)。

Thanks for the help.谢谢您的帮助。

I knew the calls where asynchronous.我知道异步调用。 I just hadn't spotted the img has an onload as well.我只是没有发现 img 也有 onload 。

The img onload happens after the reader onloadend, and on android that wasn't a problem but with iPhones it was. img onload 发生在阅读器 onloadend 之后,在 android 上这不是问题,但在 iPhone 上却是。

So I just needed to change the reader.onloaded to img.onload and everything is fine.所以我只需要将 reader.onloaded 更改为 img.onload 就可以了。

Many thanks非常感谢

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

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