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


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 相关文章推荐
Javascript的一种模块模式
Sep 08 Javascript
js防止表单重复提交实现代码
Sep 05 Javascript
qq悬浮代码(兼容各个浏览器)
Jan 29 Javascript
node.js中的emitter.on方法使用说明
Dec 10 Javascript
JavaScript 2048 游戏实例代码(简单易懂)
Mar 25 Javascript
JS中取二维数组中最大值的方法汇总
Apr 17 Javascript
javascript insertAfter()定义与用法示例
Jul 25 Javascript
JavaScript动态绑定详解
Sep 14 Javascript
vue-cli webpack 引入jquery的方法
Jan 10 jQuery
js实现图片放大并跟随鼠标移动特效
Jan 18 Javascript
微信小程序 WXML节点信息查询详解
Jul 29 Javascript
vue+element_ui上传文件,并传递额外参数操作
Dec 05 Vue.js
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
改德生G88 - 加装等响度低音提升电路
2021/03/02 无线电
PHP下几种删除目录的方法总结
2007/08/19 PHP
php开启与关闭错误提示适用于没有修改php.ini的权限
2014/10/16 PHP
Zend Framework基于Command命令行建立ZF项目的方法
2017/02/18 PHP
jquery写个checkbox——类似邮箱全选功能
2013/03/19 Javascript
关于textarea提交的内容无法换行的解决办法
2013/04/09 Javascript
控制页面按钮在后台执行期间不重复提交的JS方法
2013/06/24 Javascript
JavaScript自执行闭包的小例子
2013/06/29 Javascript
轻松创建nodejs服务器(10):处理上传图片
2014/12/18 NodeJs
jQuery使用hide方法隐藏指定元素class样式用法实例
2015/03/30 Javascript
如何屏蔽防止别的网站嵌入框架代码
2015/08/24 Javascript
如何判断Javascript对象是否存在的简单实例
2016/05/18 Javascript
JS中用childNodes获取子元素换行会产生一个子元素
2016/12/08 Javascript
js 判断数据类型的几种方法
2017/01/13 Javascript
JavaScript实现获取远程的html到当前页面中
2017/03/26 Javascript
js 数字、字符串、布尔值的转换方法(必看)
2017/04/07 Javascript
VUE预渲染及遇到的坑
2018/09/03 Javascript
Vue如何获取数据列表展示
2019/12/11 Javascript
Vue前端项目部署IIS的实现
2020/01/06 Javascript
JavaScript对象原型链原理解析
2020/01/22 Javascript
Python中暂存上传图片的方法
2015/02/18 Python
python2.7 mayavi 安装图文教程(推荐)
2017/06/22 Python
Python基于socket实现简单的即时通讯功能示例
2018/01/16 Python
实例讲解Python中整数的最大值输出
2019/03/17 Python
pandas的qcut()方法详解
2019/07/06 Python
python3模拟实现xshell远程执行liunx命令的方法
2019/07/12 Python
Python变量、数据类型、数据类型转换相关函数用法实例详解
2020/01/09 Python
python 计算方位角实例(根据两点的坐标计算)
2020/01/17 Python
python super函数使用方法详解
2020/02/14 Python
pytorch 模型的train模式与eval模式实例
2020/02/20 Python
日本PLST在线商店:日本时尚杂志刊载的人气服装
2016/12/10 全球购物
美国最大的网上冲印店:Shutterfly
2017/01/01 全球购物
简历中自我评价分享
2013/10/09 职场文书
室内设计专业个人的自我评价
2013/12/18 职场文书
使用tensorflow 实现反向传播求导
2021/05/26 Python
Django实现drf搜索过滤和排序过滤
2021/06/21 Python