JavaScript前端实现压缩图片功能


Posted in Javascript onMarch 06, 2020

为什么要前端来压缩图片

最近在做一个移动端h5上传图片的功能,本来这个功能并不复杂,只需要将图片文件通过axios传到服务端即可,但是考虑到现在手机设配的拍照功能十分强大,随便一张照片都能动辄五六兆,而服务端的要求是上传图片必须小于两兆,而且直接传这么大图片,带宽它也受不了,所以前端进行压缩图片就成了一个必要的环节。

压缩效果

JavaScript前端实现压缩图片功能

首先介绍下压缩的大概流程

  • 通过原生的input标签拿到要上传的图片文件
  • 将图片文件转化成img元素标签
  • 在canvas上压缩绘制该HTMLImageElement
  • 将canvas绘制的图像转成blob文件
  • 最后将该blob文件传到服务端
  • 完成!

接下来看下详细步骤

考虑到文章和步骤的完整性,所以我会把每个细节都写出来,即使有些东西很基础。

1.  使用Input标签来获取图片文件资源

这一步大家应该最熟悉不过了吧,原生input标签,通过设置 type 属性为file来让用户可以选择文件,设置 accept 限制选择的文件类型,绑定onchange事件,来获取确认选择后的文件

<input type="file" accept="image/*" />

点击控件,触发焦点,打开文件资源管理器,选中文件并确认后,会触发change事件,所以可以在change事件的回调中获取选中文件,它长这个样

JavaScript前端实现压缩图片功能

2. 读取文件转成img标签元素

拿到图片文件后,先将其转成HTMLImageElement,也就是普通的img标签,具体要使用 FileReader构造函数。

先new出来一个img和fileReader的实例,通过fileReader的 readAsDataURL这个api,来读取图片文件,其返回值是一个编码后的base64的字符串,然后将这个字符串赋值给img的src属性上,这样就完成了图片文件到 HTMLImageElement的转化。

// 先new一个img和fileReader的实例
const img = new Image()
const reader = new FileReader()// 读取文件资源
reader.readAsDataURL(file) 
reader.onload = function(e){ 
 img.src = e.target.result
}

转化的HTMLImageElement

JavaScript前端实现压缩图片功能

3. canvas压缩,核心步骤

拿到转化后的img元素后,先取出该元素的宽高度,这个宽高度就是实际图片文件的宽高度。

const { width: originWidth, height: originHeight } = img

然后定义一个最大限度的宽高度,如果超过这个限制宽高度,则进行等比例的缩放

// 最大尺寸限制
 const maxWidth = 1000,maxHeihgt = 1000
 // 需要压缩的目标尺寸
 let targetWidth = originWidth, targetHeight = originHeight
 // 等比例计算超过最大限制时缩放后的图片尺寸
 if (originWidth > maxWidth || originHeight > maxHeight) {
   if (originWidth / originHeight > 1) {
    // 宽图片
    targetWidth = maxWidth
    targetHeight = Math.round(maxWidth * (originHeight / originWidth))
   } else {
    // 高图片
    targetHeight = maxHeight
    targetWidth = Math.round(maxHeight * (originWidth / originHeight))
   }
  }

计算好将要压缩的尺寸后,创建canvas实例,设置canvas的宽高度为压缩计算后的尺寸,并将img绘制到上面

// 创建画布
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')

// 设置宽高度为等同于要压缩图片的尺寸
 canvas.width = targetWidth
 canvas.height = targetHeight
 context.clearRect(0, 0, targetWidth, targetHeight)
 //将img绘制到画布上
 context.drawImage(img, 0, 0, targetWidth, targetHeight)

4. 转成blob文件

canvas绘制完成后,就可以使用 toBlob来将图像转成blob文件了,这个api接受三个入参

canvas.toBlob(callback, type, encoderOptions);

回调函数中可以得到转化后的blob文件,type为要转成的图片类型,默认png。

encoderOptions为当设置的图片格式为 image/jpeg 或者 image/webp 时用来指定图片展示质量。

所以如果我们只是要压缩jpg或者webp格式的图片的话,不需要进行第3部的操作,直接使用这个api,然后填入想要的质量参数就可以了。但实际上,我们还是要考虑多种的图片格式,因此很有必要使用第三部的过程。

转成的blob长这个样子

JavaScript前端实现压缩图片功能

5. 将blob上传,大功告成。

完整的代码实现

因为整个过程中都存在着异步回调操作,所以我使用了async,实现异步代码的同步执行

// 压缩前将file转换成img对象
function readImg(file) {
 return new Promise((resolve, reject) => {
  const img = new Image()
  const reader = new FileReader()
  reader.onload = function(e) {
   img.src = e.target.result
  }
  reader.onerror = function(e) {
   reject(e)
  }
  reader.readAsDataURL(file)
  img.onload = function() {
   resolve(img)
  }
  img.onerror = function(e) {
   reject(e)
  }
 })
}
/**
 * 压缩图片
 *@param img 被压缩的img对象
 * @param type 压缩后转换的文件类型
 * @param mx 触发压缩的图片最大宽度限制
 * @param mh 触发压缩的图片最大高度限制
 */
