微信小程序对图片进行canvas压缩的方法示例详解


Posted in Javascript onNovember 12, 2020

微信小程序其实自带一个图片压缩的API wx.compressImage,但是这玩意目前感受就是个垃圾。IOS大多数情况下据说还可以,安卓有的时候降低质量压缩后体积反而变大,而且没办法控制其压缩至具体指定的大小,压缩后多大看天意。所以需要使用画布去自己实现一个图片压缩方法。

简单来讲原理就是:找个不显示在页面上的画布画上去,再取出,如果体积还是太大,缩小尺寸后再画,再取,递归下去,直到体积满足要求。(所以限制的越小,图片越大,压缩越久,递归次数越多)

第一步:新建一个zipPic.js文件(名字你开心就好),里面的代码如下

//通过canvas将图片压缩至指定大小

//判断图片大小是否满足需求,limitSize的单位是kb
function imageSizeIsLessLimitSize(imagePath,limitSize,lessCallback,moreCallback){
 //获取文件信息
 wx.getFileInfo({
 filePath:imagePath,
 success:(res)=>{
  console.log("压缩前图片大小",res.size/1024,'kb');
  //如果图片太大了走moreCallback
  if(res.size>1024*limitSize){
  moreCallback()
  }
  //图片满足要求了走lessCallback
  else{
  lessCallback()
  }
 }
 })
}

//将图片画在画布上并获取画好之后的图片的路径
function getCanvasImage(canvasId,imagePath,imageW,imageH,getImgSuccess){
 //创建画布内容
 const ctx=wx.createCanvasContext(canvasId);
 //图片画上去,imageW和imageH是画上去的尺寸,图像和画布间隔都是0
 ctx.drawImage(imagePath,0,0,imageW,imageH);
 //这里一定要加定时器,给足够的时间去画(所以每次递归最少要耗时200ms,多次递归很耗时!)
 ctx.draw(false,setTimeout(function(){
 wx.canvasToTempFilePath({
  canvasId:canvasId,
  x:0,
  y:0,
  width:imageW,
  height:imageH,
  quality:1, //最高质量,只通过尺寸放缩去压缩,画的时候都按最高质量来画
  success:(res)=>{
  getImgSuccess(res.tempFilePath);
  }
 })
 },200));
}

//主函数,默认限制大小1024kb即1mb,drawWidth是绘画区域的大小
//初始值传入为画布自身的边长(我们这是一个正方形的画布)
function getLessLimitSizeImage(canvasId,imagePath,limitSize=1024,drawWidth,callback){
 //判断图片尺寸是否满足要求
 imageSizeIsLessLimitSize(imagePath,limitSize,
 (lessRes)=>{
  //满足要求走callback,将压缩后的文件路径返回
  callback(imagePath);
 },
 (moreRes)=>{
  //不满足要求需要压缩的时候
  wx.getImageInfo({
  src:imagePath,
  success:(imageInfo)=>{
   let maxSide=Math.max(imageInfo.width,imageInfo.height);
   let windowW=drawWidth;
   let scale=1;
   /*
   这里的目的是当绘画区域缩小的比图片自身尺寸还要小的时候
   取图片长宽的最大值,然后和当前绘画区域计算出需要放缩的比例
   然后再画经过放缩后的尺寸,保证画出的一定是一个完整的图片。由于每次递归绘画区域都会缩小,
   所以不用担心scale永远都是1绘画尺寸永远不变的情况,只要不满足压缩后体积的要求
   就会缩小绘画区域,早晚会有绘画区域小于图片尺寸的情况发生
   */
   if(maxSide>windowW){
   scale=windowW/maxSide;
   }
   //trunc是去掉小数
   let imageW=Math.trunc(imageInfo.width*scale);
   let imageH=Math.trunc(imageInfo.height*scale);
   console.log('调用压缩',imageW,imageH);
   //图片在规定绘画区域上画并获取新的图片的path
   getCanvasImage(canvasId,imagePath,imageW,imageH,
   (pressImgPath)=>{
    /*
    再去检查是否满足要求,始终缩小绘画区域,让图片适配绘画区域
    这里乘以0.95是必须的,如果不缩小绘画区域,会出现尺寸比绘画区域小,
    而体积比要求压缩体积大的情况出现,就会无穷递归下去,因为scale的值永远是1
    但0.95不是固定的,你可以根据需要自己改,0到1之间,越小则绘画区域缩小的越快
    但不建议取得太小,绘画区域缩小的太快,压出来的将总是很糊的
    */
    getLessLimitSizeImage(canvasId,pressImgPath,limitSize,drawWidth*0.95,callback);
   }
   )
  }
  })
 }
 )
}

