我在早先發布的文章《如何實現微信小程序換頭像?三步幫你搞定!》中,提到實現微信小程序換頭像須要三步:html
前文已經就獲取用戶頭像和圖片模板兩個步驟進行了講解,本文就來詳細說說如何合成圖片。前端
圖片合成的過程當中很是重要的一塊功能對圖片進行剪切。該功能點很固定,大都是對圖片進行拖拽、縮放後,在必定區域內剪切出一個固定長寬的圖片。這類功能在app端和H5中都有不少成熟的插件供使用,接下來就來看看我在海豚趣圖小程序中的頭像剪切插件是如何實現的,歡迎你們提意見。canvas
爲了更好地理解接下來的代碼,建議你們先掃描體驗一下圖片裁剪效果。小程序
在H5中要實現圖片的拖拽和縮放須要一大坨代碼,具體實現網上有不少。小程序實現就簡單的多了,經過 <movable-area>
和 <movable-view>
就能夠實現上述功能微信小程序
<view class="clip-view">
<!-- clipHeight、clipWidth 分別爲剪切框的高和寬 imgHeight、imgWidth 分別對應圖片的初始高和寬 imgUrl 爲剪切圖片的url地址 -->
<movable-area class="moveare" style="height: {{clipHeight}}rpx; width: {{clipWidth}}rpx; ">
<movable-view scale="true" scale-min="{{1}}" damping="1000" style="height: {{imgHeight}}px; width: {{imgWidth}}px; " direction="all" x="{{x}}" y="{{y}}" bindchange="_onChange" bindscale="_onScale">
<image class="clip-img" src="{{imgUrl}}" />
</movable-view>
</movable-area>
<!--剪切框 裝飾用-->
<view class="clip-box" style="height: {{clipHeight}}rpx; width: {{clipWidth}}rpx; ">
<!--剪切框四個角-->
<view class="clip-border clip-border-lt"></view>
<view class="clip-border clip-border-rt"></view>
<view class="clip-border clip-border-lb"></view>
<view class="clip-border clip-border-rb"></view>
</view>
<!--剪切圖片用的canvas-->
<canvas class="clip-canvas" id="img_clip_canvas" canvas-id="img_clip_canvas" style="height: {{clipHeight}}rpx; width: {{clipWidth}}rpx; "></canvas>
</view>
複製代碼
組件的入參只須要原始圖片的地址和圖片剪切框的寬高微信
/** * 組件的屬性列表 */
properties: {
// 原始圖片路徑(要剪切的圖片)
imgUrl: {
type: String,
value: ''
},
// 剪切的寬度 (rpx)
clipWidth: {
type: Number,
value: 500
},
// 截切的高度 (rpx)
clipHeight: {
type: Number,
value: 500
}
}
複製代碼
組件的data就會多一些記錄圖片拖拽縮放的數據,須要注意的是,圖片初始位置和圖片拖拽位置沒有使用同一變量存儲,是爲了防止在拖拽過程當中產生抖動。app
/** * 組件的初始數據 */
data: {
baseScale: 1,
imgPath: '',
imgWidth: 0, // 圖片寬
imgHeight: 0, // 圖片高
x: 0, // 圖片初始時x軸位置
y: 0, // 圖片初始時y軸位置,之因此不和top共用一個變量,是由於若是頻繁改變y值,圖片會閃爍,x同理
left: 0, // 圖片拖拽後的x軸位置
top: 0, // 圖片拖拽後的y軸位置
scale: 1 // 拖拽後的縮放比例
}
複製代碼
組件的初始化方法用於把圖片縮放到合適的大小,並使剪切框位於圖片中央函數
/** * 初始化方法 * 獲取圖片寬高後,把圖片縮放至剪切框大小, * 並使剪切框位於圖片中央 **/
_init() {
if (!this.data.imgUrl) return
// 獲取屏幕寬度
let {
screenWidth
} = wx.getSystemInfoSync()
// 計算屏幕rpx
const rpx = screenWidth / 750
// 獲取圖片寬高,而後縮放至剪切框大小
wx.getImageInfo({
src: this.data.imgUrl,
success: ({
width,
height,
path
}) => {
const cw = this.data.clipWidth * rpx
const ch = this.data.clipHeight * rpx
let scale = Math.max(cw / width, ch / height)
const imgWidth = width * scale
const imgHeight = height * scale
this.setData({
imgPath: path,
imgWidth,
imgHeight,
baseScale: scale,
x: (cw - imgWidth) / 2,
y: (ch - imgHeight) / 2
})
}
})
}
複製代碼
完成初始化後就能夠監聽用戶的拖拽和縮放操做了,主要是記錄拖拽的位置和縮放的比例,具體到代碼實現就是監聽<movable-view>
的拖拽(bindchange
)和縮放(bindscale
)事件post
// 拖拽事件響應函數
_onChange: function(e) {
this.setData({
x: e.detail.x,
y: e.detail.y
})
}
// 縮放事件響應函數
_onScale: function(e) {
this.setData({
x: e.detail.x,
y: e.detail.y,
scale: e.detail.scale
})
}
複製代碼
拖拽縮放完成後就是剪切了,剪切是利用了<canvas>
從新繪製圖片的剪切區域,保存到微信臨時目錄裏,並返回保存路徑。須要注意的是拖拽和縮放後記錄的圖片剪切位置並非原圖的位置,利用canvas
的drawImage
進行繪製的時候須要轉換成原圖位置,或者先把canvas
的座標系進行縮放。注意:在自定義組件下調用 wx.createCanvasContext(string canvasId, Object this)
方法時,第二個參數this不能省略,不然canvas繪製無響應ui
/** * 圖片剪切入口方法 */
clip() {
const scale = this.data._scale * this.data._baseScale
const canvasId = 'img_clip_canvas'
imageClip(canvasId, this.data.imgPath, {
x: this.data._left * -1,
y: this.data._top * -1,
scale,
width: this.data.clipWidth,
height: this.data.clipHeight
}, this)
}
/** * 圖片剪切 * * @param canvas canvas組件id,用於繪製剪切圖片 * @param img 要剪切的圖片 * @param option 剪切的位置寬高等信息 * @param option.left 剪切圖片左邊距 * @param option.top 剪切圖片上邊距 * @param option.width 剪切圖片寬度 * @param option.height 剪切圖片高度 * @param context 組件實例對象 * * @return new Promise(resolve=>ctx) */
imageClip(canvas, img, option, context) {
return new Promise((resolve, reject) => {
option = Object.assign({
left: 0,
top: 0,
scale: 1,
width: 0,
height: 0
}, option)
let x = option.left / option.scale
let y = option.top / option.scale
let clipW = option.width / option.scale
let clipH = option.height / option.scale
const ctx = wx.createCanvasContext(canvas, context)
ctx.drawImage(img,
x,
y,
clipW,
clipH,
0,
0,
option.width,
option.height)
ctx.draw(false, (e) => {
console.log(e)
resolve()
})
})
},
/** * canvas 保存爲臨時文件 */
function saveCanvasToTemp(canvas, option) {
return new Promise((resolve, reject)=>{
wx.canvasToTempFilePath({
...option,
canvasId: canvas,
success:resolve,
fail:reject
}, this)
})
}
複製代碼
這就是小程序版圖片剪切的主要代碼實現,裏面還有一些小點兒須要注意
<movable-view>
的damping的值要設置大寫,不然可能會出現拖出界外的狀況wx.createCanvasContext(string canvasId, Object this)
方法時,第二個參數this不能省略,不然canvas繪製無響應快狗打車前端團隊專一前端技術分享,按期推送高質量文章,歡迎關注點贊。 文章同步發佈在公衆號喲,想要第一時間獲得最新的資訊,just scan it !