Skip to content

183. 地图飞线(二)

Published:

上节画了北京到上海的飞线:

2025-08-17 13.02.20.gif

这节继续把北京到其他城市的飞线画出来:

我们已经拿到每个城市中心点的经纬度了:

image.png

遍历画一遍就可以:

image.png

起点都是北京,终点是遍历的每个城市的坐标。

const beijingPos = mercator(cityCenterMap.get('北京市'));

for(let [name, center] of cityCenterMap.entries()) {
    if(!name) {
        continue;
    }
    if(name === '北京市') {
        continue;
    }
    const endPos = mercator(center);
    const start = new THREE.Vector3( beijingPos[0], -beijingPos[1], 0 );
    const end  = new THREE.Vector3( endPos[0], -endPos[1], 0 );
    const middle = start.clone().add(end).divideScalar(2);
    middle.z = 100;

    const curve = new THREE.CatmullRomCurve3([
        start,
        middle,
        end
    ]);
    const pointsArr = curve.getPoints(20);

    const geometry = new LineGeometry();
    geometry.setFromPoints(pointsArr);

    const material = new LineMaterial({ 
        color: new THREE.Color('orange'),
        linewidth: 3
    });

    const line = new Line2(geometry, material);
    chinaMap.add(line);
}

看下效果:

2025-08-17 13.18.43.gif

这样曲线是画出来了,但比较丑。

不管远近都是一样的高度,这样肯定是不好的。

我们应该根据两个点的距离来设置高度:

image.png

用两个点的 sub 再取 length 来计算距离,设置到 z

顺便把 lineWidth 调小点。

const distance = start.clone().sub(end).length();
middle.z = distance / 3;

2025-08-17 13.23.30.gif

这样,离得越近,飞线越低,越远飞线越高。

现在飞线不能标识方向,怎么知道是从北京到其他省市,而不是反过来呢?

所以我们要加一个方向的标识。

先改一下曲线颜色:

image.png

曲线取 100 个点。

然后我们从这 100 个点里取一部分画一条不同颜色的曲线。

image.png

const pointsArr2 = pointsArr.slice(30, 30 + 20);

const geometry2 = new LineGeometry();
geometry2.setFromPoints(pointsArr2);

const material2 = new LineMaterial({ 
    color: new THREE.Color('orange'),
    linewidth: 2
});
const line2 = new Line2(geometry2, material2);
chinaMap.add(line2);

从第 30 个点开始取 20 个点,画一条橙色的线。

2025-08-17 13.38.49.gif

然后让它动起来。

怎么动起来呢?

现在是从 30 开始取 20 个点画的线。

下一帧从 40 开始取 20 个点画线,是不是就动起来了?

image.png

let index = 30;
function render() {
    if(index >= 100) index = 0;
    index++;
    const arr = pointsArr.slice(index, index + 20);
    geometry2.setFromPoints(arr);
    requestAnimationFrame(render);
}

render();

2025-08-17 13.51.21.gif

有点慢,我们还是用 gsap 来控制:

pnpm install --save gsap

image.png

let obj = { index: 0 };
gsap.to(obj, {
    index: 100,
    ease: 'none',
    repeat: -1,
    duration: 2,
    onUpdate() {
        const arr = pointsArr.slice(obj.index, obj.index + 20);
        geometry2.setFromPoints(arr);
    }
})

2025-08-17 13.55.13.gif

现在就是 2s 一次飞线动画了。

我们换一个先加速后减速的缓动动画:

image.png

去掉 AxesHelper 看下效果:

2025-08-17 13.57.22.gif

这样比匀速好很多,更有方向感。

2025-08-17 14.24.17.gif

案例代码上传了小册仓库

总结

这节我们实现了飞线动画。

从曲线上取了一部分的点,又画了一条不同颜色的新的曲线。

然后用 gasp 做动画,不断改变起始点的 index,从原曲线上截取不同的位置的点来画新曲线。

这样就可以实现标识方向的动画。

评论