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


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选择器的工作原理和优化分析
Jul 25 Javascript
struts2+jquery+json实现异步加载数据(自写)
Jun 24 Javascript
JavaScript中的比较操作符&gt;、=、
Dec 31 Javascript
浅谈jQuery事件绑定原理
Jan 02 Javascript
javascript实现方法调用与方法触发小结
Mar 26 Javascript
BootStrap table删除指定行的注意事项(笔记整理)
Feb 05 Javascript
JS实现复选框的全选和批量删除功能
Apr 05 Javascript
详解原生js实现offset方法
Jun 15 Javascript
详解Vue 全局引入bass.scss 处理方案
Mar 26 Javascript
JS实现的简单下拉框联动功能示例
May 11 Javascript
10分钟彻底搞懂Http的强制缓存和协商缓存(小结)
Aug 30 Javascript
vue+echarts实现可拖动节点的折线图(支持拖动方向和上下限的设置)
Apr 12 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
SONY SRF-22W(33W)的电路分析和维修案例
2021/03/02 无线电
一个可以删除字符串中HTML标记的PHP函数
2006/10/09 PHP
在网页中控制wmplayer播放器
2006/07/01 Javascript
摘自百度的图片轮换效果代码
2007/11/19 Javascript
JavaScript调用Activex控件的事件的实现方法
2010/04/11 Javascript
从零开始学习jQuery (六) jquery中的AJAX使用
2011/02/23 Javascript
JSON字符串和对象之间的转换详解
2015/05/26 Javascript
JavaScript和JQuery的鼠标mouse事件冒泡处理
2015/06/19 Javascript
JS实现Fisheye效果动感放大菜单代码
2015/10/21 Javascript
jQuery中iframe的操作(点击按钮新增窗口)
2016/04/20 Javascript
AngularJS 路由和模板实例及路由地址简化方法(必看)
2016/06/24 Javascript
JavaScript中最容易混淆的作用域、提升、闭包知识详解(推荐)
2016/09/05 Javascript
vue实现添加标签demo示例代码
2017/01/21 Javascript
解决Vue页面固定滚动位置的处理办法
2017/07/13 Javascript
Javascript通过控制类名更改样式
2019/05/24 Javascript
Vue Element UI + OSS实现上传文件功能
2019/07/31 Javascript
解决python 无法加载downsample模型的问题
2018/10/25 Python
对django xadmin自定义菜单的实例详解
2019/01/03 Python
Python除法之传统除法、Floor除法及真除法实例详解
2019/05/23 Python
如何将你的应用迁移到Python3的三个步骤
2019/12/22 Python
python中判断数字是否为质数的实例讲解
2020/12/06 Python
Python3+Flask安装使用教程详解
2021/02/16 Python
HTML5 Canvas画线技巧——实现绘制一个像素宽的细线
2013/08/02 HTML / CSS
HTML5实现桌面通知 提示功能
2017/10/11 HTML / CSS
日本最佳原创设计品牌:Felissimo(芬理希梦)
2019/03/19 全球购物
奥地利度假券的专家:we-are.travel
2019/04/10 全球购物
医护人员英文求职信范文
2013/11/26 职场文书
学生周末长期请假条
2014/02/15 职场文书
环保公益广告语
2014/03/13 职场文书
财务会计大学生自我评价
2014/04/09 职场文书
传播学专业毕业生自荐书
2014/07/01 职场文书
城管执法人员个人对照检查材料思想汇报
2014/09/29 职场文书
会议通知
2015/04/15 职场文书
社区宣传标语口号
2015/12/26 职场文书
《法国号》教学反思
2016/02/22 职场文书
《传颂之物 虚伪的假面》BD发售宣传CM公开
2022/04/04 日漫