React で Three.js を扱うためのライブラリ、 @react-three/fiber
で、タイトル通りのことをしたい場合の話。
例えば、画面を4分割して4種類の 3D モデルを同じ角度から比較したい、などと言ったときに使える、複数の Canvas 間で OrbitControls
の操作を共有する方法。
やり方は簡単で、以下のように、 domElement
に共通の DOM Element を渡してあげるだけでよい。
import React from 'react'; import { Canvas } from '@react-three/fiber'; import { OrbitControls } from '@react-three/drei'; import { Scene } from './components/Scene'; function App() { const overlayRef = React.useRef<HTMLDivElement>(null); const [element, setElement] = React.useState<HTMLDivElement>(); React.useEffect(() => { if (overlayRef.current) { setElement(overlayRef.current); // current 直に渡すと変更が検出されなくて動かないので注意 } }, []); return ( <div className="w-full h-screen bg-gray-900 relative"> {/* Transparent Overlay */} <div ref={overlayRef} className="absolute inset-0 bg-transparent z-10" /> <div className="grid grid-cols-2 h-full gap-4 p-4 relative z-0"> {/* First Canvas */} <div className="relative rounded-lg overflow-hidden shadow-xl bg-neutral-600"> <Canvas camera={{ position: [0, 0, 6] }} className="w-full h-full"> <Scene /> <OrbitControls domElement={element} /> </Canvas> <div className="absolute top-4 left-4 text-white"> <h2 className="text-xl font-bold">Scene 1</h2> <p className="text-sm text-gray-300">Click and drag to rotate</p> </div> </div> {/* Second Canvas */} <div className="relative rounded-lg overflow-hidden shadow-xl bg-neutral-800"> <Canvas camera={{ position: [0, 0, 6] }} className="w-full h-full"> <Scene /> <OrbitControls domElement={element} /> </Canvas> <div className="absolute top-4 left-4 text-white"> <h2 className="text-xl font-bold">Scene 2</h2> <p className="text-sm text-gray-300">Click and drag to rotate</p> </div> </div> </div> </div> ); } export default App;
渡す DOM Element は Canvas である必要は無く、 div 要素でもなんでも良い。無駄に Canvas を敷くのはパフォーマンス的によろしくないと思うので、 div 要素を敷くのがよいと思う。 あとは div 要素の上をぐりぐりマウスでいじれば、左右の2つの Canvas で操作が同期していることが分かる。
以下は実際に動く StackBlitz、1分程度待てば動くサンプルが確認できる。
ということで、メモでした。
参考: