modal animation

This commit is contained in:
2024-02-29 18:37:13 +05:00
parent 9067feaca6
commit 6177bbbf8d
7 changed files with 238 additions and 91 deletions
+14 -4
View File
@@ -1,14 +1,24 @@
import { Outlet } from "react-router-dom";
import useStore from "../store/store";
import { FullScreen, useFullScreenHandle } from "react-full-screen";
import { useEffect } from "react";
import { ModalContainer } from "./ModalContainer";
const Layout = () => {
const { modal, loader } = useStore();
const { loader, setOnFullscreen } = useStore();
const onFullscreenHandle = useFullScreenHandle();
useEffect(() => {
setOnFullscreen(onFullscreenHandle);
}, []);
return (
<>
{modal}
<FullScreen handle={onFullscreenHandle}>
{/* {modal} */}
{loader}
<Outlet />
</>
<ModalContainer />
</FullScreen>
);
};
+37
View File
@@ -0,0 +1,37 @@
import { useEffect } from "react";
import useStore from "../store/store";
function ModalContainer() {
const { modal, modalAnimation, setModal, setModalAnimation } = useStore();
useEffect(() => {
if (!modal) return;
setModalAnimation(1);
}, [modal]);
useEffect(() => {
if (modalAnimation !== 0) return;
const timeout = setTimeout(() => {
setModal(null);
}, 300);
return clearTimeout(timeout);
}, [modalAnimation]);
if (modal) {
return (
<div
className="fixed top-0 left-0 w-full h-full transition-all duration-300"
style={{
transform: `translateX(${1}*100)vw, translateY(${0}*100)vh`,
// transform: `translateX(${modalAnimation}*100)vw, translateY(${-modalAnimation}*100)vh`,
}}
>
{modal}
</div>
);
}
}
export default ModalContainer;
@@ -13,15 +13,21 @@ import ImagesModal from "./ImagesModal";
import PedestrianIcon from "../../../icons/Pedestrianicon";
type ButtonPanelProps = {
handleFullscreen: FullScreenHandle;
handleFullscreen: FullScreenHandle | null;
};
const ButtonPanel = ({ handleFullscreen }: ButtonPanelProps) => {
const { setModal, is3DTour, currentView } = useStore();
const [setModal, is3DTour, currentView] = useStore((state) => [
state.setModal,
state.is3DTour,
state.currentView,
]);
const [isFullMode, setIsFullMode] = useState(false);
const handleOnResizeClick = () => {
if (!handleFullscreen) return;
setIsFullMode((prev) => !prev);
if (isFullMode) {
handleFullscreen.enter();
@@ -5,31 +5,73 @@ import CrossButton from "../../CrossButton";
import BackButton from "../../BackButton";
const ImagesModal = () => {
const { currentVilla, setModal } = useStore();
const { currentVilla, setModal, setModalAnimation } = useStore();
const [offset, setOffset] = useState(0);
const modalRef = useRef(null);
const buttonContainerRef = useRef(null);
useEffect(() => {
if (!modalRef) return;
gsap.fromTo(
modalRef.current,
{
height: 0,
x: "3000vw",
translateX: "100vw",
translateY: "-100vh",
},
{
height: "100vh",
x: 0,
translateX: 0,
translateY: 0,
duration: 0.3,
}
);
if (!buttonContainerRef) return;
gsap.fromTo(
buttonContainerRef.current,
{
opacity: 0,
},
{
opacity: 1,
duration: 0.3,
}
// { x: "-200vw", y: "-200vh" },
// { y: "300vh" },
// { y: "0", duration: 0.5 }
);
}, []);
function handleOnClose(): void {
setModal(null);
setModalAnimation(0);
const onAnimationComplete = () => {
setModal(null);
const timeout = setTimeout(() => {
clearTimeout(timeout);
}, 200);
};
// if (!modalRef) return;
// gsap.fromTo(
// modalRef.current,
// {
// translateX: 0,
// translateY: 0,
// },
// {
// translateX: "100vw",
// translateY: "-100vh",
// duration: 0.3,
// onComplete: onAnimationComplete,
// }
// );
// gsap.fromTo(
// buttonContainerRef.current,
// {
// opacity: 1,
// },
// {
// opacity: 0,
// duration: 0.3,
// }
// );
}
function handleOnRightClick(): void {
@@ -48,40 +90,40 @@ const ImagesModal = () => {
}
return (
<div className="absolute max-h-screen z-20 flex overflow-x-hidden w-screen">
<div
className="flex transition-all duration-300 bg-white h-0"
ref={modalRef}
style={{
// transform: `translateX(100vw)`,
// transform: `translateY(-90vh)`,
transform: `translateX(${offset * 100}vw)`,
}}
>
{currentVilla &&
currentVilla.perspectiveWorkings.map((image) => (
<div className="w-screen h-screen">
<img
alt=""
key={image.id}
src={image.image}
className="object-cover w-screen h-screen"
/>
</div>
))}
<>
<div className="flex overflow-x-hidden w-screen">
<div
className="flex transition-all duration-300 bg-white relative left-0 top-0"
style={{
transform: `translateX(${offset * 100}vw)`,
}}
>
{currentVilla &&
currentVilla.perspectiveWorkings.map((image) => (
<div key={image.id} className="w-screen h-screen bg-white">
<img
alt=""
src={image.image}
className="object-cover w-screen h-screen"
/>
</div>
))}
</div>
</div>
<div className="absolute right-8 top-8">
<CrossButton onClick={handleOnClose} />
<div ref={buttonContainerRef}>
<div className="absolute right-8 top-8">
<CrossButton onClick={handleOnClose} />
</div>
<BackButton
className="pl-[6px] pr-[6px] w-[40px] h-10 absolute left-8 top-[50vh]"
onClick={handleOnLeftClick}
/>
<BackButton
className="pl-[6px] pr-[6px] w-[40px] h-10 rotate absolute right-8 top-[50vh] rotate-180"
onClick={handleOnRightClick}
/>
</div>
<BackButton
className="pl-[6px] pr-[6px] w-[40px] h-10 absolute left-8 top-[50vh]"
onClick={handleOnLeftClick}
/>
<BackButton
className="pl-[6px] pr-[6px] w-[40px] h-10 rotate absolute right-8 top-[50vh] rotate-180"
onClick={handleOnRightClick}
/>
</div>
</>
);
};
@@ -1,4 +1,5 @@
import { useEffect, useState } from "react";
import { useEffect, useState, useRef } from "react";
import gsap from "gsap";
import useStore from "../../../store/store";
import UnitList from "../../UnitList";
import LayoutSlider from "../../mobile/Appartment/LayoutSlider";
@@ -11,6 +12,25 @@ const LayoutModal = () => {
const [currentUnits, setCurrentUnits] = useState(
currentVilla?.groundFloorUnits
);
const modalRef = useRef(null);
useEffect(() => {
if (!modalRef) return;
gsap.fromTo(
modalRef.current,
{
left: "100vw",
bottom: "100vh",
},
{
left: 0,
bottom: 0,
duration: 0.3,
}
);
}, []);
useEffect(() => {
if (currentVilla) {
switch (currentView) {
@@ -28,31 +48,59 @@ const LayoutModal = () => {
}, [currentView, currentVilla]);
const handleOnCloseClick = () => {
setModal(null);
const onAnimationComplete = () => {
const timeout = setTimeout(() => {
setModal(null);
clearTimeout(timeout);
}, 200);
};
gsap.fromTo(
modalRef.current,
{
left: 0,
bottom: 0,
},
{
left: "100vw",
bottom: "100vh",
duration: 0.5,
onComplete: onAnimationComplete,
}
);
};
return (
<div className="min-w-screen w-screen min-h-[100vh] bg-white absolute z-10 flex flex-col">
<div className="flex justify-between pt-8 pr-8">
// <div className="min-w-screen w-screen z-20 flex flex-col absolute">
<>
<div
className="relative left-[100vw] bottom-[100vh] bg-white h-screen "
ref={modalRef}
>
<div className="flex">
<div className="min-w-[440px] overflow-y-clip flex flex-col justify-center">
{currentUnits && <UnitList units={currentUnits} />}
</div>
<div className="w-full flex flex-col p-8">
<LayoutSlider
currentView={currentView}
setCurrentView={setCurrentView}
/>
<LayoutToggle
className="max-w-[340px] mx-auto"
currentView={currentView}
setCurrentView={setCurrentView}
/>
</div>
</div>
</div>
<div className="flex justify-between pt-8 pr-8 absolute z-20 left-0 top-0 w-full">
<div />
<CrossButton onClick={handleOnCloseClick} />
</div>
<div className="flex">
<div className="min-w-[440px] overflow-y-clip flex flex-col justify-center">
{currentUnits && <UnitList units={currentUnits} />}
</div>
<div className="w-full flex flex-col p-8">
<LayoutSlider
currentView={currentView}
setCurrentView={setCurrentView}
/>
<LayoutToggle
className="max-w-[340px] mx-auto"
currentView={currentView}
setCurrentView={setCurrentView}
/>
</div>
</div>
</div>
{/* </div>
<></> */}
</>
);
};
+17 -21
View File
@@ -2,7 +2,6 @@
import { Unity } from "react-unity-webgl";
import { useEffect, useState } from "react";
import { ReactUnityEventParameter } from "react-unity-webgl/distribution/types/react-unity-event-parameters";
import { FullScreen, useFullScreenHandle } from "react-full-screen";
import useStore from "../../store/store";
import LoaderModal from "../../components/LoaderModal";
import ButtonPanel from "../../components/desktop/Appartment/ButtonPanel";
@@ -12,8 +11,13 @@ import ViewToggle from "../../components/ViewToggle";
import ParameterDescription from "../../components/desktop/Appartment/ParameterDescription";
const DesktopApartmentPage = () => {
const { setCurrentView, setSendMessageToUnity, setLoader, setIs3DTour } =
useStore();
const {
setCurrentView,
setSendMessageToUnity,
setLoader,
setIs3DTour,
onFullscreen,
} = useStore();
const { villa } = useVilla();
@@ -66,27 +70,19 @@ const DesktopApartmentPage = () => {
}
}, [isContainerLoaded]);
const onFullscreenHandle = useFullScreenHandle();
return (
<>
<FullScreen handle={onFullscreenHandle}>
<div className={`relative transition-all duration-300 max-h-screen`}>
<div
className={`relative h-[calc(100vh-98px)]
`}
>
<ButtonPanel handleFullscreen={onFullscreenHandle} />
<Unity
unityProvider={unityProvider}
style={{ width: "100%", height: "100%" }}
/>
<ViewToggle isDesktop />
</div>
<ParameterDescription />
<div className={`relative transition-all duration-300 max-h-screen`}>
<div className={`relative h-[calc(100vh-98px)]`}>
<ButtonPanel handleFullscreen={onFullscreen} />
<Unity
unityProvider={unityProvider}
style={{ width: "100%", height: "100%" }}
/>
<ViewToggle isDesktop />
</div>
</FullScreen>
<ParameterDescription />
</div>
</>
);
};
+10 -2
View File
@@ -2,9 +2,12 @@ import { create } from "zustand";
import { ReactUnityEventParameter } from "react-unity-webgl/distribution/types/react-unity-event-parameters";
import { Villa } from "../types/appartment";
import { VILLAS } from "../consts/villas";
import { FullScreenHandle } from "react-full-screen";
interface StoreType {
currentVilla: Villa | null;
modalAnimation: number;
onFullscreen: FullScreenHandle | null;
selectedOnMapVilla: Villa | null;
loader: React.ReactNode | null;
panel: React.ReactNode | null;
@@ -19,6 +22,9 @@ interface StoreType {
currentView: number;
is3DTour: boolean;
setModalAnimation: (animation: number) => void;
setOnFullscreen: (onFullscreen: FullScreenHandle) => void;
setIs3DTour: (is3D: boolean) => void;
setCurrentVilla: (villa: Villa) => void;
setSelectedOnMapVilla: (villa: Villa | null) => void;
@@ -36,7 +42,8 @@ interface StoreType {
}
const useStore = create<StoreType>((set) => ({
// currentVilla: null,
modalAnimation: 0,
onFullscreen: null,
currentVilla: VILLAS[0],
is3DTour: false,
selectedOnMapVilla: null,
@@ -46,7 +53,8 @@ const useStore = create<StoreType>((set) => ({
loader: null,
currentView: 1,
setModalAnimation: (anim) => set(() => ({modalAnimation: anim})),
setOnFullscreen: (onFullscreen) => set(() => ({onFullscreen: onFullscreen})),
setIs3DTour: (is3DTour) => set(() => ({ is3DTour: is3DTour })),
setSelectedOnMapVilla: (villa) => set(() => ({ selectedOnMapVilla: villa })),
setCurrentVilla: (villa) => set(() => ({ currentVilla: villa })),