实现高德地图实时轨迹组件

2,026 阅读2分钟
<!--Index.vue-->
<template>
  <div
    :id="mapId"
    :style="{height,width}"
  ></div>
</template>
<script>
  /**
   *@ 实时动态轨迹, 直接单车多车同时实时动态运行
   */
  import { initMap } from './mapUtils';
  /*
  [116.418757,39.917544], // 北京
  [116.366794,39.915309], //西城区
  [116.637122,40.324272], //怀柔区
   [117.164143,39.120474], // 南开区
   [116.286968,39.863642], // 丰台区
   */
  export default {
    name: 'RealTimeTrack',
    props: {
      mapId: {
        required: false,
        type: String,
        default: 'real-time-track'
      },
      width: {
        required: false,
        type: String,
        default: '500px'
      },
      height: {
        required: false,
        type: String,
        default: '500px'
      },
      /*
      positionConfig : {
            carId1:{
            position:[],
                 },
            carId2:{
             position:[],
                }
          }
       */
      positionConfig: {
        required: true,
        type: Object
      }
    },
    data() {
      return {
        map: null,
        navigator: null,
        config: null,
        saveQueue: [],
        curIdx: -1
      };
    },
    watch: {
      positionConfig: {
        handler(newV) {
          this.saveQueue.push(newV);
        },
        deep: true
      }
    },
    mounted() {
      this.config = this.positionConfig;
      initMap(this);
    },
    methods: {
      pathLineClick(data) {
        this.$emit('pathLineClick', data);
      },
      toListen() {
        //  排除网路延时造成的速度错误影响
        this.navigator.on('pause', () => {
          this.curIdx++;
          this.config = this.saveQueue[this.curIdx];
          this.navigator.expandPath();
        });
      }
    }
  };
</script>
/* eslint-disable */

import CreateNvg from './cnvg';

const initData = (data) => {
  let list = [];
  Object.values(data).forEach((item, index) => item.id = index);
  for (let carId in data) {
    list.push({
      id: data[carId].id,
      name: carId,
      path: data[carId].position,
      ...data[carId],
      stopStatus: false
    });
  }

  return list;
};

export const initMap = (vm) => {
  vm.map = new AMap.Map(vm.mapId, {
    zoom: 5
  });

  AMapUI.load(['ui/misc/PathSimplifier', 'lib/$'], function(PathSimplifier, $) {
    if (!PathSimplifier.supportCanvas) {
      alert('当前环境不支持 Canvas!');
      return;
    }
    var emptyLineStyle = {
      lineWidth: 0,
      fillStyle: null,
      strokeStyle: null,
      borderStyle: null
    };
    var pathSimplifierIns = new PathSimplifier({
      zIndex: 10000,
      // autoSetFitView: false,
      map: vm.map, // 所属的地图实例

      getPath: function(pathData, pathIndex) {
        return pathData.path;
      },
      getHoverTitle: function(pathData, pathIndex, pointIndex) {
        return null;
      },
      renderOptions: {
        //将点、线相关的style全部置emptyLineStyle
        pathLineStyle: emptyLineStyle,
        pathLineSelectedStyle: emptyLineStyle,
        pathLineHoverStyle: emptyLineStyle,
        keyPointStyle: emptyLineStyle,
        startPointStyle: emptyLineStyle,
        endPointStyle: emptyLineStyle,
        keyPointHoverStyle: emptyLineStyle,
        keyPointOnSelectedPathLineStyle: emptyLineStyle
        // renderAllPointsIfNumberBelow: 100 // 绘制路线节点,如不需要可设置为-1
      }
    });

    pathSimplifierIns.on('pathClick pointClick', function(e, info) {
      //info.pathData 即是相关的轨迹数据,如果info.pointIndex >= 0,则表示由轨迹上的节点触发
      vm.pathLineClick(info);
    });

    const config = initData(vm.config);

    pathSimplifierIns.setData(config);

    vm.navigator = new CreateNvg(config, pathSimplifierIns, PathSimplifier);

    vm.toListen();
  });
};

