upd
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import useModal from "../../store/useModal";
|
||||
import Button from "../Button";
|
||||
import FilterIcon from "../icons/FilterIcon";
|
||||
// import HintIcon from "../icons/HintIcon";
|
||||
import LeftArrowSliderIcon from "../icons/LeftArrowSliderIcon";
|
||||
// import ResizeIcon from "../icons/ResizeIcon";
|
||||
// import HelpModal from "../modals/HelpModal";
|
||||
// import MasterplanFilters from "../modals/MasterplanFilters";
|
||||
import InfoIcon from "../icons/InfoIcon";
|
||||
// import useFullScreen from "../../store/useFullScreen";
|
||||
// import ActiveResizeIcon from "../icons/ActiveResizeIcon";
|
||||
import useWingSidebar from "../../store/useWingSidebar";
|
||||
import { MobileModalWrapper } from "../modals/mobile/MobileModalWrapper";
|
||||
import MasterplanFiltersModal from "../modals/mobile/MasterplanFiltersModal";
|
||||
|
||||
const ComplexTopPanel = () => {
|
||||
const { setModal } = useModal();
|
||||
// const { onFullscreen, isFullscreen, setIsFullscreen } = useFullScreen();
|
||||
const { setIsSidebar, isSidebar } = useWingSidebar();
|
||||
const navigate = useNavigate();
|
||||
|
||||
// const handleOnHelpClick = () => {
|
||||
// setModal(<HelpModal />);
|
||||
// };
|
||||
|
||||
// const handleOnFiltersClick = () => {
|
||||
// setModal(<MasterplanFilters />);
|
||||
// };
|
||||
|
||||
const handleOnMobileFiltersClick = () => {
|
||||
setModal(
|
||||
<MobileModalWrapper>
|
||||
<MasterplanFiltersModal />
|
||||
</MobileModalWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const handleOnBackClick = () => {
|
||||
if (isSidebar) {
|
||||
setIsSidebar(false);
|
||||
} else {
|
||||
navigate(-1);
|
||||
}
|
||||
};
|
||||
|
||||
// const handleOnFullScreenClick = () => {
|
||||
// if (!onFullscreen) return;
|
||||
|
||||
// setIsFullscreen(!isFullscreen);
|
||||
// if (!isFullscreen) {
|
||||
// onFullscreen.enter();
|
||||
// } else {
|
||||
// onFullscreen.exit();
|
||||
// }
|
||||
// };
|
||||
|
||||
const handleOnAboutComplexClick = () => {
|
||||
navigate(`../about-projects`);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="absolute top-0 w-screen z-20 select-none pointer-events-none">
|
||||
<img src="../images/top_shadow.png" className="w-screen" alt="" />
|
||||
</div>
|
||||
<div
|
||||
className={`absolute top-[62px] left-0 z-20 w-full p-4 grid grid-cols-12 select-none touch-none pointer-events-none`}
|
||||
>
|
||||
<div className="flex gap-2 sm:col-span-5 col-span-7">
|
||||
<Button
|
||||
icon={<LeftArrowSliderIcon />}
|
||||
buttonType="cta"
|
||||
onClick={handleOnBackClick}
|
||||
/>
|
||||
{/* <Button
|
||||
className="hidden lg:flex"
|
||||
icon={<FilterIcon />}
|
||||
buttonType="primary"
|
||||
text="Filters"
|
||||
onClick={handleOnFiltersClick}
|
||||
/> */}
|
||||
<Button
|
||||
className="lg:hidden flex"
|
||||
icon={<FilterIcon />}
|
||||
buttonType="primary"
|
||||
text="Filters"
|
||||
onClick={handleOnMobileFiltersClick}
|
||||
/>
|
||||
<Button
|
||||
icon={<InfoIcon />}
|
||||
buttonType="primary"
|
||||
text="About Projects"
|
||||
onClick={handleOnAboutComplexClick}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col sm:col-start-6 col-start-8 sm:col-span-4 col-span-4 text-white text-center items-start ">
|
||||
<div
|
||||
className={`text-center duration-300 ease-in-out transition-opacity hidden sm:block ${
|
||||
isSidebar ? "opacity-0" : "opacity-100"
|
||||
}`}
|
||||
>
|
||||
<h2 className="font-semibold text-subheadline-s drop-shadow drop-shadow-[0_2px_8px_0px_rgba(62, 84, 100, 0.25)] select-none">
|
||||
ROVE Home Marasi Drive
|
||||
</h2>
|
||||
{/* <p className="text-s drop-shadow drop-shadow-[0_2px_8px_0px_rgba(62, 84, 100, 0.25)]">
|
||||
Select a wing
|
||||
</p> */}
|
||||
</div>
|
||||
</div>
|
||||
{/* <div className="flex gap-2 col-span-1 col-start-12 justify-end">
|
||||
{isFullscreen ? (
|
||||
<Button
|
||||
buttonType="fab"
|
||||
icon={<ActiveResizeIcon />}
|
||||
onClick={handleOnFullScreenClick}
|
||||
isCircleRounded
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
buttonType="fab"
|
||||
icon={<ResizeIcon />}
|
||||
onClick={handleOnFullScreenClick}
|
||||
isCircleRounded
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
buttonType="fab"
|
||||
icon={<HintIcon />}
|
||||
onClick={handleOnHelpClick}
|
||||
isCircleRounded
|
||||
/>
|
||||
</div> */}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ComplexTopPanel;
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,289 @@
|
||||
/* 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) {
|
||||
setWidth(screenWidth);
|
||||
setTop(screenWidth / 2 - screenHeight / 2);
|
||||
setLeft(0);
|
||||
} else {
|
||||
if (screenWidth > mobileWidht) {
|
||||
const _top = screenHeight / 16;
|
||||
setTop(_top);
|
||||
} else {
|
||||
const _top = screenHeight / 5.5;
|
||||
setTop(_top);
|
||||
}
|
||||
const _left = (laptopWidth - screenWidth) * 1.1;
|
||||
const _width = screenWidth + _left * 1.9;
|
||||
|
||||
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) => (
|
||||
<img
|
||||
key={index}
|
||||
width={`${width}px`}
|
||||
height={`${width}px`}
|
||||
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;
|
||||
Reference in New Issue
Block a user