我在早先发布的文章《如何实现微信小程序换头像?三步帮你搞定!》中,提到实现微信小程序换头像须要三步: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 !