import { FC, useEffect, useState } from "react"; import { AnimatePresence, motion, useAnimation, usePresence } from "framer-motion"; import { emptyFunc, joinClasses, waitAsync } from "@utils/common"; import { Forceful, SlowDown } from "@utils/anims"; import bodyStyles from "@components/body/Body.module.scss"; interface IIndex { initIndex?: number; onIndexAnimStart?: (from: number, to: number) => void; onIndexAnimEnd?: (from: number, to: number) => void; onIndexChange?: (index: number) => void; } export const LeftPanel: FC = ({ onIndexAnimStart= emptyFunc, onIndexAnimEnd= emptyFunc, onIndexChange= emptyFunc, initIndex = 1 }) => { const [isPresent, safeToRemove] = usePresence(); const [init, setInit] = useState(true); const [indexSubU, setIndexSubU] = useState(initIndex - 1); const [indexMain, setIndexMain] = useState(initIndex); const [indexSubL, setIndexSubL] = useState(initIndex + 1); const [indexPolygon, setIndexPolygon] = useState(0); const [isAnimating, setIsAnimating] = useState(false); const indexMainController = useAnimation(); const indexSubLController = useAnimation(); const indexSubUController = useAnimation(); const InnerPolygonVariants = { 0: { d: "M0,0V928H0L0,0Z" }, 1: { d: "M0,28V928H175L425,0Z" }, 2: { d: "M0,28V928H425L175,0Z" }, 3: { d: "M0,28V928H280L280,0Z" } }; const OuterPolygonVariants = { 0: { d: "M0,0V928H0L0,0Z" }, 1: { d: "M0,28V928H240L490,0" }, 2: { d: "M0,28V928H490L240,0" }, 3: { d: "M0,28V928H310L310,0Z" } }; const IndexTextVariants = { 0: { transform: "translate(220px, 600px) rotate(-74deg)" }, 1: { transform: "translate(220px, 600px) rotate(-74deg)" }, 2: { transform: "translate(250px, 600px) rotate(-105deg)" }, 3: { transform: "translate(220px, 600px) rotate(-90deg)" }, }; const transition = { duration: init ? 1 : 2, ease: Forceful, }; useEffect(() => { setIndexPolygon(indexMain); onIndexChange(indexMain); indexMainController.set({ y: 830, x: 200 , transition: { duration: 1.2, ease: SlowDown }}); void indexMainController.start({ x: 0 }); waitAsync(1000).then( () => { indexMainController.set({ transition }); setInit(false); } ); indexSubLController.set({ y: 1670 }); indexSubUController.set({ y: -10 }); return () => { indexMainController.stop(); indexSubLController.stop(); indexSubUController.stop(); }; }, [isPresent]); const indexTextConfig = { fontFamily: "Jetbrains Mono", fontStyle: "bold", fontSize: 62, fill: "#fff", transition, }; const indexNumConfig = { className: "no-pointer", stroke: "#1D1D1D", fill: "#1D1D1D", fontFamily: "Jetbrains Mono", fontStyle: "italic", fontSize: 1000, x: -180, }; const HandleScroll = async (event: WheelEvent) => { if (isAnimating) return; const down = event.deltaY > 0; // LOWER_INDEX_BOUND UPPER_INDEX_BOUND await shiftNumberSequence(down, (indexMain < 2 && !down) || (indexMain > 2 && down)); }; const shiftNumberSequence = async (increment: boolean, wrap = false) => { setIsAnimating(true); const oldValue = indexMain; const newValue = wrap ? (increment ? 1 : 3) : (oldValue + (increment ? 1 : -1)); onIndexAnimStart(oldValue, newValue); if (wrap) { increment ? setIndexSubL(newValue) : setIndexSubU(newValue); } setIndexPolygon(newValue); await Promise.all( increment ? [ indexSubLController.start({ y: 830 }), indexMainController.start({ y: -10 }), ] : [ indexSubUController.start({ y: 830 }), indexMainController.start({ y: 1670 }) ] ); await waitAsync(20); setIndexMain(newValue); onIndexChange(newValue); indexMainController.set({ y: 830 }); increment ? indexSubLController.set({ y: 1670 }) : indexSubUController.set({ y: -10 }); setIndexSubL(newValue + 1); setIndexSubU(newValue - 1); setIsAnimating(false); onIndexAnimEnd(oldValue, newValue); return Promise.resolve(); }; const commonPolygonProps = { animate: indexPolygon.toString(), transition, initial: "0", }; return ( HandleScroll(e as unknown as WheelEvent)} pointerEvents={"all"} fill="black" variants={InnerPolygonVariants} {...commonPolygonProps} /> {indexSubU} {indexMain} {indexSubL} { indexPolygon === 1 && OVERVIEW } { indexPolygon === 2 && PROTOCOL FIELD RECOVERY DEPARTMENT } { indexPolygon === 3 && COORDINATE RECORDS } {"<-SCROLL->"} ); };