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


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 相关文章推荐
jQuery 学习第六课 实现一个Ajax的TreeView
May 17 Javascript
jquery实现文本框数量加减功能的例子分享
May 10 Javascript
SuperSlide2实现图片滚动特效
Jun 20 Javascript
浅析Bootstrap缩略图组件与警示框组件
Apr 29 Javascript
jQuery利用sort对DOM元素进行排序操作
Nov 07 Javascript
Ajax 加载数据 练习代码
Jan 05 Javascript
JS利用正则表达式实现简单的密码强弱判断实例
Jun 16 Javascript
Node.JS更改Windows注册表Regedit的方法小结
Aug 18 Javascript
JS实现带动画的回到顶部效果
Dec 28 Javascript
Nginx设置为Node.js的前端服务器方法总结
Mar 27 Javascript
关于小程序优化的一些建议(小结)
Dec 10 Javascript
Vue——前端生成二维码的示例
Dec 19 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
星际中的相关伤害
2020/03/04 星际争霸
php 读取文件头判断文件类型的实现代码
2013/08/05 PHP
php创建sprite
2014/02/11 PHP
Javascript的IE和Firefox兼容性汇编(zz)
2007/02/02 Javascript
Node.js模块加载详解
2014/08/16 Javascript
node.js下when.js 的异步编程实践
2014/12/03 Javascript
JS及PHP代码编写八大排序算法
2016/07/12 Javascript
AngularJS Select(选择框)使用详解
2017/01/18 Javascript
jquery封装插件时匿名函数形参和实参的写法解释
2017/02/14 Javascript
值得分享和收藏的xmlplus组件学习教程
2017/05/05 Javascript
NodeJS设计模式总结【单例模式,适配器模式,装饰模式,观察者模式】
2017/09/06 NodeJs
vue使用keep-alive实现数据缓存不刷新
2017/10/21 Javascript
vue中v-for通过动态绑定class实现触发效果
2018/12/06 Javascript
Nodejs核心模块之net和http的使用详解
2019/04/02 NodeJs
详解VSCode配置启动Vue项目
2019/05/14 Javascript
微信小程序自定义单项选择器样式
2019/07/25 Javascript
JavaScript使用prototype属性实现继承操作示例
2020/05/22 Javascript
JS禁用右键、禁用Ctrl+u、禁用Ctrl+s、禁用F12的实现代码
2020/12/01 Javascript
vue-cli中实现响应式布局的方法
2021/03/02 Vue.js
python实现dnspod自动更新dns解析的方法
2014/02/14 Python
python中使用xlrd、xlwt操作excel表格详解
2015/01/29 Python
基于python的字节编译详解
2017/09/20 Python
Python简单计算数组元素平均值的方法示例
2017/12/26 Python
python实现画圆功能
2018/01/25 Python
python的内存管理和垃圾回收机制详解
2019/05/18 Python
浅谈Python类中的self到底是干啥的
2019/11/11 Python
Django websocket原理及功能实现代码
2020/11/14 Python
详解Html5 Canvas画线有毛边解决方法
2018/03/01 HTML / CSS
会计专业毕业生推荐信
2013/11/05 职场文书
行政工作个人的自我评价
2014/02/13 职场文书
大学自主招生自荐信范文
2014/02/26 职场文书
企业开业庆典答谢词
2015/01/20 职场文书
求职简历自我评价2015
2015/03/10 职场文书
2015年见习期个人工作总结
2015/05/28 职场文书
MySQL Shell的介绍以及安装
2021/04/24 MySQL
教你做个可爱的css滑动导航条
2021/06/15 HTML / CSS