高德地图 JS API 之轨迹回放

4,071 阅读6分钟

简介

实现在 web 浏览器端用原生 JS 在高德地图上实现轨迹回放的功能,并在回放的过程中实时更新该坐标的详细信息

效果gif

track_replay.gif

来源

官网demo

在官网的demo上加了展示信息的小弹窗,实现原理:就是在卡车移动的时候实时更新信息到dom上

实现步骤

  1. 引入高德地图 JS
<script src="https://webapi.amap.com/maps?v=1.4.15&key=" + "自己申请的Key值"></script>
  1. 画html
<body>
    <div id="container">
        <div id="btn-card">
            <span class="operate">轨迹回放:</span>
            <button class="btn" onclick="start()">开始</button>
            <button class="btn" onclick="pause()">暂停</button>
            <button class="btn" onclick="reset()">重置</button>
        </div>
    </div>
    <div id="tips">
        <span id="license"></span>
        <span id="speed"></span>
        <span id="longitude"></span>
        <span id="latitude"></span>
        <span id="time"></span>
    </div>
</body
  1. 模拟后台数据,创建地图元素
<script>
// 这是模拟后台返回的数据,包括每个点的具体信息
var data = [
    { longitude: 116.478935, latitude: 39.997761, license: '陕KE1675', speed: '57.2KM/H', time: '2021-06-07 16:21:12' },
    { longitude: 116.478939, latitude: 39.997825, license: '陕KE1675', speed: '56.5KM/H', time: '2021-06-07 16:25:21' },
    { longitude: 116.478912, latitude: 39.998549, license: '陕KE1675', speed: '48.3KM/H', time: '2021-06-07 16:51:28' },
    { longitude: 116.479282, latitude: 39.998561, license: '陕KE1675', speed: '65.6KM/H', time: '2021-06-07 17:21:30' },
    { longitude: 116.479658, latitude: 39.998528, license: '陕KE1675', speed: '39.1KM/H', time: '2021-06-07 17:20:38' },
    { longitude: 116.480151, latitude: 39.998453, license: '陕KE1675', speed: '53.7KM/H', time: '2021-06-07 19:01:42' },
    { longitude: 116.482362, latitude: 39.997718, license: '陕KE1675', speed: '57.8KM/H', time: '2021-06-07 20:31:45' },
    { longitude: 116.483633, latitude: 39.998935, license: '陕KE1675', speed: '59.1KM/H', time: '2021-06-07 21:28:50' },
    { longitude: 116.483671, latitude: 39.998968, license: '陕KE1675', speed: '70.2KM/H', time: '2021-06-07 22:45:58' },
    { longitude: 116.484648, latitude: 39.999861, license: '陕KE1675', speed: '68.6KM/H', time: '2021-06-07 23:23:00' }
]

/*
构造函数介绍:

AMap.LngLat(lng:Number,lat:Number,noAutofix:bool): 构造一个地理坐标对象
lng、lat分别代表经度、纬度值;
noAutoFix表示是否自动将经度修正到 [-180,180] 区间内,缺省为false,此时会自动修正,比如传入[190,30],会被自动修正为[-170,30],noAutoFix为true时不会自动修正

AMap.Pixel(x:Number,y:Number): 构造一个像素坐标对象
x方向坐标,y方向坐标
*/

/**
 * map: 地图容器
 * marker: 移动的汽车
 * lineArr: 轨迹节点的坐标数组
 * polyline: 整条轨迹
 * passedPolyline: 已经移动过的轨迹
 * infoTip: 信息展示tips
 */
var map, marker, lineArr, polyline, passedPolyline, infoTip;
// 当前是否正在移动
var movingFlag = false;
initMap(data);
function initMap(data) {
    lineArr = data.map(item => [item.longitude, item.latitude]);
    map = new AMap.Map('container', {
        // Boolean: 是否监控地图容器尺寸变化,默认值为false
        resizeEnable: true,
        // LngLat: 地图中心点坐标值,数组类型的格式也可以
        center: [116.397428, 39.90923],
        // Number: 地图显示的缩放级别,若center与level未赋值,地图初始化默认显示用户所在城市范围
        zoom: 11
    });

    // 创建汽车
    marker = new AMap.Marker({
        // Map: 要显示该marker的地图对象
        map: map,
        // LngLat: 点标记在地图上显示的位置,默认为地图中心点,数组类型的格式也可以
        // 或 position: new AMap.LngLat(116.478935, 39.997761),
        position: lineArr[0],
        // String/Icon: 需在点标记中显示的图标。可以是一个本地图标地址,或者Icon对象。有合法的content内容时,此属性无效
        icon: "https://webapi.amap.com/images/car.png",
        /*
        Pixel: 点标记显示位置偏移量,默认值为Pixel(-10,-34)
        Marker指定position后,默认以marker左上角位置为基准点,对准所给定的position位置,若需使marker指定位置对准在position处,需根据marker的尺寸设置一定的偏移量
        */
        offset: new AMap.Pixel(-26, -13),
        // Boolean: 是否自动旋转。点标记在使用moveAlong动画时,路径方向若有变化,点标记是否自动调整角度,默认为false。广泛用于自动调节车辆行驶方向
        autoRotation: true,
        // Number: 点标记的旋转角度,广泛用于改变车辆行驶方向
        angle: -90,
    });

    // 绘制整条轨迹
    polyline = new AMap.Polyline({
        // Map: 要显示该polyline的地图对象
        map: map,
        // Array: 折线的节点坐标数组
        path: lineArr,
        // Boolean: 是否延路径显示白色方向箭头,默认false。Canvas绘制时有效,建议折线宽度大于6时使用
        showDir: true,
        // String: 线条颜色,使用16进制颜色代码赋值。默认值为#006600
        strokeColor: "#28F",
        // Number: 线条透明度,取值范围[0,1],0表示完全透明,1表示不透明。默认为0.9
        strokeOpacity: 1,
        // Number: 线条宽度,单位:像素
        strokeWeight: 6,
        // String: 线样式,实线:solid,虚线:dashed
        strokeStyle: "solid"
    });

    // 创建信息展示tip
    infoTip = new AMap.InfoWindow({
        isCustom: true,  // 使用自定义窗体
        content: document.getElementById('tips'),
        offset: new AMap.Pixel(16, -45)
    });

    // 已经移动过的轨迹
    passedPolyline = new AMap.Polyline({
        map: map,
        strokeColor: "#AF5",
        strokeWeight: 6,
        strokeStyle: "dashed"
    });
}
</script>
  1. 用户行为控制,监听移动事件
