PC端、移动端手机竖拍原图压缩上传顺时针旋转90°的解决方案

王路飞

问题背景

最近在做的项目中,不管是移动端还是后台系统都涉及到了手机照片压缩上传的问题,做完功能测试的时候发现图片回显的时候有些顺时针旋转了90°(竖拍照片,不管是ios还是android都存在这问题),后来百度了一下才知道是跟图片的EXIF(可交换图像文件格式)中的Orientation(旋转参数)有关,那么问题就好处理了,我们可以获取上传图片的Orientation值,根据不同的值对图片做旋转处理。

Orientation旋转参数的介绍与获取

可交换图像文件格式(Exchangeable image file format,官方简称Exif),是专门为数码相机的照片设定的,可以记录数码照片的属性信息和拍摄数据。只要是数码相机(包括手机)拍出来的照片都带有这个参数,旋转的方向有四种情况。exif.js可以帮助我们获取Orientation值(exif.js读取图像的元数据),后来我以import EXIF from './exif.js'方式引入发现文件中的EXIF方法并没有对外暴露出来,因此稍稍改写了一下。这是新的exif.js(你们可以引用这个exif.js
图片描述
引用exif.js后,获取Orientation的核心代码,记得先var Orientation声明Orientation

EXIF.getData(img, function () {//获取照片Orientation,主要是修复竖拍照片顺时针旋转90°的问题(Orientation:6)
    EXIF.getAllTags(this);
    Orientation = EXIF.getTag(this, 'Orientation');
});

canvas 的 rotate() 方法对不同的Orientation值旋转处理

ctx.rotate(angle);

方法介绍参照canvas rotate()

旋转的中心点默认是canvas 的起点,即圆点位置(0, 0)。旋转的原理如下图:
图片描述

旋转之后,如果从圆点(0, 0)进行 drawImage(),那么画出来的位置就是在左图中的旋转90°后的位置,不在可视区域。旋转之后,坐标轴也跟着旋转了,想要显示在可视区域呢,需要将 ( 0, 0 ) 点往 y 轴的反方向移 y 个单位,此时的起始点则为 ( x, -y )(这种情况就是我们使用手机竖拍图片上传后顺时针旋转90°的情况)。

同理,可以获得旋转 -90°后的起始点为 ( -x, y ),旋转 180 度后的起始点为 ( -x, -y )。
核心代码如下

var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
// 设置 canvas 的宽度和高度  
// canvas.width = w;
// canvas.height = h;
// ctx.drawImage(img, 0, 0, w, h);
if (Orientation == 3) {
    canvas.width = w;
    canvas.height = h;
    ctx.rotate(Math.PI);
    ctx.drawImage(img, 0, 0, -w, -h);
} else if (Orientation == 8) {
    canvas.width = h;
    canvas.height = w;
    ctx.rotate(Math.PI * 3 / 2);
    ctx.drawImage(img, 0, 0, -w, h);
} else if (Orientation == 6) {
    canvas.width = h;
    canvas.height = w;
    ctx.rotate(Math.PI / 2);
    ctx.drawImage(img, 0, 0, w, -h);
} else {
    canvas.width = w;
    canvas.height = h;
    ctx.drawImage(img, 0, 0, w, h);
}

图片压缩

随着手机拍照像素越来越高,照片大小越来越大。图片上传的压缩一般是:第一是对上传的图片宽高做大小限制。

// 不要超出最大宽度  
var w = Math.min(_this.maxWidth, img.width);
// 高度按比例计算  
var h = img.height * (w / img.width);

第二是通过canvas.toDataURL(type, encoderOptions)方法设置压缩比,方法参考canvas.toDataUrl()

核心代码如下:

setQuality: function (file) {
//alert(file.size+','+this.maxSize);
if (file.size < 1024 * 1024) {//0-1mb
    this.quality = 0.4;
}
if (file.size < 1024 * 1024 && file.size > 1024 * 1024 * 2) {//1-2mb
    this.quality = 0.3;
}
if (file.size < 1024 * 1024 * 3 && file.size > 1024 * 1024 * 2) {//2-3mb
    this.quality = 0.2;
}
if (file.size < 1024 * 1024 * 4 && file.size > 1024 * 1024 * 3) {//3-4mb
    this.quality = 0.15;
}
if (file.size < 1024 * 1024 * 5 && file.size > 1024 * 1024 * 4) {//4-5mb
    this.quality = 0.12;
}
if (file.size < 1024 * 1024 * 6 && file.size > 1024 * 1024 * 5) {//5-6mb
    this.quality = 0.11;
}
if (file.size < 1024 * 1024 * 7 && file.size > 1024 * 1024 * 6) {//6-7mb
    this.quality = 0.1;
}
if (file.size < 1024 * 1024 * 8 && file.size > 1024 * 1024 * 7) {//7-8mb
    this.quality = 0.08;
}
if (file.size < 1024 * 1024 * 10 && file.size > 1024 * 1024 * 8) {//8-10mb
    this.quality = 0.06;
}
}
var base64 = canvas.toDataURL('image/jpeg', _this.quality);
setTimeout(function () {
    _this.imgData[id].uploadFiles.push(base64);
    _this.imgData[id].filesLength = _this.imgData[id].uploadFiles.length;
}, 100);

小结

拍照上传功能可拆分为图片压缩以及对获取到数码图片Orientation值进行相应的处理。这是小白第一次写技术贴,可能我写得并不是我想表达的,写得不好的地方请各位大神指点,有疑问的可以留言交流。

阅读 2.6k

OnePiece
个人开发、学习过程中的一些总结
144 声望
1 粉丝
0 条评论
144 声望
1 粉丝
文章目录
宣传栏