前言
以小程序 【識花君】為例子,分析下在小程序中如何實現(xiàn)拍照壓縮上傳锦溪。
一汽摹、camera 和 cover-view、cover-image 組件
首先分析如何實現(xiàn)類似的設(shè)計:
- 引用 camera 組件鱼辙,并且通過樣式設(shè)置寬高為全屏。(拓展:可以在樣式中設(shè)置寬高玫镐,或者定位來調(diào)整相機組件在頁面中的大小以及位置倒戏。)
- 以 cover-view 為父容器設(shè)置定位,以嵌套的 cover-image 引用圖片
說明:
- 為什么不直接對 cover-image恐似,而要使用 在外面嵌套一層 cover-view 杜跷?
答:因為對 cover-image 設(shè)置定位樣式后,在真機上無效矫夷「鹈疲基礎(chǔ)庫 1.9.90 起最外層 cover-view 支持 position: fixed 。 - cover-image 使用本地圖片路徑會存在問題
答:圖標(biāo)路徑双藕,支持臨時路徑淑趾、網(wǎng)絡(luò)地址(1.6.0起支持)、云文件ID(2.2.3起支持)忧陪。暫不支持base64格式扣泊。
二近范、從相冊選取
點擊相冊圖標(biāo)時,觸發(fā)事件調(diào)用 wx.chooseImage(Object object)
即可延蟹。
三评矩、拍照
- 圖片 api :
wx.chooseImage(Object object)
- 相機 api :
CameraContext.takePhoto(Object object)
四、壓縮
現(xiàn)在的智能手機拍出的照片等孵,很容易達到 5M 左右稚照,上傳時不僅占用帶寬蹂空,且速度慢俯萌。
小程序提供了 3 種方式可壓縮圖片:
- 選擇照片時指定圖片的尺寸或者拍照時指定成像質(zhì)量
經(jīng)過測試,如果對壓縮要求比較高上枕,這種方法是不行的咐熙,因為壓縮效果不顯著。 -
wx.compressImage(Object object)
局限性:僅對 jpg 有效辨萍。實際業(yè)務(wù)中包含png
棋恼、jpg
等多種格式。 - 通過 canvas 來曲線救國
原理:將一張大尺寸的圖片通過 canvas 的提供的 drawImage()
方法繪制到小尺寸的畫布上锈玉,再通過 canvasToTempFilePath
將畫布內(nèi)容生成圖片爪飘,就完成了大尺寸到小尺寸的轉(zhuǎn)換,完成了壓縮拉背。
步驟:
-
wx.getSystemInfoSync()
獲取設(shè)備像素比 -
wx.chooseImg()
或者CameraContext.takePhoto
獲取圖片 -
wx.getImageInfo()
獲取圖片信息师崎,并檢測圖片是否超過指定尺寸 -
drawImage()
繪制圖片到畫布 draw()
-
wx.canvasToTempFilePath()
將畫布內(nèi)容生成圖片
說明:為什么需要設(shè)備像素比?椅棺?
看下不處理設(shè)備像素比時犁罩,普通屏和二倍屏的對比,只關(guān)注 width
即可
在高倍屏上面两疚,1px 對應(yīng)的物理像素會比普通屏幕更多床估,這就導(dǎo)致通過 drawImage()
方法繪制時,雖然在 css
層面設(shè)置的寬高是一致的诱渤,比如(w: 300px)丐巫,如果普通屏(pixelRatio: 1) 1px = 1 個物理像素,那么在二倍屏 (pixelRatio: 2) 上面 1 px = 4 個物理像素(寬是2, 高是2)勺美,所以實際上是將圖片的寬繪制為 300 * 2
個物理像素递胧,這時使用
canvasToTempFilePath()
生成圖片的寬度是 600
, 而不是期望的 300
。
主要代碼
模板 部分 (以下為 mpvue 中的語法)
<canvas class="canvas-hidden" :style="{width: cWidth + 'px', height: cHeight + 'px'}" canvas-id="CanvasId"/>
js 部分
pixelRatio 通過 wx.getSystemInfoSync()
獲取励烦。
// 將圖片繪制到畫布上
drawImage(file) {
const ctx = wx.createCanvasContext('CanvasId');
wx.getImageInfo({
src: file,
success: (res) => {
if (res.width > 300 || res.height > 300) { // 判斷圖片是否超過300像素
this.cWidth = 300 / this.pixelRatio;
this.cHeight = 300 / this.pixelRatio / scale;
// 畫出壓縮圖片
ctx.drawImage(file, 0, 0, this.cWidth, this.cHeight);
ctx.draw();
setTimeout(() => {
this.canvasToImg();
}, 3000);
} else {
this.upload(file);
}
}
});
},
// 將畫布內(nèi)容轉(zhuǎn)成圖片
canvasToImg() {
wx.canvasToTempFilePath({
canvasId: 'CanvasId',
success: (res) => {
// 上傳圖片
this.upload(res.tempFilePath);
}
});
},
五谓着、兼容性
- 小米 android 9.0 版本無法渲染出
https
協(xié)議的圖片。
解決方案:前端強制轉(zhuǎn)換成 http 坛掠。 - CanvasContext.draw(boolean reserve, function callback)
callback 在某些機型上面無效赊锚。(當(dāng)前基礎(chǔ)庫2.6.5
)
解決方案:draw 之后強制 setTimeout 3s 治筒,然后再去執(zhí)行wx.canvasToTempFilePath(Object object, Object this)
2019/5/13 更改
解決方案:通過wx.getSystemInfoSync()
獲取當(dāng)前設(shè)備信息,其中的 platform
字段代表當(dāng)前系統(tǒng)類型
- ios
CanvasContext.draw(false, () => {
wx.canvasToTempFilePath(Object object, Object this)
})
- android
draw 之后強制 setTimeout 3s 舷蒲,然后再去執(zhí)行wx.canvasToTempFilePath(Object object, Object this)
3: 對 canvas 應(yīng)用樣式 visibility
無效
解決方案: 通過 left: -9999;
或者 tranlateX()
改變位置耸袜,移至不可見區(qū)域。