Html 在移动网络浏览器中使用 input[type='file'] 捕获照片后,如何在画布中以正确的方向绘制照片?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19463126/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-29 14:37:06  来源:igfitidea点击:

How to draw photo with correct orientation in canvas after capture photo by using input[type='file'] in mobile web browser?

javascripthtmlcanvashtml5-canvasdrawimage

提问by Rock Yip

I am making a simple web app in mobile which allow visitor to capture photo by using html5 input[type=file] element. Then I will display it on the web for preview, and then visitor can choose to upload the photo to my server for other purpose(ie: upload to FB)

我正在移动设备中制作一个简单的网络应用程序,它允许访问者使用 html5 input[type=file] 元素捕获照片。然后我将它显示在网络上进行预览,然后访问者可以选择将照片上传到我的服务器以用于其他目的(即:上传到FB)

I find a problem on the orientation of photo when I take photo using my iPhone and hold vertically.The photo is in a correct orientation in tag. However, when I try to draw it into canvas by using drawImage() method, it is drawn 90 degree rotated.

当我使用 iPhone 拍照并垂直握住照片时,我发现照片方向有问题。照片在标签中的方向正确。但是,当我尝试使用 drawImage() 方法将其绘制到画布中时,它被旋转了 90 度。

I have tried to take photo in 4 orientations, only one of them can draw a correct image in canvas, others are rotated or even flipped upside down.

我尝试了 4 个方向的照片,其中只有一个可以在画布上绘制正确的图像,其他方向旋转甚至倒置。

Well, I am confused to get the correct orientation to fix this problem... Thanks for helping...

好吧,我很困惑获得正确的方向来解决这个问题......谢谢你的帮助......

here is my code, mostly copy from MDN

这是我的代码,主要是从 MDN 复制的

<div class="container">
            <h1>Camera API</h1>

            <section class="main-content">
                <p>A demo of the Camera API, currently implemented in Firefox and Google Chrome on Android. Choose to take a picture with your device's camera and a preview will be shown through createObjectURL or a FileReader object (choosing local files supported too).</p>

                <p>
                    <form method="post" enctype="multipart/form-data" action="index.php">
                        <input type="file" id="take-picture" name="image" accept="image/*">
                        <input type="hidden" name="action" value="submit">
                        <input type="submit" >
                    </form>
                </p>

                <h2>Preview:</h2>
                <div style="width:100%;max-width:320px;">
                    <img src="about:blank" alt="" id="show-picture" width="100%">
                </div>

                <p id="error"></p>
                <canvas id="c" width="640" height="480"></canvas>
            </section>

        </div>


        <script>
            (function () {
                var takePicture = document.querySelector("#take-picture"),
                    showPicture = document.querySelector("#show-picture");

                if (takePicture && showPicture) {
                    // Set events
                    takePicture.onchange = function (event) {
                        showPicture.onload = function(){
                            var canvas = document.querySelector("#c");
                            var ctx = canvas.getContext("2d");
                            ctx.drawImage(showPicture,0,0,showPicture.width,showPicture.height);
                        }
                        // Get a reference to the taken picture or chosen file
                        var files = event.target.files,
                            file;
                        if (files && files.length > 0) {
                            file = files[0];
                            try {
                                // Get window.URL object
                                var URL = window.URL || window.webkitURL;

                                // Create ObjectURL
                                var imgURL = URL.createObjectURL(file);

                                // Set img src to ObjectURL
                                showPicture.src = imgURL;

                                // Revoke ObjectURL
                                URL.revokeObjectURL(imgURL);
                            }
                            catch (e) {
                                try {
                                    // Fallback if createObjectURL is not supported
                                    var fileReader = new FileReader();
                                    fileReader.onload = function (event) {
                                        showPicture.src = event.target.result;

                                    };
                                    fileReader.readAsDataURL(file);
                                }
                                catch (e) {
                                    // Display error message
                                    var error = document.querySelector("#error");
                                    if (error) {
                                        error.innerHTML = "Neither createObjectURL or FileReader are supported";
                                    }
                                }
                            }
                        }
                    };
                }
            })();
        </script>

采纳答案by Ben Wong

You'll need to read the exif data and check if exif.Orientation is one of the following:

您需要阅读 exif 数据并检查 exif.Orientation 是否是以下之一:

fileReader.onloadend = function() {

    var exif = EXIF.readFromBinaryFile(new BinaryFile(this.result));

    switch(exif.Orientation){

       case 8:
           ctx.rotate(90*Math.PI/180);
           break;
       case 3:
           ctx.rotate(180*Math.PI/180);
           break;
       case 6:
           ctx.rotate(-90*Math.PI/180);
           break;


    }
};

回答by gburning

Ben's great answer pointed me in the right direction but as far as I can tell the actual rotations are incorrect (at least they were for me) and don't cover all possible cases. The solution below worked for me. It is based on the one found in the JavaScript-Load-Image library(which I found via this great SO question). Note that I also had to translate the Canvas context to the center as it originates from the top left corner when rotating).

