本文共 7786 字,大约阅读时间需要 25 分钟。
本文将详细介绍一个基于微信小程序canvas组件实现的可移动缩放图片裁剪框的开发方法,结合实际项目经验分享技术细节和实现方案。
传统的图片裁剪框应用在移动端应用中虽然功能完善,但在灵活性和精准度方面仍存在一定局限性。通过使用微信小程序canvas组件,可以实现一个支持自由移动和缩放的图片裁剪框解决方案。本文将从技术分析、实现细节等多个方面展开讨论。
使用canvas组件实现图片裁剪框开发涉及多个关键技术点,包括:
canvasAPI实现图片绘制和裁剪框边框绘制。canvas,避免重叠或其他显示问题。page组件的数据结构设计如下:
data: { open_camera: boolean, // 是否打开相机拍照 cut_image_canvas: {}, //裁剪画布上下文 mycanvas: {}, // canvas 实例 image_obj: {}, //绘制的图片对象 window_width: number, //屏幕宽度 window_height: number, //屏幕高度 cut_img_canvas_w: number, //画布宽度 cut_img_canvas_h: number, //画布高度 cut_area: { //裁剪区域配置 x: number, //左上角顶点x坐标 y: number, //左上角顶点y坐标 cut_width: number, //裁剪宽度 cut_height: number, //裁剪高度 cut_area_color: string, //裁剪框边框颜色 }, pixelRatio: number, //像素比 last_touches_x: number, //上一个触摸点x坐标 last_touches_y: number, //上一个触摸点y坐标 cut_area_change_status: string, //裁剪框变换类型 cut_image: string, //裁剪后的图片临时文件链接} 在page组件中,初始化屏幕参数和canvas布局:
裁剪 拍照
.take_photo { width: 240rpx; height: 96rpx; background: white; z-index: 1000; display: flex; flex-direction: row; align-content: center; align-items: center; justify-content: space-around; justify-items: center; border-radius: 10rpx; background: rgba(0, 0, 0, 0.4); color: white; position: fixed; bottom: 8%; left: 35%; text-align: center; font-size: 30rpx;}.take_photo_hover { transform: scale(0.9);} canvas绘制图片和裁剪框在page组件中,初始化canvas并绘制图片和裁剪框:
createCutImageCanvas(imageUrl) { const that = this; const query = that.createSelectorQuery(); query.select('#cut_image_canvas').fields({ node: true, size: true }).exec((res) => { const canvas = res[0].node; that.data.mycanvas = canvas; that.data.cut_image_canvas = canvas.getContext('2d'); wx.getImageInfo({ src: imageUrl, success: (res) => { const w = that.data.window_width; const h = that.data.window_height; const imgWidth = res.width; const imgHeight = res.height; canvas.width = w; canvas.height = h; that.data.cut_area.cut_width = 0.8 * w; that.data.cut_area.x = (w - that.data.cut_area.cut_width) / 2; that.data.cut_area.y = (h - that.data.cut_area.cut_height) / 2; that.setData({ cut_img_canvas_h: h, cut_img_canvas_w: w }); that.data.image_obj = canvas.createImage(); that.data.image_obj.src = imageUrl; that.data.image_obj.onload = () => { that.data.cut_image_canvas.drawImage(that.data.image_obj, 0, 0, w, h); that.createCutArea(); }; } }); });} 在page组件中,绘制裁剪框:
createCutArea() { this.data.cut_image_canvas.strokeStyle = this.data.cut_area.cut_area_color; this.data.cut_image_canvas.strokeRect( this.data.cut_area.x, this.data.cut_area.y, this.data.cut_area.cut_width, this.data.cut_area.cut_height ); // 绘制四个缩放点 this.data.cut_image_canvas.beginPath(); this.data.cut_image_canvas.arc( this.data.cut_area.x, this.data.cut_area.y, 5 * this.data.pixelRatio, 0, 2 * Math.PI ); this.data.cut_image_canvas.fillStyle = 'white'; this.data.cut_image_canvas.fill(); this.data.cut_image_canvas.beginPath(); this.data.cut_image_canvas.arc( this.data.cut_area.x, this.data.cut_area.y, 2.5 * this.data.pixelRatio, 0, 2 * Math.PI ); this.data.cut_image_canvas.fillStyle = '#1296db'; this.data.cut_image_canvas.fill(); // 其他三个缩放点类似绘制...} 在page组件中,处理触摸事件:
getCutAreaChangeStatus(e) { const that = this; const x = e.touches[0].x; const y = e.touches[0].y; that.data.cut_area_change_status = ''; const scalePointLocation = that.getScalePointLocation(); const scaleResponseRadius = 30; // 判断是否在移动区域 if ((scalePointLocation.left_up_point_x + scaleResponseRadius < x && x < scalePointLocation.right_down_point_x - scaleResponseRadius && scalePointLocation.left_up_point_y < y && y < scalePointLocation.right_down_point_y) { that.data.cut_area_change_status = 'move'; } // 判断是否在缩放区域 if (Math.sqrt( Math.pow(x - scalePointLocation.left_up_point_x, 2) + Math.pow(y - scalePointLocation.left_up_point_y, 2) ) < scaleResponseRadius) { that.data.cut_area_change_status = 'left_up_scale'; } // 类似处理其他三个缩放点...} canvas重绘在page组件中,实现canvas重绘:
redraw(mode = 0) { this.data.cut_image_canvas.clearRect(0, 0, this.data.cut_img_canvas_w, this.data.cut_img_canvas_h); this.data.cut_image_canvas.drawImage(this.data.image_obj, 0, 0, this.data.cut_img_canvas_w, this.data.cut_img_canvas_h); if (mode === 0) { this.createCutArea(); }} 在page组件中,实现裁剪框边界控制:
watchCutAreaOverflow() { const that = this; const margin = 5 * that.data.pixelRatio; const scalePointLocation = that.getScalePointLocation(); if (scalePointLocation.left_up_point_x < margin) return 1; if (scalePointLocation.right_down_point_x > (that.data.window_width - margin)) return 1; if (scalePointLocation.left_up_point_y < margin) return 1; if (scalePointLocation.right_down_point_y > (that.data.window_height - margin)) return 1; return 0;} 在page组件中,处理触摸移动事件:
cutAreaMoveAndScale(e) { const that = this; const touch_x = e.touches[0].x; const touch_y = e.touches[0].y; const dx = touch_x - that.data.last_touches_x; const dy = touch_y - that.data.last_touches_y; if (that.data.cut_area_change_status === 'left_up_scale' || that.data.cut_area_change_status === 'left_down_scale') { that.data.cut_area.cut_height += -dy; that.data.cut_area.cut_width += -dx; that.data.cut_area.x += dx; that.data.cut_area.y += dy; if (that.watchCutAreaOverflow()) { that.data.cut_area.cut_height -= -dy; that.data.cut_area.cut_width -= -dx; that.data.cut_area.x -= dx; that.data.cut_area.y -= dy; return; } that.redraw(); } if (that.data.cut_area_change_status === 'right_up_scale' || that.data.cut_area_change_status === 'right_down_scale') { that.data.cut_area.cut_height += dy; that.data.cut_area.cut_width += dx; if (that.watchCutAreaOverflow()) { that.data.cut_area.cut_width -= dx; that.data.cut_area.cut_height -= dy; return; } that.redraw(); } if (that.data.cut_area_change_status === 'move') { that.data.cut_area.x += dx; that.data.cut_area.y += dy; if (that.watchCutAreaOverflow()) { that.data.cut_area.x -= dx; that.data.cut_area.y -= dy; return; } that.redraw(); } that.data.last_touches_x = touch_x; that.data.last_touches_y = touch_y;} 在page组件中,处理裁剪图片:
cutImage() { this.redraw(1); // 1表示不绘制裁剪框 wx.canvasToTempFilePath({ x: this.data.cut_area.x / this.data.pixelRatio, y: this.data.cut_area.y / this.data.pixelRatio, width: Math.round(this.data.cut_area.cut_width / this.data.pixelRatio), height: Math.round(this.data.cut_area.cut_height / this.data.pixelRatio), canvas: this.data.mycanvas, filetype: 'png', success: (res) => { console.log('裁剪的图片', res.tempFilePath); this.setData({ cut_image: res.tempFilePath }); }, fail: (err) => { console.log('裁剪图片失败', err); wx.showModal({ title: '错误', content: '剪切图片失败' }); } });} 以上代码已经封装成一个自定义组件,方便集成到项目中。如果需要直接在page中使用,可以直接复制以上代码到相应的page中。自定义组件下载地址:链接 提取码:wbau
转载地址:http://uhet.baihongyu.com/