溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

微信小程序中使用canvas如何實(shí)現(xiàn)對(duì)進(jìn)行壓縮

發(fā)布時(shí)間:2020-11-16 15:20:12 來源:億速云 閱讀:349 作者:Leah 欄目:開發(fā)技術(shù)

微信小程序中使用canvas如何實(shí)現(xiàn)對(duì)進(jìn)行壓縮?相信很多沒有經(jīng)驗(yàn)的人對(duì)此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個(gè)問題。

微信小程序其實(shí)自帶一個(gè)圖片壓縮的API wx.compressImage,但是這玩意目前感受就是個(gè)垃圾。IOS大多數(shù)情況下?lián)f還可以,安卓有的時(shí)候降低質(zhì)量壓縮后體積反而變大,而且沒辦法控制其壓縮至具體指定的大小,壓縮后多大看天意。所以需要使用畫布去自己實(shí)現(xiàn)一個(gè)圖片壓縮方法。

簡單來講原理就是:找個(gè)不顯示在頁面上的畫布畫上去,再取出,如果體積還是太大,縮小尺寸后再畫,再取,遞歸下去,直到體積滿足要求。(所以限制的越小,圖片越大,壓縮越久,遞歸次數(shù)越多)

第一步:新建一個(gè)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){
 //創(chuàng)建畫布內(nèi)容
 const ctx=wx.createCanvasContext(canvasId);
 //圖片畫上去,imageW和imageH是畫上去的尺寸,圖像和畫布間隔都是0
 ctx.drawImage(imagePath,0,0,imageW,imageH);
 //這里一定要加定時(shí)器,給足夠的時(shí)間去畫(所以每次遞歸最少要耗時(shí)200ms,多次遞歸很耗時(shí)?。?
 ctx.draw(false,setTimeout(function(){
 wx.canvasToTempFilePath({
  canvasId:canvasId,
  x:0,
  y:0,
  width:imageW,
  height:imageH,
  quality:1, //最高質(zhì)量,只通過尺寸放縮去壓縮,畫的時(shí)候都按最高質(zhì)量來畫
  success:(res)=>{
  getImgSuccess(res.tempFilePath);
  }
 })
 },200));
}

//主函數(shù),默認(rèn)限制大小1024kb即1mb,drawWidth是繪畫區(qū)域的大小
//初始值傳入為畫布自身的邊長(我們這是一個(gè)正方形的畫布)
function getLessLimitSizeImage(canvasId,imagePath,limitSize=1024,drawWidth,callback){
 //判斷圖片尺寸是否滿足要求
 imageSizeIsLessLimitSize(imagePath,limitSize,
 (lessRes)=>{
  //滿足要求走callback,將壓縮后的文件路徑返回
  callback(imagePath);
 },
 (moreRes)=>{
  //不滿足要求需要壓縮的時(shí)候
  wx.getImageInfo({
  src:imagePath,
  success:(imageInfo)=>{
   let maxSide=Math.max(imageInfo.width,imageInfo.height);
   let windowW=drawWidth;
   let scale=1;
   /*
   這里的目的是當(dāng)繪畫區(qū)域縮小的比圖片自身尺寸還要小的時(shí)候
   取圖片長寬的最大值,然后和當(dāng)前繪畫區(qū)域計(jì)算出需要放縮的比例
   然后再畫經(jīng)過放縮后的尺寸,保證畫出的一定是一個(gè)完整的圖片。由于每次遞歸繪畫區(qū)域都會(huì)縮小,
   所以不用擔(dān)心scale永遠(yuǎn)都是1繪畫尺寸永遠(yuǎn)不變的情況,只要不滿足壓縮后體積的要求
   就會(huì)縮小繪畫區(qū)域,早晚會(huì)有繪畫區(qū)域小于圖片尺寸的情況發(fā)生
   */
   if(maxSide>windowW){
   scale=windowW/maxSide;
   }
   //trunc是去掉小數(shù)
   let imageW=Math.trunc(imageInfo.width*scale);
   let imageH=Math.trunc(imageInfo.height*scale);
   console.log('調(diào)用壓縮',imageW,imageH);
   //圖片在規(guī)定繪畫區(qū)域上畫并獲取新的圖片的path
   getCanvasImage(canvasId,imagePath,imageW,imageH,
   (pressImgPath)=>{
    /*
    再去檢查是否滿足要求,始終縮小繪畫區(qū)域,讓圖片適配繪畫區(qū)域
    這里乘以0.95是必須的,如果不縮小繪畫區(qū)域,會(huì)出現(xiàn)尺寸比繪畫區(qū)域小,
    而體積比要求壓縮體積大的情況出現(xiàn),就會(huì)無窮遞歸下去,因?yàn)閟cale的值永遠(yuǎn)是1
    但0.95不是固定的,你可以根據(jù)需要自己改,0到1之間,越小則繪畫區(qū)域縮小的越快
    但不建議取得太小,繪畫區(qū)域縮小的太快,壓出來的將總是很糊的
    */
    getLessLimitSizeImage(canvasId,pressImgPath,limitSize,drawWidth*0.95,callback);
   }
   )
  }
  })
 }
 )
}

