Vue+高德地图历史轨迹回放(进度条+倍速+时间)

Vue+高德地图历史轨迹回放(进度条+倍速+时间)

原博主原创文章,亲妈式教学,整理的很是详细,但是没有放源码,为了更方便,这里放上源码。

按照原博主的文章一步步复制的代码,希望无误,不辜负原博主的代码。

其中,infoWindow原代码未定义,以下代码已加上。

首先,安装vue-amap

npm install -S vue-amap

在main.js里引入

// 导入AMap
import AMap from 'vue-amap'
// 注册
Vue.use(AMap)
// 加载
AMap.initAMapApiLoader({
  // 高德的key
  key: '你可爱的key',
  // 插件集合
  plugin: ['AMapManager', 'AMap.Autocomplete', 'AMap.PlaceSearch', 'AMap.Scale', 'AMap.OverView', 'AMap.ToolBar', 'AMap.MapType', 'AMap.PolyEditor', 'AMap.CircleEditor', 'Geocoder', 'Geolocation', 'AMap.MarkerClusterer', 'AMap.PolyEditor', 'AMap.CircleEditor', 'AMap.MouseTool', 'AMap.Driving', 'AMap.CitySearch', 'AMap.InfoWindow', 'AMap.LngLat', 'AMap.DistrictSearch', 'AMap.TileLayer.Traffic', 'AMap.Heatmap', 'AMap.Autocomplete', 'AMap.PlaceSearch'],
  // 高德 sdk 版本,默认为 1.4.4
  v: '1.4.4'
})

其次,以下代码放在.vue文件可以直接运行看效果

<template>
  <div id="mapTrack">
    <el-card class="box-card" shadow="never" :body-style="{ padding: '0' }">

      <div id="container" :style="{ 'height': 'calc(100vh - 180' + 'px)' }">
      </div>

      <!-- 进度条 -->
      <div class="map-control">
        <!-- 开始按钮 -->
        <Icon v-if="!start" class="play-icon" type="ios-play" @click="navgControl(playIcon)" />

        <!-- 暂停按钮 -->
        <Icon v-else class="play-icon" type="ios-pause" @click="navgControl('pause')" />

        <!-- 开始时间-->
        <span class="passed-time">{{ passedTime }}</span>
        <!-- 滑动条修改2个地方 step步长 tip-forma提示框 -->
        <Slider class="map-slider" v-model="sliderVal" @on-input="sliderChange" :step="0.0001" :tip-format="hideFormat">
        </Slider>




        <!-- 倍数 -->
        <div class="map-times" @mouseenter="isTimesChoose = true" @mouseleave="isTimesChoose = false">
          <div class="times-show">倍速{{ times }}</div>
          <div class="choose-box">
            <!-- <ul v-show="isTimesChoose"> -->
            <ul v-show="isTimesChoose">
              <li v-for="item in speedList" :key="item" :class="{ active: times == item }" @click="changeSpeed(item)">倍速
                {{ item }}</li>
            </ul>
          </div>
        </div>
        <!-- 结束 -->
        <span class="passed-time">{{ totalTime }}</span>
      </div>




    </el-card>
  </div>
</template>

