封面
截圖
最近做項(xiàng)目的時(shí)候,需要做一個(gè)截圖功能。用了一個(gè)別人寫(xiě)的截圖工具,發(fā)現(xiàn)截出的圖質(zhì)量下降了,但是我們圖片要用來(lái)做識(shí)別, 需要保證截出的圖質(zhì)量不下降。而且也不支持通過(guò)拖動(dòng)來(lái)調(diào)整截圖框的大小。所以這個(gè)截圖工具無(wú)法滿足需求。因?yàn)樗裕妥约簞?dòng)手寫(xiě)了一個(gè)截圖組件。
下面介紹一下實(shí)現(xiàn)原理和使用方法。
實(shí)現(xiàn)原理
組件wxml
的層次結(jié)構(gòu)圖如下:
hierarchy.png
-
original canvas
用來(lái)繪制原圖大小的圖片,這樣能保證截圖后的質(zhì)量不會(huì)下降,這個(gè)canvas是隱藏的。 -
movable-area
是movable-view
的容器,是官方提供的拖拽移動(dòng)組件,用來(lái)移動(dòng)截取框的四個(gè)角。這個(gè)組件支持多個(gè)點(diǎn)同時(shí)移動(dòng)。 -
scale canvas
用來(lái)繪制適應(yīng)屏幕比例大小的圖片(aspectFit),因?yàn)橥ǔT瓐D大小是超過(guò)屏幕長(zhǎng)寬的。(一開(kāi)始白線框和圖片都在這一層,但后來(lái)發(fā)現(xiàn)每次移動(dòng)都要繪制一次圖片,這樣會(huì)造成卡頓、性能下降。所以就想到通過(guò)增加一個(gè)move canvas
來(lái)專門(mén)繪制白線框來(lái)降低繪制圖片帶來(lái)的資源消耗,因?yàn)閳D片是靜止的,不需要重復(fù)繪制。) -
move canvas
是根據(jù)四個(gè)movable-view
的位置繪制出截圖框。
最后截圖,通過(guò)四個(gè)點(diǎn)的位置計(jì)算出截圖框的位置,然后放大對(duì)應(yīng)原圖大小的位置,得到在原圖中的(x, y, width, height)
,最后通過(guò)官方提供的canvas
接口截圖。
wx.canvasToTempFilePath({
x: x,
y: y,
width: w,
height: h,
destWidth: w,
destHeight: h,
canvasId: 'originalCanvas',
success: function (res) {
}
)}
旋轉(zhuǎn)原理
設(shè)置旋轉(zhuǎn)圓點(diǎn)
旋轉(zhuǎn)
特點(diǎn)
- 保證截圖質(zhì)量不會(huì)被壓縮(也可以選擇壓縮圖)
- 截圖框能夠通過(guò)拖拽四個(gè)角來(lái)調(diào)整選區(qū)大小
使用
假設(shè)我們的應(yīng)用文件結(jié)構(gòu)如下:
./
├── app.js
├── app.json
├── app.wxss
├── pages
│ └── index
│ ├── index.js
│ ├── index.json
│ ├── index.wxml
│ └── index.wxss
└── welCropper
├── welCropper.js
├── welCropper.wxml
└── welCropper.wxss
調(diào)用組件時(shí),需要傳入cropperData
、cropperMovableItems
、cropperChangableData
,因?yàn)閿?shù)據(jù)和事件都是綁定在Page
上的,所以要避免使用組件里面已經(jīng)被占用的命名。
/pages/index/index.wxml
<!-- 引入組件 -->
<import src="/welCropper/welCropper.wxml" />
<!-- 調(diào)用組件 -->
<template is="welCropper" data="{{data:cropperData, cropperMovableItems:cropperMovableItems, cropperChangableData:cropperChangableData}}"></template>
<!-- 用于選擇圖片,傳入cropper中 -->
<button bindtap='selectTap'>select image</button>
/pages/index/index.js
// 獲取顯示區(qū)域長(zhǎng)寬
const device = wx.getSystemInfoSync()
const W = device.windowWidth
const H = device.windowHeight - 50
let cropper = require('../../welCropper/welCropper.js');
console.log(device)
Page({
data: {
},
onLoad: function () {
var that = this
// 初始化組件數(shù)據(jù)和綁定事件
cropper.init.apply(that, [W, H]);
},
selectTap() {
var that = this
wx.chooseImage({
count: 1, // 默認(rèn)9
sizeType: ['original', 'compressed'], // 可以指定是原圖還是壓縮圖,默認(rèn)二者都有
sourceType: ['album', 'camera'], // 可以指定來(lái)源是相冊(cè)還是相機(jī),默認(rèn)二者都有
success(res) {
const tempFilePath = res.tempFilePaths[0]
console.log(tempFilePath)
// 將選取圖片傳入cropper,并顯示cropper
// mode=rectangle 返回圖片path
// mode=quadrangle 返回4個(gè)點(diǎn)的坐標(biāo),并不返回圖片。這個(gè)模式需要配合后臺(tái)使用,用于perspective correction
// maxLength=1000 默認(rèn)2000,允許最大長(zhǎng)寬,避免分辨率過(guò)大導(dǎo)致崩潰
let modes = ["rectangle", "quadrangle"]
let mode = modes[0] //rectangle, quadrangle
that.showCropper({
src: tempFilePath,
mode: mode,
sizeType: ['original', 'compressed'], //'original'(default) | 'compressed'
maxLength: 1000,
callback: (res) => {
if (mode == 'rectangle') {
console.log("crop callback:" + res)
wx.previewImage({
current: '',
urls: [res]
})
}
else {
wx.showModal({
title: '',
content: JSON.stringify(res),
})
console.log(res)
}
// that.hideCropper() //隱藏,我在項(xiàng)目里是點(diǎn)擊完成就上傳,所以如果回調(diào)是上傳,那么隱藏掉就行了,不用previewImage
}
})
}
})
}
})
最后引入組件的樣式
/pages/index/index.wxss
@import "/welCropper/welCropper.wxss";
注意
- 因?yàn)?code>wx.canvasToTempFilePath輸出的是
.png
圖片,截出來(lái)的圖有可能遠(yuǎn)遠(yuǎn)大于原圖(比如3通道圖變成4通道的圖)
源代碼
-
Github:tomfriwel/welCropper,將
welCropper
文件夾復(fù)制到自己項(xiàng)目,引入調(diào)用就行了。 - wepy 版本:github: callmesoul/wepy-corpper
如果出現(xiàn)什么bug、問(wèn)題或者建議可以告訴我,我會(huì)盡量改進(jìn)。
效果圖
效果動(dòng)圖mode=rectangle
效果動(dòng)圖mode=quadrangle
效果圖mode=rectangle
如果將movable-view
顯示出來(lái)是這樣的:
顯示movable-view后
mode=quadrangle