需求背景:甲方需要在一些CAD图纸上动态展示特定路线导航,比如从一个房间,到另外一个房间的路径,需要显示出路径,因为以前没有接触过这类特定需求,所以先暂时调研了一下,并尝试做个demo给甲方演示。
刚开始的想法是,通过H5的canvas画布手动画出来,自己也尝试过了,但效果不是很理想,再后来调研到leaflet可以导入图片作为底图,因为其本身就是做GIS的,有点线面的各种操作,比如打点,连线,绘制区域图层等等,而且以前我也用过leaflet做gis,比较熟悉,所以决定用leaflet尝试做个demo
点击展示路径后:
实现思路:首先npm install leaflet,也可以CDN 引入,如果不清楚可以搜索leaflet安装与使用,安装完成后,在组件里面引用,再在组件的onMounted里面去创建地图实例,然后再调用leaflet的一些切换底图的api,并以像素px为坐标系单位,图片左下角为坐标系原点(不知道可不可以换成常规的x为横轴,y为竖轴,没试过,大家可以自己查leaflet文档调研一下),再手动量几个点位(基于下面图片显示的坐标系),再定义一些渲染点与点之间的连线方法,以及使用css3的动画来控制线条出现的时机,用来做到路径导航的效果,具体的方法和说明,源代码上都有,可以仔细研究
代码如下:
<template>
<div>
<a-button type="primary" @click="ShowLine">展示路径</a-button>
<a-button type="primary" @click="removeLine" style="margin-left: 10px"
>移除路径</a-button
>
<div id="map"></div>
</div>
</template>
<script setup>
import button from "ant-design-vue";
import { onMounted, ref } from "vue";
import L from "leaflet";
import "leaflet/dist/leaflet.css";
import cadImg from "../assets/cad2.jpg";
// 定义三个点的坐标
const points = ref([
/* [338, 465], // 第一个点
[55, 467], // 第二个点
[58, 172], // 第二个点
[187, 172], // 第二个点
[187, 97], // 第二个点
[280, 97], // 第二个点
[280, 172], // 第二个点
[337, 172], // 第二个点 */
[40, 467], // 第一个点
[338, 465], // 第一个点
[338, 172],
[187, 172],
[187, 97],
[110, 97],
[110, 172],
[40, 172],
]);
// 初始化地图
let map;
let line;
// 移除路径
const removeLine = () => {
if (line) {
map.removeLayer(line); // 从地图上移除线条
line = null; // 清空线条引用
}
};
/* 展示路径 */
const ShowLine = () => {
// 创建连线
line = L.polyline(points.value, {
color: "blue",
weight: 5,
className: "animated-line", // 添加CSS类以实现动画
}).addTo(map);
/* drawLine(); */
};
onMounted(() => {
// 创建地图实例
map = L.map("map", {
crs: L.CRS.Simple, // 使用简单坐标系
zoomControl: false, // 禁用缩放控件
scrollWheelZoom: false, // 禁用鼠标滚轮缩放
doubleClickZoom: false, // 禁用双击缩放
touchZoom: false, // 禁用触摸缩放
boxZoom: false, // 禁用框选缩放
keyboard: false, // 禁用键盘快捷键缩放
}).setView([0, 0], 0);
// 加载平面图
const imageBounds = [
[0, 0],
[391, 640],
];
L.imageOverlay(cadImg, imageBounds).addTo(map);
map.fitBounds(imageBounds);
});
// 动态绘制连线
const drawLine = () => {
const linePoints = []; // 存储连线的点
let currentPointIndex = 0; // 当前连接的点的索引
linePoints.push(...points.value);
line.setLatLngs(linePoints); // 更新连线的点
/* const interval = setInterval(() => {
if (currentPointIndex < points.value.length) {
// 将当前点添加到连线中
linePoints.push(points.value[currentPointIndex]);
line.setLatLngs(linePoints); // 更新连线的点
currentPointIndex++;
} else {
clearInterval(interval); // 停止动画
}
}, 500); // 每500ms连接一个点 */
};
</script>
<style>
#map {
height: 800px; /* 调整地图高度 */
width: 100%; /* 调整地图宽度 */
}
/* 定义动画效果 */
.animated-line {
stroke-dasharray: 4000; /* 控制虚线的长度 */
stroke-dashoffset: 4000; /* 初始偏移量 */
animation: dash 32s linear forwards; /* 动画时长和效果 */
}
@keyframes dash {
to {
stroke-dashoffset: 0;
}
}
</style>
有些点是需要注意的:引入底图时,不能放底图的相对路径,需要import导入进来,或者直接把底图用在线工具转成base64格式,再放到L.imageOverlay(cadImg, imageBounds).addTo(map);里面去。
另外,这些点的坐标,是我手动通过ps工具量出来的,因为后端说到时候能给到前端,所以我就没思考坐标与CAD图里的建筑图啥的是否有关系。