<script>
export default {
  data() {
    return {
      // 后台传递来的路线
      linePath: [
        {
          latitude: 39.997761,
          longitude: 116.478935,
          time: '2020-08-21 16:21:18'
        },
        {
          latitude: 39.997825,
          longitude: 116.478939,
          time: '2020-08-21 16:21:21'
        },
        {
          latitude: 39.998549,
          longitude: 116.478912,
          time: '2020-08-21 16:21:24'
        },
        {
          latitude: 39.998555,
          longitude: 116.478998,
          time: '2020-08-21 16:21:27'
        },
        {
          latitude: 39.99856,
          longitude: 116.479282,
          time: '2020-08-21 16:21:30'
        },
        {
          latitude: 39.998528,
          longitude: 116.479658,
          time: '2020-08-21 16:21:33'
        },
        {
          latitude: 39.998453,
          longitude: 116.480151,
          time: '2020-08-21 16:21:36'
        },
        {
          latitude: 39.998302,
          longitude: 116.480784,
          time: '2020-08-21 16:21:39'
        },
        {
          latitude: 39.998184,
          longitude: 116.481149,
          time: '2020-08-21 16:21:42'
        },
        {
          latitude: 39.997997,
          longitude: 116.481573,
          time: '2020-08-21 16:21:45'
        },
        {
          latitude: 39.997846,
          longitude: 116.481863,
          time: '2020-08-21 16:21:48'
        },
        {
          latitude: 39.997718,
          longitude: 116.482072,
          time: '2020-08-21 16:21:51'
        },
        {
          latitude: 39.997718,
          longitude: 116.482362,
          time: '2020-08-21 16:21:54'
        },
        {
          latitude: 39.998935,
          longitude: 116.483633,
          time: '2020-08-21 16:21:57'
        },
        {
          latitude: 39.998968,
          longitude: 116.48367,
          time: '2020-08-21 16:22:00'
        },
        {
          latitude: 39.999861,
          longitude: 116.484648,
          time: '2020-08-21 16:22:03'
        },
        {
          latitude: 39.999870,
          longitude: 116.484648,
          time: '2020-08-21 16:22:05'
        },
        {
          latitude: 39.96,
          longitude: 116.484648,
          time: '2020-08-21 16:23'
        },
        {
          latitude: 39.93,
          longitude: 116.484648,
          time: '2020-08-21 16:26'
        },
        {
          latitude: 39.91,
          longitude: 116.484648,
          time: '2020-08-21 16:28'
        },
        {
          latitude: 39.91,
          longitude: 116.5,
          time: '2020-08-21 16:30'
        }
      ],
      // 后台传递来的地图上的icon
      icon: [
        {
          latitude: 39.997761,
          longitude: 116.478935
        },
        {
          latitude: 39.99856,
          longitude: 116.479282
        },
        {
          latitude: 39.999861,
          longitude: 116.484648
        }
      ],
      pathList: [],
      // 巡航器
      navgtr: null,
      passedTime: '00:00:00',
      // 总时间
      totalTime: '00:00:00',
      playIcon: 'start', //开始按钮是重新开始还是继续
      speedList: [1, 2, 3],
      times: 1,
      start: false,
      sliderVal: 0,
      isTimesChoose: false
    }
  },
  created() {

  },
  mounted() {
    // 设置地图基本上配置
    const param = {
      // 是否监控地图容器尺寸变化
      resizeEnable: true,
      // 初始地图级别
      zoom: 15
    }
    this.map = new AMap.Map('container', param)
    this.init()
  },
  methods: {
    // 滑动条的提示
    hideFormat() {
      return this.passedTime
    },

    // 初始化
    init() {
      // 后台传递来的数据是json的,所以我们改成数组
      // 轨迹
      // 线条路线
      let linePath = this.linePath

      linePath.forEach(item => {
        // 把json转为数组
        this.pathList.push([item.longitude, item.latitude])
        // 当前时间戳
        item.stampTime = new Date(item.time).getTime()
      })

      linePath.forEach((item, i) => {

        // 获得到下一个位置
        let next = linePath[i + 1]
        // 判断是否还有下一个
        if (next) {
          // 计算出间隔时间 每秒
          item.intervalTime = (next.stampTime - item.stampTime) / 1000
          // 计算出下一站
          item.nextDistance = this.distanceFun(
            [item.longitude, item.latitude],
            [next.longitude, next.latitude]
          )
          // 求出具体的速度
          item.nextSpeed =
            item.nextDistance / 1000 / (item.intervalTime / 60 / 60)
        }
      })


      // 设置路线
      this.setPath()

      // 创建起始和经过的icon
      this.icon.forEach(item => {
        this.addIcon(item)
      })

      // 在init方法加上这行代码
      // 修改全局的路线数据
      this.linePath = linePath

    },
    // 计算两个坐标点之间的距离
    distanceFun(point1, point2) {
      // 那数组转化为经纬度
      let p1 = new AMap.LngLat(point1[0], point1[1])
      let p2 = new AMap.LngLat(point2[0], point2[1])
      // 计算2点直接的距离  distance这个函数有继续可以了解一下
      let distance = Math.round(p1.distance(p2))
      return distance
    },


    // 格式化时间(不解释)
    getTime(sTime) {
      let ss
      let mm = '00'
      let hh = '00'
      if (sTime > 60) {
        let s = sTime % 60
        ss = s > 9 ? s : '0' + s
        let mTime = parseInt(sTime / 60)
        if (mTime > 60) {
          let m = mTime % 60
          mm = m > 9 ? m : '0' + m
          hh = parseInt(mTime / 60)
        } else {
          mm = mTime > 9 ? mTime : '0' + mTime
        }
      } else {
        ss = sTime > 9 ? sTime : '0' + sTime
      }
      return hh + ':' + mm + ':' + ss
    },
    // 滑动条改变事件
    sliderChange(val) {
      // 计算开始距离
      let num = parseInt((val / 100) * this.pathList.length)
      // 计算结束的距离
      let decimal =
        String((val / 100) * this.pathList.length).split('.')[1] || 0
      // 移动小车
      this.navgtr.moveToPoint(num, Number('0.' + decimal))
      // 重新绘制
      this.pathSimplifierIns.renderLater()
    },

    // 设置路线
    setPath() {
      let that = this
      AMapUI.load(['ui/misc/PathSimplifier', 'lib/$'], function (
        PathSimplifier
      ) {
        if (!PathSimplifier.supportCanvas) {
          console.log('当前环境不支持 Canvas!')
          return
        }
        function onload() {
          that.pathSimplifierIns.renderLater()
        }
        function onerror() {
          console.log('图片加载失败!')
        }
        // 历史轨迹巡航器
        that.pathSimplifierIns = new PathSimplifier({
          zIndex: 100,
          map: that.map, // 所属的地图实例
          getPath: function (pathData) {
            // 这里的pathData保存的是路线
            return pathData.path
          },
          // 自动设置视图
          autoSetFitView: true,
          // 巡航器样式
          renderOptions: {
            // 路径导航样式
            pathNavigatorStyle: {
              // 一开始小车的旋转角度
              initRotateDegree: 0,
              // 小车的宽
              width: 20,
              // 小车的高
              height: 32,
              // 自动旋转
              autoRotate: true,
              // 折线拐点连接处样式
              lineJoin: 'round',
              // PathSimplifier提供了一个快捷方法用于创建图片内容的content(function):
              // 图片地址
              // 图片加载成功,重新绘制一次 onload方法
              // 图片加载失败 onerror方法
              content: PathSimplifier.Render.Canvas.getImageContent(
                'https://webapi.amap.com/images/car.png',
                onload,
                onerror
              ),
              // 这个位置提示一下 因为我们用的是图片所以看不见效果
              // 想看见效果删除上面的content
              // 他是一个三角形 这里就是设置边框颜色和内部颜色的
              // 填充色
              fillStyle: null,
              // 描边色
              strokeStyle: null,
              // 边的宽度
              lineWidth: 1,
              // 巡航器经过的路径的样式
              pathLinePassedStyle: {
                lineWidth: 6,
                strokeStyle: 'skyblue'
              }
            },
            // 线条样式
            pathLineStyle: {
              lineWidth: 6,
              strokeStyle: 'pink'
            },
            // 鼠标移入之后线条的样式
            pathLineHoverStyle: {
              lineWidth: 0,
              borderWidth: 0
            },
            // 鼠标单击之后线条的样式
            pathLineSelectedStyle: {
              lineWidth: 6,
              borderWidth: 0,
              strokeStyle: 'blue'
            },
            pathTolerance: 0,
            keyPointTolerance: -1,
            renderAllPointsIfNumberBelow: 0 // 绘制路线节点,如不需要可设置为-1
          }
        })
        // 历史轨迹巡航器设置数据 这里设置的就是上面pathData的数据
        that.pathSimplifierIns.setData([
          {
            name: '轨迹',
            path: that.pathList
          }
        ])

        // 开启中心自适应
        that.pathSimplifierIns.setFitView(-1)

        // 获取初始速度
        // 设置默认的为 0.1
        let startSpeed = 0.1
        if (that.linePath.length !== 0) {
          // 获取一开始的速度 并且判断是否为0 如果不为0直接返回 为0给一个初始的速度
          startSpeed =
            that.linePath[0].nextSpeed === 0 ? 0.1 : that.linePath[0].nextSpeed
        }
        // 对第一条线路(即索引 0)创建一个巡航器
        that.navgtr = that.pathSimplifierIns.createPathNavigator(0, {
          loop: false, // 循环播放
          // 速度×倍速
          speed: startSpeed * that.times
        })

        let linePath = that.linePath
        let len = linePath.length
        let startPoint = linePath[0]
        let endPoint = linePath[len - 1]

        // 计算总时间,hh:mm:ss 因为计算出来是时间搓 所以要进行格式化
        that.totalTime = that.getTime(
          (endPoint.stampTime - startPoint.stampTime) / 1000
        )

        // 创建 infoWindow 实例
        let infoWindow = new AMap.InfoWindow({
          anchor: 'bottom-center',
          autoMove: false,
          offset: new AMap.Pixel(0, -20),
          content: ''  //传入 dom 对象,或者 html 字符串
        })

        // 移动过程中
        that.navgtr.on('move', function () {
          that.playIcon = 'resume'
          // 走到了第几个点
          let idx = this.getCursor().idx
          // 至下一个节点的比例位置
          let tail = this.getCursor().tail
          // 总路程
          let totalIdx = idx + tail
          // 计算下一个距离速度
          let point = linePath[idx]
          if (idx < len - 1) {
            // 判断速度是否为0 如果为0 需要给个0.1的速度
            point.nextSpeed === 0 && (point.nextSpeed = 0.1)
            // 这里的速度记得×倍数
            that.navgtr.setSpeed(point.nextSpeed * that.times)
          }



          // 剩余公里数,窗体随时移动展示
          // 判断是否还有下一个点和时间
          point &&
            point.time &&
            infoWindow.setContent(
              `<p class="info-window">时间:<span>${point.time}`
            )
          // 设置提示框
          infoWindow.open(that.map, that.navgtr.getPosition())
          // 进度条实时展示tail
          !that.isOnSlider && (that.sliderVal = (totalIdx / len) * 100)
          // 已经播放时间
          let sTime = parseInt(
            (((endPoint.stampTime - startPoint.stampTime) / 1000) *
              that.sliderVal) /
            100
          )
          // 格式化时间
          that.passedTime = that.getTime(sTime)



          // 如果到头了,回到初始状态
          if (that.navgtr.isCursorAtPathEnd()) {
            // 设置为开始状态 让小车回到原来位置
            that.playIcon = 'start'
            // 设置图标状态
            that.start = false
            // 设置已经走过的时间
            that.passedTime = that.totalTime
            // 设置滑动条状态
            that.sliderVal = 100
          }

        })


      })
    },


    addIcon(item) {
      // 设置每个icon的内容
      const marker = new AMap.Marker({
        // 图片
        icon:
          '//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png',
        // 位置
        position: [item.longitude, item.latitude],
        //设置基点偏移
        offset: new AMap.Pixel(-13, -30)
      })
      // 把图标放在地图上
      marker.setMap(this.map)
    },
    // 暂停和播放按钮
    navgControl(action) {
      if (action === 'start') {
        let that = this
        this.passedTime = '00:00:00'
        setTimeout(() => {
          that.navgtr[action]()
        }, 300)
      } else {
        this.navgtr[action]()
      }
      // 修改图标状态
      this.start = !this.start
    },

    // 修改速度
    changeSpeed(item) {
      this.times = item
      this.isTimesChoose = false
    },


  }
}
</script>

