import { useThree } from "@react-three/fiber"; import { useEffect, useRef, useState } from "react"; import ReactDOM from "react-dom"; interface Props { showResetButton?: boolean; onReset?: () => void; } /** * Renders camera control UI buttons as a DOM overlay on top of the R3F canvas. * Must be placed inside a so it has access to useThree(). * * Uses a portal into a div that is absolutely positioned over the canvas element, * so the buttons sit in 2D screen space rather than 3D world space. */ export default function CameraOverlay({ showResetButton, onReset }: Props) { const { gl } = useThree(); const [container, setContainer] = useState(null); useEffect(() => { const canvas = gl.domElement; const parent = canvas.parentElement; if (!parent) return; // Ensure the parent is positioned so absolute children are relative to it const parentPosition = getComputedStyle(parent).position; if (parentPosition === "static") { parent.style.position = "relative"; } const div = document.createElement("div"); div.style.cssText = ` position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 10; `; parent.appendChild(div); setContainer(div); return () => { parent.removeChild(div); }; }, [gl]); if (!container) return null; return ReactDOM.createPortal(
{showResetButton && ( )}
, container ); }