<!--cnvg.js-->
export default class CreateNvg {
  constructor(config, pathSimplifierIns, PathSimplifier) {
    this.config = config;
    this.listens = {};
    this.endIdx = 0; //  只需要考虑最长路程节点的
    this.id = [];
    this.cursor = [];
    this.status = [];
    this.navigatorList = [];
    this.pathSimplifierIns = pathSimplifierIns;
    this.PathSimplifier = PathSimplifier;
    this.init();
  }

  initData() {
    this.id = this.config.map(item => item.id);
    this.cursor = new Array(this.config.length);
    this.status = new Array(this.config.length);
  }

  init() {
    this.initData();
    this.pathSimplifierIns.setData(this.config);
    this.expandPath();
    // this.createNavigation();
    this.navigatorList.forEach((navigator) => {
      navigator.start();
    });
  }

  reStart() {
    this.config.forEach(item => {
      item.stopStatus = false;
    });
  }

  isEnd() {
    return this.config.every(item => item.stopStatus);
  }

  on(type, cb) {
    this.listens[type] = cb;
  }

  emit(type) {
    this.listens[type]();
  }

  expandPath() {
    this.navigatorList.forEach((navigator, index) => {
      navigator.on('pause', () => {
        this.config[index].stopStatus = true;
        if (this.isEnd()) {
          this.emit('pause');
          this.reStart();
          return;
        }
      });
    });
    if (this.canExpand()) {
      setTimeout(() => {
        this.expandPath();
      });
    }
  }

  canExpand() {
    this.endIdx++;
    if (this.endIdx >= this.getMaxLen()) {
      this.endIdx--; // 保证正常续航
      return false;
    }
    this.handleCursor();
    this.handleStatus();
    this.pathSimplifierIns.setData(this.config); // 延展路径
    // 重新建立一个巡航器
    this.createNavigation();
    this.doStart();
    this.doMoveTo();
    return true;
  }

  createNavigation() {
    this.config.forEach((item, index) => {
      const { pathNavigatorStyle, pathLinePassedStyle, pathLinePassedHidden, id, speed } = item;
      this.navigatorList[index] = this.pathSimplifierIns.createPathNavigator(id, {
        speed: speed.v || 100, // km/h // 适合汽车
        pathNavigatorStyle: {
          width: (pathNavigatorStyle && pathNavigatorStyle.width) || 16,
          height: (pathNavigatorStyle && pathNavigatorStyle.height) || 32,
          content: this.PathSimplifier.Render.Canvas.getImageContent(
            (
              pathNavigatorStyle && pathNavigatorStyle.content) || 'https://webapi.amap.com/ui/1.1/ui/misc/PathSimplifier/examples/imgs/car.png',
            pathNavigatorStyle && pathNavigatorStyle.onload,
            pathNavigatorStyle && pathNavigatorStyle.onerror
          ),
          strokeStyle: null,
          fillStyle: null,
          pathLinePassedStyle: pathLinePassedHidden ? {} : (
            pathLinePassedStyle ? {
              lineWidth: pathLinePassedStyle.lineWidth || 5,
              strokeStyle: pathLinePassedStyle.strokeStyle || '#3F9DCC',
              borderStyle: pathLinePassedStyle.borderStyle || '#ccc'
            } : {
              lineWidth: 5,
              strokeStyle: '#3F9DCC',
              borderStyle: '#ccc'
            }
          )
        }
      });
    });
  }

  getMaxLen() {
    const sortData = this.config.sort((p, c) => c.path.length - p.path.length);
    return (sortData[0] && sortData[0].path && sortData[0].path.length) || 0;
  }

  handleCursor() {
    this.navigatorList.forEach((navigator, index) => {
      this.cursor[index] = navigator.getCursor().clone(); // 保存巡航器的位置
    });
  }

  handleStatus() {
    this.navigatorList.forEach((navigator, index) => {
      this.status[index] = navigator.getNaviStatus();
    });
  }

  doStart() {
    this.status.forEach((state, index) => {
      if (state !== 'stop') {
        this.navigatorList[index].start();
      }
    });
  }

  doMoveTo() {
    this.cursor.forEach((cursor, index) => {
      if (cursor.idx >= 0) {
        this.navigatorList[index].moveToPoint(cursor.idx, cursor.tail);
      }
    });
  }
}