modal animation
This commit is contained in:
@@ -1,14 +1,24 @@
|
|||||||
import { Outlet } from "react-router-dom";
|
import { Outlet } from "react-router-dom";
|
||||||
import useStore from "../store/store";
|
import useStore from "../store/store";
|
||||||
|
import { FullScreen, useFullScreenHandle } from "react-full-screen";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { ModalContainer } from "./ModalContainer";
|
||||||
|
|
||||||
const Layout = () => {
|
const Layout = () => {
|
||||||
const { modal, loader } = useStore();
|
const { loader, setOnFullscreen } = useStore();
|
||||||
|
const onFullscreenHandle = useFullScreenHandle();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setOnFullscreen(onFullscreenHandle);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<FullScreen handle={onFullscreenHandle}>
|
||||||
{modal}
|
{/* {modal} */}
|
||||||
{loader}
|
{loader}
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</>
|
<ModalContainer />
|
||||||
|
</FullScreen>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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";
|
import PedestrianIcon from "../../../icons/Pedestrianicon";
|
||||||
|
|
||||||
type ButtonPanelProps = {
|
type ButtonPanelProps = {
|
||||||
handleFullscreen: FullScreenHandle;
|
handleFullscreen: FullScreenHandle | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ButtonPanel = ({ handleFullscreen }: ButtonPanelProps) => {
|
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 [isFullMode, setIsFullMode] = useState(false);
|
||||||
|
|
||||||
const handleOnResizeClick = () => {
|
const handleOnResizeClick = () => {
|
||||||
|
if (!handleFullscreen) return;
|
||||||
|
|
||||||
setIsFullMode((prev) => !prev);
|
setIsFullMode((prev) => !prev);
|
||||||
if (isFullMode) {
|
if (isFullMode) {
|
||||||
handleFullscreen.enter();
|
handleFullscreen.enter();
|
||||||
|
|||||||
@@ -5,31 +5,73 @@ import CrossButton from "../../CrossButton";
|
|||||||
import BackButton from "../../BackButton";
|
import BackButton from "../../BackButton";
|
||||||
|
|
||||||
const ImagesModal = () => {
|
const ImagesModal = () => {
|
||||||
const { currentVilla, setModal } = useStore();
|
const { currentVilla, setModal, setModalAnimation } = useStore();
|
||||||
const [offset, setOffset] = useState(0);
|
const [offset, setOffset] = useState(0);
|
||||||
const modalRef = useRef(null);
|
const modalRef = useRef(null);
|
||||||
|
const buttonContainerRef = useRef(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!modalRef) return;
|
if (!modalRef) return;
|
||||||
gsap.fromTo(
|
gsap.fromTo(
|
||||||
modalRef.current,
|
modalRef.current,
|
||||||
{
|
{
|
||||||
height: 0,
|
translateX: "100vw",
|
||||||
x: "3000vw",
|
translateY: "-100vh",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
height: "100vh",
|
translateX: 0,
|
||||||
x: 0,
|
translateY: 0,
|
||||||
|
duration: 0.3,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!buttonContainerRef) return;
|
||||||
|
gsap.fromTo(
|
||||||
|
buttonContainerRef.current,
|
||||||
|
{
|
||||||
|
opacity: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
opacity: 1,
|
||||||
duration: 0.3,
|
duration: 0.3,
|
||||||
}
|
}
|
||||||
// { x: "-200vw", y: "-200vh" },
|
|
||||||
// { y: "300vh" },
|
|
||||||
// { y: "0", duration: 0.5 }
|
|
||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
function handleOnClose(): void {
|
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 {
|
function handleOnRightClick(): void {
|
||||||
@@ -48,40 +90,40 @@ const ImagesModal = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="absolute max-h-screen z-20 flex overflow-x-hidden w-screen">
|
<>
|
||||||
<div
|
<div className="flex overflow-x-hidden w-screen">
|
||||||
className="flex transition-all duration-300 bg-white h-0"
|
<div
|
||||||
ref={modalRef}
|
className="flex transition-all duration-300 bg-white relative left-0 top-0"
|
||||||
style={{
|
style={{
|
||||||
// transform: `translateX(100vw)`,
|
transform: `translateX(${offset * 100}vw)`,
|
||||||
// transform: `translateY(-90vh)`,
|
}}
|
||||||
transform: `translateX(${offset * 100}vw)`,
|
>
|
||||||
}}
|
{currentVilla &&
|
||||||
>
|
currentVilla.perspectiveWorkings.map((image) => (
|
||||||
{currentVilla &&
|
<div key={image.id} className="w-screen h-screen bg-white">
|
||||||
currentVilla.perspectiveWorkings.map((image) => (
|
<img
|
||||||
<div className="w-screen h-screen">
|
alt=""
|
||||||
<img
|
src={image.image}
|
||||||
alt=""
|
className="object-cover w-screen h-screen"
|
||||||
key={image.id}
|
/>
|
||||||
src={image.image}
|
</div>
|
||||||
className="object-cover w-screen h-screen"
|
))}
|
||||||
/>
|
</div>
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute right-8 top-8">
|
<div ref={buttonContainerRef}>
|
||||||
<CrossButton onClick={handleOnClose} />
|
<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>
|
</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 useStore from "../../../store/store";
|
||||||
import UnitList from "../../UnitList";
|
import UnitList from "../../UnitList";
|
||||||
import LayoutSlider from "../../mobile/Appartment/LayoutSlider";
|
import LayoutSlider from "../../mobile/Appartment/LayoutSlider";
|
||||||
@@ -11,6 +12,25 @@ const LayoutModal = () => {
|
|||||||
const [currentUnits, setCurrentUnits] = useState(
|
const [currentUnits, setCurrentUnits] = useState(
|
||||||
currentVilla?.groundFloorUnits
|
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(() => {
|
useEffect(() => {
|
||||||
if (currentVilla) {
|
if (currentVilla) {
|
||||||
switch (currentView) {
|
switch (currentView) {
|
||||||
@@ -28,31 +48,59 @@ const LayoutModal = () => {
|
|||||||
}, [currentView, currentVilla]);
|
}, [currentView, currentVilla]);
|
||||||
|
|
||||||
const handleOnCloseClick = () => {
|
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 (
|
return (
|
||||||
<div className="min-w-screen w-screen min-h-[100vh] bg-white absolute z-10 flex flex-col">
|
// <div className="min-w-screen w-screen z-20 flex flex-col absolute">
|
||||||
<div className="flex justify-between pt-8 pr-8">
|
<>
|
||||||
|
<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 />
|
<div />
|
||||||
<CrossButton onClick={handleOnCloseClick} />
|
<CrossButton onClick={handleOnCloseClick} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex">
|
{/* </div>
|
||||||
<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>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import { Unity } from "react-unity-webgl";
|
import { Unity } from "react-unity-webgl";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { ReactUnityEventParameter } from "react-unity-webgl/distribution/types/react-unity-event-parameters";
|
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 useStore from "../../store/store";
|
||||||
import LoaderModal from "../../components/LoaderModal";
|
import LoaderModal from "../../components/LoaderModal";
|
||||||
import ButtonPanel from "../../components/desktop/Appartment/ButtonPanel";
|
import ButtonPanel from "../../components/desktop/Appartment/ButtonPanel";
|
||||||
@@ -12,8 +11,13 @@ import ViewToggle from "../../components/ViewToggle";
|
|||||||
import ParameterDescription from "../../components/desktop/Appartment/ParameterDescription";
|
import ParameterDescription from "../../components/desktop/Appartment/ParameterDescription";
|
||||||
|
|
||||||
const DesktopApartmentPage = () => {
|
const DesktopApartmentPage = () => {
|
||||||
const { setCurrentView, setSendMessageToUnity, setLoader, setIs3DTour } =
|
const {
|
||||||
useStore();
|
setCurrentView,
|
||||||
|
setSendMessageToUnity,
|
||||||
|
setLoader,
|
||||||
|
setIs3DTour,
|
||||||
|
onFullscreen,
|
||||||
|
} = useStore();
|
||||||
|
|
||||||
const { villa } = useVilla();
|
const { villa } = useVilla();
|
||||||
|
|
||||||
@@ -66,27 +70,19 @@ const DesktopApartmentPage = () => {
|
|||||||
}
|
}
|
||||||
}, [isContainerLoaded]);
|
}, [isContainerLoaded]);
|
||||||
|
|
||||||
const onFullscreenHandle = useFullScreenHandle();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FullScreen handle={onFullscreenHandle}>
|
<div className={`relative transition-all duration-300 max-h-screen`}>
|
||||||
<div className={`relative transition-all duration-300 max-h-screen`}>
|
<div className={`relative h-[calc(100vh-98px)]`}>
|
||||||
<div
|
<ButtonPanel handleFullscreen={onFullscreen} />
|
||||||
className={`relative h-[calc(100vh-98px)]
|
<Unity
|
||||||
|
unityProvider={unityProvider}
|
||||||
`}
|
style={{ width: "100%", height: "100%" }}
|
||||||
>
|
/>
|
||||||
<ButtonPanel handleFullscreen={onFullscreenHandle} />
|
<ViewToggle isDesktop />
|
||||||
<Unity
|
|
||||||
unityProvider={unityProvider}
|
|
||||||
style={{ width: "100%", height: "100%" }}
|
|
||||||
/>
|
|
||||||
<ViewToggle isDesktop />
|
|
||||||
</div>
|
|
||||||
<ParameterDescription />
|
|
||||||
</div>
|
</div>
|
||||||
</FullScreen>
|
<ParameterDescription />
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+10
-2
@@ -2,9 +2,12 @@ import { create } from "zustand";
|
|||||||
import { ReactUnityEventParameter } from "react-unity-webgl/distribution/types/react-unity-event-parameters";
|
import { ReactUnityEventParameter } from "react-unity-webgl/distribution/types/react-unity-event-parameters";
|
||||||
import { Villa } from "../types/appartment";
|
import { Villa } from "../types/appartment";
|
||||||
import { VILLAS } from "../consts/villas";
|
import { VILLAS } from "../consts/villas";
|
||||||
|
import { FullScreenHandle } from "react-full-screen";
|
||||||
|
|
||||||
interface StoreType {
|
interface StoreType {
|
||||||
currentVilla: Villa | null;
|
currentVilla: Villa | null;
|
||||||
|
modalAnimation: number;
|
||||||
|
onFullscreen: FullScreenHandle | null;
|
||||||
selectedOnMapVilla: Villa | null;
|
selectedOnMapVilla: Villa | null;
|
||||||
loader: React.ReactNode | null;
|
loader: React.ReactNode | null;
|
||||||
panel: React.ReactNode | null;
|
panel: React.ReactNode | null;
|
||||||
@@ -19,6 +22,9 @@ interface StoreType {
|
|||||||
currentView: number;
|
currentView: number;
|
||||||
is3DTour: boolean;
|
is3DTour: boolean;
|
||||||
|
|
||||||
|
|
||||||
|
setModalAnimation: (animation: number) => void;
|
||||||
|
setOnFullscreen: (onFullscreen: FullScreenHandle) => void;
|
||||||
setIs3DTour: (is3D: boolean) => void;
|
setIs3DTour: (is3D: boolean) => void;
|
||||||
setCurrentVilla: (villa: Villa) => void;
|
setCurrentVilla: (villa: Villa) => void;
|
||||||
setSelectedOnMapVilla: (villa: Villa | null) => void;
|
setSelectedOnMapVilla: (villa: Villa | null) => void;
|
||||||
@@ -36,7 +42,8 @@ interface StoreType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const useStore = create<StoreType>((set) => ({
|
const useStore = create<StoreType>((set) => ({
|
||||||
// currentVilla: null,
|
modalAnimation: 0,
|
||||||
|
onFullscreen: null,
|
||||||
currentVilla: VILLAS[0],
|
currentVilla: VILLAS[0],
|
||||||
is3DTour: false,
|
is3DTour: false,
|
||||||
selectedOnMapVilla: null,
|
selectedOnMapVilla: null,
|
||||||
@@ -46,7 +53,8 @@ const useStore = create<StoreType>((set) => ({
|
|||||||
loader: null,
|
loader: null,
|
||||||
currentView: 1,
|
currentView: 1,
|
||||||
|
|
||||||
|
setModalAnimation: (anim) => set(() => ({modalAnimation: anim})),
|
||||||
|
setOnFullscreen: (onFullscreen) => set(() => ({onFullscreen: onFullscreen})),
|
||||||
setIs3DTour: (is3DTour) => set(() => ({ is3DTour: is3DTour })),
|
setIs3DTour: (is3DTour) => set(() => ({ is3DTour: is3DTour })),
|
||||||
setSelectedOnMapVilla: (villa) => set(() => ({ selectedOnMapVilla: villa })),
|
setSelectedOnMapVilla: (villa) => set(() => ({ selectedOnMapVilla: villa })),
|
||||||
setCurrentVilla: (villa) => set(() => ({ currentVilla: villa })),
|
setCurrentVilla: (villa) => set(() => ({ currentVilla: villa })),
|
||||||
|
|||||||
Reference in New Issue
Block a user