在 Vue 2 中使用 Canvas 绘制多边形

news/2025/3/1 2:20:33/

Canvas 是一个强大的工具,用于创建动态图形和交互式可视化内容。Vue.js 提供了便捷的双向数据绑定和组件化开发方式,使得在 Vue 中使用 Canvas 变得更加高效。本文将介绍如何在 Vue 2 中实现一个简单的多边形绘制工具,支持报警区域和安全区域的绘制。

1. 项目背景

在某些监控系统或地理信息系统(GIS)中,需要在地图或视频流上绘制报警区域和安全区域。这些区域通常以多边形的形式表示,用户可以通过鼠标绘制和编辑这些区域。本文将实现一个基于 Vue 2 和 Canvas 的绘制工具,支持以下功能:

  • 绘制报警区域(单个多边形)。

  • 绘制安全区域(多个多边形)。

  • 动态绘制和闭合多边形。

  • 清空绘制的区域。

2. 项目搭建

使用 Vue CLI 创建一个新的 Vue 项目:

vue create vue-canvas-drawing
cd vue-canvas-drawing

3. 绘制工具的实现

3.1 组件结构

我们将创建一个名为 DrawPolygon 的组件,用于实现绘制功能。组件的模板结构如下:

<template><div class="drawing-tool"><canvasref="myCanvas"class="canvas"@mousedown="handleCanvasMouseDown"@mouseup="handleCanvasMouseUp"@mouseleave="handleCanvasMouseUp"oncontextmenu="return false;"></canvas><div class="tool-buttons"><button @click="polygonType = 'alarm'; isDrawingPolygon = true">绘制报警区域</button><button @click="polygonType = 'safe'; isDrawingPolygon = true">绘制安全区域</button><button @click="handleClear('alarm')">清空报警区域</button><button @click="handleClear('safe')">清空安全区域</button></div></div>
</template>

3.2 数据结构

data 中定义绘制所需的数据:

javascript">data() {return {isDrawingPolygon: false, // 是否处于绘制状态polygonType: "", // 当前绘制的多边形类型("alarm" 或 "safe")currentShape: null, // 当前绘制的多边形detail: {alarm: [], // 报警区域(单个多边形)safe: [[]], // 安全区域(多个多边形)rate: 1, // 宽高比例scaleX: 1, // 水平缩放比例scaleY: 1 // 垂直缩放比例}};
},

3.3 绘制逻辑

3.3.1  handleCanvasMouseDown方法

在鼠标按下时,初始化多边形的绘制:

javascript">handleCanvasMouseDown(event) {if (!this.isDrawingPolygon) return; // 如果没有在绘制状态,直接返回if (event.button !== 0) return; // 只响应左键点击const { x, y } = this.getCanvasCoords(event);if (this.polygonType === "alarm") {this.detail.alarm.push({ x, y });} else if (this.polygonType === "safe") {if (!this.currentShape) {this.detail.safe.push([]);this.currentShape = this.detail.safe[this.detail.safe.length - 1];}this.currentShape.push({ x, y });}this.drawShapes(); // 重新绘制
},
3.3.2  handleCanvasMouseUp方法

处理鼠标释放事件,完成绘制:

javascript">handleCanvasMouseUp(event) {if (!this.isDrawingPolygon) return; // 如果没有在绘制状态,直接返回if (event.button !== 2) return; // 只响应右键点击this.finishDrawingPolygon();
},
3.3 .3 finishDrawingPolygon方法

完成绘制,闭合多边形:

javascript">finishDrawingPolygon() {this.isDrawingPolygon = false; // 关闭绘制状态this.currentShape = null; // 清空当前绘制的多边形引用// 检查多边形的有效性(至少需要3个点)if (this.polygonType === "alarm" && this.detail.alarm.length < 3) {this.detail.alarm = []; // 如果点数不足,清空alarm区域} else if (this.polygonType === "safe") {const lastSafePolygon = this.detail.safe[this.detail.safe.length - 1];if (lastSafePolygon.length < 3) {this.detail.safe.pop(); // 如果点数不足,移除最后一个safe区域}}this.drawShapes("finish"); // 重新绘制所有多边形
},
3.3.4 dawShape方法

绘制多边形时,根据type参数决定是否闭合路径:

javascript">drawShapes(type = "") {const canvas = this.$refs.myCanvas;const ctx = canvas.getContext("2d");ctx.clearRect(0, 0, canvas.width, canvas.height);// 绘制alarm区域(单个区域)if (this.detail.alarm.length > 1) {ctx.beginPath();ctx.moveTo(this.detail.alarm[0].x, this.detail.alarm[0].y);this.detail.alarm.forEach(point => {ctx.lineTo(point.x, point.y);});if (type === "finish") {ctx.closePath();}ctx.strokeStyle = "red"; // alarm区域用红色ctx.lineWidth = 2;ctx.stroke();}// 绘制safe区域(多个区域)this.detail.safe.forEach(region => {if (region.length > 1) {ctx.beginPath();ctx.moveTo(region[0].x, region[0].y);region.forEach(point => {ctx.lineTo(point.x, point.y);});if (type === "finish") {ctx.closePath();}ctx.strokeStyle = "green"; // safe区域用绿色ctx.lineWidth = 2;ctx.stroke();}});
},
3.3.5 坐标转换方法

