React-Three-Fiber加载Rhino3dm模型后网格鼠标事件失效的解决方法
问题:Rhino3dm模型在react-three-fiber中鼠标悬停事件失效
在react-three-fiber中加载Rhino3dm格式模型时,尝试为每个网格部件添加鼠标悬停变色功能,但鼠标事件无法正常触发。此前加载GLTF模型时,通过Object.values(gltf.nodes).map并过滤isMesh对象的方式可以正常工作,但改用object.children.map处理Rhino3dm模型时事件异常。
原因分析
- 非Mesh对象混入:Rhino3dmLoader加载返回的
object.children中包含非Mesh类型的对象(比如Group容器),这些对象没有几何体,无法触发射线检测事件。而GLTF处理时手动过滤了isMesh的对象,避免了这个问题。 - 组件嵌套错误:外层用
<mesh>包裹<primitive object={child}>会导致嵌套冲突——如果child本身已经是Mesh,这种嵌套会让射线检测逻辑混乱,无法正确触发事件。 - 材质未正确绑定:直接创建新材质并传递给
<primitive>无法正确覆盖原对象的材质,且未用useMemo缓存材质会导致不必要的重渲染,影响事件响应。
解决方法
步骤1:过滤Mesh对象
遍历Rhino3dm模型的children时,只保留isMesh为true的对象,确保只处理可交互的网格部件。
步骤2:正确绑定事件与材质
直接对Mesh对象添加事件监听,通过useMemo缓存材质避免重渲染,替换原对象的材质而非嵌套组件。
修改后的完整代码
import "./styles.css"; import * as THREE from "three"; import { useMemo, useState } from "react"; import { Canvas, useLoader } from "@react-three/fiber"; import { Rhino3dmLoader } from "three/examples/jsm/loaders/3DMLoader"; import { Environment, OrbitControls } from "@react-three/drei"; import { Suspense } from "react"; const Part = ({ mesh }) => { const [hovered, setHover] = useState(false); // 用useMemo缓存材质,避免重渲染 const material = useMemo(() => new THREE.MeshStandardMaterial({ color: hovered ? "hotpink" : "orange" }), [hovered] ); return ( <primitive key={mesh.id} object={mesh} material={material} castShadow receiveShadow onPointerOver={(e) => { e.stopPropagation(); setHover(true); }} onPointerOut={(e) => { e.stopPropagation(); setHover(false); }} /> ); }; const Model = () => { const object = useLoader(Rhino3dmLoader, "./rhino_logo.3dm", (loader) => { loader.setLibraryPath("https://cdn.jsdelivr.net/npm/rhino3dm@0.15.0-beta/"); }); // 只过滤出Mesh类型的子对象 const model = object.children .filter(child => child.isMesh) .map(mesh => <Part key={mesh.id} mesh={mesh} />); return model; }; export default function App() { return ( <div className="App"> <Canvas> <Suspense fallback={null}> <Model /> <OrbitControls /> <Environment preset="sunset" background /> </Suspense> </Canvas> </div> ); }
关键修改点说明
- 新增
filter(child => child.isMesh):只处理可交互的网格元素,排除Group等容器对象。 - 移除外层
<mesh>:直接使用<primitive>绑定原Mesh对象,避免组件嵌套导致的射线检测问题。 useMemo缓存材质:减少不必要的材质重建,提升性能同时保证事件响应稳定。- 调整
Part组件接收参数:明确传递mesh对象,避免props解构错误。
内容的提问来源于stack exchange,提问作者4kk11
相关产品推荐
相关产品推荐

