Skip to content

vue2-百度 GL 地图轨迹功能的实现

最近研究了下离线地图如何实现轨迹功能,在此记录下:

引入

首先,轨迹功能需要用到的是 BMapGLLib 而不是 BMapGL,所以我们需要额外引入一下这个库的文件。

对应轨迹功能的文件是 TrackAnimation.min.js,内容如下👇,网上也能找到。

js
var BMapGLLib=window.BMapGLLib=BMapGLLib||{};(function(){var g=1;var f=55;var e=0;var a=10000;var i=0;var c=true;var d=1;var j=2;var k=3;var b=0;var h=BMapGLLib.TrackAnimation=function(n,l,m){this._map=n;this._polyline=l;this._totalPath=l.getPath();this._overallView=n.getViewport(l.getPath());this._status=j;this._opts={zoom:this._getZoom(),tilt:f,heading:e,duration:a,delay:i,overallView:c};this._initOpts(m);this._expandPath=this._addPath(l.getPath());this._pauseTime=0;this._last2Points=[]};h.prototype._getZoom=function(){return Math.min(this._overallView.zoom+g,this._map.getMaxZoom())};h.prototype._updateAniParams=function(){this._updatePathAni();this._updateViewAni();this._polyline.setPath(this._expandPath.slice(0,1))};h.prototype._updatePathAni=function(){this._expandPath=this._addPath(this._totalPath)};h.prototype._updateViewAni=function(){this._overallView=this._map.getViewport(this._totalPath);var q=this._totalPath.length;var p=[];var r=this._opts.overallView?this._opts.duration+2000:this._opts.duration;for(var l=0;l<q;l++){var o=this._totalPath[l];var n=this._pathPercents[l]*(this._opts.duration/r);p.push({center:new BMapGL.Point(o.lng,o.lat),zoom:this._opts.zoom,tilt:l===0?0:this._opts.tilt,heading:l===0?0:this._opts.heading,percentage:n})}if(this._opts.overallView){p.push({center:new BMapGL.Point(this._overallView.center.lng,this._overallView.center.lat),zoom:this._overallView.zoom-g,tilt:0,heading:0,percentage:1})}var m={duration:r,delay:0,interation:1};this._viewAni=new BMapGL.ViewAnimation(p,m)};h.prototype._addPath=function(u){var o=this._opts.duration/10;var m=u.length;var s=0;var t=[];var p=[];for(var q=1;q<m;q++){var l=this._map.getDistance(u[q-1],u[q]);t.push(l);s+=l}var n=[0];for(var q=1;q<m;q++){var r=(t[q-1]/s).toFixed(2);n[q]=n[q-1]+parseFloat(r,10);p=p.concat(this._getPath(u[q-1],u[q],r*o))}this._pathPercents=n;return p};h.prototype._getPath=function(q,n,o){var m=[];if(o===0){return m}for(var p=0;p<=o;p++){var l=new BMapGL.Point((n.lng-q.lng)/o*p+q.lng,(n.lat-q.lat)/o*p+q.lat);m.push(l)}return m};h.prototype._initOpts=function(l){for(var m in l){if(l.hasOwnProperty(m)){this._opts[m]=l[m]}}};h.prototype.start=function(){var l=this;setTimeout(function(){l._updateAniParams();l._map.removeOverlay(l._polyline);l._map.addOverlay(l._polyline);l._status=d;l._step(performance.now());l._map.startViewAnimation(l._viewAni)},this._opts.delay)};h.prototype.cancel=function(){this._clearRAF();this._status=j;b=0;this._pauseTime=0;this._map.cancelViewAnimation(this._viewAni);this._map.removeOverlay(this._polyline)};h.prototype.pause=function(){if(this._status===d){this._clearRAF();this._map.pauseViewAnimation(this._viewAni);this._status=k;this._isPausing=performance.now()}};h.prototype.continue=function(){if(this._status===k){this._pauseTime+=performance.now()-this._isPausing;this._isPausing=undefined;this._status=d;this._step(performance.now());this._map.continueViewAnimation(this._viewAni)}};h.prototype._step=function(o){if(this._status===j){b=0;return}if(!b){b=o}o=o-this._pauseTime;var n=(o-b)/this._opts.duration;var l=Math.round(this._expandPath.length*n);var m=this._expandPath.slice(0,l);this._last2Points=m.slice(-4);this._polyline.setPath(m);if(o<b+this._opts.duration){this._timer=window.requestAnimationFrame(this._step.bind(this))}else{b=0;this._status=j;this._pauseTime=0}};h.prototype._clearRAF=function(){if(this._timer){window.cancelAnimationFrame(this._timer)}};h.prototype.setZoom=function(l){this._opts.zoom=l};h.prototype.getZoom=function(l){return this._opts.zoom};h.prototype.setTilt=function(l){this._opts.tilt=l};h.prototype.getTilt=function(l){return this._opts.tilt};h.prototype.setDelay=function(l){this._opts.delay=l};h.prototype.getDelay=function(l){return this._opts.delay};h.prototype.setDuration=function(l){this._opts.duration=l};h.prototype.getDuration=function(l){return this._opts.duration};h.prototype.enableOverallView=function(){this._opts.overallView=true};h.prototype.disableOverallView=function(){this._opts.overallView=false};h.prototype.setPolyline=function(l){this._polyline=l;this._totalPath=l.getPath()};h.prototype.getPolyline=function(){return this._polyline};h.prototype.getLastPoint=function(){return[this._last2Points[0],this._last2Points[3]]}})();

