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


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 相关文章推荐
不能再简单的无闪刷新验证码原理很简单
Nov 05 Javascript
始终在屏幕中间显示Div的代码(css+js)
Mar 10 Javascript
jQuery 选择器项目实例分析及实现代码
Dec 28 Javascript
JavaScript方法_动力节点Java学院整理
Jun 28 Javascript
vue 设置路由的登录权限的方法
Jul 03 Javascript
node.js到底要不要加分号浅析
Jul 11 Javascript
JavaScript实现数字前补“0”的五种方法示例
Jan 03 Javascript
js中Generator函数的深入讲解
Apr 07 Javascript
详解 微信小程序开发框架(MINA)
May 17 Javascript
利用d3.js制作连线动画图与编辑器的方法实例
Sep 05 Javascript
layui前端时间戳转化实例
Nov 15 Javascript
JS在Array数组中按指定位置删除或添加元素对象方法示例
Nov 19 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 数组的创建、调用和更新实现代码
2009/03/09 PHP
PHP多文件上传类实例
2015/03/07 PHP
php解析url并得到url中的参数及获取url参数的四种方式
2015/10/26 PHP
PHP简单实现生成txt文件到指定目录的方法
2016/04/25 PHP
jQuery链式调用与show知识浅析
2016/05/11 Javascript
jquery ajax后台返回list,前台用jquery遍历list的实现
2016/10/30 Javascript
JavaScript生成.xls文件的代码
2016/12/22 Javascript
自制简易打赏功能的实例
2017/09/02 Javascript
vue组件父与子通信详解(一)
2017/11/07 Javascript
Angular 容器部署的方法
2018/04/17 Javascript
Vue cli构建及项目打包以及出现的问题解决
2018/08/27 Javascript
Vue3 中的数据侦测的实现
2019/10/09 Javascript
小程序选项卡以及swiper套用(跨页面)
2020/06/19 Javascript
vue基于better-scroll实现左右联动滑动页面
2020/06/30 Javascript
听歌识曲--用python实现一个音乐检索器的功能
2016/11/15 Python
基于Django的python验证码(实例讲解)
2017/10/23 Python
pytorch中如何使用DataLoader对数据集进行批处理的方法
2019/08/06 Python
基于python进行抽样分布描述及实践详解
2019/09/02 Python
详解Python实现进度条的4种方式
2020/01/15 Python
Python接口测试结果集实现封装比较
2020/05/01 Python
浅谈keras使用预训练模型vgg16分类,损失和准确度不变
2020/07/02 Python
html5 桌面提醒:Notifycations应用介绍
2012/11/27 HTML / CSS
荷兰皇家航空公司官方网站:KLM Royal Dutch Airlines
2017/12/07 全球购物
英国行业制服供应商:Alexandra
2019/09/14 全球购物
参观监狱心得体会
2014/01/02 职场文书
初级会计求职信范文
2014/02/15 职场文书
创建卫生先进单位实施方案
2014/03/10 职场文书
英文演讲稿开场白
2014/08/25 职场文书
我与祖国共奋进演讲稿
2014/09/13 职场文书
民间个人借款协议书
2014/09/30 职场文书
学校党的群众路线教育实践活动总结材料
2014/10/30 职场文书
群众路线教育实践活动学习笔记
2014/11/05 职场文书
2016年校园社会综合治理宣传月活动总结
2016/03/16 职场文书
Python3中最常用的5种线程锁实例总结
2021/07/07 Python
浅谈MySQL之select优化方案
2021/08/07 MySQL
MySQL 自动填充 create_time 和 update_time
2022/05/20 MySQL