function compressImg(img, type, mx, mh) {
 return new Promise((resolve, reject) => {
  const canvas = document.createElement('canvas')
  const context = canvas.getContext('2d')
  const { width: originWidth, height: originHeight } = img
  // 最大尺寸限制
  const maxWidth = mx
  const maxHeight = mh
  // 目标尺寸
  let targetWidth = originWidth
  let targetHeight = originHeight
  if (originWidth > maxWidth || originHeight > maxHeight) {
   if (originWidth / originHeight > 1) {
    // 宽图片
    targetWidth = maxWidth
    targetHeight = Math.round(maxWidth * (originHeight / originWidth))
   } else {
    // 高图片
    targetHeight = maxHeight
    targetWidth = Math.round(maxHeight * (originWidth / originHeight))
   }
  }
  canvas.width = targetWidth
  canvas.height = targetHeight
  context.clearRect(0, 0, targetWidth, targetHeight)
  // 图片绘制
  context.drawImage(img, 0, 0, targetWidth, targetHeight)
  canvas.toBlob(function(blob) {
   resolve(blob)
  }, type || 'image/png') })
}

大致执行过程,具体可根据需求,自行改动

async function upload(file){
  const img = await readImg(file)
  const blob = await compressImg(img, file.type, 1000, 1000)
  const formData = new FormData()
  formData.append('file', blob, 'xxx.jpg')
  axios.post('http://xxx.com/api',formData)
}
upload(file).catch(e => console.log(e))

到此这篇关于JavaScript前端实现压缩图片功能的文章就介绍到这了,更多相关JavaScript 压缩图片内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
javascript 精粹笔记
May 09 Javascript
jQuery选择器中含有空格的使用示例及注意事项
Aug 25 Javascript
简单的两种Extjs formpanel加载数据的方式
Nov 09 Javascript
jQuery实现表单步骤流程导航代码分享
Aug 28 Javascript
jQuery+Ajax实现无刷新操作
Jan 04 Javascript
实例讲解js验证表单项是否为空的方法
Jan 09 Javascript
JS获取元素多层嵌套思路详解
May 16 Javascript
用jQuery.ajaxSetup实现对请求和响应数据的过滤
Dec 20 Javascript
原生js实现放大镜效果
Jan 11 Javascript
Mongoose学习全面理解(推荐)
Jan 21 Javascript
Vue2.0实现组件数据的双向绑定问题
Mar 06 Javascript
JS精髓原型链继承及构造函数继承问题纠正
Jun 16 Javascript
vue2路由方式--嵌套路由实现方法分析
Mar 06 #Javascript
vue2路由基本用法实例分析
Mar 06 #Javascript
vue 动态组件用法示例小结
Mar 06 #Javascript
extjs图形绘制之饼图实现方法分析
Mar 06 #Javascript
extjs图表绘制之条形图实现方法分析
Mar 06 #Javascript
Node.js中文件系统fs模块的使用及常用接口
Mar 06 #Javascript
JavaScript中的this/call/apply/bind的使用及区别
Mar 06 #Javascript
You might like
php smarty模版引擎中的缓存应用
2009/12/11 PHP
PHP 八种基本的数据类型小结
2011/06/01 PHP
获取php页面执行时间,数据库读写次数,函数调用次数等(THINKphp)
2013/06/03 PHP
Discuz批量替换帖子内容的方法(使用SQL更新数据库)
2014/06/23 PHP
php 下 html5 XHR2 + FormData + File API 上传文件操作实例分析
2020/02/28 PHP
javascript 日期常用的方法
2009/11/11 Javascript
基于jQuery的倒计时插件代码
2011/05/07 Javascript
用JavaScript修改CSS属性的代码
2013/05/06 Javascript
jQuery中fadeIn、fadeOut、fadeTo的使用方法(图片显示与隐藏)
2013/05/08 Javascript
jQuery中json对象的复制方式介绍(数组及对象)
2013/06/08 Javascript
js防止DIV布局滚动时闪动的解决方法
2014/10/30 Javascript
bootstrap与Jquery UI 按钮样式冲突的解决办法
2016/09/23 Javascript
使用ES6语法重构React代码详解
2017/05/09 Javascript
vue+springboot前后端分离实现单点登录跨域问题解决方法
2018/01/30 Javascript
vue组件(全局,局部,动态加载组件)
2018/09/02 Javascript
详解mpvue scroll-view自动回弹bug解决方案
2018/10/01 Javascript
Vue中多个元素、组件的过渡及列表过渡的方法示例
2019/02/13 Javascript
Vue的transition-group与Virtual Dom Diff算法的使用
2019/12/09 Javascript
JavaScript HTML DOM 元素 (节点)新增,编辑,删除操作实例分析
2020/03/02 Javascript
Vue Router 实现动态路由和常见问题及解决方法
2020/03/06 Javascript
JavaScript实现网页计算器功能
2020/10/29 Javascript
vue实现简易计算器功能
2021/01/20 Vue.js
分析用Python脚本关闭文件操作的机制
2015/06/28 Python
Python 普通最小二乘法(OLS)进行多项式拟合的方法
2018/12/29 Python
Python绘制堆叠柱状图的实例
2019/07/09 Python
Pandas —— resample()重采样和asfreq()频度转换方式
2020/02/26 Python
新手学习Python2和Python3中print不同的用法
2020/06/09 Python
Python APScheduler执行使用方法详解
2020/12/10 Python
轻松掌握CSS3中的字体大小单位rem的使用方法
2016/05/24 HTML / CSS
详解HTML5中download属性的应用
2015/08/06 HTML / CSS
Lookfantastic西班牙官网:英国知名美妆购物网站
2018/06/13 全球购物
澳大利亚儿童鞋在线:The Trybe
2019/07/16 全球购物
优秀体育委员自荐书
2014/01/31 职场文书
批评与自我批评总结
2014/10/17 职场文书
中学生打架检讨书之500字
2019/08/06 职场文书
MySQL 计算连续登录天数
2022/05/11 MySQL