实现效果
界面实现
在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
组件的data就会多一些记录图片拖拽缩放的数据,需要注意的是,图片初始位置和图片拖拽位置没有使用同一变量存储,是为了防止在拖拽过程中产生抖动。
/** * 组件的初始数据 */ 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
)事件
// 拖拽事件响应函数 _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绘制无响应
/** * 图片剪切入口方法 */ 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绘制无响应
- 切图时要记住转换成实际图片的大小