把这个文件放到存放静态地图包的位置里,并在对应的加载文件里面引入。我这里的是 map_load.js

js
// 地图API主目录
var JS__FILE__ = document.currentScript ? document.currentScript.src : document.scripts[document.scripts.length - 1].src;
offmapcfg.home = JS__FILE__.substr(0, JS__FILE__.lastIndexOf("/") + 1);
(function () {
  window.BMap_loadScriptTime = (new Date).getTime();
//   加载地图API主文件
  document.write('<script type="text/javascript" src="' + offmapcfg.home + 'bmapgl.min.js"></script>');
//   此处引入轨迹功能
  document.write('<script type="text/javascript" src="' + offmapcfg.home + '/tools/TrackAnimation.min.js"></script>');
})();

主要代码

到这一步,你的文件引入基本就搞定了,可以在项目里使用轨迹函数了。

下面代码中提供了封装好的 getLineAnimation 方法,只需要往里面传轨迹点位参数即可,同时也可以在父组件里通过 this.$refs['xxx'].getLineAnimation(list) 来实现方法的调用。

html
<!-- 百度离线地图 -->
<template>
  <div class="map" style="height: 100%">
    <div style="position: relative; height: 100%">
      <div id="main" style="width: 100%; height: 100%"></div>
      <span class="BMapLib_clear"></span>
    </div>
  </div>
</template>

