Files
IRTH-Touch/src/components/complexPage/SequenceSlider.tsx
T
2024-05-03 18:07:32 +05:00

230 lines
7.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* eslint-disable react-hooks/exhaustive-deps */
import gsap from "gsap";
import { useState, useEffect } from "react";
import { isMobile } from "react-device-detect";
import { useSwipeable } from "react-swipeable";
import { Transition } from "react-transition-group";
import Button from "../Button";
import LeftArrowSliderIcon from "../icons/LeftArrowSliderIcon";
import RightArrowSliderIcon from "../icons/RightArrowSliderIcon";
import useSequence from "../../store/useSequence";
import SequenceHighlighting from "./SequenceHighlighting";
import { useNavigate } from "react-router-dom";
const arrayLength = 360;
const keyframes: number[] = [51, 178, 249, 339];
interface SequenceSliderProps {
path: string;
}
function SequenceSlider({ path }: SequenceSliderProps) {
const [selectedImageRightIndex, setSelectedRightImageIndex] =
useState<number>(keyframes[3]);
const [selectedImageLeftKeyframe, setSelectedImageLeftKeyframe] =
useState<number>(arrayLength - keyframes[3]);
const [isAnimate, setIsAnimate] = useState<boolean>(false);
const [loadedImages, setLoadedImages] = useState<number>(0);
const [keyframeIndex, setKeyframeIndex] = useState<number>(3);
const { setCurrentCompassRotate, currentCompassRotate } = useSequence();
const [width, setWidth] = useState<number>();
const [top, setTop] = useState<number>();
const navigate = useNavigate();
useEffect(() => {
setCurrentCompassRotate(keyframes[3]);
}, []);
const handlers = useSwipeable({
trackMouse: true,
onSwipedLeft: () => !isAnimate && next(),
onSwipedRight: () => !isAnimate && prev(),
});
function handleLoad() {
setLoadedImages((prev) => prev + 1);
}
function animateToRight(currentOffset: number) {
const obj = { selectedImageIndex: selectedImageRightIndex };
setCurrentCompassRotate(currentCompassRotate + currentOffset);
gsap.to(obj, {
selectedImageIndex: selectedImageRightIndex + currentOffset,
duration: 0.75,
ease: "power1.inOut",
onUpdate: () => {
const roundedIndex = Math.round(obj.selectedImageIndex);
if (roundedIndex >= arrayLength) {
setSelectedRightImageIndex(roundedIndex - arrayLength);
setSelectedImageLeftKeyframe(
arrayLength - (roundedIndex - arrayLength)
);
} else {
setSelectedRightImageIndex(roundedIndex);
setSelectedImageLeftKeyframe(arrayLength - roundedIndex);
}
},
onComplete: () => {
setIsAnimate(false);
},
});
}
function animateToLeft(currentOffset: number) {
const obj = { selectedImageIndex: selectedImageLeftKeyframe };
setCurrentCompassRotate(currentCompassRotate - currentOffset);
gsap.to(obj, {
selectedImageIndex: selectedImageLeftKeyframe + currentOffset,
duration: 0.75,
ease: "power1.inOut",
onUpdate: () => {
const roundedIndex = Math.round(obj.selectedImageIndex);
if (roundedIndex > arrayLength) {
setSelectedRightImageIndex(
arrayLength - (roundedIndex - arrayLength)
);
setSelectedImageLeftKeyframe(roundedIndex - arrayLength);
} else {
setSelectedImageLeftKeyframe(roundedIndex);
setSelectedRightImageIndex(arrayLength - roundedIndex);
}
},
onComplete: () => {
setIsAnimate(false);
},
});
}
function next() {
const updatedKeyframeIndex =
keyframeIndex !== keyframes.length - 1 ? keyframeIndex + 1 : 0;
const currentOffset =
keyframeIndex !== keyframes.length - 1
? keyframes[updatedKeyframeIndex] - keyframes[keyframeIndex]
: 360 - keyframes[keyframeIndex] + keyframes[0];
setKeyframeIndex(updatedKeyframeIndex);
setIsAnimate(true);
animateToRight(currentOffset);
}
function prev() {
const updatedKeyframeIndex =
keyframeIndex !== 0 ? keyframeIndex - 1 : keyframes.length - 1;
const currentOffset =
keyframeIndex !== 0
? keyframes[keyframeIndex] - keyframes[updatedKeyframeIndex]
: 360 - keyframes[updatedKeyframeIndex] + keyframes[keyframeIndex];
setKeyframeIndex(updatedKeyframeIndex);
setIsAnimate(true);
animateToLeft(currentOffset);
}
function handleResize() {
setWidth(window.innerWidth);
setTop(window.innerWidth / 2 - window.innerHeight / 2);
}
useEffect(() => {
handleResize();
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
function handleOnSequenceClick(): void {
navigate("./wing");
}
return (
<div
{...handlers}
className="absolute w-full h-full overflow-hidden bg-black top-0 left-0 z-10"
>
<div
className="absolute left-0"
style={{ width: `${width}px`, height: `${width}px`, top: `-${top}px` }}
>
<div
className={`duration-300 transition-opacity ease-in-out opacity-100`}
>
{Array.from({ length: arrayLength }).map((_, index) => (
<>
<img
key={index}
width={`${width}px`}
height={`${width}px`}
src={`${path}/${index + 1}.${isMobile ? "jpg" : "jpg"}`}
alt=""
className={`absolute ${
index === selectedImageRightIndex
? "opacity-100"
: "opacity-0"
}`}
onLoad={handleLoad}
/>
</>
))}
</div>
<SequenceHighlighting
keyframe={selectedImageRightIndex + 1}
onClick={handleOnSequenceClick}
/>
</div>
<Button
className={`p-3 rounded-full transition-colors absolute left-6 top-[calc(50%-24px)] z-50 ${
isAnimate ? "opacity-50" : "opacity-100"
}`}
buttonType="fab"
icon={<LeftArrowSliderIcon />}
onClick={prev}
/>
<Button
className={`p-3 rounded-full transition-colors absolute right-6 top-[calc(50%-24px)] z-50 ${
isAnimate ? "opacity-50" : "opacity-100"
}`}
buttonType="fab"
icon={<RightArrowSliderIcon />}
onClick={next}
/>
<div
className="absolute left-0 z-40 select-none"
style={{ width: `${width}px`, height: `${width}px`, top: `-${top}px` }}
>
<div
className={`transition-opacity duration-75 ${
keyframes.includes(selectedImageRightIndex)
? "opacity-100"
: "opacity-0"
}`}
></div>
</div>
<Transition
in={loadedImages !== arrayLength}
timeout={300}
mountOnEnter
unmountOnExit
>
{(state) => (
<div
className={`absolute top-0 left-0 w-full h-full flex items-center justify-center bg-neutral-950 z-50 transition-opacity duration-300 ${state}`}
>
<h2 className="text-2xl font-tenor text-white whitespace-nowrap">
Загрузка... {Math.round((100 / arrayLength) * loadedImages)} %
</h2>
</div>
)}
</Transition>
</div>
);
}
export default SequenceSlider;