PanoView.vue 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541
  1. <template>
  2. <div :class="['pano-view', (msgVisible || isShowHotspotDetail2) && 'reduceZ']">
  3. <div
  4. class="pano-wrap"
  5. :class="{
  6. hide: !haveShownSceneEffect
  7. }"
  8. >
  9. <iframe
  10. ref="panoIframe"
  11. :src="iframeSrc"
  12. frameborder="0"
  13. />
  14. <button
  15. class="return-home"
  16. @click="router.push({
  17. name: 'HomeView',
  18. })"
  19. />
  20. <button
  21. v-if="sceneIdx === 1"
  22. class="ship-btn"
  23. @click="router.push({
  24. name: 'ShipGame',
  25. })"
  26. />
  27. <button
  28. v-if="sceneIdx === 0 && cameraIdx === 0"
  29. class="guide-btn"
  30. @click="showGuide = true"
  31. />
  32. <CameraDesc
  33. v-if="isShowCameraDesc"
  34. class="camera-desc"
  35. @close="isShowCameraDesc = false"
  36. />
  37. <CharacterDesc
  38. v-if="isShowCharacterDesc"
  39. class="character-desc"
  40. @close="isShowCharacterDesc = false"
  41. />
  42. <div
  43. class="character-wrap"
  44. draggable="false"
  45. >
  46. <button
  47. class="name"
  48. @click="isShowCharacterDesc = true"
  49. >
  50. <span>赵孟頫</span>
  51. </button>
  52. <div
  53. :class="['character-frames-wrapper']"
  54. @click="onClickCharacter()"
  55. >
  56. <img
  57. v-show="animationType === 1"
  58. class="default-frames"
  59. src="@/assets/images/pose1-min.png"
  60. alt=""
  61. draggable="false"
  62. >
  63. <img
  64. v-show="animationType === 2"
  65. class="frames frames-2"
  66. :class="{
  67. animating: isCharacterSpecialMoving,
  68. state1: isCharacterSpecialMoving === 0,
  69. state2: isCharacterSpecialMoving === 1,
  70. }"
  71. src="@/assets/images/pose2-min.png"
  72. alt=""
  73. draggable="false"
  74. >
  75. <img
  76. v-show="animationType === 3"
  77. class="frames frames-3"
  78. :class="{
  79. animating: isCharacterSpecialMoving,
  80. state1: isCharacterSpecialMoving === 0,
  81. state2: isCharacterSpecialMoving === 1,
  82. }"
  83. src="@/assets/images/pose3-min.png"
  84. alt=""
  85. draggable="false"
  86. >
  87. <!-- <img
  88. v-show="animationType === 4"
  89. class="frames frames-4"
  90. :class="{
  91. animating: isCharacterSpecialMoving,
  92. state1: isCharacterSpecialMoving === 0,
  93. state2: isCharacterSpecialMoving === 1,
  94. }"
  95. src="@/assets/images/bow-min.png"
  96. alt=""
  97. draggable="false"
  98. > -->
  99. <img
  100. v-show="animationType === 5"
  101. class="frames frames-5"
  102. :class="{
  103. animating: isCharacterSpecialMoving,
  104. state1: isCharacterSpecialMoving === 0,
  105. state2: isCharacterSpecialMoving === 1,
  106. }"
  107. src="@/assets/images/pose4-min.png"
  108. alt=""
  109. draggable="false"
  110. >
  111. </div>
  112. <img
  113. class="btn-track"
  114. :src="require(`@/assets/images/people-btn-track-${sceneIdx + 1}.png`)"
  115. alt=""
  116. draggable="false"
  117. >
  118. <button
  119. class="one btn-on-track"
  120. @click="showingContentIdx = 1"
  121. >
  122. <span>{{ btnOnTrack1Name }}</span>
  123. </button>
  124. <button
  125. class="two btn-on-track"
  126. @click="showingContentIdx = 2"
  127. >
  128. <span>{{ btnOnTrack2Name }}</span>
  129. </button>
  130. <button
  131. class="three btn-on-track"
  132. @click="showingContentIdx = 3"
  133. >
  134. <span>{{ btnOnTrack3Name }}</span>
  135. </button>
  136. <button
  137. class="four btn-on-track"
  138. @click="router.push({
  139. name: 'RelicList',
  140. query: {
  141. sceneIdx: route.query.sceneIdx,
  142. cameraIdx: route.query.cameraIdx,
  143. }
  144. })"
  145. >
  146. <span>文物长卷</span>
  147. </button>
  148. </div>
  149. <div
  150. class="camera-list"
  151. >
  152. <button
  153. v-for="(item, idx) in currentCameraList"
  154. :key="idx"
  155. class="camera-entry"
  156. :class="{
  157. active: idx === (mouseEnterCameraItemIdx === -1 ? cameraIdx : mouseEnterCameraItemIdx)
  158. }"
  159. @mouseenter="mouseEnterCameraItemIdx = idx"
  160. @mouseleave="mouseEnterCameraItemIdx = -1"
  161. @click="router.push({
  162. name: route.name,
  163. query:{
  164. sceneIdx: route.query.sceneIdx,
  165. cameraIdx: idx,
  166. }
  167. })"
  168. >
  169. <span>{{ item.name }}</span>
  170. <img
  171. class="bg-normal"
  172. :src="require(`@/assets/images/camera-list-item-bg-${sceneIdx + 1}.png`)"
  173. alt=""
  174. draggable="false"
  175. >
  176. <!-- <template v-if="sceneIdx === 0 && idx > 0">
  177. <img
  178. class="bg-active"
  179. :src="require(`@/assets/images/camera-list-item-bg-active-1-${idx}.png`)"
  180. alt=""
  181. draggable="false"
  182. >
  183. </template> -->
  184. <img
  185. class="bg-active"
  186. :src="require(`@/assets/images/camera-list-item-bg-active-${sceneIdx + 1}.png`)"
  187. alt=""
  188. draggable="false"
  189. >
  190. </button>
  191. <button
  192. class="next-scene"
  193. @click="onClickNextScene"
  194. />
  195. </div>
  196. </div>
  197. <CameraContent1
  198. v-if="showingContentIdx === 1"
  199. class="camera-content"
  200. @close="showingContentIdx = 0"
  201. />
  202. <CameraContent2
  203. v-if="showingContentIdx === 2"
  204. class="camera-content"
  205. @close="showingContentIdx = 0"
  206. />
  207. <CameraContent3
  208. v-if="showingContentIdx === 3"
  209. class="camera-content"
  210. @close="showingContentIdx = 0"
  211. />
  212. <RelicDetailForHotspot
  213. v-if="isShowHotspotDetail"
  214. :relic-info="hotspotRelicInfo"
  215. :relic-index="hotspotIndex"
  216. @close="isShowHotspotDetail = false"
  217. />
  218. <HotspotDialog1
  219. v-if="isShowHotspotDetail2"
  220. :relic-info="hotspotRelicInfo"
  221. @close="isShowHotspotDetail2 = false"
  222. @open-relic-detail="(e) => {
  223. hotspotIndex = e
  224. isShowHotspotDetail = true
  225. }"
  226. />
  227. <!-- <OpenState /> -->
  228. <!-- 镜头切换过渡 -->
  229. <transition name="fade-in-out-slow">
  230. <div
  231. v-if="isShowCameraIntro"
  232. class="text-wrap"
  233. >
  234. <div
  235. class="text text-indent"
  236. v-html="cameraIntroText"
  237. />
  238. <button
  239. class="skip"
  240. @click="skipCameraIntro"
  241. />
  242. <template v-if="showScene0Camera1To2Video || showScene0Camera2To3Video || showScene1Camera1To2Video || showScene1Camera2To3Video || showScene2Camera1To2Video">
  243. <video
  244. :src="require(
  245. showScene0Camera1To2Video ? '@/assets/videos/scene-1-camera-1.mp4' :
  246. showScene0Camera2To3Video ? '@/assets/videos/scene-1-camera-2.mp4' :
  247. showScene1Camera1To2Video ? '@/assets/videos/scene-2-camera-1.mp4' :
  248. showScene1Camera2To3Video ? '@/assets/videos/scene-2-camera-2.mp4' :
  249. '@/assets/videos/scene-3-camera-1.mp4'
  250. )"
  251. playsinline
  252. autoplay
  253. webkit-playsinline="true"
  254. x5-video-player-type="h5"
  255. style="position: relative; z-index: -2; width: 100vw; height: 100vh; object-fit: cover;"
  256. />
  257. <div
  258. style="position:absolute; z-index: -1; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,.5)"
  259. />
  260. </template>
  261. <!-- 人物 -->
  262. <div :class="['text-wrap__character', charactorSpeackPositionLeft && 'left']">
  263. <img
  264. draggable="false"
  265. :src="require(charactorSpeackPositionLeft ? '@/assets/images/talk2-min.png' : `@/assets/images/slow_talking.png`)"
  266. >
  267. </div>
  268. </div>
  269. </transition>
  270. <transition name="cloud-top">
  271. <img
  272. v-if="isShowCameraIntro"
  273. class="cloud cloud-top"
  274. src="@/assets/images/cloud-top.png"
  275. alt=""
  276. draggable="false"
  277. >
  278. </transition>
  279. <transition name="cloud-left-bottom">
  280. <img
  281. v-if="isShowCameraIntro"
  282. class="cloud-left-bottom"
  283. src="@/assets/images/cloud-left-bottom.png"
  284. alt=""
  285. draggable="false"
  286. >
  287. </transition>
  288. <transition name="cloud-right-bottom">
  289. <img
  290. v-if="isShowCameraIntro"
  291. class="cloud-right-bottom"
  292. src="@/assets/images/cloud-right-bottom.png"
  293. alt=""
  294. draggable="false"
  295. >
  296. </transition>
  297. <!-- end of 镜头切换过渡 -->
  298. <!-- 场景切换过渡 -->
  299. <video
  300. v-show="isShowSceneIntroVideoStart"
  301. ref="sceneIntrovideoStartEl"
  302. class="scene-intro-video scene-intro-video-start"
  303. :src="require(`@/assets/videos/scene-${sceneIdx + 1}-introduction.mp4`)"
  304. playsinline
  305. webkit-playsinline="true"
  306. x5-video-player-type="h5"
  307. @ended="onPlayedFirstSceneIntroVideoEnded"
  308. />
  309. <button
  310. v-show="isShowSceneIntroVideoStart"
  311. class="skip-scene-intro"
  312. :style="{
  313. left: skipBtnLeftTop.x + 'px',
  314. top: skipBtnLeftTop.y + 'px',
  315. width: skipBtnRightBottom.x - skipBtnLeftTop.x + 'px',
  316. height: skipBtnRightBottom.y - skipBtnLeftTop.y + 'px',
  317. }"
  318. @click="skipFirstSceneIntro"
  319. />
  320. <!-- end of 场景切换过渡 -->
  321. <MsgContent
  322. v-if="msgVisible"
  323. v-model:cur-index="curMsgIndex"
  324. :list="ZMFMsgList"
  325. @end="msgVisible = false"
  326. />
  327. </div>
  328. <UserGuide
  329. v-if="showGuide"
  330. @close="() => {
  331. showGuide = false
  332. // isShowCharacterDesc = true
  333. }"
  334. />
  335. <MutiRelicHotSpot
  336. v-if="isShowHotspotDetail3"
  337. :relic-info="hotspotRelicInfo"
  338. @close="isShowHotspotDetail3 = false"
  339. @open-relic-detail="(e) => {
  340. hotspotIndex = e
  341. isShowHotspotDetail = true
  342. }"
  343. />
  344. </template>
  345. <script setup>
  346. import { ref, computed, watch, onMounted, nextTick, watchEffect, onUnmounted, inject } from "vue"
  347. import { useRoute, useRouter, onBeforeRouteUpdate } from "vue-router"
  348. import { useStore } from "vuex"
  349. import CameraDesc from '@/components/CameraDesc.vue'
  350. import CharacterDesc from '@/components/CharacterDesc.vue'
  351. import { defineAsyncComponent } from 'vue'
  352. import sceneTree from '/public/sceneTree.js'
  353. import RelicDetailForHotspot from '@/components/RelicDetailForHotspot.vue'
  354. import HotspotDialog1 from '@/components/HotspotDialog-1.vue'
  355. import { useWindowSize } from '@vueuse/core'
  356. import MsgContent from '@/components/MsgContent.vue'
  357. import UserGuide from "@/components/UserGuide.vue"
  358. import OpenState from './OpenState.vue'
  359. import MutiRelicHotSpot from "@/components/MutiRelicHotSpot.vue"
  360. const GUIDE_KEY = 'is-open-guide'
  361. const msgVisible = ref(false)
  362. const curMsgIndex = ref(0)
  363. const showGuide = ref(false)
  364. const charactorSpeackPositionLeft = ref(false)
  365. const btnReturnHomeImgUrl = computed(() => {
  366. return `url(${process.env.VUE_APP_CLI_MODE === 'dev' ? '' : '../'}` + require(`@/assets/images/btn-return-home-${sceneIdx.value + 1}.png`) + ')'
  367. })
  368. const btnReturnHomeActiveImgUrl = computed(() => {
  369. return `url(${process.env.VUE_APP_CLI_MODE === 'dev' ? '' : '../'}` + require(`@/assets/images/btn-return-home-active-${sceneIdx.value + 1}.png`) + ')'
  370. })
  371. const btnOnTrack1ImgUrl = computed(() => {
  372. return `url(${process.env.VUE_APP_CLI_MODE === 'dev' ? '' : '../'}` + require(`@/assets/images/camera-btn-${sceneIdx.value + 1}-${cameraIdx.value + 1}-1.png`) + ')'
  373. })
  374. const btnOnTrack1ActiveImgUrl = computed(() => {
  375. return `url(${process.env.VUE_APP_CLI_MODE === 'dev' ? '' : '../'}` + require(`@/assets/images/camera-btn-${sceneIdx.value + 1}-${cameraIdx.value + 1}-1-active.png`) + ')'
  376. })
  377. const btnOnTrack2ImgUrl = computed(() => {
  378. return `url(${process.env.VUE_APP_CLI_MODE === 'dev' ? '' : '../'}` + require(`@/assets/images/camera-btn-${sceneIdx.value + 1}-${cameraIdx.value + 1}-2.png`) + ')'
  379. })
  380. const btnOnTrack2ActiveImgUrl = computed(() => {
  381. return `url(${process.env.VUE_APP_CLI_MODE === 'dev' ? '' : '../'}` + require(`@/assets/images/camera-btn-${sceneIdx.value + 1}-${cameraIdx.value + 1}-2-active.png`) + ')'
  382. })
  383. const btnOnTrack3ImgUrl = computed(() => {
  384. return `url(${process.env.VUE_APP_CLI_MODE === 'dev' ? '' : '../'}` + require(`@/assets/images/camera-btn-${sceneIdx.value + 1}-${cameraIdx.value + 1}-3.png`) + ')'
  385. })
  386. const btnOnTrack3ActiveImgUrl = computed(() => {
  387. return `url(${process.env.VUE_APP_CLI_MODE === 'dev' ? '' : '../'}` + require(`@/assets/images/camera-btn-${sceneIdx.value + 1}-${cameraIdx.value + 1}-3-active.png`) + ')'
  388. })
  389. const btnOnTrack4ImgUrl = computed(() => {
  390. return `url(${process.env.VUE_APP_CLI_MODE === 'dev' ? '' : '../'}` + require(`@/assets/images/camera-btn-${sceneIdx.value + 1}-1-4.png`) + ')'
  391. })
  392. const btnOnTrack4ActiveImgUrl = computed(() => {
  393. return `url(${process.env.VUE_APP_CLI_MODE === 'dev' ? '' : '../'}` + require(`@/assets/images/camera-btn-${sceneIdx.value + 1}-1-4-active.png`) + ')'
  394. })
  395. const cameraListBgUrl = computed(() => {
  396. return `url(${process.env.VUE_APP_CLI_MODE === 'dev' ? '' : '../'}` + require(`@/assets/images/camera-list-bg-${sceneIdx.value + 1}.png`) + ')'
  397. })
  398. const btnOnTrack1Name = computed(() => {
  399. return currentCameraList.value[cameraIdx.value].contentPageBtnNameList[0]
  400. })
  401. const btnOnTrack2Name = computed(() => {
  402. return currentCameraList.value[cameraIdx.value].contentPageBtnNameList[1]
  403. })
  404. const btnOnTrack3Name = computed(() => {
  405. return currentCameraList.value[cameraIdx.value].contentPageBtnNameList[2]
  406. })
  407. let CameraContent1 = defineAsyncComponent(() =>
  408. import(`@/components/CameraContent-${Number(route.query.sceneIdx) + 1}-${Number(route.query.cameraIdx) + 1}-1.vue`)
  409. )
  410. let CameraContent2 = defineAsyncComponent(() =>
  411. import(`@/components/CameraContent-${Number(route.query.sceneIdx) + 1}-${Number(route.query.cameraIdx) + 1}-2.vue`)
  412. )
  413. let CameraContent3 = defineAsyncComponent(() =>
  414. import(`@/components/CameraContent-${Number(route.query.sceneIdx) + 1}-${Number(route.query.cameraIdx) + 1}-3.vue`)
  415. )
  416. onBeforeRouteUpdate((to, from) => {
  417. console.log('to: ', to)
  418. if (to.name === route.name) {
  419. CameraContent1 = defineAsyncComponent(() =>
  420. import(`@/components/CameraContent-${Number(route.query.sceneIdx) + 1}-${Number(route.query.cameraIdx) + 1}-1.vue`)
  421. )
  422. CameraContent2 = defineAsyncComponent(() =>
  423. import(`@/components/CameraContent-${Number(route.query.sceneIdx) + 1}-${Number(route.query.cameraIdx) + 1}-2.vue`)
  424. )
  425. CameraContent3 = defineAsyncComponent(() =>
  426. import(`@/components/CameraContent-${Number(route.query.sceneIdx) + 1}-${Number(route.query.cameraIdx) + 1}-3.vue`)
  427. )
  428. }
  429. })
  430. const {
  431. windowSizeInCssForRef,
  432. windowSizeWhenDesignForRef,
  433. } = useSizeAdapt()
  434. const route = useRoute()
  435. const router = useRouter()
  436. const store = useStore()
  437. const sceneIdx = computed(() => {
  438. return Number(route.query.sceneIdx)
  439. })
  440. const cameraIdx = computed(() => {
  441. return Number(route.query.cameraIdx)
  442. })
  443. const ZMFMsgList = computed(() => {
  444. switch (sceneIdx.value) {
  445. case 0:
  446. switch (cameraIdx.value) {
  447. case 0:
  448. return [
  449. {
  450. inner: '<p>平地而起的元大都,是“大汗之城”,也是“天下大都”。</p><p>元世祖至元四年(1267)正月丁未,元大都城破土动工。刘秉忠以太保兼领中书省而总负新都城兴造之责。至元十一年(1274),元大都宫城建成,世祖皇帝忽必烈御正殿,在此接受皇太子和诸王百官的朝贺。两年之后的至元十三年(1276),元大都城建成。</p>',
  451. audio: 'scene-1-msg-1.mp3'
  452. },
  453. {
  454. inner: '请大家跟随我的脚步,一起游览这座13世纪世界上最伟大的都城建筑——元大都。',
  455. audio: 'follow-me.mp3'
  456. },
  457. {
  458. inner: '请进入场景开始游览。',
  459. audio: 'gogogo.mp3'
  460. }
  461. ]
  462. case 1:
  463. return [
  464. {
  465. inner: '<p>宫城在皇城的东部,成长方形,“周回九里三十步,东西四百八十步,南北六百十五步,高三十五尺”。</p><p>宫城南墙有三门,中央为宫城正门崇天门,约当今故宫太和殿址。宫城四角有角楼,上下三层,用琉璃瓦覆盖。宫城内主要建筑分为南北两部分,南面以大明殿为主体,北面以延春阁为主体,大明殿、延春阁平面皆采用“工”字殿(或阁)形制。</p>',
  466. audio: 'scene-1-msg-2.mp3'
  467. },
  468. ]
  469. default:
  470. return [
  471. {
  472. inner: '<p>元大都采用了自北宋以来的开放式街巷布局,衙署和民居就分布在这样的街道系统。</p><p>元大都内佛教寺院林立,大圣寿万安寺(白塔寺)、大庆寿寺(双塔寺)等都是元大都城的历史见证。</p>',
  473. audio: 'scene-1-msg-3.mp3'
  474. },
  475. ]
  476. }
  477. case 1:
  478. switch (cameraIdx.value) {
  479. case 0:
  480. return [
  481. {
  482. inner: '元代著名水利学家郭守敬向元世祖忽必烈面陈的水利六事,第一项就是修通惠河,以使运至通州的物资可以通过水路直通大都城内。至元三十年(1293)通惠河工程完成,总长 164 里,沿河设坝闸10 处。至此,从钱塘江边的杭州,沿着大运河北上,可以一直通行至大都城内的海子(积水潭)。',
  483. audio: 'scene-2-msg-1.mp3'
  484. },
  485. {
  486. inner: '请大家跟随我的脚步,一起游览流动的文化,滋养元大都的通惠河。',
  487. audio: 'follow-me2.mp3'
  488. },
  489. {
  490. inner: '请进入场景开始游览。',
  491. audio: 'gogogo.mp3'
  492. }
  493. ]
  494. case 1:
  495. return [
  496. {
  497. inner: '元大都的规划、建设,以及城市内从皇帝百官到寻常百姓所需的物资供给,也都与运河这条交通经济命脉紧密相连。充满活力的运河,不仅给元大都带来了便利的交通和山水交映的景观,更使全国的物资汇聚于元大都。',
  498. audio: 'scene-2-msg-2.mp3'
  499. }
  500. ]
  501. default:
  502. return [
  503. {
  504. inner: '四方宝货,齐集大都。水路的畅通,使得元代景德镇等地的瓷器可以比较便利地通过水路运至大都。积水潭中舳舻蔽水,大都城内繁华富庶,形成众多非常繁华的商业区。',
  505. audio: 'scene-2-msg-3.mp3'
  506. }
  507. ]
  508. }
  509. default:
  510. switch (cameraIdx.value) {
  511. case 0:
  512. return [
  513. {
  514. inner: '元大都汇聚了不少文人雅士,更萃集了历代的书画与珍宝。众多文人与艺术家和他们的收藏与创作集粹于元大都,不仅开启了元代文化新的篇章,更影响了此后艺术的发展。笔墨之间、舞台之上,是精神的寄托,是心灵的归宿。',
  515. audio: 'scene-3-msg-1.mp3'
  516. },
  517. {
  518. inner: '请大家跟随我的脚步,一起游览笔墨之间,舞台之上的文化魅力。',
  519. audio: 'follow-me3.mp3'
  520. },
  521. {
  522. inner: '请进入场景开始游览。',
  523. audio: 'gogogo.mp3'
  524. }
  525. ]
  526. default:
  527. return [
  528. {
  529. inner: '元代戏剧艺术走向成熟,特别是剧本创作的成就代表了当时文学的最高水平。散曲也大盛于元,登坛树帜,独领风骚。元大都更是集聚了关汉卿、马致远、王实甫等一批具有很高成就的杂剧创作作家。',
  530. audio: 'scene-3-msg-2.mp3'
  531. }
  532. ]
  533. }
  534. }
  535. })
  536. /**
  537. * 左下角人物相关
  538. */
  539. const isCharacterSpecialMoving = ref(0)
  540. const animationType = ref(1)
  541. const isShowCameraDesc = ref(false)
  542. const isShowCharacterDesc = ref(false)
  543. const showingContentIdx = ref(0)
  544. function onClickCharacter(anType) {
  545. // isShowCameraDesc.value = true
  546. if (!isCharacterSpecialMoving.value) {
  547. if (!msgVisible.value && !anType) {
  548. // 打开弹窗指定伸手动画
  549. animationType.value = 3
  550. } else {
  551. animationType.value = anType ? anType : Math.floor(Math.random() * 2) + 2
  552. }
  553. let duration = 0
  554. switch (animationType.value) {
  555. case 2:
  556. duration = 4000
  557. break
  558. case 3:
  559. duration = 5000
  560. break
  561. case 4:
  562. duration = 5000
  563. break
  564. case 5:
  565. duration = 3120
  566. break
  567. default:
  568. break
  569. }
  570. setTimeout(() => {
  571. isCharacterSpecialMoving.value = 1
  572. setTimeout(() => {
  573. isCharacterSpecialMoving.value = 0
  574. animationType.value = 1
  575. }, duration)
  576. }, 200)
  577. }
  578. if (!anType && !isShowHotspotDetail2.value) {
  579. msgVisible.value = true
  580. }
  581. }
  582. /**
  583. * end of 左下角人物相关
  584. */
  585. /**
  586. * 右下角镜头列表
  587. */
  588. const currentCameraList = computed(() => {
  589. return sceneTree[sceneIdx.value].cameraList
  590. })
  591. const mouseEnterCameraItemIdx = ref(-1)
  592. /**
  593. * end of 右下角镜头列表
  594. */
  595. /**
  596. * 镜头切换过渡
  597. */
  598. const isShowCameraIntro = ref(false)
  599. const cameraIntroText = computed(() => {
  600. return sceneTree[sceneIdx.value].cameraList[cameraIdx.value].desc
  601. })
  602. let cameraIntroAudioTimeoutId = null
  603. let cameraIntroAudio = null
  604. const showScene0Camera1To2Video = ref(false)
  605. const showScene0Camera2To3Video = ref(false)
  606. const showScene1Camera1To2Video = ref(false)
  607. const showScene1Camera2To3Video = ref(false)
  608. const showScene2Camera1To2Video = ref(false)
  609. const clearCameraIntroMedia = (showAction = true) => {
  610. isShowCameraIntro.value = false
  611. showScene0Camera1To2Video.value = false
  612. showScene0Camera2To3Video.value = false
  613. showScene1Camera1To2Video.value = false
  614. showScene1Camera2To3Video.value = false
  615. showScene2Camera1To2Video.value = false
  616. if (sceneIdx.value === 2 && cameraIdx.value === 1 && showAction) {
  617. setTimeout(() => {
  618. onClickCharacter(5)
  619. }, 2000)
  620. }
  621. }
  622. watch(cameraIdx, (vNew, pVal) => {
  623. if (haveShownSceneEffect.value) {
  624. console.log(vNew, pVal)
  625. if (sceneIdx.value === 0) {
  626. if (vNew === 1 && pVal === 0) {
  627. showScene0Camera1To2Video.value = true
  628. } else if (vNew === 2 && pVal === 1) {
  629. showScene0Camera2To3Video.value = true
  630. }
  631. } else if (sceneIdx.value === 1) {
  632. if (vNew === 1 && pVal === 0) {
  633. showScene1Camera1To2Video.value = true
  634. } else if (vNew === 2 && pVal === 1) {
  635. showScene1Camera2To3Video.value = true
  636. }
  637. } else if (sceneIdx.value === 2) {
  638. if (vNew === 1 && pVal === 0) {
  639. showScene2Camera1To2Video.value = true
  640. }
  641. }
  642. isShowCameraIntro.value = true
  643. charactorSpeackPositionLeft.value = !charactorSpeackPositionLeft.value
  644. cameraIntroAudioTimeoutId = setTimeout(() => {
  645. cameraIntroAudio?.pause()
  646. cameraIntroAudio = new Audio(require(`@/assets/audios/camera-intro-${sceneIdx.value + 1}-${cameraIdx.value + 1}.mp3`))
  647. cameraIntroAudio.play()
  648. cameraIntroAudio.addEventListener('ended', clearCameraIntroMedia)
  649. }, 1000)
  650. }
  651. })
  652. // 跳过按钮 功能
  653. function skipCameraIntro(showAction) {
  654. clearTimeout(cameraIntroAudioTimeoutId)
  655. cameraIntroAudio?.pause()
  656. clearCameraIntroMedia(showAction)
  657. }
  658. onUnmounted(() => {
  659. skipCameraIntro()
  660. })
  661. onBeforeRouteUpdate(() => {
  662. skipCameraIntro(false)
  663. })
  664. /**
  665. * end of 镜头切换过渡
  666. */
  667. const isShowSceneIntroVideoStart = ref(false)
  668. const isShowSceneIntroVideoEnd = ref(false)
  669. const sceneIntrovideoStartEl = ref(null)
  670. const sceneIntrovideoEndEl = ref(null)
  671. /**
  672. * 第一次进入某个场景时展示动效
  673. */
  674. const haveShownSceneEffect = computed(() => {
  675. return store.state.haveShownSceneEffect[sceneIdx.value]
  676. })
  677. watch(sceneIdx, (vNew) => {
  678. if (!haveShownSceneEffect.value) {
  679. isShowSceneIntroVideoStart.value = true
  680. nextTick(() => {
  681. sceneIntrovideoStartEl.value.play()
  682. })
  683. }
  684. }, {
  685. immediate: true
  686. })
  687. let sceneIntroAudioTimeoutId = null
  688. let audio = null
  689. /**
  690. * 禁用讲解视频跳过按钮
  691. */
  692. let jumpIntroduceDisable = false
  693. function onPlayedFirstSceneIntroVideoEnded() {
  694. store.commit('setHaveShownSceneEffect', {
  695. idx: sceneIdx.value,
  696. value: true,
  697. })
  698. isShowSceneIntroVideoStart.value = false
  699. if (sceneIdx.value === 0 && cameraIdx.value === 0) {
  700. showGuide.value = !localStorage.getItem(GUIDE_KEY)
  701. }
  702. handleBgAudio({
  703. sceneIdx: sceneIdx.value,
  704. cameraIdx: cameraIdx.value,
  705. canPlay: !isShowSceneIntroVideoStart.value
  706. })
  707. jumpIntroduceDisable = false
  708. }
  709. // 跳过按钮 位置
  710. const { width: windowWidth, height: windowHeight } = useWindowSize()
  711. const skipBtnLeftTop = ref({
  712. x: 0,
  713. y: 0,
  714. })
  715. const skipBtnRightBottom = ref({
  716. x: 0,
  717. y: 0,
  718. })
  719. watchEffect(() => {
  720. skipBtnLeftTop.value = utils.mapPosFromDraftToWindow({
  721. x: 850,
  722. y: 660,
  723. }, 'cover', 1920, 970, windowWidth.value, windowHeight.value)
  724. skipBtnRightBottom.value = utils.mapPosFromDraftToWindow({
  725. x: 1070,
  726. y: 720,
  727. }, 'cover', 1920, 970, windowWidth.value, windowHeight.value)
  728. })
  729. // 跳过按钮 功能
  730. function skipSceneIntro() {
  731. clearTimeout(sceneIntroAudioTimeoutId)
  732. audio?.pause()
  733. store.commit('setHaveShownSceneEffect', {
  734. idx: sceneIdx.value,
  735. value: true,
  736. })
  737. isShowSceneIntroVideoStart.value = false
  738. isShowSceneIntroVideoEnd.value = false
  739. sceneIntrovideoStartEl.value?.pause()
  740. sceneIntrovideoEndEl.value?.pause()
  741. }
  742. function skipFirstSceneIntro() {
  743. if (jumpIntroduceDisable) return
  744. jumpIntroduceDisable = true
  745. const video = sceneIntrovideoStartEl.value
  746. switch (sceneIdx.value) {
  747. case 0:
  748. video.currentTime = 34
  749. break
  750. case 1:
  751. video.currentTime = 42
  752. break
  753. default:
  754. video.currentTime = 32
  755. }
  756. }
  757. onUnmounted(() => {
  758. skipSceneIntro()
  759. })
  760. onBeforeRouteUpdate(() => {
  761. skipSceneIntro()
  762. })
  763. /**
  764. * end of 第一次进入某个场景时展示动效
  765. */
  766. const handleBgAudio = inject('handleBgAudio')
  767. watch(sceneIdx, () => {
  768. handleBgAudio({
  769. sceneIdx: sceneIdx.value,
  770. cameraIdx: cameraIdx.value,
  771. canPlay: !isShowSceneIntroVideoStart.value && !isShowCameraIntro.value
  772. })
  773. }, {
  774. immediate: true,
  775. })
  776. watch([cameraIdx, isShowCameraIntro], (vNew) => {
  777. if (sceneIdx.value > 0 && typeof vNew[0] === 'number') {
  778. handleBgAudio({
  779. sceneIdx: sceneIdx.value,
  780. cameraIdx: vNew[0],
  781. canPlay: !isShowSceneIntroVideoStart.value && !vNew[1]
  782. })
  783. }
  784. }, {
  785. immediate: true
  786. })
  787. /**
  788. * end of 背景音乐
  789. */
  790. /**
  791. * 点击“下一个场景”按钮的逻辑
  792. */
  793. function onClickNextScene() {
  794. if (sceneIdx.value === 0) {
  795. router.push({
  796. name: route.name,
  797. query: {
  798. sceneIdx: Number(route.query.sceneIdx) + 1,
  799. cameraIdx: 0,
  800. }
  801. })
  802. } else if (sceneIdx.value === 1) {
  803. router.push({
  804. name: route.name,
  805. query: {
  806. sceneIdx: Number(route.query.sceneIdx) + 1,
  807. cameraIdx: 0,
  808. }
  809. })
  810. } else if (sceneIdx.value === 2) {
  811. router.push({
  812. name: 'EpilogueView',
  813. })
  814. }
  815. }
  816. /**
  817. * end of 点击“下一个场景”按钮的逻辑
  818. */
  819. const currentVr = computed(() => {
  820. switch (Number(route.query.sceneIdx)) {
  821. case 0:
  822. switch (Number(route.query.cameraIdx)) {
  823. case 0:
  824. return 'fd720_kdDO8sPe6'
  825. case 1:
  826. return 'fd720_CKI5Ly4eo'
  827. default:
  828. return 'fd720_Ti8chaWqT'
  829. }
  830. case 1:
  831. switch (Number(route.query.cameraIdx)) {
  832. case 0:
  833. return 'fd720_f4R2wpbQC'
  834. case 1:
  835. return 'fd720_I2StypBk9'
  836. default:
  837. return 'fd720_tnhWSM7pp'
  838. }
  839. default:
  840. switch (Number(route.query.cameraIdx)) {
  841. case 0:
  842. return 'fd720_PC5bdcJnU'
  843. default:
  844. return 'fd720_0vzJY3UBA'
  845. }
  846. }
  847. })
  848. /**
  849. * iframe的逻辑
  850. */
  851. const iframeSrc = `${process.env.VUE_APP_CLI_MODE === 'dev' ? 'https://houseoss.4dkankan.com/project/yzdyh-dadu/pano/' : 'https://houseoss.4dkankan.com/project/yzdyh-dadu/pano/'}show.html?id=WK1730428603763576832&lang=zh&vr=${currentVr.value}`
  852. const panoIframe = ref(null)
  853. watch(cameraIdx, (vNew) => {
  854. console.log('parent window: post message!')
  855. panoIframe.value.contentWindow.postMessage({
  856. msgName: 'change-scene',
  857. sceneId: store.getters.catalogTopology[sceneIdx.value].children[0].children[cameraIdx.value].id
  858. }, '*')
  859. }, {
  860. deep: true,
  861. })
  862. const isShowHotspotDetail = ref(false)
  863. const isShowHotspotDetail2 = ref(!!route.query.hotspot)
  864. const isShowHotspotDetail3 = ref(false)
  865. const hotspotRelicInfo = ref(route.query.hotspot ? {
  866. name: route.query.hotspot
  867. } : {})
  868. const hotspotIndex = ref(null)
  869. // window.addEventListener('message', (e) => {
  870. // console.log('parent window: received message!', e)
  871. // })
  872. window.showHotspotDetail2 = function(hotspotInfo) {
  873. console.log('parent window: 展示热点高亮')
  874. console.log(hotspotInfo)
  875. console.log(hotspotInfo.hotspotTitle)
  876. hotspotRelicInfo.value = hotspotInfo
  877. msgVisible.value = false
  878. isShowHotspotDetail2.value = true
  879. }
  880. window.showHotspotDetail = function(hotspotInfo) {
  881. console.log('parent window: 展示热点详情')
  882. console.log(hotspotInfo)
  883. console.log(hotspotInfo.hotspotTitle)
  884. // 约定 hotspotTitle 为场景中宝盒索引
  885. hotspotRelicInfo.value = hotspotInfo
  886. if (hotspotInfo.hotspotTitle.split(',').length > 1) {
  887. // 多文物
  888. isShowHotspotDetail3.value = true
  889. console.log('====')
  890. } else {
  891. isShowHotspotDetail.value = true
  892. }
  893. }
  894. /**
  895. * end of iframe的逻辑
  896. */
  897. /**
  898. * 获取全景图somedata.json并处理 todo:改为从本地获取
  899. */
  900. function fixPanoData(panoData) {
  901. // 丢弃没有包含场景的二级分组
  902. let tmp = []
  903. panoData.scenes.forEach((item) => {
  904. panoData.catalogs.forEach((sub) => {
  905. if (item.category == sub.id) {
  906. if (tmp.indexOf(sub) < 0) {
  907. tmp.push(sub)
  908. }
  909. }
  910. })
  911. })
  912. tmp = utils.unique(tmp)
  913. panoData.catalogs = tmp
  914. // 丢弃没有包含二级分组的一级分组
  915. let rootmp = []
  916. tmp.forEach((item) => {
  917. panoData.catalogRoot.forEach((sub) => {
  918. sub.children = utils.unique(sub.children)
  919. if (sub.children.indexOf(item.id) > -1) {
  920. rootmp.push(sub)
  921. }
  922. })
  923. })
  924. rootmp = utils.unique(rootmp)
  925. // 一级分组按名称排序
  926. let sortArr = panoData.catalogRoot.map((item) => item.name)
  927. rootmp.sort((a, b) => {
  928. return sortArr.indexOf(a.name) - sortArr.indexOf(b.name)
  929. })
  930. // 各个一级分组的children去重,只留下有实际的二级分组相对应的那些children item。
  931. panoData.catalogRoot = rootmp.map((item) => {
  932. let temp = []
  933. item.children = utils.unique(item.children)
  934. item.children.forEach((sub) => {
  935. tmp.forEach((jj) => {
  936. if (jj.id == sub) {
  937. temp.push(sub)
  938. }
  939. })
  940. })
  941. return {
  942. ...item,
  943. children: temp,
  944. }
  945. })
  946. // 多余
  947. panoData.catalogs = tmp
  948. // 如果没有一级分组(一定也就没有二级分组)就创建一级分组和二级分组 有必要吗?
  949. let cid = "c_" + utils.randomWord(true, 8, 8)
  950. if (panoData.catalogRoot.length <= 0) {
  951. panoData.catalogRoot.push({
  952. id: "r_" + utils.randomWord(true, 8, 8),
  953. name: "全部场景",
  954. children: [cid],
  955. })
  956. }
  957. if (panoData.catalogs.length <= 0) {
  958. panoData.catalogs.push({
  959. id: cid,
  960. name: "默认二级分组",
  961. })
  962. }
  963. // 如果有初始场景,改为引用场景列表中对应的那个场景的js对象
  964. if (panoData.firstScene) {
  965. panoData.firstScene = panoData.scenes.find(
  966. (item) => item.sceneCode == panoData.firstScene.sceneCode
  967. )
  968. }
  969. }
  970. onMounted(() => {
  971. api.fetchPanoData('WK1730428603763576832').then((res) => {
  972. fixPanoData(res)
  973. store.commit('setPanoData', res)
  974. console.log('catalogTopology', store.getters.catalogTopology)
  975. })
  976. })
  977. /**
  978. * end of 获取全景图somedata.json并处理
  979. */
  980. </script>
  981. <style lang="less" scoped>
  982. *{
  983. user-select: none;
  984. }
  985. .pano-view{
  986. position: relative;
  987. height: 100%;
  988. &.reduceZ {
  989. .btn-on-track {
  990. z-index: 2 !important;
  991. }
  992. }
  993. .pano-wrap{
  994. position: absolute;
  995. left: 0;
  996. top: 0;
  997. width: 100%;
  998. height: 100%;
  999. &.hide{
  1000. opacity: 0;
  1001. }
  1002. >iframe{
  1003. position: absolute;
  1004. left: 0;
  1005. top: 0;
  1006. width: 100%;
  1007. height: 100%;
  1008. }
  1009. >button.guide-btn{
  1010. position: absolute;
  1011. width: 77px;
  1012. height: 77px;
  1013. top: 128px;
  1014. right: 51px;
  1015. background-image: url('@/assets/images/guide/tbn_help-min.png');
  1016. background-size: cover;
  1017. background-repeat: no-repeat;
  1018. background-position: center center;
  1019. z-index: 5;
  1020. }
  1021. >button.ship-btn{
  1022. position: absolute;
  1023. width: 77px;
  1024. height: 77px;
  1025. top: 128px;
  1026. right: 51px;
  1027. background-image: url('@/assets/images/ship-game/tbn_game_boat-min.png');
  1028. background-size: cover;
  1029. background-repeat: no-repeat;
  1030. background-position: center center;
  1031. z-index: 5;
  1032. &:hover{
  1033. background-image: url('@/assets/images/ship-game/btn_game boat_hover_green-min.png');
  1034. }
  1035. }
  1036. >button.return-home{
  1037. position: absolute;
  1038. width: 77px;
  1039. height: 77px;
  1040. top: 43px;
  1041. right: 51px;
  1042. background-image: v-bind(btnReturnHomeImgUrl);
  1043. background-size: cover;
  1044. background-repeat: no-repeat;
  1045. background-position: center center;
  1046. z-index: 5;
  1047. &:hover{
  1048. background-image: v-bind(btnReturnHomeActiveImgUrl);
  1049. }
  1050. }
  1051. >.camera-desc{
  1052. z-index: 7;
  1053. }
  1054. >.character-desc{
  1055. z-index: 7;
  1056. }
  1057. >.character-wrap{
  1058. position: absolute;
  1059. left: 40px;
  1060. bottom: 0;
  1061. width: 300px;
  1062. height: 452px;
  1063. >button.name{
  1064. position: absolute;
  1065. top: 50px;
  1066. left: 0;
  1067. transform: translateX(-50%);
  1068. width: 36px;
  1069. height: 178px;
  1070. z-index: 6;
  1071. font-size: 23px;
  1072. font-family: Source Han Serif SC, Source Han Serif SC;
  1073. font-weight: 400;
  1074. color: #43310E;
  1075. background-image: url(@/assets/images/people-name-bg.png);
  1076. background-size: contain;
  1077. background-repeat: no-repeat;
  1078. background-position: center center;
  1079. >span{
  1080. position: absolute;
  1081. left: 45%;
  1082. top: 50%;
  1083. transform: translate(-50%, -50%);
  1084. line-height: 27px;
  1085. letter-spacing: 7px;
  1086. writing-mode: vertical-lr;
  1087. }
  1088. }
  1089. @frame-width: 640px;
  1090. @frame-height: 480px;
  1091. @duration-1: 2.5s;
  1092. @frame-num-1: 60;
  1093. @duration-2: 4s;
  1094. @frame-num-2: 96;
  1095. @duration-3: 5s;
  1096. @frame-num-3: 121;
  1097. // @duration-4: 3s;
  1098. // @frame-num-4: 72;
  1099. @duration-5: 3.12s;
  1100. @frame-num-5: 161;
  1101. >.character-frames-wrapper {
  1102. position: absolute;
  1103. left: -200px;
  1104. top: -10px;
  1105. height: @frame-height;
  1106. width: @frame-width;
  1107. overflow: hidden;
  1108. z-index: 5;
  1109. cursor: pointer;
  1110. >.default-frames{
  1111. position: absolute;
  1112. left: 0;
  1113. height: 100%;
  1114. animation-name: character-default-animation;
  1115. animation-timing-function: steps(60, end);
  1116. animation-duration: 2.5s;
  1117. animation-iteration-count: infinite;
  1118. }
  1119. >.frames {
  1120. position: absolute;
  1121. height: 100%;
  1122. transition-property: none;
  1123. &.animating{
  1124. transition-property: left;
  1125. }
  1126. &.state1 {
  1127. left: 0;
  1128. }
  1129. }
  1130. >.frames-2{
  1131. transition-duration: @duration-2;
  1132. transition-timing-function: steps(@frame-num-2, jump-end);
  1133. &.state2 {
  1134. left: calc(-100% * (@frame-num-2));
  1135. }
  1136. }
  1137. >.frames-3{
  1138. transition-duration: @duration-3;
  1139. transition-timing-function: steps(@frame-num-3, jump-end);
  1140. &.state2 {
  1141. left: calc(-100% * (@frame-num-3));
  1142. }
  1143. }
  1144. // >.frames-4{
  1145. // transition-duration: @duration-4;
  1146. // transition-timing-function: steps(@frame-num-4 - 1, jump-end);
  1147. // &.state2 {
  1148. // left: calc(-100% * (@frame-num-4 - 1));
  1149. // }
  1150. // }
  1151. >.frames-5{
  1152. transition-duration: @duration-5;
  1153. transition-timing-function: steps(@frame-num-5, jump-end);
  1154. &.state2 {
  1155. left: calc(-100% * (@frame-num-5));
  1156. }
  1157. }
  1158. }
  1159. >img.btn-track{
  1160. position: absolute;
  1161. width: 598px;
  1162. height: 598px;
  1163. left: -150px;
  1164. bottom: -101px;
  1165. }
  1166. >button.btn-on-track{
  1167. position: absolute;
  1168. width: 78px;
  1169. height: 78px;
  1170. background-size: cover;
  1171. background-repeat: no-repeat;
  1172. background-position: center center;
  1173. text-align: left;
  1174. z-index: 6;
  1175. >span{
  1176. margin-left: 120px;
  1177. display: none;
  1178. font-size: 23px;
  1179. font-family: Source Han Serif SC, Source Han Serif SC;
  1180. font-weight: 600;
  1181. color: #FFF1BE;
  1182. line-height: 27px;
  1183. letter-spacing: 5px;
  1184. }
  1185. &:hover{
  1186. width: 390px;
  1187. height: 104px;
  1188. transform: translate(-13px, -5px);
  1189. >span{
  1190. display: initial;
  1191. }
  1192. }
  1193. }
  1194. >button.one{
  1195. left: 210px;
  1196. top: -42px;
  1197. background-image: v-bind(btnOnTrack1ImgUrl);
  1198. &:hover{
  1199. top: -49px;
  1200. left: 217px;
  1201. background-image: v-bind(btnOnTrack1ActiveImgUrl);
  1202. }
  1203. }
  1204. >button.two{
  1205. left: 336px;
  1206. top: 62px;
  1207. background-image: v-bind(btnOnTrack2ImgUrl);
  1208. &:hover{
  1209. top: 54px;
  1210. background-image: v-bind(btnOnTrack2ActiveImgUrl);
  1211. }
  1212. }
  1213. >button.three{
  1214. left: 385px;
  1215. top: 205px;
  1216. background-image: v-bind(btnOnTrack3ImgUrl);
  1217. &:hover{
  1218. top: 196px;
  1219. left: 391px;
  1220. background-image: v-bind(btnOnTrack3ActiveImgUrl);
  1221. }
  1222. }
  1223. >button.four{
  1224. left: 352px;
  1225. top: 353px;
  1226. background-image: v-bind(btnOnTrack4ImgUrl);
  1227. &:hover{
  1228. top: 344px;
  1229. left: 359px;
  1230. background-image: v-bind(btnOnTrack4ActiveImgUrl);
  1231. }
  1232. }
  1233. }
  1234. >div.camera-list{
  1235. position: absolute;
  1236. bottom: 0;
  1237. right: 0;
  1238. width: calc(1346 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  1239. height: calc(161 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  1240. background-image: v-bind(cameraListBgUrl);
  1241. background-size: cover;
  1242. background-repeat: no-repeat;
  1243. background-position: center center;
  1244. display: flex;
  1245. justify-content: flex-end;
  1246. align-items: center;
  1247. padding-top: calc(10 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  1248. padding-right: calc(204 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  1249. >button.camera-entry{
  1250. width: calc(198 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  1251. height: calc(41 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  1252. font-size: calc(21 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  1253. font-family: Source Han Serif SC, Source Han Serif SC;
  1254. font-weight: 600;
  1255. color: #FFED87;
  1256. line-height: calc(25 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  1257. letter-spacing: calc(9 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  1258. position: relative;
  1259. z-index: 0;
  1260. >img.bg-normal{
  1261. display: initial;
  1262. position: absolute;
  1263. left: 50%;
  1264. top: 50%;
  1265. transform: translate(-50%, -50%);
  1266. width: calc(198/ v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  1267. height: calc(55 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  1268. z-index: -1;
  1269. }
  1270. >img.bg-active{
  1271. display: none;
  1272. position: absolute;
  1273. left: 50%;
  1274. top: 0%;
  1275. transform: translate(-50%, -50%);
  1276. width: calc(230 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  1277. height: auto;
  1278. z-index: -1;
  1279. }
  1280. }
  1281. >button.camera-entry.active{
  1282. font-size: calc(21 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  1283. font-family: Source Han Serif SC, Source Han Serif SC;
  1284. font-weight: 600;
  1285. color: #794A00;
  1286. line-height: calc(25 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  1287. letter-spacing: calc(9 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  1288. >img.bg-normal{
  1289. display: none;
  1290. }
  1291. >img.bg-active{
  1292. display: initial;
  1293. }
  1294. }
  1295. >button.next-scene{
  1296. position: absolute;
  1297. top: calc(30 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  1298. right: calc(45 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  1299. width: calc(70 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  1300. height: calc(100 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  1301. }
  1302. }
  1303. }
  1304. >.camera-content{
  1305. z-index: 10;
  1306. }
  1307. /**
  1308. 镜头切换过渡
  1309. */
  1310. .text-wrap__character {
  1311. position: absolute;
  1312. right: 0;
  1313. bottom: 0;
  1314. width: 512px;
  1315. height: 512px;
  1316. overflow: hidden;
  1317. &.left {
  1318. right: unset;
  1319. left: 0;
  1320. img {
  1321. animation-duration: 3;
  1322. animation-timing-function: steps(72);
  1323. }
  1324. }
  1325. img {
  1326. height: 100%;
  1327. animation-name: character-default-animation;
  1328. animation-timing-function: steps(121);
  1329. animation-duration: 8s;
  1330. animation-iteration-count: infinite;
  1331. }
  1332. }
  1333. >.text-wrap{
  1334. position: absolute;
  1335. left: 0;
  1336. top: 0;
  1337. width: 100%;
  1338. height: 100%;
  1339. backdrop-filter: blur(10px);
  1340. z-index: 11;
  1341. background-color: rgba(0, 0, 0, 0.3);
  1342. >.text{
  1343. position: absolute;
  1344. left: 50%;
  1345. top: 50%;
  1346. max-width: 80%;
  1347. max-height: 80%;
  1348. overflow: auto;
  1349. transform: translate(-50%, -50%);
  1350. font-size: 23px;
  1351. font-family: SimSun;
  1352. // font-family: Source Han Sans CN, Source Han Sans CN;
  1353. font-weight: 400;
  1354. color: #FFFFFF;
  1355. // line-height: 39px;
  1356. line-height: 1.5;
  1357. letter-spacing: 7px;
  1358. text-shadow: 0px 15px 25px rgba(0,0,0,0.59);
  1359. padding-right: 10px;
  1360. }
  1361. >button.skip{
  1362. position: absolute;
  1363. width: 220px;
  1364. height: 58px;
  1365. right: 50px;
  1366. bottom: 50px;
  1367. background-image: url(@/assets/images/startup-video-skip.png);
  1368. background-size: cover;
  1369. background-repeat: no-repeat;
  1370. background-position: center center;
  1371. z-index: 1;
  1372. }
  1373. }
  1374. .cloud-top{
  1375. position: absolute;
  1376. width: 100%;
  1377. top: 0;
  1378. left: 0;
  1379. z-index: 12;
  1380. pointer-events: none;
  1381. }
  1382. .cloud-top-enter-active {
  1383. transition: all 1.5s;
  1384. }
  1385. .cloud-top-leave-active {
  1386. transition: all 1.5s;
  1387. pointer-events: none;
  1388. }
  1389. .cloud-top-enter-from {
  1390. opacity: 0;
  1391. translate: 0 -100%;
  1392. }
  1393. .cloud-top-leave-to {
  1394. opacity: 0;
  1395. top: -50%;
  1396. translate: 0 -100%;
  1397. }
  1398. .cloud-left-bottom{
  1399. position: absolute;
  1400. left: 0;
  1401. bottom: 0;
  1402. height: 70%;
  1403. z-index: 12;
  1404. pointer-events: none;
  1405. }
  1406. .cloud-left-bottom-enter-active {
  1407. transition: all 1.5s;
  1408. }
  1409. .cloud-left-bottom-leave-active {
  1410. transition: all 1.5s;
  1411. pointer-events: none;
  1412. }
  1413. .cloud-left-bottom-enter-from {
  1414. opacity: 0;
  1415. translate: -100% 100%;
  1416. }
  1417. .cloud-left-bottom-leave-to {
  1418. opacity: 0;
  1419. pointer-events: none;
  1420. translate: -100% 100%;
  1421. }
  1422. .cloud-right-bottom{
  1423. position: absolute;
  1424. right: 0;
  1425. bottom: 0;
  1426. height: 95%;
  1427. z-index: 12;
  1428. pointer-events: none;
  1429. }
  1430. .cloud-right-bottom-enter-active {
  1431. transition: all 1.5s;
  1432. }
  1433. .cloud-right-bottom-leave-active {
  1434. transition: all 1.5s;
  1435. pointer-events: none;
  1436. }
  1437. .cloud-right-bottom-enter-from {
  1438. opacity: 0;
  1439. translate: 100% 100%;
  1440. }
  1441. .cloud-right-bottom-leave-to {
  1442. opacity: 0;
  1443. pointer-events: none;
  1444. translate: 100% 100%;
  1445. }
  1446. /**
  1447. end of 镜头切换过渡
  1448. */
  1449. /**
  1450. * 场景切换过渡
  1451. */
  1452. >video.scene-intro-video{
  1453. position: absolute;
  1454. left: 0;
  1455. top: 0;
  1456. width: 100%;
  1457. height: 100%;
  1458. object-fit: cover;
  1459. z-index: 20;
  1460. }
  1461. >button.skip-scene-intro{
  1462. position: absolute;
  1463. z-index: 21;
  1464. // background-color: red;
  1465. // opacity: 0.5;
  1466. }
  1467. /**
  1468. * end of 场景切换过渡
  1469. */
  1470. }
  1471. @keyframes character-default-animation {
  1472. 0% {
  1473. translate: 0 0;
  1474. }
  1475. 100% {
  1476. translate: -100% 0;
  1477. }
  1478. }
  1479. </style>