321 lines
9.9 KiB
TypeScript
321 lines
9.9 KiB
TypeScript
/* 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 { useNavigate } from "react-router-dom";
|
|
import Button from "../Button";
|
|
import LeftArrowSliderIcon from "../icons/LeftArrowSliderIcon";
|
|
import RightArrowSliderIcon from "../icons/RightArrowSliderIcon";
|
|
import useCompass from "../../store/useCompass";
|
|
import SequenceHighlighting from "./SequenceHighlighting";
|
|
// import { laptopWidth, mobileWidht } from "../../consts/masterplan";
|
|
|
|
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 [left, setLeft] = useState<number>(0);
|
|
const { setCurrentCompassRotate, currentCompassRotate } = useCompass();
|
|
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.floor(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.floor(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() {
|
|
// const screenWidth = window.innerWidth;
|
|
// const screenHeight = window.innerHeight;
|
|
// // if (screenWidth > laptopWidth) {
|
|
// const _top = screenHeight / 7;
|
|
// setTop(_top);
|
|
// const _left = 2500 - screenWidth;
|
|
// // const _left = laptopWidth - screenWidth;
|
|
// const _width = screenWidth + _left * 2;
|
|
|
|
// setWidth(_width);
|
|
// setLeft(_left);
|
|
|
|
// // setWidth(screenWidth);
|
|
// // setTop(screenWidth / 2 - screenHeight / 2);
|
|
// // setLeft(0);
|
|
// // } else {
|
|
// // if (screenWidth > mobileWidht) {
|
|
// // const _top = screenHeight / 4;
|
|
// // setTop(_top);
|
|
// // } else {
|
|
// // const _top = screenHeight / 2.5;
|
|
// // setTop(_top);
|
|
// // }
|
|
// // const _left = laptopWidth - screenWidth;
|
|
// // const _width = screenWidth + _left * 2;
|
|
|
|
// // setWidth(_width);
|
|
// // setLeft(_left);
|
|
// // }
|
|
// }
|
|
function handleResize() {
|
|
const screenWidth = window.innerWidth;
|
|
const screenHeight = window.innerHeight;
|
|
const _top = screenHeight / 4.5;
|
|
setTop(_top);
|
|
const _left = screenHeight - screenWidth;
|
|
const _width = screenHeight + _left;
|
|
|
|
setWidth(_width);
|
|
setLeft(_left);
|
|
}
|
|
function handleOnSequenceClick(): void {
|
|
navigate("./wing");
|
|
}
|
|
|
|
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`,
|
|
left: `-${left}px`,
|
|
}}
|
|
>
|
|
<div
|
|
className={`duration-150 transition-opacity ease-in-out opacity-100`}
|
|
>
|
|
{Array.from({ length: arrayLength }).map((_, index) => {
|
|
// const imgSrc = `${path}/${
|
|
// !isAnimate && keyframes.includes(index + 1)
|
|
// ? `${index + 1}-4k.jpg`
|
|
// : `${index + 1}.jpg`
|
|
// }`;
|
|
return (
|
|
<>
|
|
<img
|
|
key={index}
|
|
width={`${width}px`}
|
|
height={`${width}px`}
|
|
// src={imgSrc}
|
|
src={`${path}/${index + 1}.${isMobile ? "jpg" : "jpg"}`}
|
|
alt=""
|
|
className={`absolute z-10 ${
|
|
index === selectedImageRightIndex
|
|
? "opacity-100"
|
|
: "opacity-0"
|
|
}`}
|
|
onLoad={handleLoad}
|
|
/>
|
|
</>
|
|
);
|
|
})}
|
|
{keyframes.map((keyframe, index) => {
|
|
const imgSrc = `${path}/${
|
|
keyframe === selectedImageRightIndex
|
|
? `${keyframe + 1}-4k.jpg`
|
|
: ``
|
|
}`;
|
|
return (
|
|
<>
|
|
<img
|
|
key={index}
|
|
width={`${width}px`}
|
|
height={`${width}px`}
|
|
src={imgSrc}
|
|
// src={`${path}/${index + 1}.${isMobile ? "jpg" : "jpg"}`}
|
|
alt=""
|
|
className={`absolute z-[11]`}
|
|
/>
|
|
</>
|
|
);
|
|
})}
|
|
</div>
|
|
<SequenceHighlighting
|
|
keyframe={selectedImageRightIndex + 1}
|
|
onClick={handleOnSequenceClick}
|
|
/>
|
|
</div>
|
|
<Button
|
|
disabled={isAnimate}
|
|
className={`p-3 rounded-full transition-colors absolute left-6 top-[calc(50%-24px)] z-50 ${
|
|
isAnimate ? "opacity-50" : "opacity-100"
|
|
}`}
|
|
isCircleRounded
|
|
buttonType="fab"
|
|
icon={<LeftArrowSliderIcon />}
|
|
onClick={prev}
|
|
/>
|
|
<Button
|
|
disabled={isAnimate}
|
|
className={`p-3 rounded-full transition-colors absolute right-6 top-[calc(50%-24px)] z-50 ${
|
|
isAnimate ? "opacity-50" : "opacity-100"
|
|
}`}
|
|
buttonType="fab"
|
|
isCircleRounded
|
|
icon={<RightArrowSliderIcon />}
|
|
onClick={next}
|
|
/>
|
|
<div
|
|
className="absolute left-0 z-40 select-none"
|
|
style={{
|
|
width: `${width}px`,
|
|
height: `${width}px`,
|
|
top: `-${top}px`,
|
|
left: `${left}`,
|
|
}}
|
|
>
|
|
<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 z-[99999999] top-0 left-0 w-screen bg-[#F3F3F2] h-screen flex justify-center items-center flex-col ${state}`}
|
|
>
|
|
<div className="animate-spin">
|
|
<img src="/images/loader.png" alt="" />
|
|
</div>
|
|
<div className="text-[#00BED7] text-m">
|
|
{Math.round((100 / arrayLength) * loadedImages)} %
|
|
</div>
|
|
</div>
|
|
// <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">
|
|
// Loading... {Math.round((100 / arrayLength) * loadedImages)} %
|
|
// </h2>
|
|
// </div>
|
|
)}
|
|
</Transition>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default SequenceSlider;
|