<script>
function start() {
    if (!movingFlag) {
        /*
        moveAlong(path:Array, speed:Number, f:Function, circlable:Boolean)
        以指定的速度,点标记沿指定的路径移动。
            path: 为轨迹路径的经纬度对象的数组;
            speed:为指定速度,单位:千米/小时,不可为0
            f: 为变化曲线函数,缺省为function(k){return k}
            circlable: 表明是否循环执行动画,默认为false
        */
        marker.moveAlong(lineArr, 500);
        movingFlag = true;
    } else {
        // 恢复点标记的动画效果
        marker.resumeMove();
    }
}
function pause() {
    if (movingFlag) {
        // 暂定点标记的动画效果
        marker.pauseMove();
    }
}
function reset() {
    if (movingFlag) {
        // 关闭弹窗
        infoTip.close();
        // 回到初始位置
        marker.setPosition([116.478935, 39.997761]);
        marker.stopMove();
        // 清除已经移动的轨迹
        passedPolyline.setPath([]);
        movingFlag = false;
    }
}
/*
监听移动事件:
点标记在执行moveTo,moveAlong动画时触发事件, e的格式是{passedPath:Array<LngLat>}
其中passedPath为Marker对象在moveAlong或者moveTo过程中已经走过的路径
*/
marker.on('moving', function (e) {
    // setPath: 设置组成该折线的节点数组
    passedPolyline.setPath(e.passedPath);
    var length = e.passedPath.length;
    // console.log(length, e.passedPath); 可以打印看看结构
    // 在移动时更新信息
    document.getElementById('tips').style.display = 'flex';
    document.getElementById('license').innerText = '车牌号:' + data[length - 1].license;
    document.getElementById('speed').innerText = '车速:' + data[length - 1].speed;
    document.getElementById('longitude').innerText = '经度:' + data[length - 1].longitude;
    document.getElementById('latitude').innerText = '纬度:' + data[length - 1].latitude;
    document.getElementById('time').innerText = '时间:' + data[length - 1].time;
    infoTip.open(map, marker.getPosition());
});

/*
setFitView(overlayList:Array/null, immediately:bool, avoid:[Number,Number,Number,Number], maxZoom:Number)
根据地图上添加的覆盖物分布情况,自动缩放地图到合适的视野级别,参数均可缺省
    overlayList: 为覆盖物数组,缺省时为当前地图上添加的所有覆盖物图层
    immediately: 代表是否需要动画过程
    avoid: 代表上下左右的像素避让宽度
    maxZoom: 代表fitView之后的最大级别
*/
map.setFitView();
</script>
  1. 样式文件
#container {
    position: relative;
    margin: 0 auto;
    width: 800px;
    height: calc(100vh - 50px);
}

#btn-card {
    display: flex;
    justify-content: space-evenly;
    position: absolute;
    bottom: 10px;
    right: 10px;
    align-items: center;
    width: 280px;
    height: 60px;
    background-color: #fff;
    background-clip: border-box;
    box-shadow: 0 2px 6px 0 gray;
    z-index: 999;
    padding: 0 10px;
}

.operate {
    font-size: 12px;
}

.btn {
    padding: 2px 15px;
    color: #25A5F7;
    border: 1px solid #25A5F7;
    background-color: transparent;
    border-radius: 10px;
    cursor: pointer;
}

.btn:hover {
    color: #fff;
    background-color: #25A5F7;
}

#tips {
    height: 100px;
    width: 180px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-between;
    padding: 5px;
    background: #fff;
    border: 1px solid #DEDEDD;
    border-radius: 10px;
    font-size: 12px;
    display: none;
}