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


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 带有滚动条的表格,标题固定,带排序功能.
Nov 13 Javascript
JS维吉尼亚密码算法实现代码
Nov 09 Javascript
JS 按钮点击触发(兼容IE、火狐)
Aug 07 Javascript
浅析hasOwnProperty方法的应用
Nov 20 Javascript
jquery使用正则表达式验证email地址的方法
Jan 22 Javascript
javascript解析xml实现省市县三级联动的方法
Jul 25 Javascript
学习javascript面向对象 掌握创建对象的9种方式
Jan 04 Javascript
Javascript中获取浏览器类型和操作系统版本等客户端信息常用代码
Jun 28 Javascript
javascript html5轻松实现拖动功能
Mar 01 Javascript
Vue2.0+ElementUI实现表格翻页的实例
Oct 23 Javascript
JS实现获取word文档内容并输出显示到html页面示例
Jun 23 Javascript
详解Webpack多环境代码打包的方法
Aug 03 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 Header用于页面跳转时的几个注意事项
2016/10/21 PHP
Thinkphp5.0 框架视图view的比较标签用法分析
2019/10/12 PHP
php 自定义函数实现将数据 以excel 表格形式导出示例
2019/11/13 PHP
JQuery伸缩导航练习示例
2013/11/13 Javascript
JQuery 图片滚动轮播示例代码
2014/03/24 Javascript
chrome下jq width()方法取值为0的解决方法
2014/05/26 Javascript
jQuery对JSON数据进行排序输出的方法
2015/06/24 Javascript
再谈JavaScript线程
2015/07/10 Javascript
去除字符串左右两边的空格(实现代码)
2016/05/12 Javascript
Bootstrap框架的学习教程详解(二)
2016/10/18 Javascript
如何判断出一个js对象是否一个dom对象
2016/11/24 Javascript
微信小程序 向左滑动删除功能的实现
2017/03/10 Javascript
vue实现app页面切换动画效果实例
2017/05/23 Javascript
jQuery实现下拉菜单动态添加数据点击滑出收起其他功能
2018/06/14 jQuery
深入浅析AngularJs模版与v-bind
2018/07/06 Javascript
微信小程序如何修改本地缓存key中单个数据的详解
2019/04/26 Javascript
基于elementUI竖向表格、和并列的案例
2020/10/26 Javascript
VUE项目实现主题切换的多种方法
2020/11/26 Vue.js
关于小程序优化的一些建议(小结)
2020/12/10 Javascript
通过Python来使用七牛云存储的方法详解
2015/08/07 Python
Python基于递归实现电话号码映射功能示例
2018/04/13 Python
Python3使用正则表达式爬取内涵段子示例
2018/04/22 Python
python 以16进制打印输出的方法
2018/07/09 Python
在pycharm上mongodb配置及可视化设置方法
2018/11/30 Python
python不到50行代码完成了多张excel合并的实现示例
2020/05/28 Python
python不同系统中打开方法
2020/06/23 Python
利用CSS3的transition属性实现滑动效果
2015/08/05 HTML / CSS
智乐游戏测试笔试题
2014/05/21 面试题
自学考试自我鉴定范文
2013/09/26 职场文书
电脑饰品店的创业计划书
2014/01/21 职场文书
反邪教警示教育方案
2014/05/13 职场文书
小学教研工作总结2015
2015/05/13 职场文书
如何利用python和DOS获取wifi密码
2021/03/31 Python
PHP 技巧 * SVG 保存为图片(分享图生成)
2021/04/02 PHP
MySQL主从搭建(多主一从)的实现思路与步骤
2021/05/13 MySQL
springboot @ConfigurationProperties和@PropertySource的区别
2021/06/11 Java/Android