浏览器图片旋转的兼容处理
需求:
在项目中,我们经常要实现用户上传图像的功能,由于图像“太大"或者"压缩上传服务器"或者“本地处理图片”,我们经常会用canvas对图片进行2次处理。在最近的一次开发中我发现,ios低系统、ios(13.4.1之后)高系统对图片的旋转处理不同。并且我还研究了android及chrome对旋转图片的处理,下面我研究的成果,分享给大家。
手机拍摄旋转图片ios和android的区别:
用户用手机拍照的时候,会不同方向的旋转拍照,所以手机拍摄的照片都具有exif信息。目前发现:
- android手机拍摄的照片,系统会将图片进行旋转,旋转后显示为 “正确的方向",并且oritation的属性值都是0。
- ios拍摄的照片,系统也会将图片进行,旋转后显示为“正确的方向”,但是oritation的属性值却保留了原值。
H5旋转图片的显示问题 ,对比ios系统和android系统
我研究了 ios 低(低系统),ios高(ios高系统13.4.1之后),pc(chrome 84.0.4147.68),android系统。发现ios高和pc处理旋转是相同的。所以下面讲解ios高也代表pc。我用一张oritation为6的图片在不同系统进行了测试,结果如下:
- ios低: 将图片旋转为正确的方向显示
- ios高:将图片旋转为正确的方向显示
- android: 图片旋转方向显示。
如下图所示:
正确方向(想要的方向) 方向 旋转
![]()
ios低,ios高 android
H5旋转图片canvas 简单drawImage处理,对比ios高和ios低
当我们看到上面的显示结果时,我们肯定想ios很智能啊,已经显示出我们想要的结果,其实ios真的很坑。对于图片我们上面说过会经常通过canvas进行处理,我们用canvas进行最简单的drawImage操作, 结果显示如下:
![]()
ios高 ios低,android
结论: ios低系统在canvas绘制的时候不是以显示的正方向绘制的图片,旋转信息影响着canvas 的绘制,ios高以正确的方向绘制。
通过“H5显示”和“drawImage”分析,结论如下
ios和android对旋转的图片显示机制不同
ios高和ios低 虽然显示相同但是canvas处理时,旋转图片的方向却不同
android和ios低的兼容处理
我们发现ios低和android系统,在canvas处理的时候都是以 “有旋转方向的图片” 处理的。所以我们的处理方式是,获取图片的旋转信息,canvas 根据旋转信息将图片进行相应的旋转处理,最终将canvas导出正确显示的图片:
- 这里我们使用了exif.js 获取图片exif信息和MegaPixImage.js进行图片最大尺寸和旋转处理,两个库大家可以在网上查找
function renderFileChangedImg(img, distImg) { return new Promise(function (resolve) { // MegaPixImage.js 大家可以在网上查找,用户处理图片旋转和大小 var mpImg = new MegaPixImage(img); // img 为用户上传的图片 ,通过exif获取图片的信息 EXIF.getData(img, () => { var allMetaData = EXIF.getAllTags(img); // 获取图片的旋转信息 var orientation = allMetaData.Orientation; mpImg.render(distImg, { maxWidth: 1980, maxHeight: 1980, orientation: orientation }, resolve) }) }) }
我们获取到结果canvas处理后正确的图片后,我们可以回显 “该图片” ,解决android显示旋转的问题,因为该图片没有旋转信息,所以在ios低处理图片的时候,可以以正确的方向处理图片。
ios 高 的兼容处理
由于 ios高显示的是旋转后正确的方向,canvas 绘制drawImage也是正确的方向,不受旋转信息的影响。所以我们的处理方式和android、ios低不一样 “不用获取旋转信息,不进行旋转处理” 。
“不进行旋转处理”:是的不旋转处理,如果我们和android或ios低一样,获取旋转信息(假如oritation:6),并且处理。会将一个显示正常、drawImage正常的图片,通过canvas处理反而旋转为一张方向不正确的图片。也就是说ios高不进行旋转处理。
function renderFileChangedImg(img, distImg) { return new Promise(function (resolve) { var mpImg = new MegaPixImage(img); mpImg.render(distImg, { maxWidth: 1980, maxHeight: 1980, }, resolve) }) }
ios高 和 ios低的判断并实现
上面 “android和ios低的兼容处理”,“ios 高 的兼容处理” 分析过了我们兼容处理图片旋转时,只要区分 ios高和ios低就行,我这边查了好多资料,很少有解决办法,最终发现了一篇文章(https://juejin.im/post/5ec26d3c6fb9a043681f83c2)成功解决了我的问题,感谢这位博主。
// 原理是:如果是原图的是一张 1 * 2 ,oritation为6的图片(逆时针旋转90deg)的图片,ios高 获取的图片尺寸 width为2,height为1,ios低和android获取的图片尺寸 width为1,height为2,实现代码如下
// 用一张特殊的图片来检测当前浏览器是否对带 EXIF 信息的图片进行回正 // 方法来源: https://github.com/blueimp/JavaScript-Load-Image export function detectImageAutomaticRotation() { return new Promise((resolve) => { // 一张 2x1 的 JPEG 图片, EXIF Orientation: 6 const testAutoOrientationImageURL = 'data:image/jpeg;base64,/9j/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAYAAAA' + 'AAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA' + 'QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE' + 'BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIAAEAAgMBEQACEQEDEQH/x' + 'ABKAAEAAAAAAAAAAAAAAAAAAAALEAEAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAA' + 'AAAAAEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwA/8H//2Q=='; let isImageAutomaticRotation; if (isImageAutomaticRotation === undefined) { const img = new Image(); img.onload = () => { // 如果图片变成 1x2,说明浏览器对图片进行了回正 isImageAutomaticRotation = img.width === 1 && img.height === 2; resolve(isImageAutomaticRotation); }; img.src = testAutoOrientationImageURL; } else { resolve(isImageAutomaticRotation); } }); }
最终的解决办法
function renderFileChangedImg(img, distImg) { return new Promise(function (resolve) { // MegaPixImage.js 大家可以在网上查找,用户处理图片旋转和大小 var mpImg = new MegaPixImage(img); // img 为用户上传的图片 ,通过exif获取图片的信息 detectImageAutomaticRotation().then(result=>{ // 如果为true,则为ios高,false为ios低和android if(result){ mpImg.render(distImg, { maxWidth: 1980, maxHeight: 1980 }, resolve) }else{ EXIF.getData(img, () => { var allMetaData = EXIF.getAllTags(img); // 获取图片的旋转信息 var orientation = allMetaData.Orientation; mpImg.render(distImg, { maxWidth: 1980, maxHeight: 1980, orientation: orientation }, resolve) }) } }) }) }
留言:
希望能够帮助大家,如果大家有什么建议,可以给我留言、如果喜欢的话,记得点赞哦。