Canvas拖动图形实现效果完整版

9 篇文章 0 订阅

Canvas拖动图形实现效果完整版

上次那个博客实现只能支持单张图片,并且代码的逻辑不够清晰。所以中秋节,正好一个人也没事做,专门抽出时间来对它进行一个扩展,添加对多张图片的支持。

效果展示

下面我们先来看看实现的效果,这里先提一点:当你移动图片时,注意它是在所有图片的最下层,这是我的技术实现的特点。

1.GIF动图
请添加图片描述
2.B站视频

canvas图片拖动

实现分析

多张图片移动实现分析

所有的图片存储在一个数组中,使用一个current变量存储当前选中的图片(初始值为-1,即未选中)。当用户开始点击画板时,记录下此时鼠标的位置。当鼠标在画板上进行移动时,首先清空整个画板,然后依次绘制每一张图片和外围的矩形框,并且在绘制完成之后,立刻判断鼠标是否点击到了这张图片上(矩形框内部)。如果鼠标在某个矩形框内部,说明它选中了某张图片。 然后交换它和数组第一个元素的位置,这样它就变成了整个数组的第一个元素了。然后重新绘制所有的图形,由于被选中的图片是数组中的第一个元素,所以它是最先绘制的。这样呈现的效果就是你会看到它在所有图片的底部。之所以采用这种方式进行处理,是因为如果它在其它图片的上层时,当鼠标点击的位置位于两个图片的层叠处,两张图片会出现同时移动的问题。所以,我采用了这种方式进行规避,不过最终的效果也是不错的,你只是看到它处于其它图片的底层而已。

多张图片移动边界限制分析

上一个博客中,我实现了图片不能移动出边界的功能。这里也是要加上的,你可能觉得图片变多了,不好分析了。但是,其实还是很简单的。我的图片数组会存储每一个图片的坐上顶点坐标,并且图片的宽高我们也是知道的(图片元素自身会存储这个值)。因此,当我们鼠标点击某张图片时,我们也就可以算出来图片的右下顶点了。所以我们其实是时刻知道某张图片的四个顶点的位置的,但是我们只需要知道(x1, y1)和 (x3,y3)。并且我们只需要判断这个四个数字的取值值即可。在上一篇博客是判断一个点的位置不能在某个位置,这其实是一种组合判断,我昨天发现其实只要单独判断就行了。
所以最终的判断代码大大简化了:

// 判断位置,当点越界时,进行处理
function judgePosition(obj) {
	// 这里只需要考虑点的坐标值即可,两个点,4个坐标判断四种情况。
	obj.x = obj.x < 0 ? 0 : (obj.x + obj.img.width) < width ?  obj.x : width-obj.img.width;
	obj.y = obj.y < 0 ? 0 : (obj.y + obj.img.height) < height ? obj.y : height-obj.img.height;
}

或者一个更容易理解的写法:

// 判断位置,当点越界时,进行处理
function judgePosition(obj) {
	// 这里只需要考虑点的坐标值即可,两个点,4个坐标判断四种情况。
	let x3 = obj.x + obj.img.width;
	let y3 = obj.y + obj.img.height;
	if (obj.x < 0) {
		obj.x = 0;
	}
	if (obj.y < 0) {
		obj.y = 0;
	}
	if (x3 > width) {
		obj.x = width-obj.img.width;
	}
	if (y3 > height) {
		obj.y = height-obj.img.height;
	}
}

