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


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 相关文章推荐
复制网页内容,粘贴之后自动加上网址的实现方法(脚本之家特别整理)
Oct 16 Javascript
初识Node.js
Mar 20 Javascript
简单的分页代码js实现
May 17 Javascript
javascript创建含数字字母的随机字符串方法总结
Aug 01 Javascript
jQuery绑定事件的四种方式介绍
Oct 31 Javascript
bootstrap实现每隔5秒自动轮播效果
Dec 20 Javascript
js实现背景图自适应窗口大小
Jan 10 Javascript
从零学习node.js之搭建http服务器(二)
Feb 21 Javascript
详解使用webpack+electron+reactJs开发windows桌面应用
Feb 01 Javascript
使用jQuery如何写一个含验证码的登录界面
May 13 jQuery
通过高德地图API获得某条道路上的所有坐标用于描绘道路的方法
Aug 24 Javascript
Antd的table组件表格的序号自增操作
Oct 27 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
zend framework多模块多布局配置
2011/02/26 PHP
PHP的数组中提高元素查找与元素去重的效率的技巧解析
2016/03/03 PHP
YII2框架中查询生成器Query()的使用方法示例
2020/03/18 PHP
基于PHP实现解密或加密Cloudflar邮箱保护
2020/06/24 PHP
如何重写Laravel异常处理类详解
2020/12/20 PHP
JQuery获取元素文档大小、偏移和位置和滚动条位置的方法集合
2010/01/12 Javascript
nodejs的require模块(文件模块/核心模块)及路径介绍
2013/01/14 NodeJs
Javascript高级技巧分享
2014/02/25 Javascript
深入理解javascript原型链和继承
2014/09/23 Javascript
html的DOM中Event对象onabort事件用法实例
2015/01/21 Javascript
javascript中DOM复选框选择用法实例
2015/05/14 Javascript
js图片翻书效果代码分享
2015/08/20 Javascript
Bootstrap基本组件学习笔记之导航(10)
2016/12/07 Javascript
Vue概念及常见命令介绍(1)
2016/12/08 Javascript
JavaScript中匿名函数的递归调用
2017/01/22 Javascript
vue router学习之动态路由和嵌套路由详解
2017/09/21 Javascript
浅谈Vue-cli 命令行工具分析
2017/11/22 Javascript
微信小程序使用swiper组件实现层叠轮播图
2018/11/04 Javascript
微信小程序实现折线图的示例代码
2019/06/07 Javascript
Python实现竖排打印传单手机号码易撕条
2015/03/16 Python
python中函数总结之装饰器闭包详解
2016/06/12 Python
Python 模块EasyGui详细介绍
2017/02/19 Python
Python中 传递值 和 传递引用 的区别解析
2018/02/22 Python
用python实现k近邻算法的示例代码
2018/09/06 Python
在Python中构建增广矩阵的实现方法
2019/07/01 Python
Python替换月份为英文缩写的实现方法
2019/07/15 Python
解决Python3 控制台输出InsecureRequestWarning问题
2019/07/15 Python
python3发送request请求及查看返回结果实例
2020/04/30 Python
HTML5 绘制图像(上)之:关于canvas元素引领下一代web页面的问题
2013/04/24 HTML / CSS
学术会议邀请函范文
2014/01/22 职场文书
诚实守信演讲稿
2014/09/01 职场文书
升学宴学生答谢词
2015/01/05 职场文书
加薪申请报告范本
2015/05/15 职场文书
投诉信范文
2015/07/02 职场文书
2015秋季运动会通讯稿
2015/07/18 职场文书
四十九个javascript小知识实用技巧
2021/11/20 Javascript