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
|
||||||
logs
|
logs
|
||||||
*.log
|
*.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.
119
src/App.tsx
119
src/App.tsx
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user