chenlei 1 rok temu
rodzic
commit
649afc4ad9
37 zmienionych plików z 373 dodań i 132 usunięć
  1. 9 0
      public/index.html
  2. BIN
      public/relic-data/big-photo/第139页-651.png
  3. BIN
      public/relic-data/big-photo/第140页-658.png
  4. 13 13
      public/relic-data/data.json
  5. BIN
      public/relic-data/small-photo/第64页-237.png
  6. BIN
      public/relic-data/small-photo/第65页-241.png
  7. 1 1
      public/sceneTree.js
  8. 10 5
      src/App.vue
  9. BIN
      src/assets/images/20231227_155535175.png
  10. BIN
      src/assets/images/mobile-intro/scene-0-camera-0.jpg
  11. BIN
      src/assets/images/mobile-intro/scene-0-camera-1.jpg
  12. BIN
      src/assets/images/mobile-intro/scene-0-camera-2.jpg
  13. BIN
      src/assets/images/mobile-intro/scene-1-camera-0.jpg
  14. BIN
      src/assets/images/mobile-intro/scene-1-camera-1.jpg
  15. BIN
      src/assets/images/mobile-intro/scene-1-camera-2.jpg
  16. BIN
      src/assets/images/mobile-intro/scene-2-camera-0.jpg
  17. BIN
      src/assets/images/mobile-intro/scene-2-camera-1.jpg
  18. 10 1
      src/components/HotspotDialog-1.vue
  19. 8 2
      src/components/MobileCameraContent/CameraContent-1-1-1.vue
  20. 11 2
      src/components/MobileCameraContent/CameraContent-1-1-3.vue
  21. 7 1
      src/components/MobileCameraContent/CameraContent-1-2-3.vue
  22. 9 2
      src/components/MobileCameraContent/CameraContent-2-3-3.vue
  23. 0 1
      src/components/MobileCameraContent/CameraContent-3-1-2.vue
  24. 8 2
      src/components/MobileCameraContent/CameraContent-3-1-3.vue
  25. 1 1
      src/components/MobileCameraContent/PanelView.vue
  26. 4 1
      src/components/MsgContent.vue
  27. 20 16
      src/components/RelicDetailForHotspot.vue
  28. 50 4
      src/components/StartUp.vue
  29. 1 1
      src/components/UserGuide.vue
  30. 47 9
      src/hooks/usePreloader.js
  31. 79 14
      src/views/EpilogueView.vue
  32. 1 1
      src/views/HomeView.vue
  33. 22 8
      src/views/PanoView.vue
  34. 2 2
      src/views/RelicDetail.vue
  35. 2 2
      src/views/RelicList.vue
  36. 33 30
      src/views/RelicListMobile.vue
  37. 25 13
      src/views/ShipGame/ShipGameView.vue

+ 9 - 0
public/index.html

@@ -21,4 +21,13 @@
     <!-- built files will be auto injected -->
 
   </body>
+  <script>
+    var _hmt = _hmt || [];
+    (function() {
+      var hm = document.createElement("script");
+      hm.src = "https://hm.baidu.com/hm.js?267bd15e2938ad3d43b3f64f1bb9e81c";
+      var s = document.getElementsByTagName("script")[0]; 
+      s.parentNode.insertBefore(hm, s);
+    })();
+  </script>    
 </html>

BIN
public/relic-data/big-photo/第139页-651.png


BIN
public/relic-data/big-photo/第140页-658.png


Plik diff jest za duży
+ 13 - 13
public/relic-data/data.json


BIN
public/relic-data/small-photo/第64页-237.png


BIN
public/relic-data/small-photo/第65页-241.png


+ 1 - 1
public/sceneTree.js

