【uniapp】canvas实现图片拖拽功能

发布于:2022-12-23 ⋅ 阅读:(1384) ⋅ 点赞:(1)

本实验前期准备:
①下载HBuilderX
②新建uniapp项目,选择默认模板,Vue版本选择2
③在HBuilderX工具栏中点击“插件安装”,前往uniapp插件市场安装uViewUI(选择使用HbuilderX导入插件)
点击该链接学习配置uView
(③④步骤非必要,如果要使用uViewUI组件可以尝试)

效果展示

在这里插入图片描述

页面结构

<template>
	<view>
		<view class="btnBox">
		    <!-- u-button 为uViewUI组件 -->
			<u-button text="添加图片" @click="selectImage"></u-button>
		</view>
		<!-- 创建一个画布,绑定触摸动作开始、触摸后移动、触摸动作结束事件 -->
		<canvas 
			canvas-id="myCanvas" 
			id="myCanvas"
			@touchstart="handleTouchStart"
			@touchmove="handleTouchMove"
			@touchend="handleTouchEnd"
			></canvas>
	</view>
</template>
<style>
	/* 设置画布大小 */
	#myCanvas{
		width: 750rpx;
		height: 900rpx;
	}
</style>

数据结构

layers数组中的每一组数据可以看作是一个图层,下标越往后图层层级越高。

export default {
	data(){
		return{
			ctx:'', //canvas上下文对象
			beginX:'', 
			beginY:'',
			movedX:'',
			movedY:'',
			layers:[], //存储图片对象
		}
	},
	onLoad() {
		this.ctx = uni.createCanvasContext('myCanvas')
	},
	methods:{...}
	....
}

选择并绘制图片

点击“添加图片”按钮时触发一下方法

darwImages(url,x,y,w,h){
	this.ctx.drawImage(url,x,y,w,h)
	this.ctx.draw(true)
},

layer(x,y)为图像的左上角在画布 X 轴和Y轴的位置
layer(w,h)为图像在画布中的宽高大小
layer.isDrag为当前图片是否为被拖拽对象,默认为false

selectImage(){
	uni.chooseImage({
		success:(res)=> {
		// 图片对象
			let layer = {
				type:'photo',
				resoure:res.tempFilePaths[0],
				x:0, 
				y:0,
				w:200,
				h:150,
				isDrag:false
			}
			// 将图片对象添加到layers数组中
			this.layers.push(layer)
			// 将图片绘制到画布中
			this.darwImages(res.tempFilePaths[0],layer.x,layer.y,layer.w,layer.h)
		}
	})
},

拖拽图片

拖拽图片有三个动作:一是选中图片,二是长按移动图片,三是结束移动图片。

在选中图片这一步骤中有几个要点:
①如何判断我们选中了图片?
②当触摸的点落在了两张图片重合的地方,如何将最上层的图片设置为我们要选中并拖拽的对象?

解①:
在这里插入图片描述
根据上图我们可以得出:只要同时满足 x > img.xy > img.y , x < img.w + img.x , y < img.h + img.y 四个条件,就可以断定选中了图片。同时修改isDrag属性为true,并将当前选中的图片的层级设置为最高。

解②:
在前面我已介绍过,layers数组中下标越往后图层层级越高(因为canvas默认新绘制的图片会覆盖先绘制的图片),因此我们只需要反向遍历layers,优先判断图层层级较高的图片是否被选中即可。

handleTouchStart(e){
	let {x,y} = e.changedTouches[0]
	this.beginX = x
	this.beginY = y
	for (var i=this.layers.length-1;i>=0;i--) {
		if(x>this.layers[i].x && y>this.layers[i].y && x<this.layers[i].w+this.layers[i].x && y<this.layers[i].h+this.layers[i].y){
			this.layers[i].isDrag = true
			// 获取当前选中的图片对象
			let selectObj = this.layers[i]
			// 在layers中删除该图片对象
			this.layers.splice(i,1)
			// 重新将该选中对象添加到layers数组末尾(修改当前图片对象的层级)
			this.layers.push(selectObj)
			console.log(selectObj)
			// 别忘了 break!!
			break
		}
	}
},