完整代码

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>中秋快乐!</title>
		<style>
			body, div {
				margin: 0;
                padding: 0;
			}
		</style>
	</head>
	<body>
		<div id="cas">
			<canvas id="cs" width="800" height="600" style="border: 2px solid green"></canvas>
		</div>
		
		<script type="text/javascript">
			'use strict'
			let canvas = document.getElementById("cs");
			// 获取canvas的宽高
			let width = canvas.width;
			let height = canvas.height;
			let ctx = canvas.getContext("2d");
			// 这里使用默认值即可
			//ctx.globalCompositeOperation = "source-over";
			let now = {x: 0, y: 0};  // 鼠标的位置
		
			let isDown = false;
			let current = -1;
			let rewidth = 50;  // 重定义图片的宽度
			
			let imgs = [];
			// 创建img元素
			Array.from({length: 40}, (a, i)=>i).forEach(index => {
				let img = document.createElement("img")
				img.src = "doge.png";
				// 当图片加载完成之后,将其绘制到canvas上,图片没有加载完成时,
				// 图片的宽高是0,因为这时候图片其实是空的
				img.onload = () => {
					let h = resize(img, rewidth);
					img.width = rewidth;
					img.height = h;

					let obj = {img: img, x: 0, y: 0};
					console.log(obj);
				    imgs.push(obj);      // 每张图片的默认位置
					drawCanvas(obj, "#FFFFFF");
				};
			});
			
			// 添加鼠标事情,来实现图片的拖放功能。
			// 当鼠标按下时,获取当前的坐标
			canvas.onmousedown = e => {
				isDown = true;     // 鼠标按下时,设置isDown为true,此时移动鼠标才认为是有效。
				console.log("鼠标按下")
				let x = e.pageX-canvas.offsetLeft; // 后面这个是偏移量,但是在这里为0
				let y = e.pageY-canvas.offsetTop;
				now.x = x;
				now.y = y;
				console.log(x + " -> " + y);
                // 这里取消了点击产生选中框的逻辑				
			};
			
			// 在鼠标移动时,不断重绘制整个canvas
			canvas.onmousemove = e => {
				if (!isDown) {  // 鼠标未按下则直接返回,不去响应该事件。
					return;
				}
				// 获取点的坐标可以封装成函数
				let x = e.pageX;
				let y = e.pageY;
				
				// 清空当前的canvas图形
				ctx.clearRect(0, 0, canvas.width, canvas.height);
				
				imgs.forEach((obj, index) => {
					drawCanvas(obj, "#FFFFFF");
					if (!ctx.isPointInPath(x, y)) {
						console.log("onmousemove: 鼠标没在路径内");
						return;
					}					
					// 更新current
					if (current == -1) {
						current = index;
 					}
					if (current == index) {
						imgs[current].x = imgs[current].x + (x-now.x);
						imgs[current].y = imgs[current].y + (y-now.y);
						
						// 判断并即时矫正图片的坐标
						judgePosition(imgs[current]);
						
						if (current != 0) {  // 这里去掉也是可以的
							let tobj = imgs[0];
							imgs[0] = imgs[current];
							imgs[current] = tobj;
						}
						current = 0;
					}
					console.log(imgs);
					
					// 重绘制偏移之后的canvas
					imgs.forEach(obj => drawCanvas(obj, "#FFFFFF"));				
					ctx.beginPath();
					ctx.strokeStyle="red";
					ctx.rect(imgs[index].x, imgs[index].y, imgs[index].img.width, imgs[index].img.height);  
					ctx.stroke();
					now.x = x; now.y = y;
					console.log("鼠标在移动..." + x + " ==> " + y, obj.img);
				});
			}

			canvas.onmouseup = e => {
				isDown = false;  // 鼠标松开,则上述封装的动作结束。
				current = -1;
				console.log("鼠标松开, current: ", current);
				imgs.forEach(obj => drawCanvas(obj, "#FFFFFF"));
			}
			
			// 如果鼠标按下然后移动的过程中离开了当前元素,再松开,但是无法触发鼠标松开事件了,
			// 所以当监听到鼠标移出元素时,必须也要将isDown设置成false。
			canvas.onmouseout = e => {
				isDown = false;
				current = -1;
				console.log("鼠标离开了画布元素, current: ", current);
				imgs.forEach(obj => drawCanvas(obj, "#FFFFFF"));
			}
			
			function drawCanvas(obj, color) {
				// 绘制所有的图片,这里的宽高是缩放后的宽高
			    ctx.drawImage(obj.img, obj.x, obj.y, obj.img.width, obj.img.height);
				ctx.beginPath();
				ctx.strokeStyle = color;
				ctx.rect(obj.x, obj.y, obj.img.width, obj.img.height);
				ctx.stroke();
			}
			
			// 根据传如的指定宽度,自动调整高度
			function resize(img, width) {
				let w = img.width;
				let h = img.height;
				return parseInt(h*width/w);
			}
			
			// 判断位置,当点越界时,进行处理
			function judgePosition(obj) {
				// 这里只需要考虑点的坐标值即可,两个点,4个坐标判断四种情况。
				obj.x = obj.x < 0 ? 0 : (obj.x + obj.img.width) < width ?  obj.x : width-obj.img.width;
				obj.y = obj.y < 0 ? 0 : (obj.y + obj.img.height) < height ? obj.y : height-obj.img.height;
			}
		</script>
	</body>
</html>

参考资料

Canvas画图-鼠标移动图形

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值