<script>
import { debounce, throttle } from "@/utils/optimize";
let map = null;
export default {
  name: "el-map-offline",
  components: {},
  props: {
    // 中心点x轴坐标,默认为深圳北站坐标
    centerXAxis: {
      type: String,
      default: "113.9784504635121",
    },
    centerYAxis: {
      type: String,
      default: "22.540276392608142",
    },
    zoom: {
      type: [String, Number],
      default: 15,
    },
  },
  data() {
    return {
      tempList: [
        { lng:  113.96321513233588, lat: 22.53860961189129 },
        {lng:113.9650707666733,lat:22.53843370693047,},
        {lng:113.96775954295815,lat:22.538046715218638},
        {lng:113.97059979959708,lat:22.53765972240953,},
        {lng:113.97052405942006,lat:22.54147683078636,},
        {lng:113.97328857588194,lat:22.541283339382137,},
        {lng:113.97345899128027,lat:22.545223473885166,},
        {lng:113.98071111323165,lat:22.542971982386284,},
        {lng:113.98873957199773,lat:22.539752988610463,},
      ],
    };
  },
  mounted() {},
  methods: {
    init() {
      let that = this;
      map = new BMapGL.Map("main", { enableMapClick: false });
      this.$emit("getMap", map); // 传给父组件
      // 初始化地图,设置中心点坐标和地图级别
      map.centerAndZoom(
        new BMapGL.Point(this.centerXAxis, this.centerYAxis),
        this.zoom
      );
      map.enableScrollWheelZoom(true); // 开启鼠标滚轮缩放
      map.disableDoubleClickZoom(false); // 禁止地图双击放大

      // 添加比例尺控件
      var scaleCtrl = new BMapGL.ScaleControl({
        // anchor: BMAP_ANCHOR_TOP_LEFT,
        anchor: BMAP_ANCHOR_BOTTOM_LEFT,
        // offset: new BMapGL.Size(100, 65),
      });

      map.addControl(scaleCtrl);
      // 开启路况
      //  map.setTrafficOn();
      // 关闭路况
      //  map.setTrafficOff();
      // 监听地图缩放事件
      map.addEventListener(
        "zoomend",
        debounce(function (e) {
          that.$emit("returnZoom", e.target.zoomLevel);
        }, 100)
      );

      // 监听地图拖拽事件
      map.addEventListener("dragend", function (e) {});

      // 监听地图点击事件
      map.addEventListener("click", function (e) {});

      // 监听地图双击事件
      // map.addEventListener("dblclick", (e) => {
      // that.getAroundAxis();
      // });
      this.$nextTick(() => {
         this.getLineAnimation(this.tempList);
      });
    },
    // 切换中心点
    changeCenter(xAxis, yAxis, zoom) {
      map.centerAndZoom(
        new BMapGL.Point(xAxis, yAxis),
        zoom ? zoom : this.zoom
      );
    },

    // 删除全部
    deleteAll() {
      map.clearOverlays();
    },

    getLineAnimation(list) {
      var path = list
      var point = [];
      for (var i = 0; i < path.length; i++) {
        point.push(new BMapGL.Point(path[i].lng, path[i].lat));
      }
      var pl = new BMapGL.Polyline(point);
      var trackAni = new BMapGLLib.TrackAnimation(map, pl, {
        overallView: true, // 动画完成后自动调整视野到总览
        tilt: 55, // 轨迹播放的角度,默认为55
        duration: 20000, // 动画持续时长,默认为10000,单位ms
        delay: 500, // 动画开始的延迟,默认0,单位ms
      });
      trackAni.start();
    },
  },
  beforeDestroy() {
    console.log("销毁=====================");
    if (map) {
      map.destroy();
      map = null;
    }
  },
};
</script>

<style>
.ripple {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  animation: rippler 2s linear infinite;
  transform-origin: center;
}
@-webkit-keyframes rippler {
  0% {
    width: 30px;
    height: 30px;
    opacity: 0.8;
  }
  100% {
    transform: scale(1.5);
    opacity: 0;
  }
}

@keyframes rippler {
  0% {
    width: 30px;
    height: 30px;
    opacity: 0.8;
  }
  100% {
    transform: scale(1.5);
    opacity: 0;
  }
}
/* 地图样式 */
.anchorBL {
  /* 隐藏百度地图logo */
  display: none;
  /* opacity: 0; */
}

.BMap_cpyCtrl {
  display: none;
}

.BMap_scaleCtrl {
  display: block;
}

.BMapGLLib_Drawing {
  top: 52px !important;
}

.BMapGLLib_Drawing_panel {
  height: 36px;
  border: none;
  background: #fff;
  border-radius: 2px;
}

.BMapGLLib_Drawing .BMapGLLib_box {
  width: 36px;
}

.BMapGLLib_Drawing .BMapGLLib_hander {
  background: url("~@/assets/images/map/move.svg") no-repeat center;
  background-size: 22px 22px;
}

.BMapGLLib_Drawing .BMapGLLib_marker {
  background: url("~@/assets/images/map/location.svg") no-repeat center;
  background-size: 22px 22px;
}

.BMapGLLib_Drawing .BMapGLLib_circle {
  background: url("~@/assets/images/map/circle.svg") no-repeat center;
  background-size: 22px 22px;
}

.BMapGLLib_Drawing .BMapGLLib_polyline {
  background: url("~@/assets/images/map/line.svg") no-repeat center;
  background-size: 22px 22px;
}

