Canvas图形拖动效果实现改进版

9 篇文章 0 订阅

前言: 上次实现的完整版,老实说有一些不满意的地方。这是当时思考欠缺的地方,所以在实现时进行了一些妥协,在不影响整体功能的情况下进行了实现。今天下班回家的路上,在班车上忽然想起最近的伤心事,不免有些失落,还是觉得应该找点事情做。想起我的这个东西还不够完善,当时想了一个方法,我觉得可行,然后到了住处点了一个外卖就开始进行实现了。最初的想法只是部分可用,但是实现的效果在逻辑上还是不对,不过它也让我对这个东西的逻辑更加了解了,方法再改进即可,最终完成了这个实现。我觉得很多东西就是这样,最初的想法可能不对,但是只要持续进行思考和努力,基本都会有解决办法的!当然了,这是对于事物,对于人,不行就是不行了,也没必要做无谓的努力。

改进效果

GIF图演示

在这里插入图片描述

视频演示

canvas图片拖动实现

改进的效果就是让图片拖动时或者点击时的逻辑符合正常人的思维,原来那种实现有问题,图片会会在最底层进行移动,图片的顺序也会产生问题。

具体的实现思路

鼠标点击图形时选择图形思路

图片叠加在一起时,当用户点击图片时,如果是选中了最上层的图片,那就应该拖动最上层的图片,但是原来那种实现方式其实是拖动最下层的图片。因为当时不知道如何拖动最上层的图片,多张图片重叠时,当鼠标点击到图片重叠处时,此时判断是否选中,实际上是都选中了。这次的实现方式是虽然选中了多张图片,但是我只处理最后一张被选中的。图片是依次进行绘制的。所以最底下的图片是最先进行绘制,也就是说最先被选中的,最上层的图片反而是最后被选中的。理清楚这个逻辑之后,那就只取最后被选中的这个图片即可,它就是最上层的图片了。

图形移动时保持图形的顺序

现在虽然选中了图片,但是图片移动过程中的顺序问题也要考虑清楚。当我们拖动图片经过别的图片时,通常的逻辑是被拖动的图片在上层,经过的图片在下层。我最开始的想法是直接将该图片变为对象数组中的最后一个元素,这样每次绘制时 ,它就是最后一个,自然是在所有图片的上方了。但是,在我进行测试时,我发现了不对劲。这里举一个例子说明一下:
图片1 图片2 图片3 图片4 图片5

现在我选择了图片3,此时图片数组的顺序变为了:
图片1 图片2 图片5 图片4 图片3

这里问题就出现了,可能看这个文字描述是看不出来的。这里的问题是从选中的图片之后的图片的顺序错误了,不考虑被选中的图片,最后两张图片的顺序颠倒了 。它会导致一个问题,假如图片4和图片5是重叠的,即图片4在图片5的下层,在选中图片3后,图片4和图片5的顺序错误了,导致图片4跑到了图片5的上层了。 所以解决方式就是:首先保存当前被选中的图片,然后将当前被选中图片之后的图片全部向前移动一位,最后将保存的图片移动到数组的最后。这样就保证了,对象数组中其它对象的相对位置不变了。所以,上述图片的顺序应该变为:
图片1 图片2 图片4 图片5 图片3

实现代码

注意: 优化了部分代码,建议结合前两篇博客,以便更好的进行理解。

<!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 current = -1;
			let rewidth = 150;  // 重定义图片的宽度
			
			let imgs = [];
			// 创建img元素
			Array.from({length: 6}, (a, i)=>i).forEach(index => {
				let img = document.createElement("img")
				img.src = index + ".jpg";
				// 当图片加载完成之后,将其绘制到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 => {
				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图形
				ctx.clearRect(0, 0, canvas.width, canvas.height);
				imgs.forEach((obj, index) => {
					drawCanvas(obj, "#FFFFFF");
					if (ctx.isPointInPath(x, y)) {
					    // 最上层的图片是最后绘制的,在图片重叠的情况下,
						// 最先选中的是最底层的图片,最后一张被选中的图片
						// 才是我们选中的图片。我们只需要操作重叠情况下的最后一张即可。
						current = index;
						console.log("被选中的图片:", index);
					}					
				});
				
				// 有图片被选中
				if (current != -1) {
					console.log("被选中的最后一张图片为第 %s 张图片。", current);					
					// 将当前选中元素移动到数组的最后,同时保证其它元素的保持原来的顺序
					let obj = imgs[current]
					for (let i = current; i < imgs.length-1; i++) {
						imgs[i] = imgs[i+1];
					}
					imgs[imgs.length-1] = obj;
					// 别忘了现在 current 指向的是最后一张图片了。
				    current = imgs.length-1;
					console.log(imgs);
					// 绘制红色选中框		
					drawRect(imgs[current], "red");
				}
			};
			
			// 在鼠标移动时,不断重绘制整个canvas
			canvas.onmousemove = e => {
				// 鼠标未按下或者未选中图片时则直接返回,不去响应该事件。
				if (current == -1) {
					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");
					// 更新当前图片的坐标,它一定是最后一张图片了!
					imgs[current].x = imgs[current].x + (x-now.x);
					imgs[current].y = imgs[current].y + (y-now.y);
					// 判断并即时矫正图片的坐标
					judgePosition(imgs[current]);

					console.log(imgs);
					
					// 重绘制偏移之后的canvas
					imgs.forEach(obj => drawCanvas(obj, "#FFFFFF"));
					drawRect(imgs[current], "red");
					now.x = x; now.y = y;
					console.log("鼠标在移动..." + x + " ==> " + y, obj.img);
				});
			}
			
			let stopDraw = e => {
				current = -1;       // 鼠标松开,重置图片选中状态为未选中
				console.log("鼠标松开, current: ", current);
				imgs.forEach(obj => drawCanvas(obj, "#FFFFFF"));
			}
			
			// 鼠标松开和鼠标离开元素绑定相同的处理事件。
			canvas.onmouseup = stopDraw;
			canvas.onmouseout = stopDraw;
			
			function drawCanvas(obj, color) {
				// 绘制所有的图片,这里的宽高是缩放后的宽高
			    ctx.drawImage(obj.img, obj.x, obj.y, obj.img.width, obj.img.height);
				drawRect(obj, color);
			}
			
			// 绘制选中框
			function drawRect(obj, color) {
			    // 绘制红色选中框
				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>

总结

对于问题要多进行思考,思考不是一蹴而就的,而是一个逐渐纠错的过程。可能最初的想法是迷糊的、错误的,但是在思考的过程中就会逐渐清晰、正确了。要勇于尝试,不断的尝试自己的想法,当你明白最初的想法是错误的,这才是最重要的,你知道了为什么错误,那么正确的方法也就会逐渐被找到了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值