export default getLessLimitSizeImage

好的接下来是使用的方法:

在你想压缩图片的js代码所对应的页面中。先放置一个用户看不见的画布。

(就是如果我想在index.js中chooseImage再压缩,就需要你在index.html中加上下面的html代码)

<!--用于图片压缩的canvas画布,不在页面中展示,且id固定不可变-->
 <canvas
 style="width: {{cw}}px; height: {{cw}}px;position: absolute; z-index: -1; left: -10000rpx;; top: -10000rpx;"
 canvas-id="zipCanvas"
 ></canvas>
 <!--画布结束-->

其中cw的值我个人建议选择用户屏幕的宽度,如下,在page({…})的data中添加

//画板边长默认是屏幕宽度,正方形画布
 cw:wx.getSystemInfoSync().windowWidth,

个人建议画布和绘画区域都是正方形的,毕竟你也不知道要压缩的图片是横向的还是纵向的。

然后,引入,不解释

import getLessLimitSizeImage from '../../utils/zipPic'

在js代码中:

wx.chooseImage({
  count:1, //只传一张
  sizeType:'original', //原图质量好,然后通过canvas压缩,缩略图压缩就太糊了
  sourceType: ['album', 'camera'], // 来源是相册和相机
  success:(res)=>{
  let canvasId='zipCanvas' //注意这里的id和你在页面中写的html代码的canvas的id要一致
  let imagePath=res.tempFilePaths[0];//原图的路径
  let limitSize=2048;//大小限制2048kb
  let drawWidth=wx.getSystemInfoSync().windowWidth;//初始绘画区域是画布自身的宽度也就是屏幕宽度
  wx.showLoading({title:'图片压缩中...',mask:true}) //不需要你可以删掉
  getLessLimitSizeImage(canvasId,imagePath,limitSize,drawWidth,(resPath)=>{
   wx.hideLoading(); //不需要你可以删掉
   
   //resPath就是压缩后图片的路径,然后想做什么都随你
   
  })
  }
 })

补充:

  1. 这里代码的主体不是我做的,网上一搜基本都是这个写法,这里是经过项目实践测试后没问题了做的讲解。
  2. 这里图片是只选了一张去压缩,如果你需要选多张再挨个压缩那就去写个循环,找个数组存压缩后的结果,网上也有很多内容。
  3. 回调函数中有lessRes和moreRes,细心的会发现这两个参数并没有被用到,他们只是个提醒作用,表明当前是less回调还是more回调,如果你不怕弄混删掉了或者自己另外写了两个新方法那都随你。
  4. 极限情况下比如说将图片强制压缩至10kb,这个东西我没测试过,不知道会不会有问题。
  5. 图片压缩体积的减小不是线性的,给人的感觉有点像二次函数(y=x^2左面那一半),越往后压缩的尺寸变化会越小。当然,这和用户的分辨率,屏幕本身的大小都有关系。
  6. 还是那句话,由于每次递归都要给至少200ms的时间去画,所以递归很耗时!!!而不递归进行压缩的话,网络传输又会很耗时!!!所以这个地方怎么取舍,压缩至多大,绘画区域缩小的多快,都要靠你自己的经验去调试。
  7. 图片的压缩,长宽比理论上来讲是不变的,但是因为舍弃了小数,可能会有肉眼难以察觉的误差,但是问题不大。如果前端想展示一下压缩后的图片的话,不要忘记在image中加入mode=“aspectFit” 。

