points in virtual-tour

This commit is contained in:
2024-05-28 17:25:50 +05:00
parent 9a489d64af
commit 41bfe23447
10 changed files with 318 additions and 146 deletions
+28
View File
@@ -0,0 +1,28 @@
const WalkHereIcon = () => {
return (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="12" cy="3.5" r="2" stroke="currentColor" strokeWidth="1.5" />
<path
d="M18.3547 15.1941C19.5878 15.8755 20.3278 16.6907 20.6118 17.4798C20.8826 18.2321 20.7636 19.0262 20.1767 19.8093C19.5719 20.6162 18.4956 21.3661 17.0181 21.9116C15.549 22.4541 13.8015 22.75 12 22.75C10.1985 22.75 8.45102 22.4541 6.98188 21.9116C5.50435 21.3661 4.4281 20.6162 3.82328 19.8093C3.23636 19.0262 3.11743 18.2321 3.38822 17.4798C3.67223 16.6907 4.41219 15.8755 5.64525 15.1941"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M9.5 7.75H14.5C15.1904 7.75 15.75 8.30964 15.75 9V13.8C15.75 13.8828 15.6828 13.95 15.6 13.95C15.0615 13.95 14.625 14.3865 14.625 14.925V18.75H12H9.375V14.925C9.375 14.3865 8.93848 13.95 8.4 13.95C8.31716 13.95 8.25 13.8828 8.25 13.8V9C8.25 8.30964 8.80964 7.75 9.5 7.75Z"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
</svg>
);
};
export default WalkHereIcon;
@@ -0,0 +1,40 @@
import { Html } from "@react-three/drei";
import { IAppartmentSphere, ISphereLink } from "../../types/apartmentSphere";
import WalkHereIcon from "../icons/WalkHereIcon";
import useSphere from "../../store/useSphere";
interface LaberlMarkerProps {
sphereLink: ISphereLink;
apartment: IAppartmentSphere;
}
const LabelMarker = ({ sphereLink, apartment }: LaberlMarkerProps) => {
const { setSelectedSphere } = useSphere();
const handleOnClick = () => {
const moveToShpere = apartment.spheres.find(
(sph) => sph.id === sphereLink.id
);
if (moveToShpere) {
setSelectedSphere(moveToShpere);
}
};
return (
<>
{
<Html position={sphereLink.labelPosition} center>
<div
className="bg-white w-9 h-9 rounded-full text-[#00BED7] flex items-center justify-center cursor-pointer"
onMouseEnter={() => console.log(sphereLink.id)}
onClick={handleOnClick}
>
<WalkHereIcon />
</div>
</Html>
}
</>
);
};
export default LabelMarker;
+3 -3
View File
@@ -1,13 +1,13 @@
import { Sphere, useTexture } from "@react-three/drei";
import { useEffect, useRef } from "react";
import { BackSide, MeshBasicMaterial } from "three";
import { IAppartmentSphere } from "../../types/apartmentSphere";
import { ISphere } from "../../types/apartmentSphere";
import gsap from "gsap";
// import gsap from "gsap";
interface SphereTourProps {
sphere: IAppartmentSphere;
selectedSphere: IAppartmentSphere;
sphere: ISphere;
selectedSphere: ISphere;
}
const SphereTour = ({ sphere, selectedSphere }: SphereTourProps) => {
@@ -1,32 +1,32 @@
import { OrbitControls, Html, useTexture } from "@react-three/drei";
import { Suspense, useEffect, useRef, useState } from "react";
import { OrbitControls, Html } from "@react-three/drei";
import { Suspense, useEffect, useRef } from "react";
import { OrbitControls as OrbitControlsImpl } from "three-stdlib";
import { useParams } from "react-router-dom";
import useCompass from "../../store/useCompass";
import SphereTour from "./SphereTour";
import { spheres } from "../../consts/spheres";
import { IAppartmentSphere } from "../../types/apartmentSphere";
import useSphere from "../../store/useSphere";
import LabelMarker from "./LabelMarker";
// import { spheres } from "../../consts/spheres";
const VirtualTourWrapper = () => {
interface VirtualTourWrapperProps {
appartment: IAppartmentSphere;
}
const VirtualTourWrapper = ({ appartment }: VirtualTourWrapperProps) => {
const orbitRef = useRef<OrbitControlsImpl>(null);
const { setCurrentCompassRotate, currentCompassRotate } = useCompass();
const { id } = useParams();
const [startRotatingPos, setStartRotatingPos] = useState(0);
const { setCurrentCompassRotate } = useCompass();
const { selectedSphere, setSelectedSphere } = useSphere();
// const [selectedSphere, setSelectedSphere] = useState<IAppartmentSphere>(
// spheres[1]
// );
useEffect(() => {
setSelectedSphere(spheres[0]);
setSelectedSphere(appartment.spheres[0]);
}, []);
const handleOnRotating = () => {
const radian = orbitRef.current?.getAzimuthalAngle();
if (radian) {
const currentCompasDegrees = (radian * 180) / Math.PI + 180;
const compassOffsetDegres = startRotatingPos - currentCompasDegrees;
setCurrentCompassRotate(currentCompassRotate + compassOffsetDegres);
setCurrentCompassRotate(currentCompasDegrees);
}
};
@@ -38,12 +38,23 @@ const VirtualTourWrapper = () => {
</Html>
}
>
{spheres.map((sphere) => (
<SphereTour
sphere={sphere}
selectedSphere={selectedSphere || spheres[0]}
/>
))}
{appartment.spheres.map((sphere) => {
const isLabelContains =
selectedSphere && sphere.id === selectedSphere.id;
return (
<>
{isLabelContains &&
sphere.links.map((sphereLink) => (
<LabelMarker sphereLink={sphereLink} apartment={appartment} />
))}
<SphereTour
sphere={sphere}
selectedSphere={selectedSphere || appartment.spheres[0]}
/>
</>
);
})}
<OrbitControls
ref={orbitRef}
maxDistance={0.001}
@@ -53,7 +64,7 @@ const VirtualTourWrapper = () => {
// onStart={(e) => console.log("e", e)}
// onChange={(e) => console.log("e", e?.target)}
onChange={handleOnRotating}
target={[-14.16, 0, 24.11]}
target={appartment.spheres[0].position}
/>
</Suspense>
);
-41
View File
@@ -1,41 +0,0 @@
import { IAppartmentSphere } from "../types/apartmentSphere";
const spheres: IAppartmentSphere[] = [
{
id: "1",
sphereImage: "/images/virtual-tour/studio1/Studio1_w-12_13_sp-01.webp",
roomType: "room 1",
},
{
id: "2",
sphereImage: "/images/virtual-tour/studio1/Studio1_w-12_13_sp-02.webp",
roomType: "room 2",
},
{
id: "3",
sphereImage: "/images/virtual-tour/studio1/Studio1_w-12_13_sp-03.webp",
roomType: "room 3",
},
{
id: "4",
sphereImage: "/images/virtual-tour/studio1/Studio1_w-12_13_sp-04.webp",
roomType: "room 4",
},
{
id: "5",
sphereImage: "/images/virtual-tour/studio1/Studio1_w-12_13_sp-05.webp",
roomType: "room 5",
},
{
id: "6",
sphereImage: "/images/virtual-tour/studio1/Studio1_w-12_13_sp-06.webp",
roomType: "room 6",
},
{
id: "7",
sphereImage: "/images/virtual-tour/studio1/Studio1_w-12_13_sp-07.webp",
roomType: "room 7",
},
];
export { spheres };
+123
View File
@@ -0,0 +1,123 @@
[
{
"id": "appartments-studio-1",
"spheres": [
{
"id": "studio-1_sp-01",
"sphereImage": "/images/virtual-tour/studio1/Studio1_w-12_13_sp-01.webp",
"roomType": "room 1",
"position": [-14.16, 0, 24.11],
"links": [
{
"id": "studio-1_sp-02",
"type": "default",
"labelPosition": [-15.16, 0, 44.11]
},
{
"id": "studio-1_sp-03",
"type": "default",
"labelPosition": [-0.16, 0, 24.11]
}
]
},
{
"id": "studio-1_sp-02",
"sphereImage": "/images/virtual-tour/studio1/Studio1_w-12_13_sp-02.webp",
"roomType": "room 2",
"position": [-14.16, 0, 24.11],
"links": [
{
"id": "studio-1_sp-01",
"type": "default",
"labelPosition": [-12.16, 0, 5.11]
}
]
},
{
"id": "studio-1_sp-03",
"sphereImage": "/images/virtual-tour/studio1/Studio1_w-12_13_sp-03.webp",
"roomType": "room 3",
"position": [-14.16, 0, 24.11],
"links": [
{
"id": "studio-1_sp-04",
"type": "default",
"labelPosition": [-0.16, 0, 24.11]
},
{
"id": "studio-1_sp-01",
"type": "default",
"labelPosition": [-45, 0, 24.11]
}
]
},
{
"id": "studio-1_sp-04",
"sphereImage": "/images/virtual-tour/studio1/Studio1_w-12_13_sp-04.webp",
"roomType": "room 4",
"position": [-14.16, 0, 24.11],
"links": [
{
"id": "studio-1_sp-05",
"type": "default",
"labelPosition": [-0.16, 0, 21.11]
},
{
"id": "studio-1_sp-03",
"type": "default",
"labelPosition": [-45, 0, 24.11]
}
]
},
{
"id": "studio-1_sp-05",
"sphereImage": "/images/virtual-tour/studio1/Studio1_w-12_13_sp-05.webp",
"roomType": "room 5",
"position": [-14.16, 0, 24.11],
"links": [
{
"id": "studio-1_sp-06",
"type": "default",
"labelPosition": [-0.16, 0, 24.11]
},
{
"id": "studio-1_sp-04",
"type": "default",
"labelPosition": [-45, 0, 27.11]
}
]
},
{
"id": "studio-1_sp-06",
"sphereImage": "/images/virtual-tour/studio1/Studio1_w-12_13_sp-06.webp",
"roomType": "room 6",
"position": [-14.16, 0, 24.11],
"links": [
{
"id": "studio-1_sp-07",
"type": "default",
"labelPosition": [-0.16, 0, 40.11]
},
{
"id": "studio-1_sp-05",
"type": "default",
"labelPosition": [-45, 0, 27.11]
}
]
},
{
"id": "studio-1_sp-07",
"sphereImage": "/images/virtual-tour/studio1/Studio1_w-12_13_sp-07.webp",
"roomType": "room 7",
"position": [-14.16, 0, 24.11],
"links": [
{
"id": "studio-1_sp-06",
"type": "default",
"labelPosition": [-45, 0, 5.11]
}
]
}
]
}
]
+40 -60
View File
@@ -1,77 +1,57 @@
[
{
"id": "studio-1-sp-01",
"src": "/images/virtual-tour/studio1/Studio1_w-12_13_sp-01.webp",
"position": [-23.12, 0, -13.4],
"mapPosition": [97.61, 281.92],
"links": [
{ "toId": "Dvor_16", "label": "" },
{ "toId": "Dvor_17", "label": "Детская площадка" }
]
},
{
"id": "studio-1-sp-02",
"src": "/images/virtual-tour/studio1/Studio1_w-12_13_sp-02.webp",
"id": "studio-1_sp-01",
"sphereImage": "/images/virtual-tour/studio1/Studio1_w-12_13_sp-01.webp",
"roomType": "room 1",
"position": [-14.16, 0, 24.11],
"mapPosition": [39.57, 347.47],
"links": [
{ "toId": "Dvor_16", "label": "" },
{ "toId": "Dvor_2", "label": "" },
{ "toId": "Hall_1", "label": "Лобби" }
{
"id": "studio-1_sp-02",
"type": "default",
"labelPosition": [-15.16, 0, 24.11]
}
]
},
{
"id": "studio-1-sp-03",
"src": "/images/virtual-tour/studio1/Studio1_w-12_13_sp-03.webp",
"position": [-22.27, 0, 19.69],
"mapPosition": [36.26, 327.64],
"links": [
{ "toId": "Dvor_1", "label": "" },
{ "toId": "Dvor_14", "label": "" }
]
"id": "studio-1_sp-02",
"sphereImage": "/images/virtual-tour/studio1/Studio1_w-12_13_sp-02.webp",
"roomType": "room 2",
"position": [-14.16, 0, 24.11],
"links": []
},
{
"id": "studio-1-sp-04",
"src": "/images/virtual-tour/studio1/Studio1_w-12_13_sp-04.webp",
"position": [-52.03, 0, -3.63],
"mapPosition": [41.11, 241.29],
"links": [
{ "toId": "Dvor_15", "label": "" },
{ "toId": "Dvor_4", "label": "" },
{ "toId": "Dvor_9", "label": "Детская площадка" }
]
"id": "studio-1_sp-03",
"sphereImage": "/images/virtual-tour/studio1/Studio1_w-12_13_sp-03.webp",
"roomType": "room 3",
"position": [-14.16, 0, 24.11],
"links": []
},
{
"id": "studio-1-sp-05",
"src": "/images/virtual-tour/studio1/Studio1_w-12_13_sp-05.webp",
"position": [-50.6, 0, -15.06],
"mapPosition": [64.39, 228.91],
"links": [
{ "toId": "Dvor_9", "label": "Детская площадка" },
{ "toId": "Dvor_5", "label": "" },
{ "toId": "Dvor_3", "label": "" },
{ "toId": "Dvor_6", "label": "Детская площадка" }
]
"id": "studio-1_sp-04",
"sphereImage": "/images/virtual-tour/studio1/Studio1_w-12_13_sp-04.webp",
"roomType": "room 4",
"position": [-14.16, 0, 24.11],
"links": []
},
{
"id": "studio-1-sp-06",
"src": "/images/virtual-tour/studio1/Studio1_w-12_13_sp-06.webp",
"position": [-34.36, 0, -4.72],
"mapPosition": [66.41, 273.23],
"links": [
{ "toId": "Dvor_4", "label": "" },
{ "toId": "Dvor_17", "label": "Детская площадка" }
]
"id": "studio-1_sp-05",
"sphereImage": "/images/virtual-tour/studio1/Studio1_w-12_13_sp-05.webp",
"roomType": "room 5",
"position": [-14.16, 0, 24.11],
"links": []
},
{
"id": "studio-1-sp-07",
"src": "/images/virtual-tour/studio1/Studio1_w-12_13_sp-07.webp",
"position": [-40.94, 0, -23.93],
"mapPosition": [93.88, 235.03],
"links": [
{ "toId": "Dvor_17", "label": "Детская площадка" },
{ "toId": "Dvor_7", "label": "" },
{ "toId": "Dvor_4", "label": "" }
]
"id": "studio-1_sp-06",
"sphereImage": "/images/virtual-tour/studio1/Studio1_w-12_13_sp-06.webp",
"roomType": "room 6",
"position": [-14.16, 0, 24.11],
"links": []
},
{
"id": "studio-1_sp-07",
"sphereImage": "/images/virtual-tour/studio1/Studio1_w-12_13_sp-07.webp",
"roomType": "room 7",
"position": [-14.16, 0, 24.11],
"links": []
}
]
+34 -16
View File
@@ -1,4 +1,4 @@
import { useState } from "react";
import { useEffect, useState } from "react";
import { Canvas } from "@react-three/fiber";
import VirtualTourWrapper from "../components/virtualTour/VirtualTourWrapper";
import Button from "../components/Button";
@@ -6,22 +6,37 @@ import HeartIcon from "../components/icons/Heart";
import ChevronDownIcon from "../components/icons/ChevronDownIcon";
import ButtomPanelCompass from "../components/ButtomPanelCompass";
import BookingIcon from "../components/icons/BookingIcon";
import { spheres } from "../consts/spheres";
import { IAppartmentSphere } from "../types/apartmentSphere";
import { IAppartmentSphere, ISphere } from "../types/apartmentSphere";
import useSphere from "../store/useSphere";
import { useParams } from "react-router-dom";
import _appartment from "../data/appartment.json";
const appartments = _appartment as IAppartmentSphere[];
const VirtualTour = () => {
const [isActive, setIsActive] = useState(false);
const [currentAppartment, setCurrentAppartment] =
useState<null | IAppartmentSphere>(null);
const { setSelectedSphere } = useSphere();
const { appartmentTypeId } = useParams();
const handleOnShowClick = () => {
setIsActive((prev) => !prev);
};
const handleOnLabelClick = (sphere: IAppartmentSphere) => {
const handleOnLabelClick = (sphere: ISphere) => {
setSelectedSphere(sphere);
};
useEffect(() => {
const _currentAppartment = appartments.find(
(app) => app.id === appartmentTypeId
);
if (_currentAppartment) {
setCurrentAppartment(_currentAppartment);
}
}, [appartmentTypeId]);
return (
<div className="overflow-hidden h-screen w-screen">
<div className="absolute w-screen h-screen grid grid-cols-12 z-30 pointer-events-none">
@@ -83,17 +98,18 @@ const VirtualTour = () => {
</div>
<div className="h-14 absolute -bottom-14 left-0 w-full flex justify-between">
<div className="flex gap-1 py-2 items-start flex-wrap">
{spheres.map((sphere) => {
return (
<div
onClick={() => handleOnLabelClick(sphere)}
className="bg-[#F3F3F2] font-semibold text-[#0D1922] text-caption-s py-0.5 px-2 w-fit rounded-full cursor-pointer pointer-events-auto select-none"
key={sphere.id}
>
{sphere.roomType}
</div>
);
})}
{currentAppartment &&
currentAppartment.spheres.map((sphere) => {
return (
<div
onClick={() => handleOnLabelClick(sphere)}
className="bg-[#F3F3F2] font-semibold text-[#0D1922] text-caption-s py-0.5 px-2 w-fit rounded-full cursor-pointer pointer-events-auto select-none"
key={sphere.id}
>
{sphere.roomType}
</div>
);
})}
</div>
<div className="transition-all duration-300 bg-[#FFFFFFCC] px-4 py-3 w-fit h-12 rounded-ee-lg rounded-es-lg select-none cursor-pointer pointer-events-auto items-start border border-t-[#0D1922B2] active:border-[#00BED7] hover:bg-[#FFFFFF] text-[#0D1922B2] hover:text-[#0D1922]">
<div
@@ -128,7 +144,9 @@ const VirtualTour = () => {
</div>
</div>
<Canvas>
<VirtualTourWrapper />
{currentAppartment && (
<VirtualTourWrapper appartment={currentAppartment} />
)}
</Canvas>
<ButtomPanelCompass />
</div>
+3 -3
View File
@@ -1,9 +1,9 @@
import { create } from "zustand";
import { IAppartmentSphere } from "../types/apartmentSphere";
import { ISphere } from "../types/apartmentSphere";
interface SequenceStore {
selectedSphere: null | IAppartmentSphere;
setSelectedSphere: (sphere: null | IAppartmentSphere) => void;
selectedSphere: null | ISphere;
setSelectedSphere: (sphere: null | ISphere) => void;
}
const useSphere = create<SequenceStore>((set) => ({
+15 -2
View File
@@ -1,7 +1,20 @@
interface IAppartmentSphere {
interface ISphereLink {
id: string;
type: string;
labelPosition: [number, number, number];
}
interface ISphere {
id: string;
sphereImage: string;
roomType: string;
position: [number, number, number];
links: ISphereLink[];
}
export type { IAppartmentSphere };
interface IAppartmentSphere {
id: string;
spheres: ISphere[];
}
export type { ISphere, IAppartmentSphere, ISphereLink };