背景
在上一篇,我们已经成功把图片按 path 绘制到 Canvas 上,也搞定了裁剪区域,这可是完成了相当关键的部分。接下来,就要实现移动图片、缩放图片以及保存图片这些功能了。
移动图片
先从移动图片这个相对简单的任务开始。思路就是,当手指在 Canvas 上点击并移动,我们通过计算手指移动的位置和图片当前位置,在 Canvas 上重新绘制图片。这里要用到微信小程序的touchstart
、touchmove
、touchend
这三个事件,它们分别对应手指触摸动作开始👆、手指触摸后移动🖌️、手指触摸动作结束👇 。
html
<canvas
id="canvas"
type="2d"
style="width: 100%; height: 100%;"
bind:touchstart="onTouchStart"
bind:touchmove="onTouchMove"
bind:touchend="onTouchEnd">
</canvas>
touchstart
js
onTouchStart(e) {
const { touches } = e
if (touches.length > 2) return
let move = null, scale = null
if (touches.length === 1) {
// 单指触摸
const { pageX, pageY } = touches[0]
move = { startX: pageX, startY: pageY, }
this.setData({ start: true, move, scale })
}
}
小程序的touch
事件有个touches
属性,它能告诉我们有几个手指在屏幕上操作。因为是移动图片,单指操作就行。在touchstart
事件里,我们把手指触摸屏幕时的坐标记录下来 。
touchmove
js
onTouchMove(e) {
if (this.data.start) {
const { ctx, width, height } = this.data.canvas
ctx.clearRect(0,0, width, height)
const { touches } = e
if (touches.length === 1) {
const { startX, startY } = this.data.move
const { pageX, pageY } = touches[0]
const offsetX = pageX - startX
const offsetY = pageY - startY
const { target: image, width, height, x: imageX, y: imageY } = this.data.image
const { ctx } = this.data.canvas
const x = imageX + offsetX
const y = imageY + offsetY
ctx.drawImage(image, x, y, width, height);
this.setData({
changeData: { x, y, width, height }
})
}
}
}
当手指移动时,我们要重新绘制图片的位置。移动过程中图片大小不变,重点是计算坐标位置 。移动时先清空画布,接着获取当前触摸点的坐标pageX
、pageY
,用它们减去触摸起始坐标,得到移动后的坐标偏移量,再将图片原坐标加上偏移量,得到新位置。然后调用ctx.drawImage
重新绘制图片,并保存移动后的图片位置数据。注意,移动时先不绘制裁剪区域,不然画面会闪,影响体验 。
touchend
kotlin
onTouchEnd(e) {
const { changeData } = this.data
if (changeData) {
const { x, y, width, height } = changeData
this.setData({
'image.width': width,
'image.height': height,
'image.x': x,
'image.y': y,
changeData: null
})
this.drawCrop()
}
this.setData({ start: false })
},
最后,当手指松开,我们把移动过程中的数据保存好,同时将裁剪区域重新绘制到画布上 。
缩放图片
图片缩放功能和移动有些相似,但计算会更复杂一点 。
touchstart
js
// 计算直径
caleDiameter(touches) {
const [first, second] = touches
const x = first.pageX - second.pageX
const y = first.pageY - second.pageY
return Math.abs(Math.sqrt( x * x + y * y ))
},
onTouchStart(e) {
const { touches } = e
if (touches.length > 2) return
if (touches.length === 2) {
let scale = null
if (touches.length === 2) {
// 双指触摸
const startDiameter = this.caleDiameter(touches)
const [first, second] = touches
scale = {
firstStartX: first.pageX,
firstStartY: first.pageY,
secondStartX: second.pageX,
secondStartY: second.pageY,
startDiameter
}
this.setData({ scale })
}
}
}
onTouchStart
函数用来处理触摸开始事件。它从传入的事件对象 e
里取出 touches
数组,这里面存着触摸点信息。 先检查 touches
长度,要是超过 2 个触摸点,就啥也不干直接返回,因为咱只处理双指操作。 当触摸点是 2 个(也就是双指触摸)时,声明 scale
变量,然后调用 caleDiameter
方法算出双指初始间距存到 startDiameter
里,再把两个触摸点分别解构到 first
和 second
变量。接着构建 scale
对象,把双指的初始坐标和间距都放进去,最后通过 setData
把 scale
存到组件数据里,为后续缩放操作提供初始数据 。
touchmove
onTouchMove
函数用于处理触摸移动事件,当手指在屏幕上滑动时,它就会被触发,其核心任务是实现图片的缩放功能。
js
onTouchMove(e) {
const { touches } = e
// 计算缩放比例
const { startDiameter } = this.data.scale
const diameter = this.caleDiameter(touches)
const scale = diameter / startDiameter
// 计算缩放后的尺寸
const { target: image, x: imageX, y: imageY, width: imageWidth, height: imageHeight } = this.data.image
const { ctx } = this.data.canvas
const width = imageWidth * scale
const height = imageHeight * scale
// 计算缩放后的偏移位置
const {
firstStartX, firstStartY,
secondStartX, secondStartY,
} = this.data.scale
// 中心坐标
const centerX = (firstStartX + secondStartX) / 2
const centerY = (firstStartY + secondStartY) / 2
// 相对于图片坐标的偏移量
const offsetX = centerX - imageX
const offsetY = centerY - imageY
// 新的偏移量
const newOffsetX = offsetX * scale
const newOffsetY = offsetY * scale
// 图片坐标
const x = centerX - newOffsetX
const y = centerY - newOffsetY
ctx.drawImage(image, x, y, width, height);
this.setData({
changeData: { x, y, width, height }
})
}
主要步骤
- 获取触摸点信息 :从传入的事件对象
e
中提取touches
数组,这个数组包含了当前所有触摸点的详细信息,我们后续的计算都要基于这些信息展开。 - 计算缩放比例 :从
this.data.scale
中取出双指触摸开始时的间距startDiameter
,再调用caleDiameter
方法计算当前双指的间距diameter
。将这两个间距相除,就得到了缩放比例scale
。这个比例决定了图片是放大还是缩小。 - 计算缩放后尺寸 :从
this.data.image
中获取图片的原始信息,像图片对象、原始坐标以及宽高。把原始的宽度和高度分别乘以缩放比例,就得到了缩放后图片的宽度和高度。 - 计算缩放后偏移位置 :
- 从
this.data.scale
中取出双指触摸开始时的坐标。 - 算出双指的中心坐标,这个中心坐标可以帮助我们确定图片缩放的基准点。
- 计算中心坐标相对于图片原始坐标的偏移量,再将这个偏移量乘以缩放比例,得到新的偏移量。
- 用中心坐标减去新的偏移量,就得到了缩放后图片的坐标。
- 从
- 重新绘制图片 :调用
ctx.drawImage
方法,根据新的坐标和尺寸,在 Canvas 上重新绘制图片,让我们看到缩放后的效果。 - 保存缩放后图片信息 :使用
this.setData
把缩放后的图片坐标和尺寸存到changeData
里,方便后续处理,比如在手指松开时进一步保存这些数据。
通过上述一系列操作,这段代码能够根据双指移动时间距的变化,动态地计算缩放比例以及新的图片位置和尺寸,然后重新绘制图片,实现图片的缩放效果。
touchend
touchend
事件的处理和移动图片时类似,这里就不再重复贴代码啦 。
保存图片
最后我们只需要调用wx.canvasToTempFilePath
API获取到临时图片路径,然后进行上传即可。

🦀🦀感谢看官看到这里,如果觉得文章不错的话🙌,点个关注不迷路⭐。
诚邀您加入我的微信技术交流群🎉,群里都是志同道合的开发者👨💻,大家能一起交流分享摸鱼🐟。期待与您在群里相见🚀,咱们携手在开发路上共同进步✨ !
👉点我
感谢各位大侠一路相伴,实在感激! 不瞒您说,在下还有几个开源项目 📦,它们就像精心培育的幼苗 🌱,急需您的浇灌。要是您瞧着还不错,麻烦动动手指,给它们点亮几颗 Star ⭐,您的支持就是它们成长的最大动力,在此谢过各位大侠啦!
Nova UI
组件库:github.com/gmingchen/n...- 基于 Vue3 + Element-plus 管理后台基础功能框架
- 预览:admin.gumingchen.icu
- Github:github.com/gmingchen/a...
- Gitee:gitee.com/shychen/agi...
- 基础版后端:github.com/gmingchen/j...
- 文档:admin.gumingchen.icu/doc/
- 基于 Vue3 + Element-plus + websocket 即时聊天系统
- 基于 node 开发的后端服务:github.com/gmingchen/n...