Add option to pause spinning in showcase
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
import { CoinProps } from "@/types/coin";
|
||||
import { Canvas, MeshProps, useFrame } from "@react-three/fiber";
|
||||
import { Suspense, useEffect, useRef, useState } from "react";
|
||||
import { Suspense, useCallback, useEffect, useRef, useState } from "react";
|
||||
import { CoinInstance } from "@/components/coin";
|
||||
import { Mesh } from "three";
|
||||
import { BanknoteInstance } from "@/components/banknote";
|
||||
import { InfoBox } from "./info-box";
|
||||
import { OrbitControls } from "@react-three/drei";
|
||||
import { OrbitControls as OrbitControlsType } from "three-stdlib";
|
||||
|
||||
type Props = {
|
||||
category: "coins" | "banknotes";
|
||||
@@ -13,16 +15,21 @@ type Props = {
|
||||
|
||||
const SpinninType = ({
|
||||
isSelected,
|
||||
isSpinning,
|
||||
category,
|
||||
typeProps,
|
||||
...props
|
||||
}: { typeProps: CoinProps; isSelected?: boolean } & MeshProps &
|
||||
}: {
|
||||
typeProps: CoinProps;
|
||||
isSelected?: boolean;
|
||||
isSpinning?: boolean;
|
||||
} & MeshProps &
|
||||
Pick<Props, "category">) => {
|
||||
const ref = useRef<Mesh>(null!);
|
||||
const Instance = category === "banknotes" ? BanknoteInstance : CoinInstance;
|
||||
|
||||
useFrame((_state, delta) => {
|
||||
if (!isSelected) {
|
||||
if (!isSelected || !isSpinning) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -66,7 +73,8 @@ const Row = ({
|
||||
category,
|
||||
types,
|
||||
selectedIndex,
|
||||
}: Props & { selectedIndex: number }) => {
|
||||
isSpinning,
|
||||
}: Props & { selectedIndex: number; isSpinning?: boolean }) => {
|
||||
const ref = useRef<Mesh>(null!);
|
||||
const spacing = category === "banknotes" ? 8 : 6;
|
||||
|
||||
@@ -91,6 +99,7 @@ const Row = ({
|
||||
<Suspense key={type.id} fallback={null}>
|
||||
<SpinninType
|
||||
category={category}
|
||||
isSpinning={isSpinning}
|
||||
isSelected={selectedIndex === index}
|
||||
typeProps={type}
|
||||
position={[index * spacing, 1, 0]}
|
||||
@@ -105,6 +114,18 @@ const Row = ({
|
||||
|
||||
export const Showcase = (props: Props) => {
|
||||
const [selectedIndex, selectIndex] = useState(0);
|
||||
const [isSpinning, setIsSpinning] = useState(true);
|
||||
const orbitControlsRef = useRef<OrbitControlsType>(null!);
|
||||
const toggleSpin = useCallback(() => {
|
||||
setIsSpinning((value) => {
|
||||
if (!value) {
|
||||
orbitControlsRef.current?.setAzimuthalAngle(0);
|
||||
orbitControlsRef.current?.setPolarAngle(Math.PI / 2);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const onKeyDown = ({ key }: KeyboardEvent) => {
|
||||
@@ -115,11 +136,13 @@ export const Showcase = (props: Props) => {
|
||||
return selectIndex((value) =>
|
||||
Math.min(value + 1, props.types.length - 1),
|
||||
);
|
||||
case " ":
|
||||
return toggleSpin();
|
||||
}
|
||||
};
|
||||
document.addEventListener("keydown", onKeyDown);
|
||||
return () => document.removeEventListener("keydown", onKeyDown);
|
||||
}, [props.types.length]);
|
||||
}, [props.types.length, toggleSpin]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -127,7 +150,13 @@ export const Showcase = (props: Props) => {
|
||||
<color attach="background" args={["#111"]} />
|
||||
<ambientLight />
|
||||
<directionalLight position={[0, 0.125, 1]} />
|
||||
<Row {...props} selectedIndex={selectedIndex} />
|
||||
<directionalLight position={[0, 0.125, -1]} />
|
||||
<Row {...props} selectedIndex={selectedIndex} isSpinning={isSpinning} />
|
||||
<OrbitControls
|
||||
ref={orbitControlsRef}
|
||||
enableRotate={!isSpinning}
|
||||
enablePan={false}
|
||||
/>
|
||||
</Canvas>
|
||||
|
||||
<InfoBox
|
||||
@@ -135,6 +164,8 @@ export const Showcase = (props: Props) => {
|
||||
type={props.types[selectedIndex]}
|
||||
selectIndex={selectIndex}
|
||||
selectedIndex={selectedIndex}
|
||||
isSpinning={isSpinning}
|
||||
toggleSpin={toggleSpin}
|
||||
maxIndex={props.types.length - 1}
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -7,12 +7,16 @@ export const InfoBox = ({
|
||||
maxIndex,
|
||||
selectIndex,
|
||||
selectedIndex,
|
||||
isSpinning,
|
||||
toggleSpin,
|
||||
}: {
|
||||
category: "banknotes" | "coins";
|
||||
type: CoinProps;
|
||||
maxIndex: number;
|
||||
selectIndex: Dispatch<SetStateAction<number>>;
|
||||
selectedIndex: number;
|
||||
isSpinning: boolean;
|
||||
toggleSpin: () => void;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
@@ -55,6 +59,13 @@ export const InfoBox = ({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<button
|
||||
style={{ fontSize: "1.2rem", marginBottom: "8px" }}
|
||||
onClick={toggleSpin}
|
||||
>
|
||||
{isSpinning ? "⏸️" : "▶️"}
|
||||
</button>
|
||||
|
||||
<div
|
||||
style={{
|
||||
width: 128,
|
||||
|
||||
Reference in New Issue
Block a user