.BMapGLLib_Drawing .BMapGLLib_polygon {
  background: url("~@/assets/images/map/polygon.svg") no-repeat center;
  background-size: 22px 22px;
}

.BMapGLLib_Drawing .BMapGLLib_rectangle {
  background: url("~@/assets/images/map/rectangle.svg") no-repeat center;
  background-size: 22px 22px;
}

.BMapGLLib_Drawing .BMapGLLib_hander_hover {
  background: #d4ebfe url("~@/assets/images/map/move.svg") no-repeat center;
  background-size: 22px 22px;
}

.BMapGLLib_Drawing .BMapGLLib_marker_hover {
  background: #d4ebfe url("~@/assets/images/map/location.svg") no-repeat center;
  background-size: 22px 22px;
}

.BMapGLLib_Drawing .BMapGLLib_circle_hover {
  background: #d4ebfe url("~@/assets/images/map/circle.svg") no-repeat center;
  background-size: 22px 22px;
}

.BMapGLLib_Drawing .BMapGLLib_polyline_hover {
  background: #d4ebfe url("~@/assets/images/map/line.svg") no-repeat center;
  background-size: 22px 22px;
}

.BMapGLLib_Drawing .BMapGLLib_polygon_hover {
  background: #d4ebfe url("~@/assets/images/map/polygon.svg") no-repeat center;
  background-size: 22px 22px;
}

.BMapGLLib_Drawing .BMapGLLib_rectangle_hover {
  background: #d4ebfe url("~@/assets/images/map/rectangle.svg") no-repeat center;
  background-size: 22px 22px;
}
</style>

<style lang='scss' scoped>
// 地图工具箱
// .BMapLib_clear {
//   display: none;
//   position: absolute;
//   right: 17.61%;
//   top: 6.03%;
//   cursor: pointer;
//   border-top-left-radius: 2px;
//   border-bottom-left-radius: 2px;
//   width: 36px;
//   height: 36px;
//   box-shadow: 0 1px 0 rgba(0, 0, 0, 0.3);
//   border-right: 1px solid #d2d2d2;
//   background: #fff url("~@/assets/images/map/clear.svg") no-repeat center;
//   background-size: 22px 22px;
// }

// .BMapLib_clear_hover {
//   background: #d4ebfe url("~@/assets/images/map/clear.svg") no-repeat center;
//   background-size: 22px 22px;
// }
.map {
  position: relative;

  .hoverInfo {
    position: absolute;
    min-width: 100px;
    height: 30px;
    text-align: center;
    background-color: #fff;
    z-index: 500;
    border-radius: 5px;
    line-height: 30px;
    font-size: 14px;
    padding: 0 5px;

    .title {
      position: relative;
      width: 100%;
      height: 100%;
    }
  }

  .animation-class {
    position: absolute;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background-color: rgba(62, 154, 230, 0.2);
    background-color: rgba(20, 78, 240, 0.2);
    animation: spreadOne 1s alternate infinite;
  }

  @keyframes spreadOne {
    0% {
      // top: calc((100% - 30px)/2);
      // left: calc((100% - 30px)/2);
      width: 30px;
      height: 30px;
    }

    100% {
      // top: calc((100% - 50px)/2);
      // left: calc((100% - 50px)/2);
      transform: translate(-10px, -10px);
      width: 50px;
      height: 50px;
    }
  }

  #tool {
    position: absolute;
    right: 410px;
    top: 0;
    cursor: pointer;
    z-index: 100;

    > p {
      position: relative;
      height: 36px;
      width: 36px;
      border-radius: 3px;
      background: #fff;
      text-align: center;
      line-height: 36px;

      img:last-child {
        position: absolute;
        bottom: 1px;
        left: 15px;
      }
    }
  }

  .delete-box {
    position: absolute;
    height: 30px;
    width: 60px;
    padding: 0 5px;
    line-height: 30px;
    background-color: #fff;
    font-size: 12px;
    border: 1px solid lightgray;
    border-radius: 5px;
    z-index: 9999;
    cursor: pointer;
    text-align: center;
    box-shadow: 0 0 10px lightgray;
  }
}

// ::v-deep .BMapLabel {
//   background-color: transparent !important;
// }
</style>