需求:


在项目中,我们经常要实现用户上传图像的功能,由于图像“太大”或者”压缩上传服务器”或者“本地处理图片”,我们经常会用canvas对图片进行2次处理。在最近的一次开发中我发现,ios低系统、ios(13.4.1之后)高系统对图片的旋转处理不同。并且我还研究了android及chrome对旋转图片的处理,下面我研究的成果:

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: 图片旋转方向显示。

    1. 如下图所示:

    rar.jpeg rar.jpeg 正确方向(想要的方向) 方向旋转 ios低,ios高 android

canvas 绘制(drawImage)旋转图片处理,对比ios高和ios低


当我们看到上面的显示结果时,我们肯定想ios很智能啊,已经显示出我们想要的结果,其实ios真的很坑。对于图片我们上面说过会经常通过canvas进行处理,我们用canvas进行最简单的drawImage操作, 结果显示如下: 浏览器图片旋转的兼容处理 - 图3image.gif 浏览器图片旋转的兼容处理 - 图5image.gif ios高 ios低,android 结论: ios低系统在canvas绘制的时候不是以显示的正方向绘制的图片,旋转信息影响着canvas 的绘制,ios高以正确的方向绘制。

通过“H5显示”和“drawImage”分析,结论如下


  • iosandroid对旋转的图片显示机制不同
  • ios高ios低 虽然显示相同但是canvas处理时,旋转图片的方向却不同

android和ios低的兼容处理


我们发现ios低android系统,在canvas处理的时候都是以 “有旋转方向的图片” 处理的。所以我们的处理方式是,获取图片的旋转信息,canvas 根据旋转信息将图片进行相应的旋转处理,最终将canvas导出正确显示的图片:

  1. function renderFileChangedImg(img, distImg) {
  2. return new Promise(function (resolve) {
  3. // MegaPixImage.js 大家可以在网上查找,用户处理图片旋转和大小
  4. var mpImg = new MegaPixImage(img);
  5. // img 为用户上传的图片 ,通过exif获取图片的信息
  6. EXIF.getData(img, () => {
  7. var allMetaData = EXIF.getAllTags(img);
  8. // 获取图片的旋转信息
  9. var orientation = allMetaData.Orientation;
  10. mpImg.render(distImg, {
  11. maxWidth: 1980,
  12. maxHeight: 1980,
  13. orientation: orientation
  14. }, resolve)
  15. })
  16. })
  17. }

我们获取到结果canvas处理后正确的图片后,我们可以回显 “该图片” ,解决android显示旋转的问题,因为该图片没有旋转信息,所以在ios低处理图片的时候,可以以正确的方向处理图片。

ios 高 的兼容处理


由于 ios高显示的是旋转后正确的方向,canvas 绘制drawImage也是正确的方向,不受旋转信息的影响。所以我们的处理方式和android、ios低不一样 “不用获取旋转信息,不进行旋转处理”

“不进行旋转处理”:如果我们和android或ios低一样,获取旋转信息(假如oritation:6),并且处理。会将一个显示正常、通过canvas绘制drawImage之后,反而旋转为一张方向不正确的图片。也就是说ios高不进行旋转处理。

  1. function renderFileChangedImg(img, distImg) {
  2. return new Promise(function (resolve) {
  3. var mpImg = new MegaPixImage(img);
  4. mpImg.render(distImg, {
  5. maxWidth: 1980,
  6. maxHeight: 1980,
  7. }, resolve)
  8. })
  9. }

ios高 和 ios低的判断并实现