<style scoped lang="less">
@deep: ~'>>>';

// 设置图标大小
/deep/.amap-icon img {
  width: 25px;
  height: 34px;
}

// 设置循环器样式
.map-control {
  // 绝对定位
  position: absolute;
  bottom: 0;
  left: 0;
  width: 1200px;
  height: 80px;
  line-height: 80px;
  color: #fff;
  background-image: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent);
  padding: 0 40px;
  z-index: 1000;

  // 设置按钮大小
  .play-icon {
    font-size: 36px;
  }

  // 设置时间样式
  .passed-time {
    // 相对定位
    position: relative;
    // 转为行内元素
    display: inline-block;
    top: 1px;
    margin-left: 15px;
    font-size: 14px;
  }

  // 进度条样式
  .map-slider {
    // 转为行内元素
    display: inline-block;
    width: 75%;
    margin-left: 15px;
    position: relative;
    top: 14px;
  }

  // 修改默认的进度条样式
  .choose-box {
    // 相对定位
    position: absolute;
    top: -135px;
    left: -6px;
    height: 162px;
    // 过滤
    transition: all 0.5s linear;
  }

  // 倍数
  .map-times {
    display: inline-block;
    position: relative;
    margin-left: 15px;

    // 倍速样式
    .times-show {
      padding: 0 10px;
      line-height: 24px;
      font-size: 13px;
      border: 1px solid #fff;
      border-radius: 4px;
      // 鼠标设置为默认样式
      cursor: default;
    }

    // 倍数
    ul {
      background: rgba(0, 0, 0, 0.7);
      padding: 10px;
      width: 70px;
      text-align: center;
      border-radius: 3px;

      li {
        height: 26px;
        line-height: 26px;
        cursor: pointer;
      }

      li.active {
        color: #ff8533;
      }

      li:hover {
        font-size: 13px;
      }
    }
  }
}
</style>

谢谢原博主的提供的代码,再次附上原创文章链接
https://blog.csdn.net/qq_47410017/article/details/120341090

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值