React + Three.jsでウェブサイトを作る際、こんな感じのカスタムフックを使って、WebGLRenderer、PerspectiveCamera、Sceneを取得しています。
useThree.tsx
import { useEffect, useState } from 'react'; import * as THREE from '~/build/three.module'; export default function useThree(canvas: HTMLCanvasElement | null) { const [ renderer, setRenderer ] = useState<THREE.WebGLRenderer>(); const [ camera, setCamera ] = useState<THREE.PerspectiveCamera>(); const [ scene ] = useState<THREE.Scene>(new THREE.Scene); useEffect(() => { if (!canvas) { return; } setRenderer(new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true })); }, [canvas]); useEffect(() => { if (!renderer) { return; } setCamera(new THREE.PerspectiveCamera()); }, [renderer]); return { THREE, renderer, camera, scene }; }
使い方は、こんな感じです。
index.tsx
export default function IndexPage() { const canvasRef = useRef<HTMLCanvasElement>(null); const { windowWidth, windowHeight } = useResize(); const { THREE, renderer, camera, scene } = useThree(canvasRef.current); useEffect(() => { if (!renderer || !camera) { return; } camera.position.set(4, 4, 8); camera.lookAt(new THREE.Vector3(0, 0, 0)); const cube = new THREE.Mesh( new THREE.BoxGeometry(1, 1, 1), new THREE.MeshNormalMaterial() ); scene.add(cube); renderer.setClearColor(0xFFFFFF, 1); renderer.setAnimationLoop(() => { renderer.render(scene, camera); }); }, [renderer, camera]); useEffect(() => { if (!renderer || !camera) { return; } renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(windowWidth, windowHeight); camera.aspect = windowWidth / windowHeight; camera.updateProjectionMatrix(); }, [renderer, camera, windowWidth, windowHeight]); return <canvas ref={ canvasRef } />; }
useResizeは 以前つくったもの を流用しています。