移动端图片上传旋转、压缩问题的方法


Posted in Javascript onOctober 16, 2018

前言

在手机上通过网页 input 标签拍照上传图片,有一些手机会出现图片旋转了90度d的问题,包括 iPhone 和个别三星手机。这些手机竖着拍的时候才会出现这种问题,横拍出来的照片就正常显示。因此,可以通过获取手机拍照角度来对照片进行旋转,从而解决这个问题。

Orientation

这个参数并不是所有图片都有的,不过手机拍出来的图片是带有这个参数的。

旋转角度 参数值
1
顺时针90° 6
逆时针90° 8
180° 3

参数为 1 的时候显示正常,那么在这些横拍显示正常,即 Orientation = 1 的手机上,竖拍的参数为 6。

想要获取 Orientation 参数,可以通过 exif.js 库来操作。exif.js 功能很多,体积也很大,未压缩之前足足有 30k,这对手机页面加载来说是非常大影响的。而我只需要获取 Orientation 信息而已,所以我这里删减了 exif.js 库的一些代码,将代码缩小到几KB。

exif.js 获取 Orientation :

EXIF.getData(file, function() { 
 var Orientation = EXIF.getTag(this, 'Orientation');
});

file 则是 input 文件表单上传的文件。上传的文件经过 fileReader.readAsDataURL(file) 就可以实现预览图片了,这方面不清楚的可以查看:HTML5 进阶系列:文件上传下载

旋转

旋转需要用到 canvas 的 rotate() 方法。

ctx.rotate(angle);

rotate 方法的参数为旋转弧度。需要将角度转为弧度:degrees * Math.PI / 180

旋转的中心点默认都在 canvas 的起点,即 ( 0, 0 )。旋转的原理如下图:

移动端图片上传旋转、压缩问题的方法

旋转之后,如果从 ( 0, 0 ) 点进行 drawImage(),那么画出来的位置就是在左图中的旋转 90 度后的位置,不在可视区域呢。旋转之后,坐标轴也跟着旋转了,想要显示在可视区域呢,需要将 ( 0, 0 ) 点往 y 轴的反方向移 y 个单位,此时的起始点则为 ( 0, -y )。

同理,可以获得旋转 -90 度后的起始点为 ( -x, 0 ),旋转 180 度后的起始点为 ( -x, -y )。

压缩

手机拍出来的照片太大,而且使用 base64 编码的照片会比原照片大,那么上传的时候进行压缩就非常有必要的。现在的手机像素这么高,拍出来的照片宽高都有几千像素,用 canvas 来渲染这照片的速度会相对比较慢。

因此第一步需要先对上传照片的宽高做限制,判断宽度或高度是否超出哪个范围,则等比压缩其宽高。

var ratio = width / height;if(imgWidth > imgHeight && imgWidth > xx){
 imgWidth = xx;
 imgHeight = Math.ceil(xx / ratio);
}else if(imgWidth < imgHeight && imgHeight > yy){
 imgWidth = Math.ceil(yy * ratio);
 imgHeight = yy;
}

第二步就通过 canvas.toDataURL() 方法来压缩照片质量。

canvas.toDataURL("image/jpeg", 1);

toDataURL() 方法返回一个包含图片展示的 data URI 。使用两个参数,第一个参数为图片格式,默认为 image/png。第二个参数为压缩质量,在指定图片格式为 image/jpeg 或 image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量。

总结

综合以上,例子的代码包括精简的exif.js库地址:file-demo

主要的核心代码如下:

