feat: smooth focus object on click

This commit is contained in:
2025-05-24 18:19:00 +02:00
parent 94305aacbf
commit 484205bc8a
7 changed files with 1662 additions and 842 deletions

3
.gitignore vendored
View File

@@ -1,3 +1,6 @@
# Temporary
_old
# Logs # Logs
logs logs
*.log *.log

2380
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

BIN
public/art.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 KiB

BIN
public/room.blend Normal file

Binary file not shown.

BIN
public/room.blend1 Normal file

Binary file not shown.

BIN
public/room.fbx Normal file

Binary file not shown.

View File

@@ -1,44 +1,99 @@
import * as THREE from "three"; import { useRef, useState, type ComponentRef } from "react";
import { useRef, useState } from "react"; import {
import { Canvas, type ThreeElements } from "@react-three/fiber"; Canvas,
import { OrbitControls } from '@react-three/drei' useFrame,
useThree,
type ThreeEvent,
useLoader,
} from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";
import { FBXLoader } from "three/examples/jsm/Addons.js";
import { Vector3 } from "three";
import "./App.css"; import "./App.css";
function Box(props: ThreeElements["mesh"]) { const Room = () => {
const ref = useRef<THREE.Mesh>(null!); const controls = useRef<ComponentRef<typeof OrbitControls>>(null);
const [hovered, hover] = useState(false); const room = useLoader(FBXLoader, "/room.fbx");
const [clicked, click] = useState(false); const [focus, setFocus] = useState<string | null>(null);
const [cameraData, setCameraData] = useState<{
position: Vector3;
target: Vector3;
} | null>(null);
const { camera } = useThree();
useFrame(() => {
if (!controls.current) return;
if (cameraData) {
camera.position.lerp(cameraData.position, 0.09);
controls.current.target.lerp(cameraData.target, 0.09);
if (camera.position.distanceTo(cameraData.position) < 0.01) {
setCameraData(null);
}
}
});
const handleClick = (e: ThreeEvent<MouseEvent>) => {
if (!controls.current) return;
e.stopPropagation();
if (e.object.name.includes("Poster") && e.object.name !== focus) {
const objectPos = new Vector3();
const objectDir = new Vector3();
e.object.getWorldPosition(objectPos);
e.object.getWorldDirection(objectDir);
setCameraData({
position: objectPos.clone().add(objectDir.multiplyScalar(-0.65)),
target: objectPos,
});
controls.current.enableZoom = false;
controls.current.enableRotate = false;
setFocus(e.object.name);
} else if (focus) {
controls.current.enableZoom = true;
controls.current.enableRotate = true;
setCameraData({
position: new Vector3(3, 3, -3),
target: new Vector3(0, 1, 0),
});
setFocus(null);
}
controls.current.update();
};
return ( return (
<mesh <>
{...props} <ambientLight intensity={Math.PI / 2} color={"#ffffff"} />
ref={ref} <directionalLight intensity={5} position={[5, 10, 5]} castShadow />
scale={clicked ? 1.5 : 1} <OrbitControls
onClick={() => click(!clicked)} ref={controls}
onPointerOver={() => hover(true)} target={[0, 1, 0]}
onPointerOut={() => hover(false)} maxPolarAngle={Math.PI / 2}
> enablePan={false}
<boxGeometry args={[1, 1, 1]} /> onStart={() => setCameraData(null)}
<meshStandardMaterial color={hovered ? "hotpink" : "orange"} /> />
<mesh onClick={handleClick}>
<primitive object={room} />
</mesh> </mesh>
</>
); );
} };
export default function App() { export default function App() {
return ( return (
<Canvas> <Canvas camera={{ position: [3, 3, -3] }}>
<ambientLight intensity={Math.PI / 2} /> <Room />
<spotLight
position={[10, 10, 10]}
angle={0.15}
penumbra={1}
decay={0}
intensity={Math.PI}
/>
<pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />
<Box position={[-1.2, 0, 0]} />
<Box position={[1.2, 0, 0]} />
<OrbitControls />
</Canvas> </Canvas>
); );
} }