Paul Henschel has created some mini demos of React + Three.JS based on requests from Reddit.
This is one of the demos where the camera move along with the model:
import { useRef } from "react";
import { Canvas, useFrame } from "@react-three/fiber";
import {
ScrollControls,
useScroll,
Cloud,
Sky,
Clouds,
Text,
} from "@react-three/drei";
import { easing } from "maath";
import tunnel from "tunnel-rat";
const t = tunnel();
export default function App() {
return (
<>
<Canvas shadows camera={{ position: [0, 2, 10] }}>
<ambientLight intensity={Math.PI / 2} />
<spotLight
position={[10, 3, 10]}
angle={0.45}
penumbra={1}
decay={0}
intensity={Math.PI * 1.25}
castShadow
shadow-bias={-0.00001}
/>
<directionalLight position={[10, -20, 5]} intensity={Math.PI} />
<ScrollControls pages={4} infinite>
<Player position={[0, -0.1, 6.25]} scale={0.35} />
<World />
<Annotation />
</ScrollControls>
<Sky />
</Canvas>
<t.Out />
</>
);
}
function Player(props) {
const ref = useRef();
const scroll = useScroll();
useFrame((state, delta) => {
easing.damp3(
state.camera.position,
[state.pointer.x, 2 + state.pointer.y, 10],
0.2,
delta,
);
state.camera.lookAt(0, 0, -2);
ref.current.rotation.z = -Math.PI * 8 * scroll.offset;
ref.current.position.y = 1 + Math.sin(-Math.PI * 8 * scroll.offset);
});
return (
<group {...props}>
<mesh castShadow receiveShadow ref={ref}>
<torusGeometry args={[0.8, 0.5, 6, 5]} />
<meshStandardMaterial flatShading color="orange" />
</mesh>
</group>
);
}
function World() {
const ref = useRef();
const scroll = useScroll();
useFrame(
(state, delta) => (ref.current.rotation.y = -Math.PI * 2 * scroll.offset),
);
return (
<group ref={ref}>
<mesh castShadow receiveShadow position={[0, -5.5, 0]}>
<cylinderGeometry args={[7, 7, 10, 16]} />
<meshStandardMaterial flatShading color="seagreen" />
</mesh>
<Clouds texture="/cloud.png">
<Mountain position={[0, 0.5, 2]} height={0.75} />
<Mountain position={[-1.5, 1, -1]} height={0.85} color="gray" />
<Cloud
speed={0.1}
seed={1}
opacity={1}
volume={100}
bounds={[90, 20, 90]}
position={[0, 10, 0]}
/>
</Clouds>
</group>
);
}
function Mountain({ height: h = 1, color = "peachpuff", ...props }) {
const clouds = useRef();
useFrame((state, delta) => (clouds.current.rotation.y -= (delta / 4) * h));
return (
<mesh castShadow receiveShadow {...props}>
<coneGeometry args={[4.5 * h, 6 * h, 16]} />
<meshStandardMaterial color={color} flatShading />
<mesh castShadow receiveShadow position={[0, 2.5 * h, 0]} rotation-y={1}>
<coneGeometry args={[1.1 * h, 1.6 * h, 16]} />
<meshStandardMaterial flatShading />
</mesh>
<group ref={clouds} scale={0.3}>
<Cloud
speed={0.2}
seed={4}
opacity={2}
segments={5}
volume={10 * h}
bounds={[7 * h, 1, 7 * h]}
position={[0, 10 * h, 0]}
/>
</group>
</mesh>
);
}
function Annotation() {
const ref = useRef();
const scroll = useScroll();
useFrame((state, delta) => {
easing.damp(
ref.current.style,
"opacity",
scroll.offset > 0.25 && scroll.offset < 0.6 ? 1 : 0,
0.2,
delta,
);
});
return (
<t.In>
<div
ref={ref}
style={{ position: "absolute", top: 30, left: 30, fontSize: "2em" }}
>
Hello ๐
</div>
</t.In>
);
}
You can check out all the demos, including the video and CodePen, on this post