本文详解如何使用three.js结合raycaster实现多建筑3d场景的鼠标交互:实时检测悬停与点击,动态显示标注文本并触发对应模态框,适合初学者快速上手。
本文详解如何使用three.js结合raycaster实现多建筑3d场景的鼠标交互:实时检测悬停与点击,动态显示标注文本并触发对应模态框,适合初学者快速上手。
在Three.js中为多个3D模型(如城市中的4栋建筑)添加交互能力完全可行——无需将每栋楼拆分为独立场景,而是通过Raycaster射线投射精准识别鼠标下的物体,从而模拟mouseenter/mouseleave和click事件。核心在于将视觉坐标转换为标准化设备坐标(NDC),再由相机发射射线检测相交对象。
✅ 基础准备:初始化Raycaster与鼠标坐标监听
// 1. 创建射线投射器
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
// 2. 监听鼠标移动,将屏幕坐标转为[-1, 1]范围的NDC坐标
window.addEventListener('mousemove', (event) => {
mouse.x = (event.clientX / sizes.width) * 2 - 1;
mouse.y = - (event.clientY / sizes.height) * 2 + 1;
});
⚠️ 注意:sizes需提前定义为{ width: window.innerWidth, height: window.innerHeight },并在窗口缩放时更新。
✅ 实时检测悬停:模拟mouseenter/mouseleave
在渲染循环(如requestAnimationFrame回调)中执行:
// 设置射线起点(相机位置)和方向(经鼠标坐标计算)
raycaster.setFromCamera(mouse, camera);
// 定义待检测的建筑模型数组(确保已添加到场景中)
const buildings = [building1, building2, building3, building4];
const intersects = raycaster.intersectObjects(buildings);
let currentIntersect = null; // 全局状态变量,记录当前悬停对象
if (intersects.length > 0) {
if (!currentIntersect) {
// 首次悬停:显示对应建筑的浮动文本(如使用CSS3DRenderer或Sprite)
showLabel(intersects[0].object.userData.label);
console.log('进入建筑:', intersects[0].object.name);
}
currentIntersect = intersects[0];
} else {
if (currentIntersect) {
// 离开悬停区域:隐藏文本
hideLabel();
console.log('离开建筑');
}
currentIntersect = null;
}
✅ 绑定点击事件:触发模态框
window.addEventListener('click', () => {
if (currentIntersect) {
const clickedBuilding = currentIntersect.object;
// 根据模型userData或引用匹配具体建筑
switch (clickedBuilding) {
case building1:
openModal('金融中心', '这是一座50层高的现代写字楼...');
break;
case building2:
openModal('科技园区', '包含3个研发中心与创新孵化器...');
break;
case building3:
openModal('文化广场', '融合剧院、美术馆与公共艺术装置...');
break;
case building4:
openModal('生态住宅', '采用光伏幕墙与雨水回收系统的绿色社区...');
break;
}
}
});
? 推荐实践:为每个建筑模型预先设置model.userData = { label: 'xxx', id: 'b1' },便于解耦逻辑与数据。
✅ 关键注意事项
- 性能优化:仅对需要交互的模型调用intersectObjects(),避免遍历整个场景;
- 层级穿透:若模型有透明材质(如玻璃幕墙),需设置material.transparent = true并确保material.depthWrite = false;
- 响应式适配:窗口大小变更时,务必同步更新sizes并重置相机投影矩阵(camera.aspect = sizes.width / sizes.height; camera.updateProjectionMatrix(););
- 无障碍增强:为模态框添加aria-label,并支持键盘Enter/Space触发,提升可访问性。
通过以上结构化实现,你不仅能为城市3D模型赋予直观的交互体验,更掌握了Three.js中交互开发的核心范式——Raycaster是连接2D输入与3D世界的桥梁,合理封装后可复用于任意复杂场景。











