在移动网络浏览器中使用 input[type='file'] 捕获照片后,如何在画布中以正确的方向绘制照片?
Posted
技术标签:
【中文标题】在移动网络浏览器中使用 input[type=\'file\'] 捕获照片后,如何在画布中以正确的方向绘制照片?【英文标题】:How to draw photo with correct orientation in canvas after capture photo by using input[type='file'] in mobile web browser?在移动网络浏览器中使用 input[type='file'] 捕获照片后,如何在画布中以正确的方向绘制照片? 【发布时间】:2013-10-28 02:12:30 【问题描述】:我正在制作一个简单的移动网络应用程序,允许访问者使用 html5 input[type=file] 元素捕获照片。然后我会把它展示在网上预览,然后访客可以选择将照片上传到我的服务器以用于其他目的(即:上传到FB)
当我使用 iPhone 拍照并垂直握住时,我发现照片的方向有问题。照片在标签中的方向正确。 但是,当我尝试使用 drawImage() 方法将其绘制到画布中时,它被旋转了 90 度。
我尝试从 4 个方向拍照,其中只有一个可以在画布上绘制正确的图像,其他的可以旋转甚至倒置。
嗯,我很困惑获得正确的方向来解决这个问题...... 感谢您的帮助...
这是我的代码,主要是从 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" id="show-picture" >
</div>
<p id="error"></p>
<canvas id="c" ></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>
【问题讨论】:
【参考方案1】:您需要读取 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;
;
【讨论】:
完美运行。非常感谢。 fileReader 的读取方法需要从 readAsDataURL(file) 改为 readAsBinaryString(file) 感谢您的出色回答,本!但是,我不太确定旋转是否完全正确。请参阅下面的答案。 @gburning 我同意您在下面的回答要好得多,并且涵盖了大多数情况!请使用下面的答案而不是我的答案。 这个答案缺少的是 EXIF 库的链接! 我收到一个错误,找不到“BinaryFile”,这是从哪里来的?它不在 exif.js 中【参考方案2】:Ben 的出色回答为我指明了正确的方向,但据我所知,实际旋转是不正确的(至少它们对我来说是正确的)并且没有涵盖所有可能的情况。下面的解决方案对我有用。它基于在javascript-Load-Image library(我通过this great 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;
;
【讨论】:
Ben Wong 和 gburning 提出的解决方案适用于所有类型的图像,即。人像、风景...我主要面临 EXIF.Orientation=6 的人像图像问题。图像是从顶部或底部剪切的。我在 ctx.drawImage 方法中使用多个值尝试了这些选项: ctx.drawImage(img, 0,0); ctx.drawImage(img, -img.width/2,-img.length/2); ctx.drawImage(img, 0,-img.length/2); ctx.drawImage(img, -img.width/2,0);但无法正确处理。在此处添加了新问题:***.com/questions/32968805/… 如果它是一个完整的例子会是一个更好的答案 @ShammelLee 够公平的。 :) 完成。 @gburning 你有解决切割问题的方法吗? @ArthurMenezes 我的肖像图像也被从顶部或底部剪切。我通过添加“canvas.height = width;”解决了这个问题案例 5 - 8。谢谢!【参考方案3】:将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);
);
方向块(使用平移和旋转)是从https://github.com/blueimp/JavaScript-Load-Image/blob/master/js/load-image-orientation.js 复制的,因此我认为它得到了很好的证明。它当然对我很有效,而其他方法却没有。
【讨论】:
这是我认为的最佳答案。它准确地显示了您如何取回正确的画布。 感谢@RafaelKr - 我总是尝试发布完整的解决方案,而不是让您瞥见如何做某事但给您留下其他问题的 sn-ps。【参考方案4】:如果你只想要方向标签,使用exif.js:
EXIF.getData(file, function ()
alert(this.exifdata.Orientation);
);
在我的测试中,ios 相机仅返回 1、3、6 或 8。
【讨论】:
【参考方案5】:根据您的回答,我创建了一个自动将 iphone 照片旋转到正确方向的功能。 只需传入一个 input.files[0] 和一个可选的最大宽度或高度,它就会输出一个用于表单提交的 blob。https://github.com/gonnavis/iphone_photo_rotation_adjust
【讨论】:
以上是关于在移动网络浏览器中使用 input[type='file'] 捕获照片后,如何在画布中以正确的方向绘制照片?的主要内容,如果未能解决你的问题,请参考以下文章
使 Input Type="Password" 在移动设备上使用数字键盘
在 Rails 中使用“f.input 集合”的 onChange 方法
笔记移动端H5数字键盘input type=number的处理(IOS和Android)