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

这节根据点击的国家名在地图中定位到对应国家。
首先我们暴露一个方法:

function focusCountry(name) {
console.log('focus', name);
}
在组件里接收下:

const focusCountryRef = useRef(() => {});
focusCountryRef.current = focusCountry;
点击国家名的时候调用:

onClick={() => {
focusCountryRef.current(item);
}}
测试下先:

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

province.name = 'shape' + feature.properties.name_zh;
label.name = 'label' + feature.properties.name_zh;
这样就可以根据 name 来查找国家对应的轮廓、标签对象了:

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
把它设为相机位置。
看下效果:

可以看到,位置都是对的。
我们把选中的国家的国家名变大一点:

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 的方向移动一段距离,避免和其他的线重合。
看下效果:

有点太近了,远一点:


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


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;
}
就是恢复之前的数据。
试一下:

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

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


这样,国家的定位就完成了。
案例代码上传了小册仓库
总结
这节我们实现了点击国家名定位到对应国家的功能。
定位对应国家时,我们通过 label 的位置单位向量化作为方向,让相机定位到这个方向的某个位置。
然后改一下轮廓的颜色、label 的字体大小。
当选中别的国家时,把之前的恢复原状。
这样我们的交互式地球仪就完成了。