feat: smooth focus object on click
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,6 @@
|
||||
# Temporary
|
||||
_old
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
2380
pnpm-lock.yaml
generated
2380
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
BIN
public/art.webp
Normal file
BIN
public/art.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 503 KiB |
BIN
public/room.blend
Normal file
BIN
public/room.blend
Normal file
Binary file not shown.
BIN
public/room.blend1
Normal file
BIN
public/room.blend1
Normal file
Binary file not shown.
BIN
public/room.fbx
Normal file
BIN
public/room.fbx
Normal file
Binary file not shown.
121
src/App.tsx
121
src/App.tsx
@@ -1,44 +1,99 @@
|
||||
import * as THREE from "three";
|
||||
import { useRef, useState } from "react";
|
||||
import { Canvas, type ThreeElements } from "@react-three/fiber";
|
||||
import { OrbitControls } from '@react-three/drei'
|
||||
import { useRef, useState, type ComponentRef } from "react";
|
||||
import {
|
||||
Canvas,
|
||||
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";
|
||||
|
||||
function Box(props: ThreeElements["mesh"]) {
|
||||
const ref = useRef<THREE.Mesh>(null!);
|
||||
const [hovered, hover] = useState(false);
|
||||
const [clicked, click] = useState(false);
|
||||
const Room = () => {
|
||||
const controls = useRef<ComponentRef<typeof OrbitControls>>(null);
|
||||
const room = useLoader(FBXLoader, "/room.fbx");
|
||||
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 (
|
||||
<mesh
|
||||
{...props}
|
||||
ref={ref}
|
||||
scale={clicked ? 1.5 : 1}
|
||||
onClick={() => click(!clicked)}
|
||||
onPointerOver={() => hover(true)}
|
||||
onPointerOut={() => hover(false)}
|
||||
>
|
||||
<boxGeometry args={[1, 1, 1]} />
|
||||
<meshStandardMaterial color={hovered ? "hotpink" : "orange"} />
|
||||
</mesh>
|
||||
<>
|
||||
<ambientLight intensity={Math.PI / 2} color={"#ffffff"} />
|
||||
<directionalLight intensity={5} position={[5, 10, 5]} castShadow />
|
||||
<OrbitControls
|
||||
ref={controls}
|
||||
target={[0, 1, 0]}
|
||||
maxPolarAngle={Math.PI / 2}
|
||||
enablePan={false}
|
||||
onStart={() => setCameraData(null)}
|
||||
/>
|
||||
|
||||
<mesh onClick={handleClick}>
|
||||
<primitive object={room} />
|
||||
</mesh>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Canvas>
|
||||
<ambientLight intensity={Math.PI / 2} />
|
||||
<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 camera={{ position: [3, 3, -3] }}>
|
||||
<Room />
|
||||
</Canvas>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user