到此这篇关于微信小程序对图片进行canvas压缩的文章就介绍到这了,更多相关微信小程序对图片canvas压缩内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
jquery HotKeys轻松搞定键盘事件代码
Aug 30 Javascript
js列举css中所有图标的实现代码
Jul 04 Javascript
关于jQuery中.attr()和.prop()的问题探讨
Sep 06 Javascript
js判断登录与否并确定跳转页面的方法
Jan 30 Javascript
基于JavaScript实现单选框下拉菜单添加文件效果
Jun 26 Javascript
JS计算输出100元钱买100只鸡问题的解决方法
Jan 04 Javascript
浅谈jquery fullpage 插件增加头部和版权的方法
Mar 20 jQuery
使用JS代码实现俄罗斯方块游戏
Aug 03 Javascript
layui问题之模拟select点击事件的实例讲解
Aug 15 Javascript
vue watch关于对象内的属性监听
Apr 22 Javascript
使用imba.io框架得到比 vue 快50倍的性能基准
Jun 17 Javascript
解决vue单页面 回退页面 keeplive 缓存问题
Jul 22 Javascript
解决vuex改变了state的值,但是页面没有更新的问题
Nov 12 #Javascript
javascript实现搜索筛选功能实例代码
Nov 12 #Javascript
vue实现两个组件之间数据共享和修改操作
Nov 12 #Javascript
vue 解决mintui弹窗弹起来,底部页面滚动bug问题
Nov 12 #Javascript
vue打包通过image-webpack-loader插件对图片压缩优化操作
Nov 12 #Javascript
详解ES6 扩展运算符的使用与注意事项
Nov 12 #Javascript
在vue中使用image-webpack-loader实例
Nov 12 #Javascript
You might like
php简单开启gzip压缩方法(zlib.output_compression)
2013/04/13 PHP
Zend Guard使用指南及问题处理
2015/01/07 PHP
php采集中国代理服务器网的方法
2015/06/16 PHP
PHP+MySQL之Insert Into数据插入用法分析
2015/09/27 PHP
php使用文本统计访问量的方法
2016/05/12 PHP
玩转jQuery按钮 请告诉我你最喜欢哪些?
2012/01/08 Javascript
JavaScript返回0-1之间随机数的方法
2015/04/06 Javascript
浅谈JavaScript 浏览器对象
2016/06/03 Javascript
nodejs读写json文件的简单方法(必看)
2017/03/09 NodeJs
从零开始学习Node.js系列教程之SQLite3和MongoDB用法分析
2017/04/13 Javascript
js实现简易聊天对话框
2017/08/17 Javascript
Vue开发之封装分页组件与使用示例
2019/04/25 Javascript
vue随机验证码组件的封装实现
2020/02/19 Javascript
《javascript设计模式》学习笔记四:Javascript面向对象程序设计链式调用实例分析
2020/04/07 Javascript
[01:33:59]真人秀《加油 DOTA》 第六期
2014/09/09 DOTA
[01:08:33]OG vs VGJ.T 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
Python中用Decorator来简化元编程的教程
2015/04/13 Python
Python程序中用csv模块来操作csv文件的基本使用教程
2016/03/03 Python
Python操作SQLite数据库过程解析
2019/09/02 Python
Python SELENIUM上传文件或图片实现过程
2019/10/28 Python
python 通过邮件控制实现远程控制电脑操作
2020/03/16 Python
python json.dumps中文乱码问题解决
2020/04/01 Python
Python 实现RSA加解密文本文件
2020/12/30 Python
Html5 video标签视频的最佳实践
2020/02/26 HTML / CSS
美国综合购物商城:UnbeatableSale.com
2018/11/28 全球购物
手工制作的意大利礼服鞋:Ace Marks
2018/12/15 全球购物
物业经理求职自我评价
2013/09/22 职场文书
大门门卫岗位职责
2013/11/30 职场文书
大型会议接待方案
2014/03/01 职场文书
企业厂务公开实施方案
2014/03/26 职场文书
企业安全生产标语
2014/06/06 职场文书
工地安全生产标语
2014/06/06 职场文书
党的群众教育实践活动实施方案
2014/06/12 职场文书
85句关于理想的名言警句大全
2019/08/22 职场文书
2019年冬至:天冷暖人心的问候祝福语大全
2019/12/20 职场文书
Python爬虫中urllib3与urllib的区别是什么
2021/07/21 Python