You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用react-three-drei的View结合Routes时OrbitControls失效问题

路由切换后react-three-drei View组件的OrbitControls失效解决方案

问题根源

当前实现中,路由切换时页面内被View追踪的DOM元素(canvasView1、canvasView2)会被销毁重建,而<View>的track引用未同步更新,同时两个<OrbitControls>都设置了makeDefault,导致事件监听冲突,最终引发操控失效。

具体修复步骤

1. 移除多个OrbitControls的makeDefault属性

同一个Canvas下多个控件设置makeDefault会抢占事件绑定权限,直接去掉该属性:

// App.js 中修改两个OrbitControls
<OrbitControls />

2. 动态同步View的track引用

路由切换后,页面内的DOM元素是全新创建的,需要将ref同步到状态中,确保<View>能获取最新的DOM节点:

// 在App.js中添加状态和监听
import { useLocation } from 'react-router-dom';
import { useState, useEffect, useRef } from 'react';

export default function App() {
  const [canvasWrapperRef, canvasView1, canvasView2] = useRefs();
  const canvasRefs = { canvasView1, canvasView2 };
  const location = useLocation();
  const [view1Ref, setView1Ref] = useState(null);
  const [view2Ref, setView2Ref] = useState(null);
  const canvasStateRef = useRef(null);

  // 路由切换时同步最新的DOM引用并重新绑定事件
  useEffect(() => {
    setView1Ref(canvasView1.current);
    setView2Ref(canvasView2.current);
    if (canvasStateRef.current) {
      canvasStateRef.current.events.connect(canvasWrapperRef.current);
    }
  }, [location]);

  return (
    <div className="App">
      <div ref={canvasWrapperRef}>
        <Suspense fallback={null}>
          <Routes>
            <Route path="/" element={<Homepage ref={canvasRefs} />} />
            <Route path="/other" element={<OtherPage ref={canvasRefs} />} />
          </Routes>
          <Canvas
            onCreated={(state) => {
              canvasStateRef.current = state;
              state.events.connect(canvasWrapperRef.current);
            }}
            className="canvas"
          >
            {/* 使用状态中的ref而非原始useRefs */}
            <View track={view1Ref}>
              <Scene />
              <TransformControls>
                <Soda scale={6} position={[0, -1.6, 0]} />
              </TransformControls>
              <OrbitControls enabled={location.pathname === '/'} />
            </View>
            <View track={view2Ref}>
              <color attach="background" args={["lightblue"]} />
              <Scene />
              <TransformControls position={[0, -1, 0]}>
                <Duck scale={2} position={[0, -1.6, 0]} />
              </TransformControls>
              <OrbitControls enabled={location.pathname === '/other'} />
            </View>
            <Preload all />
          </Canvas>
        </Suspense>
      </div>
    </div>
  );
}

3. 按需启用OrbitControls

通过enabled属性,让每个路由页面只激活对应的控件,避免不必要的事件冲突:

  • 首页仅启用第一个View的OrbitControls
  • 其他页面仅启用第二个View的OrbitControls

关键说明

  • 路由切换会触发页面组件卸载重建,因此track绑定的DOM节点会变化,必须同步更新View的引用
  • Canvas事件系统需要在路由切换后重新绑定,确保鼠标事件能正确传递到Canvas
  • 多个OrbitControls共存时,必须避免同时设置makeDefault,改用enabled属性控制激活状态

内容的提问来源于stack exchange,提问作者Monstar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 12:40:29