started work on the bin summary part of the new bin page, built the framework and the new 3d visualizer, still doing some work on the camera to create an overlay with a reset button
This commit is contained in:
parent
b3cf5b5e83
commit
4b2c67dfd9
11 changed files with 974 additions and 220 deletions
90
src/3dModels/CameraControls/CameraOverlay.tsx
Normal file
90
src/3dModels/CameraControls/CameraOverlay.tsx
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
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 <Canvas> 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<HTMLDivElement | null>(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(
|
||||
<div style={{
|
||||
position: "absolute",
|
||||
bottom: 12,
|
||||
right: 12,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: 8,
|
||||
pointerEvents: "auto",
|
||||
}}>
|
||||
{showResetButton && (
|
||||
<button
|
||||
onClick={onReset}
|
||||
title="Reset camera"
|
||||
style={{
|
||||
width: 36,
|
||||
height: 36,
|
||||
borderRadius: 6,
|
||||
border: "1px solid rgba(255,255,255,0.2)",
|
||||
background: "rgba(0,0,0,0.45)",
|
||||
color: "white",
|
||||
cursor: "pointer",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
fontSize: 16,
|
||||
backdropFilter: "blur(4px)",
|
||||
transition: "background 0.15s",
|
||||
}}
|
||||
onMouseEnter={e => (e.currentTarget.style.background = "rgba(0,0,0,0.7)")}
|
||||
onMouseLeave={e => (e.currentTarget.style.background = "rgba(0,0,0,0.45)")}
|
||||
>
|
||||
⟳
|
||||
</button>
|
||||
)}
|
||||
</div>,
|
||||
container
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue