如何实现vue项目中使用Baidu Map,有多个点,鼠标移入出现文字标注,移除消失文字标注,并且点的轨迹连线有箭头指向。

发布时间 2023-04-26 14:56:21作者: 清风引佩下瑶台

直接来案例,再分析;

<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条还是很快,但是有个问题,坐标点都太近,那就会报错,只要有一个点比较远就能正常,也是很奇怪,排查了问题,最终解决了,此代码就是解决之后的。
文中注释很多,顺便提一下,我这个部门搜索,可根据下拉框中显示的姓名搜索外,还可以根据里面参数搜索。