<input type="file" id="files" ><img src="blank.gif" id="preview">
<script src="small-exif.js"></script>
<script>
 var ipt = document.getElementById('files'),
 img = document.getElementById('preview'),
 Orientation = null;
 ipt.onchange = function () { 
  var file = ipt.files[0],
 reader = new FileReader(),
 image = new Image(); 
 if(file){
  EXIF.getData(file, function() { 
   Orientation = EXIF.getTag(this, 'Orientation');
  });
  reader.onload = function (ev) {
   image.src = ev.target.result;
   image.onload = function () {  
    var imgWidth = this.width,
    imgHeight = this.height;    // 控制上传图片的宽高 
    if(imgWidth > imgHeight && imgWidth > 750){
     imgWidth = 750;
     imgHeight = Math.ceil(750 * this.height / this.width);
    }else if(imgWidth < imgHeight && imgHeight > 1334){
     imgWidth = Math.ceil(1334 * this.width / this.height);
     imgHeight = 1334;
    }    
    var canvas = document.createElement("canvas"),
    ctx = canvas.getContext('2d');
    canvas.width = imgWidth;
    canvas.height = imgHeight; 
    if(Orientation && Orientation != 1){
     switch(Orientation){      
     case 6: // 旋转90度
      canvas.width = imgHeight; 
      canvas.height = imgWidth; 
      ctx.rotate(Math.PI / 2);       
      // (0,-imgHeight) 从旋转原理图那里获得的起始点
      ctx.drawImage(this, 0, -imgHeight, imgWidth, imgHeight); 
      break;      
      case 3: // 旋转180度
      ctx.rotate(Math.PI); 
      ctx.drawImage(this, -imgWidth, -imgHeight, imgWidth, imgHeight); 
      break;      
      case 8: // 旋转-90度
      canvas.width = imgHeight;
      canvas.height = imgWidth; 
      ctx.rotate(3 * Math.PI / 2);
      ctx.drawImage(this, -imgWidth, 0, imgWidth, imgHeight);
      break;
     }
    }else{
     ctx.drawImage(this, 0, 0, imgWidth, imgHeight);
    }
    img.src = canvas.toDataURL("image/jpeg", 0.8);
   }
  }
  reader.readAsDataURL(file);
 }
}</script>
Javascript 相关文章推荐
如何实现JS函数的重载
Sep 22 Javascript
node.js中的events.emitter.once方法使用说明
Dec 10 Javascript
java必学必会之static关键字
Dec 03 Javascript
js倒计时简单实现代码
Aug 11 Javascript
js获取一组日期中最近连续的天数
May 25 Javascript
微信小程序实现收藏与取消收藏切换图片功能
Aug 03 Javascript
JavaScript碎片—函数闭包(模拟面向对象)
Mar 13 Javascript
js继承的这6种方式!(上)
Apr 23 Javascript
vue项目强制清除页面缓存的例子
Nov 06 Javascript
JavaScript canvas实现跟随鼠标事件
Feb 10 Javascript
Vue.js中使用Vuex实现组件数据共享案例
Jul 31 Javascript
vue项目页面嵌入代码块vue-prism-editor的实现
Oct 30 Javascript
JavaScript实现表单注册、表单验证、运算符功能
Oct 15 #Javascript
从零开始搭建vue移动端项目到上线的步骤
Oct 15 #Javascript
jQuery轻量级表单模型验证插件
Oct 15 #jQuery
详解ESLint在Vue中的使用小结
Oct 15 #Javascript
手淘flexible.js框架使用和源代码讲解小结
Oct 15 #Javascript
javascript匿名函数中的'return function()'作用
Oct 15 #Javascript
Vue Cli3 创建项目的方法步骤
Oct 15 #Javascript
You might like
swfupload 多文件上传实现代码
2008/08/27 PHP
《PHP编程最快明白》第七讲:php图片验证码与缩略图
2010/11/01 PHP
域名和cookie问题(域名后缀)
2012/10/10 PHP
php输入流php://input使用浅析
2014/09/02 PHP
php+mysql大量用户登录解决方案分析
2014/12/29 PHP
JS的千分位算法实现思路
2013/07/31 Javascript
没有document.getElementByName方法
2013/08/19 Javascript
Javascript事件实例详解
2013/11/06 Javascript
jquery validate 自定义验证方法介绍 日期验证
2014/02/27 Javascript
jQuery实现加入购物车飞入动画效果
2015/03/14 Javascript
jquery简单实现幻灯片的方法
2015/08/03 Javascript
JS+CSS实现仿msn风格选项卡效果代码
2015/10/22 Javascript
javascript如何写热点图
2015/12/08 Javascript
简单谈谈json跨域
2016/03/13 Javascript
JS实现输入框提示文字点击时消失效果
2016/07/19 Javascript
如何利用JQuery实现从底部回到顶部的功能
2016/12/27 Javascript
AngularJS实现controller控制器间共享数据的方法示例
2017/10/30 Javascript
vuejs中监听窗口关闭和窗口刷新事件的方法
2018/09/21 Javascript
vue.js父子组件通信动态绑定的实例
2018/09/28 Javascript
jQuery+Datatables实现表格批量删除功能【推荐】
2018/10/24 jQuery
js实现下拉框二级联动
2018/12/04 Javascript
详解React的回调渲染模式
2020/09/10 Javascript
前端vue如何使用高德地图
2020/11/05 Javascript
[51:36]EG vs VP 2018国际邀请赛淘汰赛BO3 第一场 8.24
2018/08/25 DOTA
浅析Python中元祖、列表和字典的区别
2016/08/17 Python
Python正则表达式知识汇总
2017/09/22 Python
Python和Go语言的区别总结
2019/02/20 Python
python3.7环境下安装Anaconda的教程图解
2019/09/10 Python
TensorFLow 变量命名空间实例
2020/02/11 Python
HTML5 解析规则分析
2009/08/14 HTML / CSS
html5文字阴影效果text-shadow使用示例
2013/07/25 HTML / CSS
导游词400字
2015/02/13 职场文书
清明祭英烈活动总结
2015/05/11 职场文书
SQL Server连接查询的实用教程
2021/04/07 SQL Server
mysql left join快速转inner join的过程
2021/06/30 MySQL
MySQL示例讲解数据库约束以及表的设计
2022/06/16 MySQL