App.tsx 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290
  1. import { useState, useMemo, useCallback, useEffect } from "react";
  2. import classNames from "classnames";
  3. import {
  4. HotSpot,
  5. Krpano,
  6. ROTATE_DIRECTION,
  7. Scene,
  8. View,
  9. ZOOM_ACTION,
  10. } from "@dage/krpano";
  11. import useUrlState from "@ahooksjs/use-url-state";
  12. import { useNavigate } from "react-router";
  13. import { MouseHoldView, SceneList } from "./components";
  14. import { ISceneProps } from "./utils";
  15. import "./App.scss";
  16. enum SCENE_NAME {
  17. HOME = "1",
  18. SCENE_1 = "2",
  19. }
  20. function App() {
  21. const navigate = useNavigate();
  22. const [state, setState] = useUrlState({ scene: SCENE_NAME.HOME });
  23. const [menuVisible, setMenuVisible] = useState(false);
  24. const [fullScreen, setFullScreen] = useState(false);
  25. const [toolbarVisible, setToolbarVisible] = useState(true);
  26. const [currentScene, setCurrentScene] = useState(state.scene);
  27. const [hasPreviousPage, setHasPreviousPage] = useState(false);
  28. const isHome = useMemo(
  29. () => currentScene === SCENE_NAME.HOME,
  30. [currentScene]
  31. );
  32. useEffect(() => {
  33. if (window.history.length > 1) {
  34. setHasPreviousPage(true);
  35. }
  36. }, [isHome]);
  37. const handleScene = useCallback(
  38. (scene: SCENE_NAME) => {
  39. setCurrentScene(scene);
  40. setState({
  41. scene,
  42. });
  43. },
  44. [setState]
  45. );
  46. useEffect(() => {
  47. if (state.scene !== currentScene) {
  48. handleScene(state.scene);
  49. }
  50. }, [state, currentScene, handleScene]);
  51. const goToScene = (m: string) => {
  52. window.location.href = `https://houseoss.4dkankan.com/project/ZHCHXS/scene/index.html#/?m=${m}&showBack=1`;
  53. };
  54. const SCENE_LIST = useMemo<ISceneProps[]>(
  55. () => [
  56. {
  57. name: "1",
  58. title: "翠湖香山1",
  59. thumbUrl: process.env.PUBLIC_URL + "/panos/1.tiles/thumb.jpg",
  60. previewUrl: process.env.PUBLIC_URL + "/panos/1.tiles/preview.jpg",
  61. imageTagAttributes: {
  62. type: "cube",
  63. tileSize: 512,
  64. multires: true,
  65. },
  66. images: [
  67. {
  68. tiledImageWidth: 4608,
  69. tiledImageHeight: 4608,
  70. url:
  71. process.env.PUBLIC_URL +
  72. "/panos/1.tiles/%s/l3/%0v/l3_%s_%0v_%0h.jpg",
  73. },
  74. {
  75. tiledImageWidth: 2304,
  76. tiledImageHeight: 2304,
  77. url:
  78. process.env.PUBLIC_URL +
  79. "/panos/1.tiles/%s/l2/%0v/l2_%s_%0v_%0h.jpg",
  80. },
  81. {
  82. tiledImageWidth: 1152,
  83. tiledImageHeight: 1152,
  84. url:
  85. process.env.PUBLIC_URL +
  86. "/panos/1.tiles/%s/l1/%0v/l1_%s_%0v_%0h.jpg",
  87. },
  88. ],
  89. children: (
  90. <>
  91. <View
  92. hlookat={10}
  93. vlookat={20}
  94. hlookatMin={-190}
  95. hlookatMax={85}
  96. vlookatMin={-70}
  97. vlookatMax={80}
  98. fovType="MFOV"
  99. fov={120}
  100. maxPixelZoom={2}
  101. fovMin={70}
  102. fovMax={140}
  103. limitView="range"
  104. />
  105. <HotSpot
  106. name="hotspot1"
  107. type="text"
  108. atv={6}
  109. ath={-1}
  110. scale={0.5}
  111. edge="top"
  112. bg={false}
  113. distorted={true}
  114. onOver={() => {}}
  115. onOut={() => {}}
  116. onClick={goToScene.bind(undefined, "KJ-XW4mfRey6mK")}
  117. >
  118. <div className="hotspot-item">
  119. <p>香园</p>
  120. </div>
  121. </HotSpot>
  122. <HotSpot
  123. name="hotspot2"
  124. type="text"
  125. atv={-3}
  126. ath={23.5}
  127. scale={0.5}
  128. edge="top"
  129. bg={false}
  130. distorted={true}
  131. onOver={() => {}}
  132. onOut={() => {}}
  133. onClick={goToScene.bind(undefined, "KJ-5iy1XKL3pYu")}
  134. >
  135. <div className="hotspot-item">
  136. <p style={{ letterSpacing: 2, fontSize: 16 }}>
  137. 留香苑传统中式合院别墅<span>(</span>B2<span>)</span>
  138. </p>
  139. </div>
  140. </HotSpot>
  141. <HotSpot
  142. name="hotspot3"
  143. type="text"
  144. atv={-13}
  145. ath={19}
  146. scale={0.5}
  147. edge="top"
  148. bg={false}
  149. distorted={true}
  150. onOver={() => {}}
  151. onOut={() => {}}
  152. onClick={goToScene.bind(undefined, "KJ-vrMFXRis6MK")}
  153. >
  154. <div className="hotspot-item">
  155. <p>留香苑AB区归家大堂</p>
  156. </div>
  157. </HotSpot>
  158. <HotSpot
  159. name="hotspot4"
  160. type="text"
  161. atv={-18}
  162. ath={28}
  163. scale={0.5}
  164. edge="top"
  165. bg={false}
  166. distorted={true}
  167. onOver={() => {}}
  168. onOut={() => {}}
  169. onClick={goToScene.bind(undefined, "KJ-WqBcApJJRfd")}
  170. >
  171. <div className="hotspot-item">
  172. <p>留香苑C区归家大堂</p>
  173. </div>
  174. </HotSpot>
  175. </>
  176. ),
  177. },
  178. {
  179. name: "2",
  180. title: "翠湖香山2",
  181. thumbUrl: process.env.PUBLIC_URL + "/panos/2.tiles/thumb.jpg",
  182. previewUrl: process.env.PUBLIC_URL + "/panos/2.tiles/preview.jpg",
  183. imageTagAttributes: {
  184. type: "cube",
  185. tileSize: 512,
  186. multires: true,
  187. },
  188. images: [
  189. {
  190. tiledImageWidth: 4608,
  191. tiledImageHeight: 4608,
  192. url:
  193. process.env.PUBLIC_URL +
  194. "/panos/2.tiles/%s/l3/%0v/l3_%s_%0v_%0h.jpg",
  195. },
  196. {
  197. tiledImageWidth: 2304,
  198. tiledImageHeight: 2304,
  199. url:
  200. process.env.PUBLIC_URL +
  201. "/panos/2.tiles/%s/l2/%0v/l2_%s_%0v_%0h.jpg",
  202. },
  203. {
  204. tiledImageWidth: 1152,
  205. tiledImageHeight: 1152,
  206. url:
  207. process.env.PUBLIC_URL +
  208. "/panos/2.tiles/%s/l1/%0v/l1_%s_%0v_%0h.jpg",
  209. },
  210. ],
  211. children: (
  212. <>
  213. <View
  214. hlookat={0}
  215. vlookat={20}
  216. fovType="MFOV"
  217. fov={120}
  218. maxPixelZoom={2}
  219. fovMin={70}
  220. fovMax={140}
  221. vlookatMin={-70}
  222. limitView="range"
  223. />
  224. <HotSpot
  225. name="hotspot1"
  226. type="text"
  227. atv={-10}
  228. ath={-46}
  229. scale={0.5}
  230. edge="top"
  231. bg={false}
  232. distorted={true}
  233. onOver={() => {}}
  234. onOut={() => {}}
  235. onClick={goToScene.bind(undefined, "KJ-XW4mfRey6mK")}
  236. >
  237. <div className="hotspot-item">
  238. <p>香园</p>
  239. </div>
  240. </HotSpot>
  241. <HotSpot
  242. name="hotspot2"
  243. type="text"
  244. atv={1}
  245. ath={-40.5}
  246. scale={0.5}
  247. edge="top"
  248. bg={false}
  249. distorted={true}
  250. onOver={() => {}}
  251. onOut={() => {}}
  252. onClick={goToScene.bind(undefined, "KJ-5iy1XKL3pYu")}
  253. >
  254. <div className="hotspot-item">
  255. <p style={{ letterSpacing: 2, fontSize: 16 }}>
  256. 留香苑传统中式合院别墅<span>(</span>B2<span>)</span>
  257. </p>
  258. </div>
  259. </HotSpot>
  260. <HotSpot
  261. name="hotspot3"
  262. type="text"
  263. atv={14}
  264. ath={3}
  265. scale={0.5}
  266. edge="top"
  267. bg={false}
  268. distorted={true}
  269. onOver={() => {}}
  270. onOut={() => {}}
  271. onClick={goToScene.bind(undefined, "KJ-vrMFXRis6MK")}
  272. >
  273. <div className="hotspot-item">
  274. <p>留香苑AB区归家大堂</p>
  275. </div>
  276. </HotSpot>
  277. <HotSpot
  278. name="hotspot4"
  279. type="text"
  280. atv={-4}
  281. ath={116}
  282. scale={0.5}
  283. edge="top"
  284. bg={false}
  285. distorted={true}
  286. onOver={() => {}}
  287. onOut={() => {}}
  288. onClick={goToScene.bind(undefined, "KJ-WqBcApJJRfd")}
  289. >
  290. <div className="hotspot-item">
  291. <p>留香苑C区归家大堂</p>
  292. </div>
  293. </HotSpot>
  294. <HotSpot
  295. name="hotspot5"
  296. type="text"
  297. atv={-18}
  298. ath={-30}
  299. scale={0.4}
  300. edge="top"
  301. bg={false}
  302. distorted={true}
  303. onOver={() => {}}
  304. onOut={() => {}}
  305. >
  306. <div className="hotspot-item">
  307. <p>翠湖高尔夫</p>
  308. </div>
  309. </HotSpot>
  310. </>
  311. ),
  312. },
  313. {
  314. name: "3",
  315. title: "翠湖香山3",
  316. thumbUrl: process.env.PUBLIC_URL + "/panos/3.tiles/thumb.jpg",
  317. previewUrl: process.env.PUBLIC_URL + "/panos/3.tiles/preview.jpg",
  318. imageTagAttributes: {
  319. type: "cube",
  320. tileSize: 512,
  321. multires: true,
  322. },
  323. images: [
  324. {
  325. tiledImageWidth: 4608,
  326. tiledImageHeight: 4608,
  327. url:
  328. process.env.PUBLIC_URL +
  329. "/panos/3.tiles/%s/l3/%0v/l3_%s_%0v_%0h.jpg",
  330. },
  331. {
  332. tiledImageWidth: 2304,
  333. tiledImageHeight: 2304,
  334. url:
  335. process.env.PUBLIC_URL +
  336. "/panos/3.tiles/%s/l2/%0v/l2_%s_%0v_%0h.jpg",
  337. },
  338. {
  339. tiledImageWidth: 1152,
  340. tiledImageHeight: 1152,
  341. url:
  342. process.env.PUBLIC_URL +
  343. "/panos/3.tiles/%s/l1/%0v/l1_%s_%0v_%0h.jpg",
  344. },
  345. ],
  346. children: (
  347. <>
  348. <View
  349. hlookat={200}
  350. vlookat={50}
  351. fovType="MFOV"
  352. fov={120}
  353. maxPixelZoom={2}
  354. fovMin={70}
  355. fovMax={140}
  356. vlookatMin={-70}
  357. limitView="range"
  358. />
  359. <HotSpot
  360. name="hotspot1"
  361. type="text"
  362. atv={-1}
  363. ath={260}
  364. scale={0.5}
  365. edge="top"
  366. bg={false}
  367. distorted={true}
  368. onOver={() => {}}
  369. onOut={() => {}}
  370. onClick={goToScene.bind(undefined, "KJ-XW4mfRey6mK")}
  371. >
  372. <div className="hotspot-item">
  373. <p>香园</p>
  374. </div>
  375. </HotSpot>
  376. <HotSpot
  377. name="hotspot2"
  378. type="text"
  379. atv={7}
  380. ath={240}
  381. scale={0.5}
  382. edge="top"
  383. bg={false}
  384. distorted={true}
  385. onOver={() => {}}
  386. onOut={() => {}}
  387. onClick={goToScene.bind(undefined, "KJ-5iy1XKL3pYu")}
  388. >
  389. <div className="hotspot-item">
  390. <p style={{ letterSpacing: 2, fontSize: 16 }}>
  391. 留香苑传统中式合院别墅<span>(</span>B2<span>)</span>
  392. </p>
  393. </div>
  394. </HotSpot>
  395. <HotSpot
  396. name="hotspot3"
  397. type="text"
  398. atv={21}
  399. ath={176}
  400. scale={0.5}
  401. edge="top"
  402. bg={false}
  403. distorted={true}
  404. onOver={() => {}}
  405. onOut={() => {}}
  406. onClick={goToScene.bind(undefined, "KJ-vrMFXRis6MK")}
  407. >
  408. <div className="hotspot-item">
  409. <p>留香苑AB区归家大堂</p>
  410. </div>
  411. </HotSpot>
  412. <HotSpot
  413. name="hotspot4"
  414. type="text"
  415. atv={-11}
  416. ath={142}
  417. scale={0.5}
  418. edge="top"
  419. bg={false}
  420. distorted={true}
  421. onOver={() => {}}
  422. onOut={() => {}}
  423. onClick={goToScene.bind(undefined, "KJ-WqBcApJJRfd")}
  424. >
  425. <div className="hotspot-item">
  426. <p>留香苑C区归家大堂</p>
  427. </div>
  428. </HotSpot>
  429. <HotSpot
  430. name="hotspot5"
  431. type="text"
  432. atv={-17}
  433. ath={-40}
  434. scale={0.4}
  435. edge="top"
  436. bg={false}
  437. distorted={true}
  438. onOver={() => {}}
  439. onOut={() => {}}
  440. >
  441. <div className="hotspot-item">
  442. <p>翠湖高尔夫</p>
  443. </div>
  444. </HotSpot>
  445. </>
  446. ),
  447. },
  448. {
  449. name: "4",
  450. title: "翠湖香山4",
  451. thumbUrl: process.env.PUBLIC_URL + "/panos/4.tiles/thumb.jpg",
  452. previewUrl: process.env.PUBLIC_URL + "/panos/4.tiles/preview.jpg",
  453. imageTagAttributes: {
  454. type: "cube",
  455. tileSize: 512,
  456. multires: true,
  457. },
  458. images: [
  459. {
  460. tiledImageWidth: 4608,
  461. tiledImageHeight: 4608,
  462. url:
  463. process.env.PUBLIC_URL +
  464. "/panos/4.tiles/%s/l3/%0v/l3_%s_%0v_%0h.jpg",
  465. },
  466. {
  467. tiledImageWidth: 2304,
  468. tiledImageHeight: 2304,
  469. url:
  470. process.env.PUBLIC_URL +
  471. "/panos/4.tiles/%s/l2/%0v/l2_%s_%0v_%0h.jpg",
  472. },
  473. {
  474. tiledImageWidth: 1152,
  475. tiledImageHeight: 1152,
  476. url:
  477. process.env.PUBLIC_URL +
  478. "/panos/4.tiles/%s/l1/%0v/l1_%s_%0v_%0h.jpg",
  479. },
  480. ],
  481. children: (
  482. <>
  483. <View
  484. hlookat={-20}
  485. vlookat={2}
  486. fovType="MFOV"
  487. fov={110}
  488. maxPixelZoom={2}
  489. fovMin={70}
  490. fovMax={140}
  491. vlookatMin={-70}
  492. limitView="range"
  493. />
  494. <HotSpot
  495. name="hotspot1"
  496. type="text"
  497. atv={-12.5}
  498. ath={0}
  499. scale={0.5}
  500. edge="top"
  501. bg={false}
  502. distorted={true}
  503. onOver={() => {}}
  504. onOut={() => {}}
  505. onClick={goToScene.bind(undefined, "KJ-XW4mfRey6mK")}
  506. >
  507. <div className="hotspot-item">
  508. <p>香园</p>
  509. </div>
  510. </HotSpot>
  511. <HotSpot
  512. name="hotspot2"
  513. type="text"
  514. atv={-11}
  515. ath={-13}
  516. scale={0.5}
  517. edge="top"
  518. bg={false}
  519. distorted={true}
  520. onOver={() => {}}
  521. onOut={() => {}}
  522. onClick={goToScene.bind(undefined, "KJ-5iy1XKL3pYu")}
  523. >
  524. <div className="hotspot-item">
  525. <p style={{ letterSpacing: 2, fontSize: 16 }}>
  526. 留香苑传统中式合院别墅<span>(</span>B2<span>)</span>
  527. </p>
  528. </div>
  529. </HotSpot>
  530. <HotSpot
  531. name="hotspot3"
  532. type="text"
  533. atv={-10}
  534. ath={-37}
  535. scale={0.5}
  536. edge="top"
  537. bg={false}
  538. distorted={true}
  539. onOver={() => {}}
  540. onOut={() => {}}
  541. onClick={goToScene.bind(undefined, "KJ-vrMFXRis6MK")}
  542. >
  543. <div className="hotspot-item">
  544. <p>留香苑AB区归家大堂</p>
  545. </div>
  546. </HotSpot>
  547. <HotSpot
  548. name="hotspot4"
  549. type="text"
  550. atv={-5}
  551. ath={-63}
  552. scale={0.3}
  553. edge="top"
  554. bg={false}
  555. distorted={true}
  556. onOver={() => {}}
  557. onOut={() => {}}
  558. onClick={goToScene.bind(undefined, "KJ-WqBcApJJRfd")}
  559. >
  560. <div className="hotspot-item">
  561. <p>留香苑C区归家大堂</p>
  562. </div>
  563. </HotSpot>
  564. <HotSpot
  565. name="hotspot5"
  566. type="text"
  567. atv={-16}
  568. ath={79}
  569. scale={0.4}
  570. edge="top"
  571. bg={false}
  572. distorted={true}
  573. onOver={() => {}}
  574. onOut={() => {}}
  575. >
  576. <div className="hotspot-item">
  577. <p>翠湖高尔夫</p>
  578. </div>
  579. </HotSpot>
  580. </>
  581. ),
  582. },
  583. {
  584. name: "5",
  585. title: "翠湖香山5",
  586. thumbUrl: process.env.PUBLIC_URL + "/panos/5.tiles/thumb.jpg",
  587. previewUrl: process.env.PUBLIC_URL + "/panos/5.tiles/preview.jpg",
  588. imageTagAttributes: {
  589. type: "cube",
  590. tileSize: 512,
  591. multires: true,
  592. },
  593. images: [
  594. {
  595. tiledImageWidth: 4608,
  596. tiledImageHeight: 4608,
  597. url:
  598. process.env.PUBLIC_URL +
  599. "/panos/5.tiles/%s/l3/%0v/l3_%s_%0v_%0h.jpg",
  600. },
  601. {
  602. tiledImageWidth: 2304,
  603. tiledImageHeight: 2304,
  604. url:
  605. process.env.PUBLIC_URL +
  606. "/panos/5.tiles/%s/l2/%0v/l2_%s_%0v_%0h.jpg",
  607. },
  608. {
  609. tiledImageWidth: 1152,
  610. tiledImageHeight: 1152,
  611. url:
  612. process.env.PUBLIC_URL +
  613. "/panos/5.tiles/%s/l1/%0v/l1_%s_%0v_%0h.jpg",
  614. },
  615. ],
  616. children: (
  617. <>
  618. <View
  619. hlookat={154}
  620. vlookat={0}
  621. fovType="MFOV"
  622. fov={120}
  623. maxPixelZoom={2}
  624. fovMin={70}
  625. fovMax={140}
  626. vlookatMin={-70}
  627. limitView="range"
  628. />
  629. <HotSpot
  630. name="hotspot1"
  631. type="text"
  632. atv={-14}
  633. ath={164}
  634. scale={0.5}
  635. edge="top"
  636. bg={false}
  637. distorted={true}
  638. onOver={() => {}}
  639. onOut={() => {}}
  640. onClick={goToScene.bind(undefined, "KJ-XW4mfRey6mK")}
  641. >
  642. <div className="hotspot-item">
  643. <p>香园</p>
  644. </div>
  645. </HotSpot>
  646. <HotSpot
  647. name="hotspot2"
  648. type="text"
  649. atv={-8}
  650. ath={156}
  651. scale={0.5}
  652. edge="top"
  653. bg={false}
  654. distorted={true}
  655. onOver={() => {}}
  656. onOut={() => {}}
  657. onClick={goToScene.bind(undefined, "KJ-5iy1XKL3pYu")}
  658. >
  659. <div className="hotspot-item">
  660. <p style={{ letterSpacing: 2, fontSize: 16 }}>
  661. 留香苑传统中式合院别墅<span>(</span>B2<span>)</span>
  662. </p>
  663. </div>
  664. </HotSpot>
  665. <HotSpot
  666. name="hotspot3"
  667. type="text"
  668. atv={8}
  669. ath={150}
  670. scale={0.5}
  671. edge="top"
  672. bg={false}
  673. distorted={true}
  674. onOver={() => {}}
  675. onOut={() => {}}
  676. onClick={goToScene.bind(undefined, "KJ-vrMFXRis6MK")}
  677. >
  678. <div className="hotspot-item">
  679. <p>留香苑AB区归家大堂</p>
  680. </div>
  681. </HotSpot>
  682. <HotSpot
  683. name="hotspot4"
  684. type="text"
  685. atv={-5}
  686. ath={50}
  687. scale={0.5}
  688. edge="top"
  689. bg={false}
  690. distorted={true}
  691. onOver={() => {}}
  692. onOut={() => {}}
  693. onClick={goToScene.bind(undefined, "KJ-WqBcApJJRfd")}
  694. >
  695. <div className="hotspot-item">
  696. <p>留香苑C区归家大堂</p>
  697. </div>
  698. </HotSpot>
  699. <HotSpot
  700. name="hotspot5"
  701. type="text"
  702. atv={-19}
  703. ath={200}
  704. scale={0.4}
  705. edge="top"
  706. bg={false}
  707. distorted={true}
  708. onOver={() => {}}
  709. onOut={() => {}}
  710. >
  711. <div className="hotspot-item">
  712. <p>翠湖高尔夫</p>
  713. </div>
  714. </HotSpot>
  715. </>
  716. ),
  717. },
  718. {
  719. name: "6",
  720. title: "翠湖香山6",
  721. thumbUrl: process.env.PUBLIC_URL + "/panos/6.tiles/thumb.jpg",
  722. previewUrl: process.env.PUBLIC_URL + "/panos/6.tiles/preview.jpg",
  723. imageTagAttributes: {
  724. type: "cube",
  725. tileSize: 512,
  726. multires: true,
  727. },
  728. images: [
  729. {
  730. tiledImageWidth: 4608,
  731. tiledImageHeight: 4608,
  732. url:
  733. process.env.PUBLIC_URL +
  734. "/panos/6.tiles/%s/l3/%0v/l3_%s_%0v_%0h.jpg",
  735. },
  736. {
  737. tiledImageWidth: 2304,
  738. tiledImageHeight: 2304,
  739. url:
  740. process.env.PUBLIC_URL +
  741. "/panos/6.tiles/%s/l2/%0v/l2_%s_%0v_%0h.jpg",
  742. },
  743. {
  744. tiledImageWidth: 1152,
  745. tiledImageHeight: 1152,
  746. url:
  747. process.env.PUBLIC_URL +
  748. "/panos/6.tiles/%s/l1/%0v/l1_%s_%0v_%0h.jpg",
  749. },
  750. ],
  751. children: (
  752. <>
  753. <View
  754. hlookat={220}
  755. vlookat={20}
  756. fovType="MFOV"
  757. fov={120}
  758. maxPixelZoom={2}
  759. fovMin={70}
  760. fovMax={140}
  761. vlookatMin={-70}
  762. limitView="range"
  763. />
  764. <HotSpot
  765. name="hotspot5"
  766. type="text"
  767. atv={-19}
  768. ath={-125}
  769. scale={0.4}
  770. edge="top"
  771. bg={false}
  772. distorted={true}
  773. onOver={() => {}}
  774. onOut={() => {}}
  775. >
  776. <div className="hotspot-item">
  777. <p>翠湖高尔夫</p>
  778. </div>
  779. </HotSpot>
  780. <HotSpot
  781. name="hotspot1"
  782. type="text"
  783. atv={-16}
  784. ath={217}
  785. scale={0.5}
  786. edge="top"
  787. bg={false}
  788. distorted={true}
  789. onOver={() => {}}
  790. onOut={() => {}}
  791. onClick={goToScene.bind(undefined, "KJ-XW4mfRey6mK")}
  792. >
  793. <div className="hotspot-item">
  794. <p>香园</p>
  795. </div>
  796. </HotSpot>
  797. <HotSpot
  798. name="hotspot2"
  799. type="text"
  800. atv={-12.5}
  801. ath={222}
  802. scale={0.5}
  803. edge="top"
  804. bg={false}
  805. distorted={true}
  806. onOver={() => {}}
  807. onOut={() => {}}
  808. onClick={goToScene.bind(undefined, "KJ-5iy1XKL3pYu")}
  809. >
  810. <div className="hotspot-item">
  811. <p style={{ letterSpacing: 2, fontSize: 16 }}>
  812. 留香苑传统中式合院别墅<span>(</span>B2<span>)</span>
  813. </p>
  814. </div>
  815. </HotSpot>
  816. <HotSpot
  817. name="hotspot3"
  818. type="text"
  819. atv={-7}
  820. ath={235}
  821. scale={0.5}
  822. edge="top"
  823. bg={false}
  824. distorted={true}
  825. onOver={() => {}}
  826. onOut={() => {}}
  827. onClick={goToScene.bind(undefined, "KJ-vrMFXRis6MK")}
  828. >
  829. <div className="hotspot-item">
  830. <p>留香苑AB区归家大堂</p>
  831. </div>
  832. </HotSpot>
  833. <HotSpot
  834. name="hotspot4"
  835. type="text"
  836. atv={25}
  837. ath={-10}
  838. scale={0.7}
  839. edge="top"
  840. bg={false}
  841. distorted={true}
  842. onOver={() => {}}
  843. onOut={() => {}}
  844. onClick={goToScene.bind(undefined, "KJ-WqBcApJJRfd")}
  845. >
  846. <div className="hotspot-item">
  847. <p>留香苑C区归家大堂</p>
  848. </div>
  849. </HotSpot>
  850. </>
  851. ),
  852. },
  853. {
  854. name: "7",
  855. title: "翠湖香山7",
  856. thumbUrl: process.env.PUBLIC_URL + "/panos/7.tiles/thumb.jpg",
  857. previewUrl: process.env.PUBLIC_URL + "/panos/7.tiles/preview.jpg",
  858. imageTagAttributes: {
  859. type: "cube",
  860. tileSize: 512,
  861. multires: true,
  862. },
  863. images: [
  864. {
  865. tiledImageWidth: 4608,
  866. tiledImageHeight: 4608,
  867. url:
  868. process.env.PUBLIC_URL +
  869. "/panos/7.tiles/%s/l3/%0v/l3_%s_%0v_%0h.jpg",
  870. },
  871. {
  872. tiledImageWidth: 2304,
  873. tiledImageHeight: 2304,
  874. url:
  875. process.env.PUBLIC_URL +
  876. "/panos/7.tiles/%s/l2/%0v/l2_%s_%0v_%0h.jpg",
  877. },
  878. {
  879. tiledImageWidth: 1152,
  880. tiledImageHeight: 1152,
  881. url:
  882. process.env.PUBLIC_URL +
  883. "/panos/7.tiles/%s/l1/%0v/l1_%s_%0v_%0h.jpg",
  884. },
  885. ],
  886. children: (
  887. <>
  888. <View
  889. hlookat={-20}
  890. vlookat={0}
  891. fovType="MFOV"
  892. fov={120}
  893. maxPixelZoom={2}
  894. fovMin={70}
  895. fovMax={140}
  896. vlookatMin={-70}
  897. limitView="range"
  898. />
  899. <HotSpot
  900. name="hotspot5"
  901. type="text"
  902. atv={-19.5}
  903. ath={-32}
  904. scale={0.4}
  905. edge="top"
  906. bg={false}
  907. distorted={true}
  908. onOver={() => {}}
  909. onOut={() => {}}
  910. >
  911. <div className="hotspot-item">
  912. <p>翠湖高尔夫</p>
  913. </div>
  914. </HotSpot>
  915. <HotSpot
  916. name="hotspot1"
  917. type="text"
  918. atv={-19.5}
  919. ath={-42}
  920. scale={0.5}
  921. edge="top"
  922. bg={false}
  923. distorted={true}
  924. onOver={() => {}}
  925. onOut={() => {}}
  926. onClick={goToScene.bind(undefined, "KJ-XW4mfRey6mK")}
  927. >
  928. <div className="hotspot-item">
  929. <p>香园</p>
  930. </div>
  931. </HotSpot>
  932. <HotSpot
  933. name="hotspot2"
  934. type="text"
  935. atv={-17.5}
  936. ath={-37}
  937. scale={0.5}
  938. edge="top"
  939. bg={false}
  940. distorted={true}
  941. onOver={() => {}}
  942. onOut={() => {}}
  943. onClick={goToScene.bind(undefined, "KJ-5iy1XKL3pYu")}
  944. >
  945. <div className="hotspot-item">
  946. <p style={{ letterSpacing: 2, fontSize: 16 }}>
  947. 留香苑传统中式合院别墅<span>(</span>B2<span>)</span>
  948. </p>
  949. </div>
  950. </HotSpot>
  951. <HotSpot
  952. name="hotspot3"
  953. type="text"
  954. atv={-16}
  955. ath={-28}
  956. scale={0.5}
  957. edge="top"
  958. bg={false}
  959. distorted={true}
  960. onOver={() => {}}
  961. onOut={() => {}}
  962. onClick={goToScene.bind(undefined, "KJ-vrMFXRis6MK")}
  963. >
  964. <div className="hotspot-item">
  965. <p>留香苑AB区归家大堂</p>
  966. </div>
  967. </HotSpot>
  968. <HotSpot
  969. name="hotspot4"
  970. type="text"
  971. atv={-10}
  972. ath={-9}
  973. scale={0.7}
  974. edge="top"
  975. bg={false}
  976. distorted={true}
  977. onOver={() => {}}
  978. onOut={() => {}}
  979. onClick={goToScene.bind(undefined, "KJ-WqBcApJJRfd")}
  980. >
  981. <div className="hotspot-item">
  982. <p>留香苑C区归家大堂</p>
  983. </div>
  984. </HotSpot>
  985. </>
  986. ),
  987. },
  988. {
  989. name: "8",
  990. title: "翠湖香山8",
  991. thumbUrl: process.env.PUBLIC_URL + "/panos/8.tiles/thumb.jpg",
  992. previewUrl: process.env.PUBLIC_URL + "/panos/8.tiles/preview.jpg",
  993. imageTagAttributes: {
  994. type: "cube",
  995. tileSize: 512,
  996. multires: true,
  997. },
  998. images: [
  999. {
  1000. tiledImageWidth: 4608,
  1001. tiledImageHeight: 4608,
  1002. url:
  1003. process.env.PUBLIC_URL +
  1004. "/panos/8.tiles/%s/l3/%0v/l3_%s_%0v_%0h.jpg",
  1005. },
  1006. {
  1007. tiledImageWidth: 2304,
  1008. tiledImageHeight: 2304,
  1009. url:
  1010. process.env.PUBLIC_URL +
  1011. "/panos/8.tiles/%s/l2/%0v/l2_%s_%0v_%0h.jpg",
  1012. },
  1013. {
  1014. tiledImageWidth: 1152,
  1015. tiledImageHeight: 1152,
  1016. url:
  1017. process.env.PUBLIC_URL +
  1018. "/panos/8.tiles/%s/l1/%0v/l1_%s_%0v_%0h.jpg",
  1019. },
  1020. ],
  1021. children: (
  1022. <>
  1023. <View
  1024. hlookat={20}
  1025. vlookat={10}
  1026. fovType="MFOV"
  1027. fov={120}
  1028. maxPixelZoom={2}
  1029. fovMin={70}
  1030. fovMax={140}
  1031. vlookatMin={-70}
  1032. limitView="range"
  1033. />
  1034. <HotSpot
  1035. name="hotspot1"
  1036. type="text"
  1037. atv={-18}
  1038. ath={36.5}
  1039. scale={0.5}
  1040. edge="top"
  1041. bg={false}
  1042. distorted={true}
  1043. onOver={() => {}}
  1044. onOut={() => {}}
  1045. onClick={goToScene.bind(undefined, "KJ-XW4mfRey6mK")}
  1046. >
  1047. <div className="hotspot-item">
  1048. <p>香园</p>
  1049. </div>
  1050. </HotSpot>
  1051. <HotSpot
  1052. name="hotspot2"
  1053. type="text"
  1054. atv={-15.5}
  1055. ath={31}
  1056. scale={0.5}
  1057. edge="top"
  1058. bg={false}
  1059. distorted={true}
  1060. onOver={() => {}}
  1061. onOut={() => {}}
  1062. onClick={goToScene.bind(undefined, "KJ-5iy1XKL3pYu")}
  1063. >
  1064. <div className="hotspot-item">
  1065. <p style={{ letterSpacing: 2, fontSize: 16 }}>
  1066. 留香苑传统中式合院别墅<span>(</span>B2<span>)</span>
  1067. </p>
  1068. </div>
  1069. </HotSpot>
  1070. <HotSpot
  1071. name="hotspot3"
  1072. type="text"
  1073. atv={-12}
  1074. ath={42}
  1075. scale={0.5}
  1076. edge="top"
  1077. bg={false}
  1078. distorted={true}
  1079. onOver={() => {}}
  1080. onOut={() => {}}
  1081. onClick={goToScene.bind(undefined, "KJ-vrMFXRis6MK")}
  1082. >
  1083. <div className="hotspot-item">
  1084. <p>留香苑AB区归家大堂</p>
  1085. </div>
  1086. </HotSpot>
  1087. <HotSpot
  1088. name="hotspot4"
  1089. type="text"
  1090. atv={10}
  1091. ath={7}
  1092. scale={0.7}
  1093. edge="top"
  1094. bg={false}
  1095. distorted={true}
  1096. onOver={() => {}}
  1097. onOut={() => {}}
  1098. onClick={goToScene.bind(undefined, "KJ-WqBcApJJRfd")}
  1099. >
  1100. <div className="hotspot-item">
  1101. <p>留香苑C区归家大堂</p>
  1102. </div>
  1103. </HotSpot>
  1104. <HotSpot
  1105. name="hotspot5"
  1106. type="text"
  1107. atv={-19.5}
  1108. ath={55}
  1109. scale={0.4}
  1110. edge="top"
  1111. bg={false}
  1112. distorted={true}
  1113. onOver={() => {}}
  1114. onOut={() => {}}
  1115. >
  1116. <div className="hotspot-item">
  1117. <p>翠湖高尔夫</p>
  1118. </div>
  1119. </HotSpot>
  1120. </>
  1121. ),
  1122. },
  1123. ],
  1124. []
  1125. );
  1126. const handleView = (direction: ROTATE_DIRECTION) => {
  1127. window.ReactKrpanoActionProxy?.rotateView(direction);
  1128. };
  1129. const handleZoom = (action: ZOOM_ACTION) => {
  1130. window.ReactKrpanoActionProxy?.zoomView(action);
  1131. };
  1132. return (
  1133. <div className="App">
  1134. <Krpano
  1135. className="container"
  1136. currentScene={currentScene}
  1137. littlePlanetIntro={isHome}
  1138. >
  1139. {SCENE_LIST.map(
  1140. (sc) => sc.imageTagAttributes && <Scene key={sc.name} {...sc} />
  1141. )}
  1142. </Krpano>
  1143. {hasPreviousPage && !isHome && (
  1144. <div className="back" onClick={() => navigate(-1)} />
  1145. )}
  1146. <div className={classNames("toolbar", { active: toolbarVisible })}>
  1147. <SceneList
  1148. visible={menuVisible}
  1149. className={classNames({ active: menuVisible })}
  1150. currentScene={currentScene}
  1151. list={SCENE_LIST}
  1152. handleScene={handleScene}
  1153. />
  1154. <div
  1155. className="menu toolbar__btn"
  1156. onClick={() => setMenuVisible(!menuVisible)}
  1157. />
  1158. <div className="toolbar__main">
  1159. <div className="toolbar__group">
  1160. <MouseHoldView
  1161. onHold={handleView.bind(undefined, ROTATE_DIRECTION.LEFT)}
  1162. >
  1163. <div className="toolbar__btn left" />
  1164. </MouseHoldView>
  1165. <MouseHoldView
  1166. onHold={handleView.bind(undefined, ROTATE_DIRECTION.RIGHT)}
  1167. >
  1168. <div className="toolbar__btn right" />
  1169. </MouseHoldView>
  1170. </div>
  1171. <div className="toolbar__group">
  1172. <MouseHoldView
  1173. onHold={handleView.bind(undefined, ROTATE_DIRECTION.UP)}
  1174. >
  1175. <div className="toolbar__btn top" />
  1176. </MouseHoldView>
  1177. <MouseHoldView
  1178. onHold={handleView.bind(undefined, ROTATE_DIRECTION.DOWN)}
  1179. >
  1180. <div className="toolbar__btn bottom" />
  1181. </MouseHoldView>
  1182. </div>
  1183. <div className="toolbar__group">
  1184. <MouseHoldView onHold={handleZoom.bind(undefined, ZOOM_ACTION.IN)}>
  1185. <div className="toolbar__btn plus" />
  1186. </MouseHoldView>
  1187. <MouseHoldView onHold={handleZoom.bind(undefined, ZOOM_ACTION.OUT)}>
  1188. <div className="toolbar__btn minus" />
  1189. </MouseHoldView>
  1190. </div>
  1191. </div>
  1192. <div className="toolbar__right">
  1193. {document.fullscreenEnabled && (
  1194. <div
  1195. className={classNames("fullscreen toolbar__btn", {
  1196. exit: fullScreen,
  1197. })}
  1198. onClick={async () => {
  1199. if (fullScreen) {
  1200. await document.exitFullscreen();
  1201. } else {
  1202. await document.getElementById("root")?.requestFullscreen();
  1203. }
  1204. setFullScreen(!fullScreen);
  1205. }}
  1206. />
  1207. )}
  1208. <div
  1209. className="hide toolbar__btn"
  1210. onClick={() => {
  1211. setMenuVisible(false);
  1212. setToolbarVisible(false);
  1213. }}
  1214. />
  1215. </div>
  1216. </div>
  1217. <div className="toolbar2">
  1218. {!isHome && (
  1219. <div
  1220. className="home"
  1221. onClick={() => setState({ scene: SCENE_NAME.HOME })}
  1222. />
  1223. )}
  1224. {/* <div className="music" /> */}
  1225. </div>
  1226. <div
  1227. className={classNames("show-toolbar", { active: !toolbarVisible })}
  1228. onClick={() => setToolbarVisible(true)}
  1229. />
  1230. </div>
  1231. );
  1232. }
  1233. export default App;