Skip to content

190. 实战:交互式地球仪(四)

Published:

上节实现了国家列表和搜索:

2025-08-24 17.18.10.gif

这节根据点击的国家名在地图中定位到对应国家。

首先我们暴露一个方法:

image.png

function focusCountry(name) {
    console.log('focus', name);
}

在组件里接收下:

image.png

const focusCountryRef = useRef(() => {});
focusCountryRef.current = focusCountry;

点击国家名的时候调用:

image.png

onClick={() => {
    focusCountryRef.current(item);
}}

测试下先:

2025-08-24 22.31.14.gif

然后我们给国家的轮廓多边形、label 加一个 name:

image.png

province.name = 'shape' + feature.properties.name_zh;
label.name = 'label' + feature.properties.name_zh;

这样就可以根据 name 来查找国家对应的轮廓、标签对象了:

image.png

function focusCountry(name) {
    const focusLabel = scene.getObjectByName('label' + name);

    const dir = focusLabel.position.clone().normalize();
    const newCameraPos = focusLabel.position.clone().add(dir.multiplyScalar(300));
    camera.position.copy(newCameraPos);
}

我们查找到国家的标签对象。

它的 position 向量作为方向,先转为单位向量,乘以 300,就是在这个向量的方向上平移 300

把它设为相机位置。

看下效果:

2025-08-24 23.22.43.gif

可以看到,位置都是对的。

我们把选中的国家的国家名变大一点:

image.png

focusLabel.textHeight = 20;

const focusShape = scene.getObjectByName('shape' + name);

focusShape.traverse((obj) => {
    if(obj.isLine2) {
        obj.material.color.set('red');
        obj.position.add(dir.clone().normalize().multiplyScalar(2));
    }
})

把国家名字变大,并且把国家边界线变为红色。

这里为了让国家边界线沿着 label 的方向移动一段距离,避免和其他的线重合。

看下效果:

2025-08-24 23.28.55.gif

有点太近了,远一点:

image.png

2025-08-24 23.30.17.gif

然后当选中另一个国家的时候,之前的选中效果要去掉:

image.png

image.png

let prevLabel;
let prevShape;
function focusCountry(name) {
    if(prevLabel) {
        prevLabel.textHeight = 3;
    }
    if(prevShape) {
        const dir = prevLabel.position.clone().normalize();

        prevShape.traverse((obj) => {
            if(obj.isLine2) {
                obj.material.color.set('gold');
                obj.position.sub(dir.clone().normalize().multiplyScalar(2));
            }
        })
    }
    const focusLabel = scene.getObjectByName('label' + name);

    const dir = focusLabel.position.clone().normalize();
    const newCameraPos = focusLabel.position.clone().add(dir.multiplyScalar(600));
    camera.position.copy(newCameraPos);

    focusLabel.textHeight = 20;

    const focusShape = scene.getObjectByName('shape' + name);

    focusShape.traverse((obj) => {
        if(obj.isLine2) {
            obj.material.color.set('red');
            obj.position.add(dir.clone().normalize().multiplyScalar(2));
        }
    })
    prevLabel = focusLabel;
    prevShape = focusShape;
}

就是恢复之前的数据。

试一下:

2025-08-24 23.36.34.gif

这个相机位置的变化我们用 gsap 来做缓动动画:

image.png

gsap.to(camera.position, {
    ...newCameraPos,
    ease: 'none',
    repeat: 0,
    duration: 1
});

2025-08-24 23.41.58.gif

2025-08-24 23.42.25.gif

这样,国家的定位就完成了。

案例代码上传了小册仓库

总结

这节我们实现了点击国家名定位到对应国家的功能。

定位对应国家时,我们通过 label 的位置单位向量化作为方向,让相机定位到这个方向的某个位置。

然后改一下轮廓的颜色、label 的字体大小。

当选中别的国家时,把之前的恢复原状。

这样我们的交互式地球仪就完成了。

评论