高德地图轨迹播放

MapDemo

<template>
  <div>
    <button @click="clearMap">清除地图</button>
    <button @click="initTrack">初始化轨迹</button>
    <button @click="playTrack">轨迹播放</button>
    <button @click="playTrack2">带状态的轨迹播放</button>
    <button @click="pauseTrack">暂停轨迹播放</button>
    <button @click="continueTrack">继续轨迹播放</button>
    <button @click="stopTrack">结束轨迹播放</button>
    <button @click="clearTrack">清除轨迹播放</button>
    <el-slider
      v-model="speed"
      @change="setTrackSpeed"
    ></el-slider>
    <div id="mapContainer"></div>
    <div id="trackContainer"></div>
  </div>
</template>

<script>
  import { MapTrack } from './track';

  export default {
    name: 'MapDemo',
    data() {
      return {
        map: null,
        speed: 0,
        mapTrack: null,
      };
    },
    mounted() {
      this.initMap();
    },
    beforeDestroy() {
      this.destroyMap();
    },
    methods: {
      initMap() {
        this.map = new AMap.Map('mapContainer', {
          zoom: 15,
          center: this.$mapCenter, // 设置地图中心点
          doubleClickZoom: false,
        });
        this.mapTrack = new MapTrack(this.map, { iconFn: this.trackIconFn, infoWindowContentFn: this.trackInfoWindowContentFn });
      },
      destroyMap() {
        this.mapTrack?.destroy();
        this.map?.destroy();
        this.map = null;
      },
      clearMap() {
        this.map.clearMap();
      },
      initTrack() {
        const lines = [
          { lng: 114.987519, lat: 30.125872, status: 1 },
          { lng: 114.992969, lat: 30.128545, status: 1 },
          { lng: 114.992969, lat: 30.128545, status: 2 },
          { lng: 114.99563, lat: 30.127617, status: 2 },
          { status: 1 },
          { status: 1 },
          { status: 1 },
          { lng: 114.996831, lat: 30.126355, status: 2 },
          { status: 1 },
          { lng: 114.998634, lat: 30.126318, status: 1 },
          { lng: 115.001337, lat: 30.127914, status: 3 },
          { lng: 115.004427, lat: 30.127673, status: 2 },
          { lng: 115.00623, lat: 30.126838, status: 2 },
          { lng: 115.00623, lat: 30.126838, status: 1 },
        ];
        this.mapTrack.initTrack(lines);
      },
      playTrack2() {
        this.mapTrack.playTrack(null, 100);
      },
      playTrack() {
        const lines = [
          [114.987519, 30.125872],
          [114.992969, 30.128545],
          [114.99563, 30.127617],
          [114.996831, 30.126355],
          null,
          [114.998634, 30.126318],
          [115.001337, 30.127914],
          [115.004427, 30.127673],
          [115.00623, 30.126838],
          [115.00623, 30.126838],
        ];
        this.mapTrack.playTrack(lines, 100);
      },
      trackIconFn(item) {
        if (item.status === 1) return { fillColor: '#00ff00' };
        if (item.status === 2) return { fillColor: '#ff0000' };
        return { fillColor: '#0000ff' };
      },
      trackInfoWindowContentFn(item) {
        return `
        <div id="track-infoWindow">
          <span>我是自定义的信息窗口: ${item.status}</span>
        </div>
      `;
      },
      pauseTrack() {
        this.mapTrack.pauseTrack();
      },
      continueTrack() {
        this.mapTrack.continueTrack();
      },
      stopTrack() {
        this.mapTrack.stopTrack();
      },
      clearTrack() {
        this.mapTrack.clearTrack();
      },
      setTrackSpeed(speed) {
        this.mapTrack.setSpeed(speed);
      },
    },
  };
</script>

<style scoped lang="scss">
  #mapContainer {
    width: 100%;
    height: 500px;
  }
</style>
<style lang="scss">
  #track-infoWindow {
    padding: 10px;
  }
</style>

transform.js 见!地图工具类:地图坐标系转换、获取点位经纬度信息
track.js

import IconDefault from './icon/icon_marker_default.svg';
import IconEnd from './icon/icon_marker_end.svg';
import IconStart from './icon/icon_marker_start.svg';
import { getLngLatOfPoint } from './transform';

const ICON_SIZE = new AMap.Size(36, 36);
const ICON_OFFSET = new AMap.Pixel(-18, -36);