export default getLessLimitSizeImage

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

在你想壓縮圖片的js代碼所對(duì)應(yīng)的頁面中。先放置一個(gè)用戶看不見的畫布。

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

<!--用于圖片壓縮的canvas畫布,不在頁面中展示,且id固定不可變-->
 <canvas
 
 canvas-id="zipCanvas"
 ></canvas>
 <!--畫布結(jié)束-->

其中cw的值我個(gè)人建議選擇用戶屏幕的寬度,如下,在page({…})的data中添加

	//畫板邊長默認(rèn)是屏幕寬度,正方形畫布
 cw:wx.getSystemInfoSync().windowWidth,

個(gè)人建議畫布和繪畫區(qū)域都是正方形的,畢竟你也不知道要壓縮的圖片是橫向的還是縱向的。

然后,引入,不解釋

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

在js代碼中:

 wx.chooseImage({
  count:1, //只傳一張
  sizeType:'original', //原圖質(zhì)量好,然后通過canvas壓縮,縮略圖壓縮就太糊了
  sourceType: ['album', 'camera'], // 來源是相冊(cè)和相機(jī)
  success:(res)=>{
  let canvasId='zipCanvas' //注意這里的id和你在頁面中寫的html代碼的canvas的id要一致
  let imagePath=res.tempFilePaths[0];//原圖的路徑
  let limitSize=2048;//大小限制2048kb
  let drawWidth=wx.getSystemInfoSync().windowWidth;//初始繪畫區(qū)域是畫布自身的寬度也就是屏幕寬度
  wx.showLoading({title:'圖片壓縮中...',mask:true}) //不需要你可以刪掉
  getLessLimitSizeImage(canvasId,imagePath,limitSize,drawWidth,(resPath)=>{
   wx.hideLoading(); //不需要你可以刪掉
   
   //resPath就是壓縮后圖片的路徑,然后想做什么都隨你
   
  })
  }
 })

補(bǔ)充:

  1. 這里代碼的主體不是我做的,網(wǎng)上一搜基本都是這個(gè)寫法,這里是經(jīng)過項(xiàng)目實(shí)踐測(cè)試后沒問題了做的講解。
  2. 這里圖片是只選了一張去壓縮,如果你需要選多張?jiān)侔€(gè)壓縮那就去寫個(gè)循環(huán),找個(gè)數(shù)組存壓縮后的結(jié)果,網(wǎng)上也有很多內(nèi)容。
  3. 回調(diào)函數(shù)中有l(wèi)essRes和moreRes,細(xì)心的會(huì)發(fā)現(xiàn)這兩個(gè)參數(shù)并沒有被用到,他們只是個(gè)提醒作用,表明當(dāng)前是less回調(diào)還是more回調(diào),如果你不怕弄混刪掉了或者自己另外寫了兩個(gè)新方法那都隨你。
  4. 極限情況下比如說將圖片強(qiáng)制壓縮至10kb,這個(gè)東西我沒測(cè)試過,不知道會(huì)不會(huì)有問題。
  5. 圖片壓縮體積的減小不是線性的,給人的感覺有點(diǎn)像二次函數(shù)(y=x^2左面那一半),越往后壓縮的尺寸變化會(huì)越小。當(dāng)然,這和用戶的分辨率,屏幕本身的大小都有關(guān)系。
  6. 還是那句話,由于每次遞歸都要給至少200ms的時(shí)間去畫,所以遞歸很耗時(shí)!??!而不遞歸進(jìn)行壓縮的話,網(wǎng)絡(luò)傳輸又會(huì)很耗時(shí)?。?!所以這個(gè)地方怎么取舍,壓縮至多大,繪畫區(qū)域縮小的多快,都要靠你自己的經(jīng)驗(yàn)去調(diào)試。
  7. 圖片的壓縮,長寬比理論上來講是不變的,但是因?yàn)樯釛壛诵?shù),可能會(huì)有肉眼難以察覺的誤差,但是問題不大。如果前端想展示一下壓縮后的圖片的話,不要忘記在image中加入mode=“aspectFit” 。

看完上述內(nèi)容,你們掌握微信小程序中使用canvas如何實(shí)現(xiàn)對(duì)進(jìn)行壓縮的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI