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 继承实现方法
Aug 26 Javascript
javascript操作table(insertRow,deleteRow,insertCell,deleteCell方法详解)
Dec 16 Javascript
购物车选中得到价格实现示例
Jan 26 Javascript
解决extjs grid 不随窗口大小自适应的改变问题
Jan 26 Javascript
浅谈Javascript数据属性与访问器属性
Jul 26 Javascript
jQuery插件HighCharts实现的2D堆条状图效果示例【附demo源码下载】
Mar 14 Javascript
Angular中使用MathJax遇到的一些问题
Dec 15 Javascript
Node.JS循环删除非空文件夹及子目录下的所有文件
Mar 12 Javascript
解决vue中虚拟dom,无法实时更新的问题
Sep 15 Javascript
JS左右无缝轮播功能完整实例
May 16 Javascript
JS函数动态传递参数的方法分析【基于arguments对象】
Jun 05 Javascript
Javascript 类型转换、封闭函数及常见内置对象操作示例
Nov 15 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 远程文件管理,可以给表格排序,遍历目录,时间排序
2009/08/07 PHP
Ping服务的php实现方法,让网站快速被收录
2012/02/04 PHP
php提示无法加载或mcrypt没有找到 PHP 扩展 mbstring解决办法
2012/03/27 PHP
php中删除数组的第一个元素和最后一个元素的函数
2015/03/07 PHP
Gambit vs CL BO3 第二场 2.13
2021/03/10 DOTA
如何用js控制frame的隐藏或显示的解决办法
2013/03/20 Javascript
在页面加载完成后通过jquery给多个span赋值
2014/05/21 Javascript
javaScript中两个等于号和三个等于号之间的区别介绍
2014/06/27 Javascript
如何调试异步加载页面里包含的js文件
2014/10/30 Javascript
JS实现进入页面时渐变背景色的方法
2015/02/25 Javascript
JavaScript模拟实现键盘打字效果
2015/06/29 Javascript
常用javascript表单验证汇总
2020/07/20 Javascript
Javascript之String对象详解
2016/06/08 Javascript
JS封装的三级联动菜单(使用时只需要一行js代码)
2016/10/24 Javascript
IntersectionObserver实现图片懒加载的示例
2017/09/29 Javascript
详解在create-react-app使用less与antd按需加载
2018/12/06 Javascript
微信小程序实现动态显示和隐藏某个控件功能示例
2018/12/14 Javascript
用Python实现一个简单的多线程TCP服务器的教程
2015/05/05 Python
在Python操作时间和日期之asctime()方法的使用
2015/05/22 Python
python实现比较两段文本不同之处的方法
2015/05/30 Python
Python应用03 使用PyQT制作视频播放器实例
2016/12/07 Python
python调用Delphi写的Dll代码示例
2017/12/05 Python
python 通过可变参数计算n个数的乘积方法
2019/06/13 Python
pycharm配置当鼠标悬停时快速提示方法参数
2019/07/31 Python
appium+python adb常用命令分享
2020/03/06 Python
Ubuntu20下的Django安装的方法步骤
2021/01/24 Python
新闻专业应届生求职信
2013/10/31 职场文书
互联网创业计划书写作技巧攻略
2014/03/23 职场文书
欢迎横幅标语
2014/06/17 职场文书
三严三实学习心得体会
2014/10/13 职场文书
2014年调度员工作总结
2014/11/19 职场文书
2014年档案室工作总结
2014/12/01 职场文书
2014年绿化工作总结
2014/12/09 职场文书
终止合同协议书范本
2016/03/22 职场文书
nginx限制并发连接请求数的方法
2021/04/01 Servers
Python如何将list中的string转换为int
2022/07/15 Ruby