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


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 相关文章推荐
百度 popup.js 完美修正版非常的不错 脚本之家推荐
Apr 17 Javascript
jQuery 动态酷效果实现总结
Dec 27 Javascript
JavaScript 放大镜 移动镜片效果代码
May 09 Javascript
JS实现文件动态顺序载入的方法
Mar 07 Javascript
jquery实现简易的移动端验证表单
Nov 08 Javascript
基于jQuery实现左右图片轮播(原理通用)
Dec 24 Javascript
JS函数的定义与调用方法推荐
May 12 Javascript
AngularJS基础 ng-repeat 指令简单示例
Aug 03 Javascript
C#微信小程序服务端获取用户解密信息实例代码
Mar 10 Javascript
详解Vue2 SSR 缓存 Api 数据
Nov 20 Javascript
Angular使用ControlValueAccessor创建自定义表单控件
Mar 08 Javascript
仿照Element-ui实现一个简易的$message方法
Sep 14 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上传文件中文文件名乱码的解决方法
2013/11/01 PHP
php根据身份证号码计算年龄的实例代码
2014/01/18 PHP
PHP获取短链接跳转后的真实地址和响应头信息的方法
2014/07/25 PHP
Thinkphp自定义代码生成工具及用法说明(附下载地址)
2016/05/27 PHP
PHP获取ttf格式文件字体名的方法示例
2019/03/06 PHP
YII2框架中使用RBAC对模块,控制器,方法的权限控制及规则的使用示例
2020/03/18 PHP
JS option location 页面跳转实现代码
2008/12/27 Javascript
JavaScript 权威指南(第四版) 读书笔记
2009/08/11 Javascript
尝试在让script的type属性等于text/html
2013/01/15 Javascript
js数组转json并在后台对其解析具体实现
2013/11/20 Javascript
使用jquery实现的一个图片延迟加载插件(含图片延迟加载原理)
2014/06/05 Javascript
javascript中函数作为参数调用的方法
2015/02/09 Javascript
简介JavaScript中toTimeString()方法的使用
2015/06/12 Javascript
javascript实现禁止复制网页内容汇总
2015/12/30 Javascript
jQuery之简单的表单验证实例
2016/07/07 Javascript
AngularJS基础 ng-if 指令用法
2016/08/01 Javascript
详解JavaScript权威指南之对象
2016/09/27 Javascript
vuex存储token示例
2019/11/11 Javascript
vue 实现websocket发送消息并实时接收消息
2019/12/09 Javascript
webpack 最佳配置指北(推荐)
2020/01/07 Javascript
JS变量提升及函数提升实例解析
2020/09/03 Javascript
[01:50]WODOTA制作 DOTA2中文宣传片《HERO》
2013/04/28 DOTA
[39:11]DOTA2上海特级锦标赛C组资格赛#2 LGD VS Newbee第二局
2016/02/28 DOTA
[02:23]完美世界全国高校联赛街访DOTA2第一期
2019/11/28 DOTA
将Python中的数据存储到系统本地的简单方法
2015/04/11 Python
tensorflow 恢复指定层与不同层指定不同学习率的方法
2018/07/26 Python
win7下 python3.6 安装opencv 和 opencv-contrib-python解决 cv2.xfeatures2d.SIFT_create() 的问题
2019/10/24 Python
缓解脚、腿和背部疼痛:Z-CoiL鞋
2019/03/12 全球购物
巴西24小时在线药房:Droga Raia
2020/05/12 全球购物
《菜园里》教学反思
2014/04/17 职场文书
安全协议书
2014/04/23 职场文书
2014年管理工作总结
2014/11/22 职场文书
教学副校长工作总结
2015/08/13 职场文书
使用golang编写一个并发工作队列
2021/05/08 Golang
Python 中random 库的详细使用
2021/06/03 Python
关于python3 opencv 图像二值化的问题(cv2.adaptiveThreshold函数)
2022/04/04 Python