Ben 的出色回答为我指明了正确的方向,但据我所知,实际旋转是不正确的(至少对我来说是这样)并且没有涵盖所有可能的情况。下面的解决方案对我有用。它基于在JavaScript-Load-Image 库中找到的(我通过这个很棒的 SO question 找到的)。请注意,我还必须将 Canvas 上下文转换到中心,因为它在旋转时来自左上角)。

fileReader.onloadend = function() {

    var exif = EXIF.readFromBinaryFile(new BinaryFile(this.result));

    switch(exif.Orientation){

        case 2:
            // horizontal flip
            ctx.translate(canvas.width, 0);
            ctx.scale(-1, 1);
            break;
        case 3:
            // 180° rotate left
            ctx.translate(canvas.width, canvas.height);
            ctx.rotate(Math.PI);
            break;
        case 4:
            // vertical flip
            ctx.translate(0, canvas.height);
            ctx.scale(1, -1);
            break;
        case 5:
            // vertical flip + 90 rotate right
            ctx.rotate(0.5 * Math.PI);
            ctx.scale(1, -1);
            break;
        case 6:
            // 90° rotate right
            ctx.rotate(0.5 * Math.PI);
            ctx.translate(0, -canvas.height);
            break;
        case 7:
            // horizontal flip + 90 rotate right
            ctx.rotate(0.5 * Math.PI);
            ctx.translate(canvas.width, -canvas.height);
            ctx.scale(-1, 1);
            break;
        case 8:
            // 90° rotate left
            ctx.rotate(-0.5 * Math.PI);
            ctx.translate(-canvas.width, 0);
            break;


    }
};

回答by Andy Lorenz

add exif.jsto your project, then:

exif.js添加到您的项目中,然后:

EXIF.getData(file,function() {
  var orientation = EXIF.getTag(this,"Orientation");
  var can = document.createElement("canvas");
  var ctx = can.getContext('2d');
  var thisImage = new Image;
  thisImage.onload = function() {
    can.width  = thisImage.width;
    can.height = thisImage.height;
    ctx.save();
    var width  = can.width;  var styleWidth  = can.style.width;
    var height = can.height; var styleHeight = can.style.height;
    if (orientation) {
      if (orientation > 4) {
        can.width  = height; can.style.width  = styleHeight;
        can.height = width;  can.style.height = styleWidth;
      }
      switch (orientation) {
      case 2: ctx.translate(width, 0);     ctx.scale(-1,1); break;
      case 3: ctx.translate(width,height); ctx.rotate(Math.PI); break;
      case 4: ctx.translate(0,height);     ctx.scale(1,-1); break;
      case 5: ctx.rotate(0.5 * Math.PI);   ctx.scale(1,-1); break;
      case 6: ctx.rotate(0.5 * Math.PI);   ctx.translate(0,-height); break;
      case 7: ctx.rotate(0.5 * Math.PI);   ctx.translate(width,-height); ctx.scale(-1,1); break;
      case 8: ctx.rotate(-0.5 * Math.PI);  ctx.translate(-width,0); break;
      }
    }

    ctx.drawImage(thisImage,0,0);
    ctx.restore();
    var dataURL = can.toDataURL();

    // at this point you can save the image away to your back-end using 'dataURL'
  }

  // now trigger the onload function by setting the src to your HTML5 file object (called 'file' here)
  thisImage.src = URL.createObjectURL(file);

});

The orientation block (using translate and rotate) is copied from https://github.com/blueimp/JavaScript-Load-Image/blob/master/js/load-image-orientation.jsand so I consider it well proven. It certainly worked perfectly for me, whereas other approaches didn't.

方向块(使用平移和旋转)是从https://github.com/blueimp/JavaScript-Load-Image/blob/master/js/load-image-orientation.js复制的,所以我认为它得到了很好的证明。它当然对我很有效,而其他方法则不然。

回答by Felix Turner

If you just want the Orientation tag, using exif.js:

如果您只想要 Orientation 标签,请使用exif.js

EXIF.getData(file, function () {
    alert(this.exifdata.Orientation);
});

In my tests, iOS camera only returns 1,3,6 or 8.

在我的测试中,iOS 相机只返回 1、3、6 或 8。

回答by gonnavis

Based on your answers, I created a function to auto rotate iphone photo to right direction.
Just pass in an input.files[0] and an optional max width or height, it'll output a blob used for form submit.
https://github.com/gonnavis/iphone_photo_rotation_adjust

根据您的回答,我创建了一个功能来自动将 iphone 照片旋转到正确的方向。
只需传入一个 input.files[0] 和一个可选的最大宽度或高度,它会输出一个用于表单提交的 blob。
https://github.com/gonnavis/iphone_photo_rotation_adjust