modal animation
This commit is contained in:
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
<></> */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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
@@ -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 })),
|
||||
|
||||
Reference in New Issue
Block a user