将鼠标事件的坐标转换为Canvas坐标:

javascript">getCanvasCoords(event) {const canvas = this.$refs.myCanvas;const rect = canvas.getBoundingClientRect();return {x: (event.clientX - rect.left) * this.detail.scaleX,y: (event.clientY - rect.top) * this.detail.scaleY};
},
3.3.6 清空方法
javascript">handleClear(type) {if (type === "alarm") {this.detail.alarm = [];} else if (type === "safe") {this.detail.safe = [];}this.drawShapes();
},

4. 数据存储与获取

4.1 数据存储

在用户完成绘制后,可以将绘制的多边形数据发送到后端进行存储。

数据格式根据项目情况调整。

javascript">alarm 的数据格式:[{"x": 39.198113207547166,"y": 43.977987421383645},{"x": 31.556603773584904,"y": 125.4874213836478},{"x": 124.95283018867924,"y": 121.21069182389937},{"x": 39.198113207547166,"y": 43.977987421383645},{"x": 39.198113207547166,"y": 43.977987421383645},
]
javascript">safe的数据格式:
[[{"x": 180.14150943396226,"y": 54.795597484276726},{"x": 188.91509433962264,"y": 111.3993710691824},{"x": 257.40566037735846,"y": 95.04716981132076},{"x": 180.14150943396226,"y": 54.795597484276726}],[{"x": 227.4056603773585,"y": 23.852201257861637},{"x": 237.5943396226415,"y": 62.84591194968554},{"x": 268.4433962264151,"y": 60.07861635220126},{"x": 227.4056603773585,"y": 23.852201257861637}]
]

x,y 的最终结果,处理公式: 

javascript">最终x = x / this.detail.scaleX * this.detail.rate
最终y = y / this.detail.scaleY * this.detail.rate

5.效果图


http://www.ppmy.cn/news/1575655.html

相关文章

常用的HTML meta标签有哪些

meta是 HTML 中的一个元数据标签&#xff0c;位于 <head> 标签内&#xff0c;不会在页面上直接显示&#xff0c;但能为浏览器和搜索引擎提供关于网页的重要信息。以下是一些常用的 <meta> 标签及其用途&#xff1a; 1. 字符编码声明 用于指定 HTML 文档的字符编码…

[已解决] ModuleNotFoundError: No module named ‘langgraph.checkpoint.sqlite‘

目录 0 背景 1 报错 2 解决方案 3 成果 参考文章-来自Google搜索&#xff1a;Short course "AI Agents in LangGraph" needs to be fixed in Lesson 4 - Short Course Q&A / AI Agents in LangGraph - DeepLearning.AI 0 背景 LangGraph 是一个开源的基于图…

MySQL--》如何通过选择合适的存储引擎提高查询效率?

目录 MySQL体系结构 存储引擎简介 存储引擎特点 存储引擎选择 MySQL体系结构 体系结构&#xff1a;可以看作是由多个模块和组件组成的一个系统&#xff0c;每个部分都承担着不同的职责&#xff0c;从客户端到存储引擎每一层都精心设计来提供高效、可靠的数据库服务&#x…

C#开发——日期操作类DateTime

在C#中&#xff0c;日期和时间的操作主要通过 System.DateTime 类来实现。 DateTime 提供了丰富的属性和法&#xff0c;用于处理日期和时间的创建、格式化、比较和计算等操作。以下是一些常用的日期函数和特性&#xff1a; 一、创建日期和时间 1、直接指定日期和时间&…

简单易懂,解析Go语言中的struct结构体

目录 4. struct 结构体4.1 初始化4.2 内嵌字段4.3 可见性4.4 方法与函数4.4.1 区别4.4.2 闭包 4.5 Tag 字段标签4.5.1定义4.5.2 Tag规范4.5.3 Tag意义 4. struct 结构体 go的结构体类似于其他语言中的class&#xff0c;主要区别就是go的结构体没有继承这一概念&#xff0c;但可…

从工程师到系统架构设计师

在技术领域&#xff0c;从一名初出茅庐的工程师成长为独当一面的系统架构设计师&#xff0c;是一条需要长期积累、持续突破的路径。这一过程不仅需要扎实的技术功底&#xff0c;更需要思维的升级和视野的拓展。以下将结合不同阶段的特征&#xff0c;为你梳理一条清晰的成长路线…

docker file中ADD命令的介绍

在 Docker 的世界里&#xff0c;Dockerfile 是一个用于定义镜像内容和行为的脚本文件。其中&#xff0c;ADD 指令是 Dockerfile 中一个非常重要的命令&#xff0c;用于将文件或目录从主机文件系统复制到容器的文件系统中。本文将详细介绍 ADD 指令的作用、使用方式以及一些最佳…

《以太坊账户模型与数据结构:探秘区块链世界的架构密码》

目录 引言一、以太坊账户模型二、数据结构的选择&#xff08;一&#xff09; 哈希表&#xff08;二&#xff09; Merkle Tree&#xff08;三&#xff09; Sorted Merkle TreeSorted Merkle Tree 的优缺点Sorted Merkle Tree 的应用场景Sorted Merkle Tree 与普通 Merkle Tree 对…