上面 “android和ios低的兼容处理”,“ios 高 的兼容处理” 分析过了我们兼容处理图片旋转时,只要区分 ios高和ios低就行,我这边查了好多资料,很少有解决办法,最终发现了一篇文章(https://juejin.im/post/5ec26d3c6fb9a043681f83c2)成功解决了我的问题, // 原理是:如果是原图的是一张 2 * 1 ,oritation为6的图片(逆时针旋转90deg)的图片,ios高 获取的图片尺寸 width为2,height为1,ios低和android获取的图片尺寸 width为1,height为2,实现代码如下

  1. // 用一张特殊的图片来检测当前浏览器是否对带 EXIF 信息的图片进行回正
  2. // 方法来源: https://github.com/blueimp/JavaScript-Load-Image
  3. export function detectImageAutomaticRotation() {
  4. return new Promise((resolve) => {
  5. // 一张 2x1 的 JPEG 图片, EXIF Orientation: 6
  6. const testAutoOrientationImageURL =
  7. 'data:image/jpeg;base64,/9j/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAYAAAA' +
  8. 'AAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA' +
  9. 'QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE' +
  10. 'BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIAAEAAgMBEQACEQEDEQH/x' +
  11. 'ABKAAEAAAAAAAAAAAAAAAAAAAALEAEAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAA' +
  12. 'AAAAAEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwA/8H//2Q==';
  13. let isImageAutomaticRotation;
  14. if (isImageAutomaticRotation === undefined) {
  15. const img = new Image();
  16. img.onload = () => {
  17. // 如果图片变成 1x2,说明浏览器对图片进行了回正
  18. isImageAutomaticRotation = img.width === 1 && img.height === 2;
  19. resolve(isImageAutomaticRotation);
  20. };
  21. img.src = testAutoOrientationImageURL;
  22. } else {
  23. resolve(isImageAutomaticRotation);
  24. }
  25. });
  26. }

最终的解决办法


  1. function renderFileChangedImg(img, distImg) {
  2. return new Promise(function (resolve) {
  3. // MegaPixImage.js 大家可以在网上查找,用户处理图片旋转和大小
  4. var mpImg = new MegaPixImage(img);
  5. // img 为用户上传的图片 ,通过exif获取图片的信息
  6. detectImageAutomaticRotation().then(result=>{
  7. // 如果为true,则为ios高,false为ios低和android
  8. if(result){
  9. mpImg.render(distImg, {
  10. maxWidth: 1980,
  11. maxHeight: 1980
  12. }, resolve)
  13. }else{
  14. EXIF.getData(img, () => {
  15. var allMetaData = EXIF.getAllTags(img);
  16. // 获取图片的旋转信息
  17. var orientation = allMetaData.Orientation;
  18. // 旋转图片
  19. if(orientation !== 0){
  20. console.log('压缩并纠正旋转')
  21. mpImg.render(distImg, {
  22. maxWidth: 1980,
  23. maxHeight: 1980,
  24. orientation: orientation
  25. }, resolve)
  26. }else{
  27. console.log('压缩图片尺寸')
  28. mpImg.render(distImg, {
  29. maxWidth: 1980,
  30. maxHeight: 1980
  31. }, resolve)
  32. }
  33. })
  34. }
  35. })
  36. })
  37. }

Foodie App内应用:


目前测试foodie android系统,通过jsBridge拍照或相册选择拿到的图片,在exif读取的时候,读取不成功,不执行回调,所以android处理方式和ios高一样,幸运的是android拍摄的照片,都纠正了方向,并且旋转 oritation 的值为0,影响范围不大,代码如下:

  1. Handlers.detectImageAutomaticRotation().then(res=>{
  2. // res 为 true 则浏览器对图片进行了回正,canvas操作的是纠正后的图片
  3. if(res||!BrowserChecker.isIos()){
  4. setTimeout(function () {
  5. img = null;
  6. },1000)
  7. return sucCb(img);
  8. }else{
  9. EXIF.getData(img, () => {
  10. var allMetaData = EXIF.getAllTags(img);
  11. var orientation = allMetaData.Orientation;
  12. var mpImg = new MegaPixImage(img);
  13. mpImg.render($('#bridgeImg')[0], {
  14. maxWidth: 3980,
  15. maxHeight: 3980,
  16. // quality: 0.6,
  17. orientation: orientation
  18. }, function () {
  19. setTimeout(function () {
  20. img = null;
  21. },1000)
  22. return sucCb($('#bridgeImg')[0])
  23. });
  24. })
  25. }
  26. })