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


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 相关文章推荐
关于Ext中form移除textfield方法:hide(),setVisible(false),remove()
Dec 02 Javascript
Extjs TimeField 显示正常时间格式的代码
Jun 28 Javascript
基于jquery的图片轮播 tab切换组件
Jul 19 Javascript
javascript加号&quot;+&quot;的二义性说明
Mar 04 Javascript
js数字转换为float,取N位小数
Feb 08 Javascript
jQuery获取动态生成的元素示例
Jun 15 Javascript
jQuery中filter()方法用法实例
Jan 06 Javascript
Jquery promise实现一张一张加载图片
Nov 13 Javascript
JS图片定时翻滚效果实现方法
Jun 21 Javascript
jQuery封装animate.css的实例
Jan 04 jQuery
vue路由拦截及页面跳转的设置方法
May 24 Javascript
JavaScript冒泡算法原理与实现方法深入理解
Jun 04 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
php实现无限级分类实现代码(递归方法)
2011/01/01 PHP
DISCUZ在win2003环境下 Unable to access ./include/common.inc.php in... 的问题终极解决方案
2011/11/21 PHP
使用JSON实现数据的跨域传输的php代码
2011/12/20 PHP
php 无法加载mysql的module的时候的配置的解决方案引发的思考
2012/01/27 PHP
使用php清除bom示例
2014/03/03 PHP
Thinkphp模板标签if和eq的区别和比较实例分析
2015/07/01 PHP
thinkphp3.2实现上传图片的控制器方法
2016/04/28 PHP
Yii2分页的使用及其扩展方法详解
2016/05/23 PHP
在html页面中包含共享页面的方法
2008/10/24 Javascript
浅析JavaScript中的隐式类型转换
2013/12/05 Javascript
JavaScript+CSS无限极分类效果完整实现方法
2015/12/22 Javascript
全屏滚动插件fullPage.js使用实例解析
2016/10/21 Javascript
javascript Function函数理解与实战
2017/12/01 Javascript
JS实现字符串翻转的方法分析
2018/08/31 Javascript
在vue中使用Echarts画曲线图的示例
2020/10/03 Javascript
Python简单遍历字典及删除元素的方法
2016/09/18 Python
python利用正则表达式提取字符串
2016/12/08 Python
python实现一组典型数据格式转换
2018/12/15 Python
python基础知识(一)变量与简单数据类型详解
2019/04/17 Python
解决Keras使用GPU资源耗尽的问题
2020/06/22 Python
python exit出错原因整理
2020/08/31 Python
简单了解Python字典copy与赋值的区别
2020/09/16 Python
python 实现超级玛丽游戏
2020/11/25 Python
实例讲解使用CSS实现多边框和透明边框的方法
2015/09/08 HTML / CSS
html5利用canvas实现颜色容差抠图功能
2019/12/23 HTML / CSS
FOREO官方网站:LUNA露娜洁面仪
2016/11/28 全球购物
缅甸网上购物:Shop.com.mm
2017/12/05 全球购物
台湾演唱会订票网站:StubHub台湾
2019/06/11 全球购物
某/etc/fstab文件中的某行如下: /dev/had5 /mnt/dosdata msdos defaults,usrquota 1 2 请解释其含义
2013/04/11 面试题
办公室内勤岗位职责范本
2013/12/09 职场文书
美国留学经济担保书
2014/05/20 职场文书
教师党的群众路线教育实践活动个人对照检查材料
2014/09/23 职场文书
升职自我推荐信范文
2015/03/25 职场文书
法制教育观后感
2015/06/17 职场文书
2016年校园植树节广播稿
2015/12/17 职场文书
浅谈如何提高PHP代码质量之单元测试
2021/05/28 PHP