简单实现小车轨迹动画

488 阅读1分钟

实现思路

创建一个marker,用n记录次数

turf工具库计算路线每帧要走的点数组、相对正北的旋转角度数组

requestAnimation更新n,移动marker到数组的第n项并设置其旋转角度

计算点数组、旋转角度数组

主要用到turf计算两点距离和向北的偏转角度

// data:LineString的geojson数据
// v:速度,m/s
// t:轮询间隔,s
function createPath(data, v = 30, t = 5) {
  // 因为这里data我用的是LineString类型,所以数组这样取,实际上找到连续的点位二维数组即可
  const lineArray = data.features[0].geometry.coordinates;
  // 存储所有算出来的点(每隔t要移动的点位数组)
  const allPath = [];
  for (let i = 0; i < lineArray.length - 1; i++) {
    const pre = lineArray[i];
    const next = lineArray[i + 1];
    // 计算距离,也可以用地图自带的工具
    const dis = turf.distance(pre, next)*1000;
    const time = dis / v;
    const n = parseInt(time / t);
    // next最多到lineArray倒数第1位
    const ary = cutLine(pre, next, n);
    // 将分隔出来的点合并保存
    allPath.push(...ary);
  }
  // 将最后一个点手动加到末尾
  allPath.push(lineArray[lineArray.length - 1]);
  // 存储要旋转的角度数组(每隔t要旋转的角度)
  const res = [];
  for (let i = 0; i < allPath.length - 1; i++) {
    const pre = allPath[i];
    const next = allPath[i + 1];
    // 计算2点与正北的夹角
    res.push(turf.rhumbBearing(turf.point(pre), turf.point(next)));
  }
  return { allPath, allRotate: res };
}
​
// 将一条线段分成n段,返回所有的点的二维数组(不包括最后一个)
function cutLine(p1, p2, n) {
  const x1 = p1[0],
    x2 = p2[0],
    y1 = p1[1],
    y2 = p2[1];
  const ary = [];
  // 这里并没有放p2
  for (let i = 0; i < n; i++) {
    ary.push([x1 + ((x2 - x1) / n) * i, y1 + ((y2 - y1) / n) * i]);
  }
  return ary;
}

更新动画

let timer =null // requestAnimation
let marker = null // 点位,这里用真实dom
let ary= [] // 转换后的位置数组
let rotateAry=[] //转换的角度数组
let length =0 // 记录转换后的数组长度
let n = 0 // 记录当前循环到第几个点
// 地图加载后初始化一个marker
// 这里请自行查阅相关地图方法手写
map.on('load',()=>{
 //marker = initMarker()
 // 用上面的方法生成地图,这里的时间按60帧的时间取16.7ms,也可以写个方法计算下帧间隔
 const { allPath, allRotate } = createPath(lineData, 30, 16.7/1000);
 ary = allPath;
 rotateAry = allRotate;
 length = ary.length;
  // 初始化定时器
 timer = requestAnimationFrame(updatePoint);
})
//
// 更新动画
updatePoint() {
    if (n < length) {
        console.log(n);
        // 更新marker位置和角度
        marker.setLngLat(ary[n]);
        marker.setRotation(rotateAry[n]);
        n = n + 1;
        //再次回调
        updatePoint()
    }
}
// 
​

其他说明

  • 之所以用marker去移动位置,而不是用canvas重新绘制,是因为计算出来的数组可能很长,移动dom反而能省去很多GPU渲染的消耗,其实这个计算本身并不耗时
  • requestAnimationFrame的机制就是浏览器后台时不会运行,所以,如果想保持后台运行请用setTimeout或setInterval代替,内存会占用稍多点,渲染间隔不宜过长,适合模拟1~3秒上报一次位置的情景
  • 计算2点距离turfjs.fenxianglu.cn/category/me…
  • 计算2点恒向线夹角turfjs.fenxianglu.cn/category/me…
  • 类似效果参考高德lbs.amap.com/demo/jsapi-…