如何实现vue项目中使用Baidu Map,有多个点,鼠标移入出现文字标注,移除消失文字标注,并且点的轨迹连线有箭头指向。
直接来案例,再分析;
<template>
<div ref="map" style="height: 100vh;"></div>
</template>
<script>
export default {
data() {
return {
points: [
{ lng: 116.404, lat: 39.915, name: '北京天安门' },
{ lng: 121.47, lat: 31.23, name: '上海东方明珠' },
{ lng: 120.16, lat: 30.25, name: '杭州西湖' },
],
labels: [], // 记录已添加的所有 label 对象
};
},
mounted() {
const map = new BMap.Map(this.$refs.map);
// 循环遍历每一个坐标点,并创建标注对象和文字标注对象
const polylinePoints = this.points.map((p) => new BMap.Point(p.lng, p.lat)); // 创建用于绘制连线的 BMap.Point 数组
const polyline = new BMap.Polyline(polylinePoints, {
strokeWeight: 3,
strokeColor: '#F00',
strokeOpacity: 0.7,
strokeStyle: 'dashed',
enableClicking: false,
arrowStyle: 'classic', // 设置箭头标志符号样式
arrowSize: 12, // 设置箭头标志符号大小
});
map.addOverlay(polyline);
// 循环遍历每一个坐标点,并创建标注对象和文字标注对象
for (let i = 0; i < this.points.length; i++) {
const point = new BMap.Point(this.points[i].lng, this.points[i].lat);
const icon = new BMap.Icon('https://lbsyun.baidu.com/jsdemo/img/fox.gif', new BMap.Size(300, 157));
const marker = new BMap.Marker(point, { icon });
map.addOverlay(marker);
// 创建 label 对象
const label = new BMap.Label(this.points[i].name, {
offset: new BMap.Size(-20, -30),
});
marker.addEventListener('mouseover', () => {
label.setPosition(point); // 设置 label 的地理位置
map.addOverlay(label); // 将 label 添加到地图上
this.labels.push(label); // 记录已添加的 label 对象
});
marker.addEventListener('mouseout', () => {
map.removeOverlay(label); // 将 label 从地图上移除
this.labels = this.labels.filter((item) => item !== label); // 从记录中删掉该 label 对象
});
}
// 初始化地图控件
const bounds = new BMap.Bounds(polylinePoints[0], polylinePoints[polylinePoints.length - 1]);
map.centerAndZoom(bounds.getCenter(), map.getViewport([bounds]).zoom);
map.enableScrollWheelZoom(true);
},
beforeDestroy() {
const map = this.$refs.map.BMapInstance;
// 页面销毁时,将所有 label 对象从地图上移除,并清空记录数组
this.labels.forEach((label) => {
map.removeOverlay(label);
});
this.labels = [];
},
};
</script>
这个示例代码中创建了一个包含多个坐标点的数组 points
,并在循环遍历每个点时创建了一个 BMap.Marker
标注对象和对应的文字标注 BMap.Label
对象。使用 marker.addEventListener()
方法分别绑定鼠标进入和移出事件,在进入事件回调函数中添加 label 对象到地图中,并记录到数组 labels
中;在移出事件回调函数中从地图上移除 label 对象,并从 labels
数组中删除。
创建连线用于连接多个坐标点,并设置箭头标志样式和大小。
在页面销毁时,通过 beforeDestroy()
钩子函数将所有 label 对象从地图上移除,并清空 labels
数组,以防止内存泄漏。
ps:注意在 Vue 项目中使用 Baidu Map,需要将地图容器 div
挂载到组件 ref
上,并在地图初始化时使用 this.$refs.map.BMapInstance
获取 BMap.Map
对象。
但是最后写在项目中的实例。
以下是实例:
<template>
<div class="mapTrajectories">
<div class="queryBox">
<el-row>
<div class="lableText">姓名:</div>
<el-col :span="6">
<el-select
size="mini"
v-model="formDatas.user"
style="width: 100%"
placeholder="请选择"
filterable
clearable
@change="querySearch"
:filter-method="dataFilter"
>
<el-option
v-for="item in userInfoListFilter"
:key="item.id"
:label="item.realname"
:value="item.id"
>
</el-option>
</el-select>
</el-col>
<div class="lableText">日期:</div>
<el-col :span="8">
<el-date-picker
size="mini"
v-model="formDatas.time"
type="daterange"
range-separator="至"
style="width: 100%"
start-placeholder="开始日期"
end-placeholder="结束日期"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
>
</el-date-picker>
</el-col>
<el-col :span="3">
<el-button
size="mini"
type="primary"
style="margin-left: 20px"
@click="queryMap(false)"
>查询打卡记录</el-button
>
</el-col>
<el-col :span="3">
<el-button
size="mini"
plain
type="primary"
style="margin-left: 20px"
:disabled="!formDatas.user"
@click="queryMap(true)"
>查询打卡轨迹</el-button
>
</el-col>
</el-row>
</div>
<div
id="allmap"
ref="map"
v-loading="loading"
element-loading-text="拼命加载中"
element-loading-spinner="el-icon-loading"
element-loading-background="rgba(0, 0, 0, 0.5)"
></div>
</div>
</template>
<script>
import { mapGetters } from "vuex";
import { getLocation_clockin } from "@/api/business/basicBusiness";
import { queryIsLeader } from "@/api/business/myApprove";
import { queryAllUsers } from "@/api/business/index";
import icons from "@/assets/images/location2.png";
export default {
name: "basicBusiness-mapTrajectories",
components: {},
data() {
return {
map: null,
isLeader: false,
points: [
{ lng: 118.80542, lat: 32.039748, name: "南京1" },
{ lng: 118.79542, lat: 32.019748, name: "南京2" },
{ lng: 118.77542, lat: 32.029748, name: "南京3" },
{ lng: 118.60942, lat: 32.049748, name: "南京4" },
{ lng: 118.80942, lat: 32.049748, name: "南京5" },
],
polyline: null,
labels: [], // 记录已添加的所有 label 对象
userInfoList: [], //查询条件--数据状态
userInfoFilterVal: "", // 筛选字段
formDatas: {
user: "",
time: [],
},
loading: false,
isAddOverlay: false, // 是否显示轨迹
};
},
computed: {
...mapGetters(["userInfo"]),
userInfoListFilter() {
if (!this.userInfoFilterVal) return this.userInfoList;
return this.userInfoList.filter((v) => {
if (v.realname && v.realname.includes(this.userInfoFilterVal))
return true;
if (
v.department &&
v.department.namePath.includes(this.userInfoFilterVal)
)
return true;
return false;
});
},
},
mounted() {
this.initParams();
// this.initMap();
// this.setPointMarkers()
// this.queryisLeader()
const myDate = new Date();
let M = myDate.getMonth() + 1;
if (M < 10) {
M = "0" + M;
}
console.log(M, "MMMM");
//
this.formDatas.time = [
// myDate.getFullYear() +'-'+ M +"-01",
myDate.toLocaleDateString().replaceAll("/", "-"),
myDate.toLocaleDateString().replaceAll("/", "-"),
];
this.queryMap(false);
},
methods: {
initParams() {
let temp = [
// {
// id: "all",
// realname: "全部",
// },
];
//填报人
queryAllUsers().then((res) => {
this.$nextTick(() => {
this.userInfoList = res;
// this.userInfoList = temp.concat(this.userInfoList); //option--add全部
});
});
},
queryMap(isAddOverlay) {
this.isAddOverlay = isAddOverlay;
this.loading = true;
let data = {
userid: this.userInfo.user.id, //当前登录用户id
start:
this.formDatas.time && this.formDatas.time.length > 0
? this.formDatas.time[0]
: null, //默认当前月
end:
this.formDatas.time && this.formDatas.time.length > 0
? this.formDatas.time[1]
: null, //默认当前月
user: this.formDatas.user, //打卡人员id
};
getLocation_clockin(data).then((res) => {
if (res.code == "200") {
this.loading = false;
new Promise((resolve, reject) => {
this.points = res.data;
resolve();
}).then((res) => {
this.initMap();
});
}
});
},
dataFilter(val) {
this.userInfoFilterVal = val;
},
querySearch() {
this.userInfoFilterVal = "";
},
openDrawer() {
this.$refs.detailsDrawer.drawer = true;
},
beforeDestroy() {
this.map = this.map.BMapInstance;
// 页面销毁时,将所有 label 对象从地图上移除,并清空记录数组
this.labels.forEach((label) => {
this.map.removeOverlay(label);
});
this.labels = [];
},
initMap() {
this.map = new BMap.Map("allmap");
this.map.centerAndZoom(
new BMap.Point(106.34067861392542, 34.129025520059834),
6
);
// 初始化地图控件
this.map.enableScrollWheelZoom();
this.map.setMaxZoom(15);
// 循环遍历每一个坐标点,并创建标注对象和文字标注对象
for (let i = 0; i < this.points.length; i++) {
const point = new BMap.Point(this.points[i].lng, this.points[i].lat);
const icon = new BMap.Icon(icons, new BMap.Size(16, 25));
const marker = new BMap.Marker(point, { icon });
this.map.addOverlay(marker);
// 创建 label 对象
const label = new BMap.Label(this.points[i].name, {
offset: new BMap.Size(-20, -30),
});
marker.addEventListener("mouseover", () => {
label.setPosition(point); // 设置 label 的地理位置
this.map.addOverlay(label); // 将 label 添加到地图上
this.labels.push(label); // 记录已添加的 label 对象
});
marker.addEventListener("mouseout", () => {
this.map.removeOverlay(label); // 将 label 从地图上移除
this.labels = this.labels.filter((item) => item !== label); // 从记录中删掉该 label 对象
});
}
// 循环遍历每一个坐标点,并创建标注对象和文字标注对象
const polylinePoints = this.points.map((p) => {
// console.log(p,'ppppppppp');
return new BMap.Point(p.lng, p.lat);
}); // 创建用于绘制连线的 BMap.Point 数组
console.log(polylinePoints, "polylinePoints====", this.points);
let sy = new BMap.Symbol(BMap_Symbol_SHAPE_BACKWARD_OPEN_ARROW, {
scale: 0.5, //图标缩放大小
strokeColor: "#f00", //设置矢量图标的线填充颜色
strokeWeight: "1", //设置线宽
});
let iconsarrow = new BMap.IconSequence(sy, "0", "20");
setTimeout(() => {
let iconDatas = [iconsarrow];
// 创建箭头标志符号
const polyline = new BMap.Polyline(polylinePoints, {
strokeWeight: 0.1, //折线的宽度,以像素为单位
strokeColor: "#F00",
strokeOpacity: 0.1, //折线的透明度,取值范围0 - 1
strokeStyle: "dashed",
icons: iconDatas,
enableClicking: false,
arrowStyle: "classic", // 设置箭头标志符号样式
arrowSize: 2, // 设置箭头标志符号大小
// strokeOffset: new BMap.Size(-10, 10) // 改变线的位置
enableEditing: false, //是否启用线编辑,默认为false
enableClicking: true, //是否响应点击事件,默认为true
});
console.log(polyline, "polylineeeeeeeeeee");
if (this.isAddOverlay) {
this.map.addOverlay(polyline);
}
}, 100);
// 自动缩放地图,使轨迹全部可视化
let viewport = this.map.getViewport(this.points);
this.map.centerAndZoom(viewport.center, viewport.zoom);
},
},
};
</script>
<style scoped lang="scss">
.mapTrajectories {
position: relative;
top: 0px;
bottom: 0;
width: 100%;
}
#allmap {
width: 100%;
height: 100%;
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Chrome/Safari/Opera */
-khtml-user-select: none; /* Konqueror */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently not supported by any browser */
}
.queryBox {
width: 900px;
position: absolute;
top: 20px;
left: 20px;
z-index: 99;
background: #fff;
height: 40px;
line-height: 38px;
padding: 0px 10px;
border-radius: 6px;
.lableText {
width: 60px;
padding-left: 20px;
float: left;
}
}
</style>
最后说一下开发中遇到的坑,在绘制点的时候没啥问题,就是大概5000条数据大概8秒钟,1000条还是很快,但是有个问题,坐标点都太近,那就会报错,只要有一个点比较远就能正常,也是很奇怪,排查了问题,最终解决了,此代码就是解决之后的。
文中注释很多,顺便提一下,我这个部门搜索,可根据下拉框中显示的姓名搜索外,还可以根据里面参数搜索。
长风破浪会有时,直挂云帆济沧海