export class MapTrack {
  #mapObj = null; // 地图对象
  #infoWindow = null; // 信息窗口
  speed = 5000; // 轨迹播放速度
  status = 'stop'; // 状态
  #marker = null;
  isInited = false;
  pointDatas = []; // 轨迹数据
  paths = []; // 路径集合
  #polylines = []; //
  #pointMarkers = []; //
  #currentPointIndex = null;
  iconFn = null;
  infoWindowContentFn = null;

  constructor(mapObj, { iconFn, infoWindowContentFn } = {}) {
    if (!this.#mapObj) {
      this.#mapObj = mapObj;
    }
    if (typeof iconFn === 'function') {
      this.iconFn = iconFn;
    }
    if (typeof infoWindowContentFn === 'function') {
      this.infoWindowContentFn = infoWindowContentFn;
      this.#initInfoWindow();
    }
    this.#mapObj.plugin(['AMap.MoveAnimation'], function () {});
  }
  /* 销毁 */
  destroy() {
    this.status = 'stop';
    this.clearTrack();
  }
  /* 初始化信息窗口 */
  #initInfoWindow() {
    if (this.#infoWindow) return;

    this.#infoWindow = new AMap.InfoWindow({
      anchor: 'top-left',
      offset: new AMap.Pixel(10, 20),
    });
  }
  /* 清空轨迹*/
  clearTrack() {
    if (!this.isInited) return;

    this.paths = [];
    this.pointDatas = [];
    this.status = 'stop';
    this.#mapObj.remove(this.#polylines);
    this.removeMoveMarker();
    this.#mapObj.remove(this.#pointMarkers);
    this.#polylines = [];
    this.#pointMarkers = [];
    this.#currentPointIndex = null;
    this.isInited = false;
  }
  /* 轨迹初始化*/
  initTrack(path) {
    if (path.length < 2) return;

    this.clearTrack();
    this.isInited = true;
    // 过滤掉无坐标的数据
    this.pointDatas = path.filter((v) => {
      if (Array.isArray(v) && v.length === 2) return true;

      return v?.lng && v?.lat;
    });
    this.paths = this.pointDatas.map((item) => getLngLatOfPoint(item));
    this.#intPolylines(path);
    this.#creatPoints(this.pointDatas);
  }
  /* 创建移动点*/
  #createMarker() {
    if (!this.pointDatas?.length) return;

    const firstPosition = getLngLatOfPoint(this.pointDatas[0]);
    this.#marker = new AMap.Marker({
      map: this.#mapObj,
      position: firstPosition,
      icon: new AMap.Icon({
        size: ICON_SIZE,
        imageSize: ICON_SIZE,
        image: IconDefault,
      }),
      offset: ICON_OFFSET,
    });
    this.#marker.on('moving', (e) => this.#onMarkerMove(e));
  }
  /* 轨迹播放*/
  playTrack(path, speed) {
    if (path?.length >= 2) {
      this.initTrack(path);
    } else if (!this.#marker) {
      this.#createMarker();
    }
    this.status = 'play';
    this.speed = speed;
    this.#markerMovingAlong();
  }
  /* 暂停轨迹播放*/
  pauseTrack() {
    this.status = 'pause';
    this.#marker?.pauseMove();
  }
  /* 继续轨迹播放*/
  continueTrack() {
    this.status = 'play';
    this.#marker?.resumeMove();
  }
  /* 停止轨迹播放*/
  stopTrack() {
    this.status = 'stop';
    this.#marker?.stopMove();
  }
  /* 设置轨迹播放速度*/
  setSpeed(speed) {
    this.speed = speed;
    if (!this.#marker) return;

    this.#markerMovingAlong();
  }
  #markerMovingAlong() {
    if (!this.paths?.length) return;

    this.#marker.moveAlong(this.paths, {
      duration: this.speed * 10, // 每一段的时长
      // JSAPI2.0 是否延道路自动设置角度在 moveAlong 里设置
      autoRotation: false,
    });
  }
  #creatPoints(pointDatas) {
    this.#pointMarkers = [];
    pointDatas.forEach((item, index) => {
      const position = getLngLatOfPoint(item);
      if (index === 0) {
        const startElement = new AMap.Marker({
          map: this.#mapObj,
          position,
          icon: new AMap.Icon({
            size: ICON_SIZE,
            imageSize: ICON_SIZE,
            image: IconStart,
          }),
          offset: ICON_OFFSET,
        });
        this.#mapObj.add(startElement);
        this.#pointMarkers.push(startElement);
      } else if (index === pointDatas.length - 1) {
        const endElement = new AMap.Marker({
          map: this.#mapObj,
          position,
          icon: new AMap.Icon({
            size: ICON_SIZE,
            imageSize: ICON_SIZE,
            image: IconEnd,
          }),
          offset: ICON_OFFSET,
        });
        this.#mapObj.add(endElement);
        this.#pointMarkers.push(endElement);
      }

      const config = this.iconFn?.(item) || {};
      let element = new AMap.CircleMarker({
        center: position, // 圆心
        radius: 6, // 半径
        // strokeColor: '#165dff', // 轮廓线颜色
        strokeWeight: 0, // 轮廓线宽度
        fillColor: '#165dff', // 圆点填充颜色
        fillOpacity: 1, // 圆点填充透明度
        zIndex: 10, // 圆点覆盖物的叠加顺序
        cursor: 'pointer', // 鼠标悬停时的鼠标样式
        ...config,
        extData: item,
      });
      this.#pointMarkers.push(element);
      element.setMap(this.#mapObj);
      this.#elementBindEvent(element);
    });
  }
  /* 创建线 */
  #createPolylineElement(path, strokeStyle = 'solid') {
    const element = new AMap.Polyline({
      map: this.#mapObj,
      path: path,
      strokeStyle,
      strokeColor: strokeStyle === 'solid' ? '#28F' : '#bfbfbf', // 线颜色
      strokeWeight: 6, // 线宽
      zIndex: 1,
    });
    this.#polylines.push(element);
    this.#mapObj.add(element);
    return;
  }
  /* 创建线集合*/
  #intPolylines(paths) {
    let currentPath = [];
    let dashedPaths = [];
    const pathsLength = paths.length;
    for (let index = 0; index < pathsLength - 1; index++) {
      const point = getLngLatOfPoint(paths[index]);
      let nextPoint;
      if (index + 1 < pathsLength - 1) {
        nextPoint = getLngLatOfPoint(paths[index + 1]);
      }
      // 如果当前点及下一个点有数据,则推送到currentPath中
      if (point && nextPoint) {
        currentPath.push(point, nextPoint);
        index++;
      } else {
        // 出现中断,绘制已有实线
        if (currentPath.length > 1) {
          this.#createPolylineElement(currentPath);
          currentPath = [];
        }

        // 找到上一个及下一个有数据的点,绘制虚线
        let prevIndex;
        if (point) {
          prevIndex = index;
        } else {
          prevIndex = index - 1;
          while (prevIndex >= 0 && !getLngLatOfPoint(paths[prevIndex])) {
            prevIndex--;
          }
        }
        let nextIndex = index + 1;
        while (nextIndex < pathsLength - 1 && !getLngLatOfPoint(paths[nextIndex])) {
          nextIndex++;
        }
        if (prevIndex >= 0 && nextIndex < pathsLength) {
          const prevPoint = getLngLatOfPoint(paths[prevIndex]);
          const nextPoint = getLngLatOfPoint(paths[nextIndex]);
          dashedPaths.push([prevPoint, nextPoint]);
        }
        index = nextIndex - 1; // 循环结束会++,此处-1
      }
    }

    if (currentPath.length > 1) {
      this.#createPolylineElement(currentPath);
    }

    dashedPaths.forEach((path) => {
      this.#createPolylineElement(path, 'dashed');
    });
  }
  #onMarkerMove(e) {
    // this.#mapObj.setCenter(e.target.getPosition(), true);
    if (e.index !== this.#currentPointIndex) {
      this.#currentPointIndex = e.index;
    }
    if (e.progress === 1 && this.#currentPointIndex === this.pointDatas.length - 2) {
      this.status = 'stop';
      this.removeMoveMarker();
    }
  }
  removeMoveMarker() {
    if (!this.#marker) return;

    this.stopTrack();
    this.#marker?.off('moving', (e) => this.#onMarkerMove(e));
    this.#mapObj.remove(this.#marker);
    this.#marker = null;
  }
  /* 元素绑定事件*/
  #elementBindEvent(element) {
    element.on('mouseover', (e) => {
      if (this.#infoWindow) {
        const data = e.target.getExtData();
        const content = this.infoWindowContentFn?.(data);
        const lnglat = getLngLatOfPoint(data);
        this.#infoWindow.setContent(content);
        this.#infoWindow.open(this.#mapObj, lnglat);
      }
    });
    element.on('mouseout', () => {
      this.#infoWindow?.close();
    });
  }
}


作者:yiping5

出处:https://www.cnblogs.com/yiping5/p/18558902

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Ping5-1  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示