微信小程序对图片进行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 相关文章推荐
JS 去除Array中的null值示例代码
Nov 20 Javascript
flash遮住div问题的正确解决方法
Feb 27 Javascript
Node.js中使用Buffer编码、解码二进制数据详解
Aug 16 Javascript
jQuery实现的简单折叠菜单(折叠面板)效果代码
Sep 16 Javascript
Javascript字符串拼接小技巧(推荐)
Jun 02 Javascript
Bootstrap CSS组件之输入框组
Dec 17 Javascript
JavaScript实现自定义媒体播放器方法介绍
Jan 03 Javascript
微信端开发--登录小程序步骤
Jan 11 Javascript
vue2.0 实现导航守卫的具体用法(路由守卫)
May 17 Javascript
如何让node运行es6模块文件及其原理详解
Dec 11 Javascript
vue 路由懒加载中给 Webpack Chunks 命名的方法
Apr 24 Javascript
vue2实现provide inject传递响应式
May 21 Vue.js
解决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
关于时间计算的结总
2006/12/06 PHP
php用xpath解析html的代码实例讲解
2019/02/14 PHP
PHP实现笛卡尔积算法的实例讲解
2019/12/22 PHP
JavaScript中计算网页中某个元素的位置
2015/06/10 Javascript
详解JavaScript对Date对象的操作问题(生成一个倒数7天的数组)
2015/10/01 Javascript
Angular2实现自定义双向绑定属性
2017/03/22 Javascript
vue.js给动态绑定的radio列表做批量编辑的方法
2018/02/28 Javascript
Vue.js实现可配置的登录表单代码详解
2018/03/29 Javascript
简单的vuex 的使用案例笔记
2018/04/13 Javascript
微信小程序实现默认第一个选中变色效果
2018/07/17 Javascript
jQuery仿移动端支付宝键盘的实现代码
2018/08/15 jQuery
JQuery模拟实现网页中自定义鼠标右键菜单功能
2018/11/14 jQuery
Vuex mutitons和actions初使用详解
2019/03/04 Javascript
详解VScode编辑器vue环境搭建所遇问题解决方案
2019/04/26 Javascript
layui prompt 设置允许空白提交的方法
2019/09/24 Javascript
JavaScript监听触摸事件代码实例
2019/12/30 Javascript
微信小程序实现单个或多个倒计时功能
2020/11/01 Javascript
python文件操作整理汇总
2014/10/21 Python
通过数据库对Django进行删除字段和删除模型的操作
2015/07/21 Python
Django学习笔记之ORM基础教程
2018/03/27 Python
Python之循环结构
2019/01/15 Python
python向字符串中添加元素的实例方法
2019/06/28 Python
python 项目目录结构设置
2020/02/14 Python
详解Python中pyautogui库的最全使用方法
2020/04/01 Python
DjangoWeb使用Datatable进行后端分页的实现
2020/05/18 Python
基于Keras 循环训练模型跑数据时内存泄漏的解决方式
2020/06/11 Python
教育孩子心得体会
2014/01/01 职场文书
城市创卫标语
2014/06/17 职场文书
授权委托书
2014/09/17 职场文书
工商局局长个人对照检查材料思想汇报
2014/09/23 职场文书
银行领导班子四风对照检查材料
2014/09/27 职场文书
学生个人总结范文
2015/02/15 职场文书
图书馆义工感想
2015/08/07 职场文书
Python实现文字pdf转换图片pdf效果
2022/04/03 Python
Golang map映射的用法
2022/04/22 Golang