Canvas进阶篇:鼠标交互动画

Canvas进阶篇:鼠标交互动画

前言

在上一篇文章Canvas进阶篇:基本动画详解 中,我们讲述了在Canvas中实现动画的基本步骤和动画的绘制方法。本文将进一步讲述如何通过鼠标事件增加动画和用户的交互,包括捕获鼠标的点击和拖动事件,获取鼠标在 Canvas 中坐标等。

获取鼠标坐标

在进行鼠标交互时,最主要的是要获取鼠标在Canvas中画布中的坐标。通常我们获取到的鼠标坐标都是相对于浏览器窗口的;如果想要获取相对于 Canvas 画布的坐标,就需要进行坐标转换,可以通过以下方式实现:

javascript 复制代码
function getCanvasCoordinates(event) {
  const rect = canvas.getBoundingClientRect();
  const x = event.clientX - rect.left;
  const y = event.clientY - rect.top;
  return { x, y };
}

上述代码中,canvas.getBoundingClientRect()方法返回 Canvas 在浏览器窗口中的位置和尺寸信息,通过用鼠标相对于窗口的坐标减去 Canvas 左上角相对于窗口的坐标,就得到了鼠标在 Canvas 中的坐标。

鼠标事件

鼠标事件一共分为4类:鼠标点击事件click、鼠标按下事件mousedown、鼠标移动事件mousemove和鼠标松开事件mouseup

点击事件监听

利用点击事件,我们可以在鼠标点击的位置绘制各种图形,比如圆形等,示例代码如下:

代码示例

html 复制代码
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>鼠标点击画圆</title>
		<style>
			#canvas {
				border: 1px solid #000;
			}
		</style>
	</head>
	<body>
		<canvas id="canvas" width="600" height="600"></canvas>
		<script>
			const canvas = document.getElementById('canvas');
			const ctx = canvas.getContext('2d');
			
			function getCanvasCoordinates(event) {
			  const rect = canvas.getBoundingClientRect();
			  const x = event.clientX - rect.left;
			  const y = event.clientY - rect.top;
			  return { x, y };
			}

			function handleClick(event) {
				const {
					x,
					y
				} = getCanvasCoordinates(event);
				ctx.beginPath();
				ctx.arc(x, y, 20, 0, 2 * Math.PI);
				ctx.fillStyle = 'red';
				ctx.fill();
			}

			// 绑定鼠标点击事件
			canvas.addEventListener('click', handleClick);
		</script>
	</body>
</html>

效果预览

拖动事件监听

实现拖动元素的功能,需要结合mousedownmousemovemouseup三个事件,我们以拖动一个矩形为例:

代码示例

html 复制代码
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>鼠标拖动矩形</title>
		<style>
			#canvas {
				border: 1px solid #000;
			}
		</style>
	</head>
	<body>
		<canvas id="canvas" width="600" height="600"></canvas>
		<script>
			const canvas = document.getElementById('canvas');
			const ctx = canvas.getContext('2d');

			function getCanvasCoordinates(event) {
				const rect = canvas.getBoundingClientRect();
				const x = event.clientX - rect.left;
				const y = event.clientY - rect.top;
				return {
					x,
					y
				};
			}

			// 矩形数组
			let rectangles = [];

			// 拖拽状态
			let isDragging = false;
			let startX, startY;
			let offsetX, offsetY;
			let draggedRect = null;

			function handleMouseDown(event) {
				const {
					x,
					y
				} = getCanvasCoordinates(event);
				for (const rect of rectangles) {
					if (x >= rect.x && x <= rect.x + rect.width && y >= rect.y && y <= rect.y + rect.height) {
						isDragging = true;
						draggedRect = rect;
						startX = x;
						startY = y;
						offsetX = x - rect.x;
						offsetY = y - rect.y;
						break;
					}
				}
			}

			function handleMouseMove(event) {
				if (isDragging) {
					const {
						x,
						y
					} = getCanvasCoordinates(event);
					if (draggedRect) {
						draggedRect.x = x - offsetX;
						draggedRect.y = y - offsetY;
						drawCanvas();
					}
				}
			}

			function handleMouseUp() {
				isDragging = false;
				draggedRect = null;
			}

			function drawCanvas() {
				ctx.clearRect(0, 0, canvas.width, canvas.height);
				for (const rect of rectangles) {
					ctx.beginPath();
					ctx.rect(rect.x, rect.y, rect.width, rect.height);
					ctx.fillStyle = 'blue';
					ctx.fill();
				}
			}

            // 绘制矩形
			function addRectangle() {
				const rect = {
					x: Math.random() * (canvas.width - 100),
					y: Math.random() * (canvas.height - 100),
					width: 80 + Math.random() * 40,
					height: 60 + Math.random() * 40,
					color: `hsl(${Math.random() * 360}, 70%, 60%)`
				};
				rectangles.push(rect);
				drawCanvas();
			}

			// 绑定鼠标按下事件,用于拖动起始
			canvas.addEventListener('mousedown', handleMouseDown);
			// 绑定鼠标移动事件,用于拖动过程
			canvas.addEventListener('mousemove', handleMouseMove);
			// 绑定鼠标松开事件,用于拖动结束
			canvas.addEventListener('mouseup', handleMouseUp);
			addRectangle();
		</script>
	</body>
</html>

效果预览

结语

本文主要介绍了在 Canvas 中处理鼠标点击和拖动事件的方法,对于文章中错误的地方或者有任何问题,欢迎在评论区留言分享!

相关推荐
sq8002 分钟前
ag-grid-vue3 降级,支持低版本浏览器
前端·javascript·vue.js
兔年鸿运Q小Q4 分钟前
html转word下载
javascript·vue.js·word
华洛11 分钟前
Agent应用落地,必不可少的三大辅助系统
前端·javascript·vue.js
前端小巷子31 分钟前
Vue 2 组件通信全景指南
前端·javascript·面试
江城开朗的豌豆32 分钟前
Vue的双向绑定已经能精确追踪变化,为什么还要用虚拟DOM?揭秘背后的性能哲学!
前端·javascript·vue.js
掘金安东尼1 小时前
⏰前端周刊第424期(2025年7月21日–7月27日)
前端·javascript·面试
江城开朗的豌豆1 小时前
Vue和React的数据流之争:双向绑定 vs 单向数据流,谁更适合你?
前端·javascript·vue.js
芜青1 小时前
JavaScript手录09-内置对象【String对象】
开发语言·javascript·ecmascript
世伟爱吗喽1 小时前
最新面试题总结
前端·javascript·vue.js
江城开朗的豌豆1 小时前
前端权限控制实战:手把手教你玩转角色权限分配
前端·javascript·vue.js