上面我们已经将当前被选中的对象移到了layers数组的尾部,因此在长按移动图片这一步骤中,我们就只需判断layers[layers.length - 1] 对象的isDrag属性是否为true,如果是则获取每次移动了的多少距离,并不断修改被选中图片的左上角坐标点。

*注:每次移动都要清空画布,不然会在画布上留下图片被拖动的残影。。be like:
在这里插入图片描述

handleTouchMove(e){
	if(this.layers.length != 0 && this.layers[this.layers.length-1].isDrag == true){
		let {x,y} = e.changedTouches[0]
		this.movedX = x-this.beginX
		this.movedY = y-this.beginY
		this.layers[this.layers.length-1].x += this.movedX
		this.layers[this.layers.length-1].y += this.movedY
		this.beginX = x
		this.beginY = y
	}
	this.ctx.clearRect(0,0,750,900)
	this.layers.forEach(l=>this.darwImages(l.resoure,l.x,l.y,l.w,l.h))
},

结束移动图片只需要重新将isDrag改为false就行了,没啥要注意的了

handleTouchEnd(e){
	if(this.layers.length != 0){
		this.layers[this.layers.length-1].isDrag = false
	}
}

完整代码

<template>
	<view>
		<view class="btnBox">
			<u-button text="添加图片" @click="selectImage"></u-button>
		</view>
		<canvas 
			canvas-id="myCanvas" 
			id="myCanvas"
			@touchstart="handleTouchStart"
			@touchmove="handleTouchMove"
			@touchend="handleTouchEnd"
			></canvas>
	</view>
</template>

<script>
	export default {
		data(){
			return{
				ctx:'',
				beginX:'',
				beginY:'',
				movedX:'',
				movedY:'',
				layers:[],
			}
		},
		onLoad() {
			this.ctx = uni.createCanvasContext('myCanvas')
		},
		methods:{
			selectImage(){
				uni.chooseImage({
					success:(res)=> {
						let layer = {
							type:'photo',
							resoure:res.tempFilePaths[0],
							x:0,
							y:0,
							w:200,
							h:150,
							isDrag:false
						}
						this.layers.push(layer)
						this.darwImages(res.tempFilePaths[0],layer.x,layer.y,layer.w,layer.h)
					}
				})
			},
			darwImages(url,x,y,w,h){
				this.ctx.drawImage(url,x,y,w,h)
				this.ctx.draw(true)
			},
			handleTouchStart(e){
				let {x,y} = e.changedTouches[0]
				this.beginX = x
				this.beginY = y
				for (var i=this.layers.length-1;i>=0;i--) {
					if(x>this.layers[i].x && y>this.layers[i].y && x<this.layers[i].w+this.layers[i].x && y<this.layers[i].h+this.layers[i].y){
						this.layers[i].isDrag = true
						let selectObj = this.layers[i]
						this.layers.splice(i,1)
						this.layers.push(selectObj)
						console.log(selectObj)
						break
					}
				}
			},
				
			handleTouchMove(e){
				if(this.layers.length != 0 && this.layers[this.layers.length-1].isDrag == true){
					let {x,y} = e.changedTouches[0]
					this.movedX = x-this.beginX
					this.movedY = y-this.beginY
					this.layers[this.layers.length-1].x += this.movedX
					this.layers[this.layers.length-1].y += this.movedY
					this.beginX = x
					this.beginY = y
				}
				this.ctx.clearRect(0,0,750,900)
				this.layers.forEach(l=>this.darwImages(l.resoure,l.x,l.y,l.w,l.h))
			},
			handleTouchEnd(e){
				if(this.layers.length != 0){
					this.layers[this.layers.length-1].isDrag = false
				}
			}

		}
	}
</script>

<style>i
	.btnBox{
		display: flex;
	}
	
	#myCanvas{
		width: 750rpx;
		height: 900rpx;
	}
</style>

(>人<;)文字表达能力有限 && 菜鸟一枚 ,如有问题,欢迎指出~

本文含有隐藏内容,请 开通VIP 后查看