如何在React Three Fiber中实现几何体面的悬停高亮效果?
Three.js原生立方体面悬停高亮转译为React Three Fiber版本
原生Three.js实现核心逻辑回顾
你找到的原生方案核心是通过Raycaster检测鼠标与立方体面的交点,根据materialIndex切换对应面材质的颜色,核心代码片段如下:
// 省略场景/相机/渲染器初始化代码 const geometry = new THREE.BoxGeometry(); const materials = Array.from({ length: 6 }, () => new THREE.MeshPhongMaterial({ color: 0x00ff00 })); const cube = new THREE.Mesh(geometry, materials); const raycaster = new THREE.Raycaster(); let activeFaceIndex = -1; renderer.domElement.addEventListener("mousemove", (event) => { const mouse = new THREE.Vector2( (event.clientX / renderer.domElement.clientWidth) * 2 - 1, -(event.clientY / renderer.domElement.clientHeight) * 2 + 1 ); raycaster.setFromCamera(mouse, camera); const intersects = raycaster.intersectObject(cube, false); // 颜色切换逻辑 if (intersects.length > 0) { const faceIndex = intersects[0].face.materialIndex; if (faceIndex !== activeFaceIndex) { if (activeFaceIndex !== -1) materials[activeFaceIndex].color.setHex(0x00ff00); activeFaceIndex = faceIndex; materials[faceIndex].color.setHex(0xff0000); } } else { if (activeFaceIndex !== -1) materials[activeFaceIndex].color.setHex(0x00ff00); activeFaceIndex = -1; } });
React Three Fiber转译实现
React Three Fiber通过声明式API封装了Three.js底层逻辑,以下是转译后的完整实现:
依赖安装
先安装所需依赖:
npm install three @react-three/fiber @react-three/drei
完整组件代码
import { useState, useRef, useFrame } from 'react'; import { Canvas } from '@react-three/fiber'; import { OrbitControls } from '@react-three/drei'; import * as THREE from 'three'; function HoverCube() { // 存储立方体实例 const cubeRef = useRef(null); // 跟踪当前高亮的面索引 const [activeFaceIndex, setActiveFaceIndex] = useState(-1); // 存储6个面的材质(避免React状态频繁更新) const materialsRef = useRef([]); useFrame((state) => { const { mouse, camera } = state; // 初始化材质(仅执行一次) if (materialsRef.current.length === 0 && cubeRef.current) { materialsRef.current = Array.from({ length: 6 }, () => new THREE.MeshPhongMaterial({ color: 0x00ff00 }) ); cubeRef.current.material = materialsRef.current; } // 鼠标交互检测 const raycaster = new THREE.Raycaster(); raycaster.setFromCamera(mouse, camera); if (cubeRef.current) { const intersects = raycaster.intersectObject(cubeRef.current, false); if (intersects.length > 0) { const faceIndex = intersects[0].face.materialIndex; if (faceIndex !== activeFaceIndex) { // 恢复上一个面的颜色 if (activeFaceIndex !== -1) { materialsRef.current[activeFaceIndex].color.setHex(0x00ff00); } setActiveFaceIndex(faceIndex); // 设置当前面为高亮色 materialsRef.current[faceIndex].color.setHex(0xff0000); } } else { // 鼠标离开时恢复所有面颜色 if (activeFaceIndex !== -1) { materialsRef.current[activeFaceIndex].color.setHex(0x00ff00); setActiveFaceIndex(-1); } } // 立方体旋转动画 cubeRef.current.rotation.x += 0.01; cubeRef.current.rotation.y += 0.01; } }); return ( <mesh ref={cubeRef}> <boxGeometry /> </mesh> ); } export default function App() { return ( <Canvas camera={{ position: [0, 0, 2] }}> {/* 平行光,对应原生的DirectionalLight */} <directionalLight position={[2, 2, 10]} /> <HoverCube /> {/* 轨道控制器,自动绑定相机和渲染器 */} <OrbitControls /> </Canvas> ); }
关键差异说明
- 封装层级:
Canvas组件自动处理场景、相机、渲染器的创建与resize适配,无需手动监听窗口事件 - 状态管理:使用
useRef存储材质数组,避免因React状态更新触发不必要的重渲染;useState仅跟踪当前高亮面索引 - 动画与交互:
useFrame钩子替代原生的requestAnimationFrame循环,每帧执行交互检测和动画逻辑,state.mouse已自动转换为Three.js所需的设备坐标 - 组件化:将立方体的交互逻辑封装为独立组件,符合React的组件化开发模式
扩展方案(三角形面高亮)
若需要实现单个三角形面的高亮(而非整个立方体面),核心逻辑类似,只需将intersects[0].face.materialIndex替换为intersects[0].faceIndex,并调整材质逻辑:
- 若使用单材质,需启用几何体的顶点颜色,通过修改
geometry.colors数组实现单个三角形的颜色切换 - 或使用自定义着色器,通过uniform变量传递高亮的三角形索引,在片元着色器中控制颜色
内容的提问来源于stack exchange,提问作者psygo
相关产品推荐
相关产品推荐