@@ -20,7 +20,7 @@ export default [
         desc: `
         <p>感受完刘秉忠的大都城规划设计,“我”由衷地赞叹大都城的雄伟壮阔。忽接通报,世祖皇帝忽必烈要召见自己。</p>
         <br>
-        <p>接到诏令,“我”便从和义门下来 自西向东,沿外城、皇城、宫城行进。最后从宫城南部崇天门来到大明殿,接受世祖皇帝忽必烈的召见。</p>
+        <p>接到诏令,“我”便从和义门下来自西向东,沿外城、皇城、宫城行进。最后从宫城南部崇天门来到大明殿,接受世祖皇帝忽必烈的召见。</p>
         `,
         contentPageBtnNameList: [
           '皇城与宫城',

+ 10 - 5
src/App.vue

@@ -10,7 +10,7 @@
             alt=""
             draggable="false"
           >
-          <span>请横屏浏览</span>
+          <span>请打开屏幕自动旋转,进行横屏浏览</span>
           <span v-if="$isMobile && $isWeChat && $uaInfo.os.name === 'Android'">请确认微信-通用“开启横屏模式”按钮是否开启</span>
         </div>
       </div>
@@ -133,6 +133,8 @@ const HISTORY_KEY = 'yddFakeHistory'
 router.beforeEach((to, from) => {
   const cache = sessionStorage.getItem(HISTORY_KEY) ? JSON.parse(sessionStorage.getItem(HISTORY_KEY)) : []
 
+  if (from.query.back || (to.name === 'RelicListMobile' && from.name === 'RelicListMobile')) return
+
   if (cache.length && JSON.stringify({
     name: to.name,
     query: to.query
@@ -159,12 +161,15 @@ const fakeBack = () => {
     const cache = sessionStorage.getItem(HISTORY_KEY) ? JSON.parse(sessionStorage.getItem(HISTORY_KEY)) : []
     if (cache.length) {
       const info = cache.pop()
+      if (info.name === 'PanoView') {
+        info.query.back = 1
+      }
       router.replace(info).then(() => {
         sessionStorage.setItem(HISTORY_KEY, JSON.stringify(cache))
       })
     } else {
       router.replace({
-        name: 'HomeView'
+        name: 'HomeView',
       })
     }
   } else {
@@ -486,7 +491,8 @@ button.logo{
   align-items: center;
   justify-content: center;
   cursor: pointer;
-  z-index: 2;
+  background: rgba(255, 255, 255, .25);
+  z-index: 10;
 
   span {
     display: flex;
@@ -533,8 +539,7 @@ button.logo{
   }
   [class^="camera-content-"] {
     button.return {
-      left: 42px;
-      top: 30px;
+      top: 10px !important;
       width: 100px !important;
       height: 100px !important;
       z-index: 10;

BIN
src/assets/images/20231227_155535175.png


BIN
src/assets/images/mobile-intro/scene-0-camera-0.jpg


BIN
src/assets/images/mobile-intro/scene-0-camera-1.jpg


BIN
src/assets/images/mobile-intro/scene-0-camera-2.jpg


BIN
src/assets/images/mobile-intro/scene-1-camera-0.jpg


BIN
src/assets/images/mobile-intro/scene-1-camera-1.jpg


BIN
src/assets/images/mobile-intro/scene-1-camera-2.jpg


BIN
src/assets/images/mobile-intro/scene-2-camera-0.jpg


BIN
src/assets/images/mobile-intro/scene-2-camera-1.jpg


+ 10 - 1
src/components/HotspotDialog-1.vue

@@ -256,6 +256,7 @@
         <video
           autoplay
           controls
+          controlsList="nodownload"
           x5-playsinline="true"
           playsinline="true"
           webkit-playsinline="true"
@@ -354,6 +355,7 @@
         <video
           autoplay
           controls
+          controlsList="nodownload"
           x5-playsinline="true"
           playsinline="true"
           webkit-playsinline="true"
@@ -498,6 +500,7 @@
           class="dmd-video"
           autoplay
           controls
+          controlsList="nodownload"
           x5-playsinline="true"
           playsinline="true"
           webkit-playsinline="true"
@@ -865,7 +868,7 @@ const close = () => {
     animation-iteration-count: infinite;
   }
   &.spot-qy {
-    margin: -100px 0 0 200px;
+    margin: calc(-100 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef')) 0 0 200px;
   }
   &.spot-hc {
     margin-top: -160px;
@@ -1387,6 +1390,12 @@ const close = () => {
   }
 }
 
+@media screen and (max-height: 480px) {
+  .hotspot-icon.spot-qss {
+    margin-top: -50px;
+  }
+}
+
 @keyframes flashing {
   0% {
     opacity: 1;

+ 8 - 2
src/components/MobileCameraContent/CameraContent-1-1-1.vue

@@ -35,7 +35,12 @@
         class="design-wrap"
       >
         <h1>{{ title }}</h1>
-        <div class="design-wrap__container">
+        <div
+          class="design-wrap__container"
+          :style="$isAndroidFullScreen ? {
+            paddingLeft: '50px'
+          } : {}"
+        >
           <h2 class="character-name">
             <span>刘秉忠</span><span>太保兼领中书省事</span>
           </h2>
@@ -192,7 +197,7 @@
 </template>
 
 <script setup>
-import { ref, computed } from "vue"
+import { ref, computed, inject } from "vue"
 const {
   windowSizeInCssForRef,
   windowSizeWhenDesignForRef,
@@ -244,6 +249,7 @@ const isCharacterSpecialMoving2 = ref(0)
 const animationType = ref(1)
 const TIPS_COVER_KEY = 'ydd-1-1-1-tips'
 const showTipsCover = ref(!localStorage.getItem(TIPS_COVER_KEY))
+const $isAndroidFullScreen = inject('$isAndroidFullScreen')
 
 setTimeout(() => {
   isCharacterSpecialMoving1.value = 1

+ 11 - 2
src/components/MobileCameraContent/CameraContent-1-1-3.vue

@@ -6,7 +6,12 @@
       @click="emit('close')"
     />
     <div class="content-wrap">
-      <div class="left-c">
+      <div
+        class="left-c"
+        :style="$isAndroidFullScreen ? {
+          paddingLeft: '50px'
+        } : {}"
+      >
         <p class="card-title left one">
           《马可波罗行纪》中对大都城的描述
         </p>
@@ -33,12 +38,15 @@
 </template>
 
 <script setup>
+import { inject } from 'vue'
+
 const {
   windowSizeInCssForRef,
   windowSizeWhenDesignForRef,
 } = useSizeAdapt(1920, 970)
 
 const emit = defineEmits(['close'])
+const $isAndroidFullScreen = inject('$isAndroidFullScreen')
 </script>
 
 <style lang="less" scoped>
@@ -74,7 +82,8 @@ const emit = defineEmits(['close'])
     background: url(@/assets/images/mobile/bg_1-min.jpg) no-repeat center / 100% 100%;
 
     .left-c {
-      flex: 0 0 1;
+      flex: 1;
+      width: 0;
       padding-left: constant(safe-area-inset-left);
       padding-left: env(safe-area-inset-left);
 

+ 7 - 1
src/components/MobileCameraContent/CameraContent-1-2-3.vue

@@ -6,7 +6,12 @@
       @click="handleClose"
     />
     <div class="content-wrap">
-      <div class="content-wrap__left">
+      <div
+        class="content-wrap__left"
+        :style="$isAndroidFullScreen ? {
+          paddingLeft: '50px'
+        } : {}"
+      >
         <p class="card-title left">
           忽必烈召见赵孟頫
         </p>
@@ -68,6 +73,7 @@ const {
   windowSizeWhenDesignForRef,
 } = useSizeAdapt(1920, 970)
 
+const $isAndroidFullScreen = inject('$isAndroidFullScreen')
 const TIPS_COVER_KEY = 'ydd-1-2-3-tips'
 const startBgAudio = inject("startBgAudio")
 const stopBgAudio = inject("stopBgAudio")

+ 9 - 2
src/components/MobileCameraContent/CameraContent-2-3-3.vue

@@ -6,7 +6,12 @@
       @click="emit('close')"
     />
     <div class="content-wrap">
-      <div class="left-c">
+      <div
+        class="left-c"
+        :style="$isAndroidFullScreen ? {
+          paddingLeft: '50px'
+        } : {}"
+      >
         <p class="card-title left one">
           元代商品贸易使用的货币
         </p>
@@ -24,13 +29,15 @@
 </template>
 
 <script setup>
+import { inject } from 'vue'
+
 const {
   windowSizeInCssForRef,
   windowSizeWhenDesignForRef,
 } = useSizeAdapt(1920, 970)
 
 const emit = defineEmits(['close'])
-
+const $isAndroidFullScreen = inject('$isAndroidFullScreen')
 </script>
 
 <style lang="less" scoped>

+ 0 - 1
src/components/MobileCameraContent/CameraContent-3-1-2.vue

@@ -115,7 +115,6 @@ const activeTabIdx = ref(0)
   justify-content: center;
   >.left-list {
     margin-right: calc(30 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
-    width: calc(600 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
     >.left-list-item{
       .item-title,
       .item-span{

+ 8 - 2
src/components/MobileCameraContent/CameraContent-3-1-3.vue

@@ -10,7 +10,12 @@
       <div
         class="design-wrap"
       >
-        <div class="design-wrap-left">
+        <div
+          class="design-wrap-left"
+          :style="$isAndroidFullScreen ? {
+            paddingLeft: '50px'
+          } : {}"
+        >
           <p class="card-title left">
             {{ curContent.title }}
           </p>
@@ -47,7 +52,7 @@
 </template>
 
 <script setup>
-import { ref, computed } from "vue"
+import { ref, computed, inject } from "vue"
 
 const {
   windowSizeInCssForRef,
@@ -56,6 +61,7 @@ const {
 
 
 const emit = defineEmits(['close'])
+const $isAndroidFullScreen = inject('$isAndroidFullScreen')
 
 const currentSwitchIdx = ref(0)
 

+ 1 - 1
src/components/MobileCameraContent/PanelView.vue

@@ -106,7 +106,7 @@ const activeTabIdx = computed({
       position: absolute;
       width: 100px;
       height: 100px;
-      top: 30px;
+      top: 10px;
       left: 42px;
       background-image: url(@/assets/images/btn-return.png);
       background-size: contain;

+ 4 - 1
src/components/MsgContent.vue

@@ -123,7 +123,6 @@ watch(curMsg, handleAudio, {
         top: 20px;
         left: 0;
         right: 0;
-        font-size: 18px;
         color: rgba(255,255,255,0.85);
         text-align: center;
       }
@@ -166,6 +165,10 @@ watch(curMsg, handleAudio, {
     .msg-content__tips {
       width: 338px;
       top: -187px;
+
+      p {
+        font-size: 28px !important;
+      }
     }
   }
 </style>

+ 20 - 16
src/components/RelicDetailForHotspot.vue

@@ -56,15 +56,17 @@
       <h1 :title="relicInfo['名称']">
         {{ relicInfo['名称'] }}
       </h1>
-      <div
-        v-if="relicInfo['年份']"
-        class="age"
-        v-html="relicInfo['年份']"
-      />
-      <div
-        class="detail text-indent"
-        v-html="relicInfo['详细描述']"
-      />
+      <div class="right-scroll">
+        <div
+          v-if="relicInfo['年份']"
+          class="age"
+          v-html="relicInfo['年份']"
+        />
+        <div
+          class="detail text-indent"
+          v-html="relicInfo['详细描述']"
+        />
+      </div>
       <button
         class="show-hide"
         :class="{
@@ -204,6 +206,7 @@ const handleNext = () => {
       >.swiper-wrapper{
         width: 100%;
         height: 100%;
+        pointer-events: none;
         >img.swiper-slide {
           width: 100%;
           height: 100%;
@@ -234,7 +237,7 @@ const handleNext = () => {
       cursor: pointer;
     }
   }
-  >.right{
+  .right{
     height: 100%;
     flex: 0 0 auto;
     width: calc(577 / @page-width-design-px * 100vw);
@@ -247,6 +250,10 @@ const handleNext = () => {
     position: relative;
     margin-right: 0;
     transition: margin-right 0.5s;
+    &-scroll {
+      margin-top: 40px;
+      overflow: auto;
+    }
     >h1{
       flex: 0 0 auto;
       margin-top: 100px;
@@ -267,9 +274,8 @@ const handleNext = () => {
       -webkit-line-clamp: 3;
       overflow: hidden;
     }
-    >.age{
+    .age{
       flex: 0 0 auto;
-      margin-top: 40px;
       font-size: 20px;
       font-family: Source Han Sans SC, Source Han Sans SC;
       font-weight: 300;
@@ -279,7 +285,7 @@ const handleNext = () => {
       margin-left: calc(70 / 577 * 100%);
       margin-right: calc(70 / 577 * 100%);
     }
-    >.detail{
+    .detail{
       display: flex;
       flex-direction: column;
       flex: 0 1 auto;
@@ -291,8 +297,6 @@ const handleNext = () => {
       font-family: Source Han Sans SC, Source Han Sans SC;
       font-weight: 300;
       color: #000000;
-      // letter-spacing: 4px;
-      overflow: auto;
       padding-right: calc(35 / 577 * 100%);
       word-wrap: break-word;
       white-space: pre-wrap;
@@ -301,7 +305,7 @@ const handleNext = () => {
         line-height: 30px;
       }
     }
-    >button.show-hide{
+    button.show-hide{
       position: absolute;
       left: 0;
       top: 50%;

+ 50 - 4
src/components/StartUp.vue

@@ -1,5 +1,8 @@
 <template>
-  <div class="start-up">
+  <div
+    v-if="loaded"
+    class="start-up"
+  >
     <!-- <div class="title-wrap">
       <img
         class="title"
@@ -24,6 +27,10 @@
       class="progress"
     >
       {{ loadingProgress }}%
+
+      <p>
+        加载中,请耐心等待
+      </p>
     </div>
     <transition name="btn-begin-fade-in">
       <button
@@ -37,7 +44,6 @@
       src="@/assets/images/startup-animation.png"
       alt=""
       draggable="false"
-      @load="start"
     >
     <div
       v-show="isShowVideo"
@@ -66,7 +72,7 @@
 </template>
 
 <script setup>
-import { ref, computed, watch, inject } from "vue"
+import { ref, computed, watch, inject, onMounted } from "vue"
 import { useStore } from "vuex"
 // import startVideo from '@/assets/videos/start-up-video.mp4'
 // import startVideoMobile from '@/assets/videos/start-up-video-mb.mp4'
@@ -82,7 +88,35 @@ const { percent: loadingProgress, status, start, reload } = usePreloader({
     store.commit('declareCanStart', videoBlobMap)
   },
 })
-const startUpVideo = computed(() => store.getters.videoBlobMap?.get(`start-up-video${$isMobile ? '-mb' : ''}`))
+
+const {
+  windowSizeInCssForRef,
+  windowSizeWhenDesignForRef,
+} = useSizeAdapt(1920, 968)
+const loaded = ref(false)
+
+onMounted(() => {
+  Promise.all([
+    preloaderImage(require('@/assets/images/start-up-bg.jpg'))
+  ]).then(() => {
+    loaded.value = true
+    start()
+  })
+})
+
+const preloaderImage = (url) => {
+  return new Promise(res => {
+    const img = new Image()
+    img.onload = () => {
+      res()
+    }
+    img.src = url
+  })
+}
+
+const startUpVideo = computed(() => {
+  return store.getters.videoBlobMap?.get(`start-up-video${$isMobile ? '-mb' : ''}`)
+})
 
 const canStart = computed(() => {
   return store.state.canStart
@@ -110,6 +144,7 @@ watch(haveShownStartUp, (v) => {
 
 function onVideoEnd() {
   store.commit('setHaveShownStartUp', true)
+  URL.revokeObjectURL(startUpVideo)
 }
 
 // const startUpAudio = new Audio(startupVoiceUrl)
@@ -167,6 +202,14 @@ function onClickSkip() {
     transform: translate(-50%, -50%);
     color: #614B39;
     font-size: 36px;
+    text-align: center;
+
+    p {
+      margin-top: calc(20 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
+      color: #AA503C;
+      font-family: "SourceHanSerifCN-SemiBold" !important;
+      font-size: 30px !important;
+    }
   }
   >button.start{
     position: absolute;
@@ -236,5 +279,8 @@ function onClickSkip() {
   .start-up > .video-wrap > button.skip {
     width: 320px;
   }
+  .start-up > .progress p {
+    font-size: 40px !important;
+  }
 }
 </style>

+ 1 - 1
src/components/UserGuide.vue

@@ -99,7 +99,7 @@
         <div class="guide-box">
           <div class="guide-box__contain">
             <img
-              src="https://4dkk.4dage.com/720yun_fd_manage/fodder/20231227_155535175.png"
+              src="@/assets/images/20231227_155535175.png"
             >
           </div>
 

+ 47 - 9
src/hooks/usePreloader.js

@@ -1,5 +1,9 @@
 import { ref } from "vue"
 import { throttle } from "lodash"
+import UAParser from "@/libs/ua-parser.min.js"
+
+const uaParser = new UAParser()
+const uaInfo = uaParser.getResult()
 
 const parseUrlName = (url) => {
   const regex = /\/([^/]+?)(?:\.[^.]+)(?:$|\.)/
@@ -39,17 +43,20 @@ export function usePreloader(params) {
 
   const setThrottlePercent = throttle((val) => percent.value = val, 200)
 
-  const handlePreload = (url) => {
+  const handlePreload = (url, retry = 3, loadedLength = 0) => {
     const controller = new AbortController()
     const signal = controller.signal
 
     return new Promise((res, rej) => {
+      let loadingLength = 0
       const isVideo = isVideoUrl(url)
 
       fetch(url, { signal })
         .then((response) => {
           const contentLength = Number(response.headers.get("Content-Length"))
-          fileSizeStack.push(contentLength)
+          if (retry === 3) {
+            fileSizeStack.push(contentLength)
+          }
 
           if (response.ok) {
             return response.body
@@ -66,13 +73,23 @@ export function usePreloader(params) {
           const chunks = []
           const reader = body.getReader()
           const loop = true
+          let diffed = false
 
           while (loop) {
             const { value, done } = await reader.read()
 
             if (done) break
 
-            downloadLength += value.length
+            loadingLength += value.length
+            if (loadingLength >= loadedLength) {
+              if (!diffed && loadedLength) {
+                const diff = loadingLength - loadedLength
+                downloadLength += diff
+                diffed = true
+              }
+
+              downloadLength += value.length
+            }
 
             if (isVideo) {
               chunks.push(value)
@@ -86,7 +103,7 @@ export function usePreloader(params) {
                 (downloadLength / mediaSize) * 100 * decimals
               )
 
-              setThrottlePercent((percent / decimals).toFixed(params.decimals))
+              setThrottlePercent(Math.min((percent / decimals).toFixed(params.decimals), 100))
             }
           }
 
@@ -98,11 +115,20 @@ export function usePreloader(params) {
           res(true)
         })
         .catch((err) => {
-          if (isVideo) {
-            videoBlobMap.set(parseUrlName(url), url)
+          if (retry > 0) {
+            console.log('尝试重新预加载')
+            setTimeout(() => {
+              handlePreload(url, retry - 1, loadingLength)
+                .then(res)
+                .catch(rej)
+            }, 2000)
+          } else {
+            if (isVideo) {
+              videoBlobMap.set(parseUrlName(url), url)
+            }
+            console.log(url, err)
+            rej(err)
           }
-          console.log(url, err)
-          rej(err)
         })
 
       fetchControllerStack.push(controller)
@@ -113,8 +139,20 @@ export function usePreloader(params) {
     if (status.value !== 'waiting') return
 
     status.value = 'loading'
+    let copyList = [...params.list]
+
+    // if (uaInfo.browser.name === 'Android Browser' && Number(uaInfo.browser.version) < 4.4) {
+    if (navigator.userAgent.match(/android/i)) {
+      // 不支持 blob url
+      const videoList = copyList.filter(url => isVideoUrl(url))
+      copyList = copyList.filter(url => !isVideoUrl(url))
+
+      videoList.filter(url => isVideoUrl(url)).forEach(url => {
+        videoBlobMap.set(parseUrlName(url), url)
+      })
+    }
 
-    Promise.all(params.list.map((url) => handlePreload(url)))
+    Promise.all(copyList.map((url) => handlePreload(url)))
       .then(() => {
         params.cb?.(videoBlobMap)
         status.value = 'done'

+ 79 - 14
src/views/EpilogueView.vue

@@ -1,5 +1,8 @@
 <template>
-  <div class="epilogue-view">
+  <div
+    v-loading="loading"
+    class="epilogue-view"
+  >
     <img
       class="character"
       src="@/assets/images/character-zhaomengfu-2.png"
@@ -8,18 +11,33 @@
     >
 
     <transition name="fade-out">
-      <video
+      <div
         v-if="isShowVideo"
         class="epilogue-video"
-        src="@/assets/videos/epilogue.mp4"
-        autoplay
-        x5-playsinline="true"
-        playsinline="true"
-        webkit-playsinline="true"
-        x-webkit-airplay="true"
-        x5-video-player-type="h5-page"
-        @ended="isShowVideo = false"
-      />
+      >
+        <video
+          ref="videoRef"
+          src="@/assets/videos/epilogue.mp4"
+          autoplay
+          x5-playsinline="true"
+          playsinline="true"
+          webkit-playsinline="true"
+          x-webkit-airplay="true"
+          x5-video-player-type="h5-page"
+          @play="showVideoPlay = false"
+          @ended="isShowVideo = false"
+        />
+
+        <img
+          v-if="$isMobile && showVideoPlay"
+          class="play-icon"
+          src="@/assets/images/play.png"
+          @click="() => {
+            videoRef?.play()
+            showVideoPlay = false
+          }"
+        >
+      </div>
     </transition>
     <img
       v-show="isShowVideo"
@@ -79,10 +97,18 @@
       </div>
     </div>
   </div>
+
+  <iframe
+    v-if="showIframe"
+    class="ep-iframe"
+    :src="iframeUrl"
+    frameborder="0"
+    @load="loading = false"
+  />
 </template>
 
 <script setup>
-import { inject, onMounted, ref } from "vue"
+import { inject, onMounted, onUnmounted, ref } from "vue"
 // import QRCode from 'qrcode'
 import { useRouter } from "vue-router"
 
@@ -91,14 +117,31 @@ const {
   windowSizeWhenDesignForRef,
 } = useSizeAdapt(1920, 970)
 
+const showIframe = ref(false)
+const showVideoPlay = ref(true)
+const videoRef = ref()
+const iframeUrl = process.env.VUE_APP_CLI_MODE === 'dev' ? 'https://houseoss.4dkankan.com/project/yuanDaDu/index.html' : `${location.origin}${location.pathname.substring(0, location.pathname.indexOf('/yzdyh-dadu/'))}/yuanDaDu/index.html`
+
+const closeIframe = (e) => {
+  if (typeof e.data === "object" && e.data.command === "back") {
+    showIframe.value = false
+  }
+}
+
 const $isMobile = inject('$isMobile')
 const router = useRouter()
 const isShowVideo = ref(true)
+const loading = ref(false)
 const stopBgAudio = inject('stopBgAudio')
 // const homeUrl = location.origin + location.pathname
 
 onMounted(() => {
   stopBgAudio()
+
+  window.addEventListener("message", closeIframe)
+})
+onUnmounted(() => {
+  window.removeEventListener("message", closeIframe)
 })
 
 // const imageUrl = ref('')
@@ -107,12 +150,29 @@ onMounted(() => {
 // })
 
 function onClickOfflineMuseumEntry() {
-  location.assign('https://houseoss.4dkankan.com/project/yuanDaDu/index.html')
+  loading.value = true
+  showIframe.value = true
 }
 
 </script>
 
 <style lang="less" scoped>
+.play-icon {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  width: 120px;
+  transform: translate(-50%, -50%);
+  z-index: 1;
+}
+.ep-iframe {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 9999;
+}
 .epilogue-view{
   position: absolute;
   left: 0;
@@ -145,7 +205,7 @@ function onClickOfflineMuseumEntry() {
     cursor: pointer;
     z-index: 4;
   }
-  >video.epilogue-video{
+  .epilogue-video{
     position: absolute;
     left: 0;
     top: 0;
@@ -154,6 +214,11 @@ function onClickOfflineMuseumEntry() {
     background-color: black;
     object-fit: cover;
     z-index: 3;
+
+    video {
+      width: 100%;
+      height: 100%;
+    }
   }
   >img.handwritting{
     width: 744px;

+ 1 - 1
src/views/HomeView.vue

@@ -96,7 +96,7 @@
     </button>
     <div class="btn-group">
       <button
-        v-for="idx in 3"
+        v-for="idx of 3"
         :key="idx"
         :class="`scene-entry entry-${idx} ${($isMobile && idx === hoveringEntryIdx) ? 'hover' : ''}`"
         @mouseenter="!$isMobile && (hoveringEntryIdx = idx)"

+ 22 - 8
src/views/PanoView.vue

@@ -355,7 +355,8 @@
           >
           <video
             ref="videoRef"
-            :src="require(`@/assets/videos/mobile-intro/${toggleCameraVideoName}`)"
+            :poster="require(`@/assets/images/mobile-intro/${toggleCameraVideoName}.jpg`)"
+            :src="require(`@/assets/videos/mobile-intro/${toggleCameraVideoName}.mp4`)"
             autoplay
             x5-playsinline="true"
             playsinline="true"
@@ -487,7 +488,7 @@ let screensaverTimer = null
 
 const charactorSpeackPositionLeft = ref(false)
 
-const videoIntroUrl = computed(() => store.getters.videoBlobMap?.get(`scene-${sceneIdx.value + 1}-introduction${$isMobile ? '-m' : ''}`))
+const videoIntroUrl = computed(() => store.getters.videoBlobMap?.get(`scene-${sceneIdx.value + 1}-introduction${$isMobile ? '-m' : ''}`) || '')
 
 const btnReturnHomeImgUrl = computed(() => {
   return `url(${process.env.VUE_APP_CLI_MODE === 'dev' ? '' : '../'}` + require(`@/assets/images/btn-return-home-${sceneIdx.value + 1}.png`) + ')'
@@ -793,7 +794,7 @@ watch(cameraIdx, (vNew, pVal) => {
         showScene2Camera1To2Video.value = true
       }
     }
-    toggleCameraVideoName.value = `scene-${sceneIdx.value}-camera-${vNew}.mp4`
+    toggleCameraVideoName.value = `scene-${sceneIdx.value}-camera-${vNew}`
     isShowCameraIntro.value = true
     charactorSpeackPositionLeft.value = !charactorSpeackPositionLeft.value
     if (!$isMobile) {
@@ -847,9 +848,9 @@ watch(sceneIdx, (vNew) => {
   } else if (!$isMobile) {
     // pc展示屏保
     showScreensaver.value = true
-  } else {
+  } else if (!route.query.back) {
     // 移动展示转场视频
-    toggleCameraVideoName.value = `scene-${sceneIdx.value}-camera-0.mp4`
+    toggleCameraVideoName.value = `scene-${sceneIdx.value}-camera-0`
     isShowCameraIntro.value = true
   }
 }, {
@@ -869,7 +870,7 @@ function onPlayedFirstSceneIntroVideoEnded() {
   })
   isShowSceneIntroVideoStart.value = false
 
-  if (sceneIdx.value === 0 && cameraIdx.value === 0) {
+  if (cameraIdx.value === 0) {
     const guideLocal = !localStorage.getItem(GUIDE_KEY)
     showGuide.value = guideLocal
 
@@ -888,6 +889,8 @@ function onPlayedFirstSceneIntroVideoEnded() {
   })
 
   jumpIntroduceDisable = false
+
+  URL.revokeObjectURL(videoIntroUrl)
 }
 
 // 跳过按钮 位置
@@ -1042,7 +1045,7 @@ const currentVr = computed(() => {
 /**
  * iframe的逻辑
  */
-const iframeSrc = `${process.env.VUE_APP_CLI_MODE === 'dev' ? 'http://192.168.0.44:8081/' : 'https://houseoss.4dkankan.com/project/yzdyh-dadu/pano/'}show.html?id=WK1730428603763576832&lang=zh&vr=${currentVr.value}`
+const iframeSrc = `${process.env.VUE_APP_CLI_MODE === 'dev' ? 'http://192.168.0.44:8081/' : `${location.origin}${location.pathname.substring(0, location.pathname.indexOf('/flexible/'))}/pano/`}show.html?id=WK1730428603763576832&lang=zh&vr=${currentVr.value}`
 const panoIframe = ref(null)
 
 watch(cameraIdx, (vNew) => {
@@ -1072,7 +1075,7 @@ const isShowHotspotDetail3 = ref(false)
 const hotspotRelicInfo = ref(route.query.hotspot ? {
   name: route.query.hotspot
 } : {
-  // hotspotTitle: '16,11,13'
+  // hotspotTitle: '1,2,13'
 })
 const hotspotIndex = ref(null)
 
@@ -1218,6 +1221,17 @@ onMounted(() => {
       }
     }, 1000)
   }
+
+  if (route.query.back) {
+    const clone = {
+      ...route.query
+    }
+    delete clone.back
+    router.replace({
+      name: 'PanoView',
+      query: clone
+    })
+  }
 })
 
 /**

+ 2 - 2
src/views/RelicDetail.vue

@@ -53,8 +53,8 @@
         hide: !isShowDesc,
       }"
     >
-      <h1 :title="relicInfo['名称']">
-        {{ relicInfo['名称'] }}
+      <h1 :title="relicInfo['名称'].replace(/<br\s*\/?>/g, '')">
+        {{ relicInfo['名称'].replace(/<br\s*\/?>/g, '') }}
       </h1>
       <div
         class="age"

+ 2 - 2
src/views/RelicList.vue

@@ -60,8 +60,8 @@
           <div
             class="name"
             :class="{
-              wide: item['名称'].length > 9,
-              smallFontSize: item['名称'].length > 18,
+              wide: item['名称'].replace(/<br\s*\/?>/g, '').length > 9,
+              smallFontSize: item['名称'].replace(/<br\s*\/?>/g, '').length > 17,
             }"
             :title="item['名称']"
             v-html="item['名称']"

+ 33 - 30
src/views/RelicListMobile.vue

@@ -2,7 +2,9 @@
   <div class="relic-list">
     <button
       class="return"
-      @click="fakeBack"
+      @click="() => fakeBack({
+        hideIntroVideo: 1
+      })"
     />
     <el-cascader
       v-model="cascaderValue"
@@ -62,7 +64,7 @@
             class="name"
             :class="{
               wide: item['名称'].length > 9,
-              smallFontSize: item['名称'].length > 18,
+              smallFontSize: item['名称'].length > 17,
             }"
             :title="item['名称']"
             v-html="item['名称']"
@@ -77,7 +79,7 @@
       </div>
     </div>
   </div>
-  <div style="height: 100vh;" />
+  <div :style="{height: `${clientHeight}px`}" />
 </template>
 
 <script setup>
@@ -97,6 +99,7 @@ const SCROLL_KEY = 'relicListScrollLeft'
 const route = useRoute()
 const router = useRouter()
 const store = useStore()
+const clientHeight = document.body.clientHeight
 
 const { height: windowHeight } = useWindowSize()
 
@@ -251,12 +254,20 @@ onMounted(() => {
 const handleScroll = debounce(() => {
   listEl.value && localStorage.setItem(SCROLL_KEY, listEl.value.scrollLeft)
 }, 100)
+
+const {
+  windowSizeInCssForRef,
+  windowSizeWhenDesignForRef,
+} = useSizeAdapt(1920, 968)
 </script>
 
 <style lang="less" scoped>
 @page-height-design-px: 970;
 
 .relic-list {
+  display: flex;
+  flex-direction: column;
+  justify-content: flex-end;
   height: 100vh;
   background-image: url(@/assets/images/relic-list-bg.jpg);
   background-size: cover;
@@ -267,8 +278,8 @@ const handleScroll = debounce(() => {
     position: absolute;
     left: calc(42 / 970* 100vh);
     top: calc(5 / 970* 100vh);
-    width: calc(110 / 970* 100vh);
-    height: calc(110 / 970* 100vh);
+    width: calc(110 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
+    height: calc(110 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
     background-image: url(@/assets/images/btn-return.png);
     background-size: contain;
     background-repeat: no-repeat;
@@ -278,10 +289,10 @@ const handleScroll = debounce(() => {
   :deep {
     .el-cascader {
       position: absolute;
-      width: calc(250 / @page-height-design-px * 100vh);
-      height: calc(70 / @page-height-design-px * 100vh);
+      width: calc(250 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
+      height: calc(70 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
       left: calc(190 / @page-height-design-px * 100vh);
-      top: calc(26 / @page-height-design-px * 100vh);
+      top: calc(26 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
       z-index: 1;
 
       .el-input {
@@ -309,7 +320,7 @@ const handleScroll = debounce(() => {
         }
         .icon-arrow-down {
           font-size: calc(24 / @page-height-design-px * 100vh);
-          margin-top: calc(10 / @page-height-design-px * 100vh);
+          margin-top: calc(10 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
         }
 
       }
@@ -321,35 +332,35 @@ const handleScroll = debounce(() => {
 
   >.search-ui {
     position: absolute;
-    top: calc(26 / @page-height-design-px * 100vh);
+    top: calc(26 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
     right: calc(27 / @page-height-design-px * 100vh);
+    padding: calc(10 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef')) 0;
     width: calc(502 / @page-height-design-px * 100vh);
-    height: calc(70 / @page-height-design-px * 100vh);
+    height: calc(70 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
     background-image: url(@/assets/images/search-bg.png);
     background-size: cover;
     background-repeat: no-repeat;
     background-position: center center;
     z-index: 1;
+    box-sizing: border-box;
 
     >input {
       position: absolute;
       left: 50px;
       top: 50%;
       transform: translateY(-50%);
-      height: calc(35 / @page-height-design-px * 100vh);
-      width: calc(350 / @page-height-design-px * 100vh);
-      font-size: calc(30 / @page-height-design-px * 100vh);
+      height: 100%;
+      width: calc(350 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
+      font-size: calc(30 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
       font-family: Source Han Sans CN, Source Han Sans CN;
       font-weight: 400;
       color: rgba(255, 255, 255, 0.7);
-      line-height: calc(28 / @page-height-design-px * 100vh);
 
       &::placeholder {
-        font-size: calc(30 / @page-height-design-px * 100vh);
+        font-size: calc(30 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
         font-family: Source Han Sans CN, Source Han Sans CN;
         font-weight: 400;
         color: rgba(255, 255, 255, 0.3);
-        line-height: calc(28 / @page-height-design-px * 100vh);
       }
     }
 
@@ -371,14 +382,12 @@ const handleScroll = debounce(() => {
   >.the-list {
     position: absolute;
     left: 0;
-    top: 55%;
-    transform: translateY(-50%);
+    bottom: 0;
     width: 100%;
-    height: calc(650 / @page-height-design-px * 100vh);
+    height: calc(850 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
     background-size: auto 100%;
     background-repeat: no-repeat;
     background-position: left center;
-    padding-left: calc(54 / @page-height-design-px * 100vh);
     box-sizing: border-box;
 
     &.scene1 {
@@ -528,19 +537,13 @@ const handleScroll = debounce(() => {
 
 @media screen and (max-height: 480px) {
   .relic-list > .the-list {
-    top: unset;
-    bottom: 0;
-    padding-left: 0;
-    height: calc(850 / 970* 100vh);
-    transform: unset;
-
     .content-wrap {
       margin-top: 0;
       height: inherit;
 
       .first-item,
       .relic-item {
-        width: calc(450 / 970* 100vh* 0.83);
+        width: calc(450 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
         height: inherit;
       }
       .relic-item {
@@ -561,10 +564,10 @@ const handleScroll = debounce(() => {
           }
         }
         &.isOdd > img {
-          bottom: calc(425 / 970* 100vh* 0.83);
+          bottom: calc(340 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
         }
         &.isEven > img {
-          bottom: calc(215 / 970* 100vh* 0.83);
+          bottom: calc(140 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
         }
       }
     }

+ 25 - 13
src/views/ShipGame/ShipGameView.vue

@@ -79,11 +79,13 @@
             curDragKey = item.key
           }"
         >
-          <img
-            :class="`module${index + 1}`"
-            draggable="false"
-            :src="item.img"
-          >
+          <div class="ship-game-footer__item__cover">
+            <img
+              :class="`module${index + 1}`"
+              draggable="false"
+              :src="item.img"
+            >
+          </div>
 
           <div
             v-if="checkedModule.includes(item.key)"
@@ -157,7 +159,7 @@ const showVideo = ref(false)
 const {
   windowSizeInCssForRef,
   windowSizeWhenDesignForRef,
-} = useSizeAdapt(1920, 970)
+} = useSizeAdapt(1920, 1080)
 const MODULE_LIST = [
   {
     label: '甲板',
@@ -252,22 +254,22 @@ const back = () => {
   }
 }
 .drag-class {
-  &.module1 {
+  .module1 {
     width: calc(986 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
   }
-  &.module2 {
+  .module2 {
     width: calc(945 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
   }
-  &.module3 {
+  .module3 {
     width: calc(221 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
   }
-  &.module4 {
+  .module4 {
     width: calc(271 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
   }
-  &.module5 {
+  .module5 {
     width: calc(147 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
   }
-  &.module6 {
+  .module6 {
     width: calc(806 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
   }
 }
@@ -314,7 +316,7 @@ const back = () => {
   }
   &-container {
     position: absolute;
-    top: 40px;
+    top: calc(80 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
     left: 50%;
     width: calc(1144 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
     height: calc(611 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
@@ -373,6 +375,16 @@ const back = () => {
       box-sizing: border-box;
       background: #524B4B;
 
+      &::before {
+        content: '';
+        position: absolute;
+        top: 0;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        cursor: pointer;
+        z-index: 1;
+      }
       &.checked {
         user-select: none;