Files
IRTH/src/components/complexPage/SequenceSlider.tsx
T
2024-05-02 17:03:46 +05:00

240 lines
7.2 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 ActiveResizeIcon from "../icons/ActiveResizeIcon";
import LeftArrowSliderIcon from "../icons/LeftArrowSliderIcon";
import RightArrowSliderIcon from "../icons/RightArrowSliderIcon";
// const marks = [
// {
// id: "1",
// label: "Башня 1",
// icon: "tower1",
// x: [1210, 1095, 770, 660, 820, 1060],
// y: [675, 795, 790, 670, 590, 590],
// url: "/tower1",
// },
// {
// id: "2",
// label: "Башня 2",
// icon: "tower2",
// x: [650, 835, 1080, 1240, 1080, 720],
// y: [650, 565, 575, 670, 800, 780],
// url: "/tower2",
// },
// {
// id: "3",
// label: "Двор",
// icon: "walk",
// x: [930, 930, 930, 930, 930, 930],
// y: [930, 930, 930, 930, 930, 930],
// url: "/virtual-tour",
// },
// {
// id: "4",
// label: "Внешний двор",
// icon: "walk",
// x: [1310, 980, 600, -100, -100, 1185],
// y: [1075, 1280, 1130, -100, -100, 860],
// url: "/virtual-tour-2",
// },
// ];
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 [selectedImageLeftIndex, setSelectedImageLeftIndex] = useState<number>(
arrayLength - keyframes[3]
);
const [isAnimate, setIsAnimate] = useState<boolean>(false);
const [keyframeIndex, setKeyframeIndex] = useState<number>(3);
const handlers = useSwipeable({
trackMouse: true,
onSwipedLeft: () => !isAnimate && next(),
onSwipedRight: () => !isAnimate && prev(),
});
const [loadedImages, setLoadedImages] = useState<number>(0);
function handleLoad() {
setLoadedImages((prev) => prev + 1);
}
function animateToRight(currentOffset: number) {
const obj = { selectedImageIndex: selectedImageRightIndex };
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);
setSelectedImageLeftIndex(arrayLength - (roundedIndex - arrayLength));
} else {
setSelectedRightImageIndex(roundedIndex);
setSelectedImageLeftIndex(arrayLength - roundedIndex);
}
},
onComplete: () => {
setIsAnimate(false);
},
});
}
function animateToLeft(currentOffset: number) {
const obj = { selectedImageIndex: selectedImageLeftIndex };
gsap.to(obj, {
selectedImageIndex: selectedImageLeftIndex + currentOffset,
duration: 0.75,
ease: "power1.inOut",
onUpdate: () => {
const roundedIndex = Math.round(obj.selectedImageIndex);
if (roundedIndex > arrayLength) {
setSelectedRightImageIndex(
arrayLength - (roundedIndex - arrayLength)
);
setSelectedImageLeftIndex(roundedIndex - arrayLength);
} else {
setSelectedImageLeftIndex(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);
}
const [width, setWidth] = useState<number>();
const [top, setTop] = useState<number>();
function handleResize() {
setWidth(window.innerWidth);
setTop(window.innerWidth / 2 - window.innerHeight / 2);
}
useEffect(() => {
handleResize();
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
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` }}
>
{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 t select-none pointer-events-none ${
index === selectedImageRightIndex ? "opacity-100" : "opacity-0"
}`}
onLoad={handleLoad}
/>
</>
))}
</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;