记一次移动设备拍照被旋转90度的填坑之路

948 阅读2分钟

一、H5场景介绍

需求:APP里面内嵌H5页面,页面需要调用webview原生的拍照和上传图片功能,最后拿到照片渲染到H5页面上。

二、遇到的问题

1、部分Android手机拍照后渲染的照片会自动顺时针旋转90度,例如:小米MIX 2(Android 9.0)

2、iphone手机有些ios系统拍照后渲染的照片会自动顺时针旋转90度,例如:ios13.4之前的系统

三、解决方案

1、借助 EXIF.js 插件读取图像的元数据,获取图像的拍摄方向 Orientation的值,根据相应的方向修正图像的位置。

EXIF.getData(imgObj, callback) //获取图像的圆数据
EXIF.getTag(imgObj, "Orientation")  //获取图像的拍摄方向,1:正常,6:图片顺时针旋转90度,8:图片逆时针旋转90度,3:图片旋转180度,倒置

2、目前只发现部分手机出现 Orientation6 的情况,图片被顺时针旋转90度,处理方案如下

//获取修正过的图片
function getCorrectImg(file) {
	let Orientation = 0; //图片方向默认为0,代表正常方向
	let reader = new FileReader();
        reader.readAsDataURL(file);
        reader.onload = (e) => {
           let image = new Image();
           image.src = e.target.result;
           //获取照片方向角属性,旋转控制图片
           image.onload = function () {
             EXIF.getData(image, function () {
             	Orientation = EXIF.getTag(this, "Orientation"); // 获取图像的拍摄方向
                let rotateCanvas = document.createElement("canvas");
              	let rotateCtx = rotateCanvas.getContext("2d");
            	let rotateDegs = 0; //旋转角度
                //根据不同方向控制旋转角度
                switch (Orientation) {
                //正常角度,图片绘制
                    case 1:
                	rotateCanvas.width = image.width;
                	rotateCanvas.height = image.height;
                	rotateCtx.drawImage(image, 0, 0, image.width, image.height);
                	break;
                //顺时针旋转90度,手动对图片校正处理
                    case 6:
                    	let sy = 0; //canvas左上角Y轴坐标
                        //ios 13.4以上版本竖屏拍照会自动校正,所以需要兼容高版本ios
                        //其他设备方向为6的全部需要顺时针旋转90度
                        if((isIOS() && compareVersion(getIosVer(), "13.4") < 0) || !isIOS()) {
                           rotateCanvas.width = image.height;
                           rotateCanvas.height = image.width;
                           rotateCtx.translate(0, 0);
                           rotateDegs = (90 * Math.PI) / 180;
                           sy = -image.height;
                        } else {
                            rotateCanvas.width = image.width;
                            rotateCanvas.height = image.height;
                            rotateCtx.rotate(rotateDegs);
                            rotateCtx.drawImage(image, 0, sy, image.width, image.height);
                        }
                	break;
                //逆时针旋转90度,手动对图片校正处理
                    case 8:
                	rotateCanvas.width = image.height;
                	rotateCanvas.height = image.width;
                	rotateCtx.translate(0, 0);
                	rotateCtx.rotate((-90 * Math.PI) / 180);
                	rotateCtx.drawImage(image, -image.width, 0 , image.width , image.height);
                	break;
                //旋转180度,手动对图片校正处理
                    case 3:
                	rotateCanvas.width = image.width;
                	rotateCanvas.height = image.height;
                        rotateCtx.translate(0, 0);
                        rotateCtx.rotate(Math.PI);
                	rotateCtx.drawImage(image, -image.width, -image.width , image.width , image.height);
                	break;
                    default:
                	rotateCanvas.width = image.width;
                	rotateCanvas.height = image.height;
                	rotateCtx.drawImage(image, 0, 0, image.width, image.height);
                }
                return rotateCanvas.toDataURL("image/jpeg", 0.8);  //修正过的图片地址
             })
           }
}

3、上面用到的一些函数,获取ios版本、比较系统版本号等

/*
 *  获取ios系统版本
 */
const getIosVer = () => {
  let userAgent = navigator.userAgent;
  let reg = /CPU iPhone OS (.*?) like Mac OS/i;
  let verStr = userAgent.match(reg)[1];
  return verStr.replace(/_/g, ".");
};

/**
 * 判断软件/系统的版本是否低于设定的版本值,返回1表示当前版本大于设定的版本,返回0表示两个版本相等,返回-1表示当前版本小于设定的版本
 * @param {string} ver  软件/系统版本
 * @param {string} compareVer  设定的版本值
 */
const compareVersion = (ver, compareVer) => {
  ver = ver.split(".");
  compareVer = compareVer.split(".");
  const len = Math.max(ver.length, compareVer.length);

  while (ver.length < len) {
    ver.push("0");
  }
  while (compareVer.length < len) {
    compareVer.push("0");
  }

  for (let i = 0; i < len; i++) {
    const num1 = parseInt(ver[i]);
    const num2 = parseInt(compareVer[i]);
    if (num1 > num2) {
      return 1;
    } else if (num1 < num2) {
      return -1;
    }
  }

  return 0;
};