lanxin 4 dagar sedan
förälder
incheckning
9b3bca41be

+ 22 - 0
pano/public/tour.xml

@@ -316,5 +316,27 @@
 
 	</scene>
 
+	
+	
+	<scene name="scene_center1" title="center1" onstart="" thumburl="panos/center1.tiles/thumb.jpg" lat="45.75742156" lng="126.63626586" heading="0.0">
+
+		<view hlookat="0.0" vlookat="0.0" fovtype="MFOV" fov="120" maxpixelzoom="2.0" fovmin="70" fovmax="140" limitview="auto" />
+
+		<preview url="panos/center1.tiles/preview.jpg" />
+
+		<image type="CUBE" prealign="0|0.0|0" multires="true" tilesize="512">
+			<level tiledimagewidth="4608" tiledimageheight="4608">
+				<cube url="panos/center1.tiles/%s/l3/%0v/l3_%s_%0v_%0h.jpg" />
+			</level>
+			<level tiledimagewidth="2304" tiledimageheight="2304">
+				<cube url="panos/center1.tiles/%s/l2/%0v/l2_%s_%0v_%0h.jpg" />
+			</level>
+			<level tiledimagewidth="1152" tiledimageheight="1152">
+				<cube url="panos/center1.tiles/%s/l1/%0v/l1_%s_%0v_%0h.jpg" />
+			</level>
+		</image>
+
+	</scene>
+
 
 </krpano>

+ 69 - 1
pano/src/components/Pano/index.tsx

@@ -16,7 +16,7 @@ interface ISceneProps extends Partial<SceneProps> {
 }
 
 const Panoramic = ({ openHot }: { openHot: (id: string) => void }) => {
-  const [currentScene, setCurrentScene] = useState('p6')
+  const [currentScene, setCurrentScene] = useState('center1')
   window.setCurrentScene = (name: string) => {
     setCurrentScene(name)
   }
@@ -32,6 +32,74 @@ const Panoramic = ({ openHot }: { openHot: (id: string) => void }) => {
   const CENTER_SCENE_LIST = useMemo<ISceneProps[]>(
     () => [
       {
+        name: 'center1',
+        thumbUrl: 'panos/center1.tiles/thumb.jpg',
+        previewUrl: 'panos/center1.tiles/preview.jpg',
+        imageTagAttributes: {
+          type: 'cube',
+          tileSize: 512,
+          multires: true
+        },
+        images: [
+          {
+            tiledImageWidth: 4608,
+            tiledImageHeight: 4608,
+            url: 'panos/center1.tiles/%s/l3/%0v/l3_%s_%0v_%0h.jpg'
+          },
+          {
+            tiledImageWidth: 2304,
+            tiledImageHeight: 2304,
+            url: 'panos/center1.tiles/%s/l2/%0v/l2_%s_%0v_%0h.jpg'
+          },
+          {
+            tiledImageWidth: 1152,
+            tiledImageHeight: 1152,
+            url: 'panos/center1.tiles/%s/l1/%0v/l1_%s_%0v_%0h.jpg'
+          }
+        ],
+        children: (
+          <>
+            <View
+              hlookat={0}
+              vlookat={40}
+              fovType='MFOV'
+              fov={120}
+              maxPixelZoom={2}
+              fovMin={70}
+              fovMax={180}
+              limitView='range'
+              vlookatMax={90}
+              vlookatMin={-55}
+            />
+
+            <HotSpot
+              name='dccgftTitle'
+              type='text'
+              atv={25.83}
+              ath={-1.08}
+              scale={0.5}
+              edge='top'
+              bg={false}
+              distorted={true}
+              onOver={() => {}}
+              // onHover={() => setHotspotHover(true)}
+              // onOut={() => setHotspotHover(false)}
+              // onDown='draggable_hotspot()'
+              onClick={() => {
+                    if (window.parent) {
+                      console.log('setIsHotPano')
+                  window.parent.window.gotoScene()
+                }
+              }}
+            >
+              <div className='A0hotspot'>
+                <div className='text'>革命领袖视察黑龙江纪念馆</div>
+              </div>
+            </HotSpot>
+          </>
+        )
+      },
+      {
         name: 'p1',
         thumbUrl: 'panos/p1.tiles/thumb.jpg',
         previewUrl: 'panos/p1.tiles/preview.jpg',

+ 5 - 0
project/public/index.html

@@ -24,6 +24,11 @@
       src: url('./syst.otf') format('opentype');
       font-display: swap;
     }
+    * {
+      .nameEn {
+        font-family: 'syst';
+      }
+    }
   </style>
   <script src="./jsmpeg.min.js"></script>
   <script src="./f-video.js"></script>

+ 1 - 1
project/public/three/data.js

@@ -2631,7 +2631,7 @@ const myDataTemp = {
       name: '纪录片',
       time: '纪录片',
       imgSrc: 'myData/img/Atest2.png',
-      videoSrc: 'myData/media/bgVideo.mp4',
+      videoSrc: 'myData/media/Record.mp4',
       desc: '纪录片'
     }
   ],

BIN
project/src/assets/img/loading_bg.jpg


BIN
project/src/assets/img/loading_logo.png


+ 184 - 121
project/src/pages/A0base/index.tsx

@@ -1,136 +1,168 @@
-import React, { useEffect, useLayoutEffect, useState, useMemo, useRef, useCallback } from 'react';
-import history from '@/utils/history';// 如果用 react-router 跳转
-import styles from './index.module.scss';
-import A0_home_1 from '@/assets/img/A0_home_1.jpg';
-import A0_home_2 from '@/assets/img/A0_home_2.jpg';
-import A0_home_3 from '@/assets/img/A0_home_3.jpg';
-import homeLogo from '@/assets/img/A0_home_logo.png'; // 推荐直接 import 图片
-import { baseOssUrl } from '@/utils/http';
-
-/** 以「页面上真实 img」为准:load + decode + 双 rAF,避免与独立 new Image() 不同步 */
-function useDomImageReady(
-  ref: React.RefObject<HTMLImageElement | null>,
-  onReady: () => void,
-) {
-  const onReadyRef = useRef(onReady);
-  onReadyRef.current = onReady;
-  const firedRef = useRef(false);
+import React, { useEffect, useLayoutEffect, useState, useMemo, useRef, useCallback } from 'react'
+import history from '@/utils/history'
+import styles from './index.module.scss'
 
-  useLayoutEffect(() => {
-    const el = ref.current;
-    if (!el) return;
+import A0_home_1 from '@/assets/img/A0_home_1.jpg'
+import A0_home_2 from '@/assets/img/A0_home_2.jpg'
+import A0_home_3 from '@/assets/img/A0_home_3.jpg'
+import homeLogo from '@/assets/img/A0_home_logo.png'
+import { baseOssUrl } from '@/utils/http'
 
-    const fire = () => {
-      if (firedRef.current) return;
-      firedRef.current = true;
-      void (async () => {
-        try {
-          if (typeof el.decode === 'function') await el.decode();
-        } catch {
-          /* ignore */
-        }
-        requestAnimationFrame(() => {
-          requestAnimationFrame(() => {
-            onReadyRef.current();
-          });
-        });
-      })();
-    };
+const VIDEO_URL = `${baseOssUrl}myData/media/siji.mp4`
+const MIN_STAY_MS = 3000 // 至少停留3秒
+const EARLY_NAVIGATE_PROGRESS = 50 // 实际下载到50%即可尝试跳转(后台继续下载完整视频)
 
-    if (el.complete && el.naturalWidth > 0) {
-      fire();
-    } else {
-      el.addEventListener('load', fire, { once: true });
-      el.addEventListener('error', fire, { once: true });
-    }
+function A0base() {
+  const [currentIndex, setCurrentIndex] = useState(0)
+  const [progress, setProgress] = useState(0)
+  const [assetsReady, setAssetsReady] = useState(false)
 
-    return () => {
-      el.removeEventListener('load', fire);
-      el.removeEventListener('error', fire);
-    };
-  }, [ref]);
-}
+  const images = useMemo(() => [A0_home_1, A0_home_2, A0_home_3], [])
 
-/** 与首页背景视频同源,直接预拉取 mp4 以便快速命中缓存 */
-function getIntroVideoMp4Url(): string {
-  return `${baseOssUrl}myData/media/siji.mp4`;
-}
+  const slide0Ref = useRef<HTMLImageElement>(null)
+  const slide1Ref = useRef<HTMLImageElement>(null)
+  const slide2Ref = useRef<HTMLImageElement>(null)
+  const logoRef = useRef<HTMLImageElement>(null)
 
-function A0base() {
-  const [currentIndex, setCurrentIndex] = useState(0);
-  const [progress, setProgress] = useState(0); // 四图就绪后:0 ~ 100 为视频预加载字节进度
-  const [assetsReady, setAssetsReady] = useState(false);
-  const images = useMemo(() => [A0_home_1, A0_home_2, A0_home_3], []);
-
-  const slide0Ref = useRef<HTMLImageElement>(null);
-  const slide1Ref = useRef<HTMLImageElement>(null);
-  const slide2Ref = useRef<HTMLImageElement>(null);
-  const logoRef = useRef<HTMLImageElement>(null);
-  const pendingRef = useRef(4);
-  const hasNavigatedRef = useRef(false);
+  const pendingRef = useRef(4)
+  const hasNavigatedRef = useRef(false)
 
   const markOneReady = useCallback(() => {
-    if (pendingRef.current <= 0) return;
-    pendingRef.current -= 1;
-    if (pendingRef.current <= 0) setAssetsReady(true);
-  }, []);
+    pendingRef.current -= 1
+    if (pendingRef.current <= 0) setAssetsReady(true)
+  }, [])
 
-  useDomImageReady(slide0Ref, markOneReady);
-  useDomImageReady(slide1Ref, markOneReady);
-  useDomImageReady(slide2Ref, markOneReady);
-  useDomImageReady(logoRef, markOneReady);
+  // 图片加载
+  useDomImageReady(slide0Ref, markOneReady)
+  useDomImageReady(slide1Ref, markOneReady)
+  useDomImageReady(slide2Ref, markOneReady)
+  useDomImageReady(logoRef, markOneReady)
 
   useEffect(() => {
-    if (!assetsReady) return;
-
-    const url = getIntroVideoMp4Url();
-    const xhr = new XMLHttpRequest();
-    xhr.open('GET', url, true);
-    xhr.responseType = 'blob';
-
-    xhr.onprogress = (e) => {
-      if (e.lengthComputable && e.total > 0) {
-        const nextProgress = Math.min(100, Math.round((e.loaded / e.total) * 100));
-        setProgress(nextProgress);
-        if (nextProgress >= 70 && !hasNavigatedRef.current) {
-          hasNavigatedRef.current = true;
-          history.push('/home');
-        }
-      }
-    };
+    if (!assetsReady) return
+
+    const abortController = new AbortController()
+    let rafId: number | null = null
+    let timeoutId: number | null = null
+    let minStayReached = false
+    let reachedNavigateProgress = false
+    const startTime = performance.now()
+
+    const goHomeOnce = () => {
+      if (hasNavigatedRef.current) return
+      hasNavigatedRef.current = true
+      history.push('/home')
+    }
+
+    const tryGoHome = () => {
+      if (minStayReached && reachedNavigateProgress) goHomeOnce()
+    }
 
-    xhr.onload = () => {
-      setProgress(100);
-      if (!hasNavigatedRef.current) {
-        hasNavigatedRef.current = true;
-        history.push('/home');
+    // 最小停留时间
+    timeoutId = window.setTimeout(() => {
+      minStayReached = true
+      tryGoHome()
+    }, MIN_STAY_MS)
+
+    // 3秒后平滑动画到100%
+    const animateTo100 = () => {
+      const elapsed = performance.now() - startTime
+      if (elapsed >= MIN_STAY_MS) {
+        setProgress(100)
+        return
       }
-    };
+      const next = Math.min(99, Math.round(60 + (elapsed / MIN_STAY_MS) * 40))
+      setProgress(next)
+      rafId = requestAnimationFrame(animateTo100)
+    }
+
+    const preloadFullVideo = async () => {
+      try {
+        const response = await fetch(VIDEO_URL, {
+          signal: abortController.signal,
+          cache: 'force-cache'
+        })
+
+        if (!response.ok) throw new Error('Fetch failed')
+
+        const contentLength = response.headers.get('content-length')
+        const total = contentLength ? parseInt(contentLength, 10) : 0
+
+        if (!total) {
+          handleFallback()
+          return
+        }
+
+        const reader = response.body?.getReader()
+        if (!reader) throw new Error('No reader')
+
+        let loaded = 0
+
+        while (true) {
+          const { done, value } = await reader.read()
+          if (done) break
+
+          loaded += value.length
+          const realProgress = Math.floor((loaded / total) * 100)
+
+          // 显示进度策略:
+          // 前3秒内最多显示60%,之后显示真实下载进度
+          const displayProgress =
+            performance.now() - startTime < MIN_STAY_MS ? Math.min(realProgress, 60) : realProgress
 
-    xhr.onerror = () => {
-      setProgress(100);
-      if (!hasNavigatedRef.current) {
-        hasNavigatedRef.current = true;
-        history.push('/home');
+          setProgress(displayProgress)
+
+          // 下载到50%即可尝试跳转(但后台继续完整下载)
+          if (realProgress >= EARLY_NAVIGATE_PROGRESS) {
+            reachedNavigateProgress = true
+            tryGoHome()
+          }
+        }
+
+        // 完整下载完成
+        reachedNavigateProgress = true
+        const elapsed = performance.now() - startTime
+
+        if (elapsed < MIN_STAY_MS) {
+          setProgress(60)
+          rafId = requestAnimationFrame(animateTo100)
+        } else {
+          setProgress(100)
+        }
+
+        tryGoHome()
+      } catch (err: any) {
+        if (err.name !== 'AbortError') {
+          console.error('Video preload failed:', err)
+        }
+        setProgress(100)
+        reachedNavigateProgress = true
+        tryGoHome()
       }
-    };
+    }
 
-    xhr.send();
+    const handleFallback = () => {
+      setProgress(60)
+      rafId = requestAnimationFrame(animateTo100)
+      reachedNavigateProgress = true
+      tryGoHome()
+    }
 
-    // 不 abort:避免 Strict Mode / 快速重挂载导致重复请求、进度异常
-    return () => {};
-  }, [assetsReady]);
+    preloadFullVideo()
 
-  // 根据进度切换图片(进度条在四图就绪后才会从 0 增长)
-  useEffect(() => {
-    if (progress >= 65) {
-      setCurrentIndex(2);
-    } else if (progress >= 30) {
-      setCurrentIndex(1);
-    } else {
-      setCurrentIndex(0);
+    return () => {
+      abortController.abort()
+      if (timeoutId) clearTimeout(timeoutId)
+      if (rafId) cancelAnimationFrame(rafId)
     }
-  }, [progress]);
+  }, [assetsReady])
+
+  // 进度切换背景图
+  useEffect(() => {
+    if (progress >= 65) setCurrentIndex(2)
+    else if (progress >= 30) setCurrentIndex(1)
+    else setCurrentIndex(0)
+  }, [progress])
 
   return (
     <div className={styles.A0base}>
@@ -145,15 +177,46 @@ function A0base() {
       ))}
 
       <div className={styles.homeLogo}>
-        <img ref={logoRef} src={homeLogo} alt="homeLogo" />
-        <div className={'process'}>
-          {progress}%
-        </div>
+        <img ref={logoRef} src={homeLogo} alt='home logo' />
+        <div className='process'>{Math.floor(progress)}%</div>
       </div>
     </div>
-  );
+  )
 }
 
-const MemoA0base = React.memo(A0base);
+function useDomImageReady(ref: React.RefObject<HTMLImageElement | null>, onReady: () => void) {
+  const onReadyRef = useRef(onReady)
+  onReadyRef.current = onReady
+  const firedRef = useRef(false)
+
+  useLayoutEffect(() => {
+    const el = ref.current
+    if (!el) return
+
+    const fire = () => {
+      if (firedRef.current) return
+      firedRef.current = true
+      void (async () => {
+        try {
+          if (typeof el.decode === 'function') await el.decode()
+        } catch {}
+        requestAnimationFrame(() => requestAnimationFrame(() => onReadyRef.current()))
+      })()
+    }
+
+    if (el.complete && el.naturalWidth > 0) {
+      fire()
+    } else {
+      el.addEventListener('load', fire, { once: true })
+      el.addEventListener('error', fire, { once: true })
+    }
+
+    return () => {
+      el.removeEventListener('load', fire)
+      el.removeEventListener('error', fire)
+    }
+  }, [ref])
+}
 
-export default MemoA0base;
+const MemoA0base = React.memo(A0base)
+export default MemoA0base

+ 7 - 0
project/src/pages/A1home/index.tsx

@@ -16,6 +16,13 @@ function A1home() {
   const totalScenes = sceneList.length
 
   useEffect(() => {
+    if (videoRef.current) {
+      videoRef.current.src = `${baseOssUrl}myData/media/siji.mp4` // 直接使用原始 URL,浏览器会从缓存读取
+      videoRef.current.play().catch(console.warn)
+    }
+  }, [])
+
+  useEffect(() => {
     window.gotoScene = () => {
       console.log('gotoScene')
       history.push('/scene')

+ 78 - 0
project/src/pages/A3architecture/LoadingVideo/index.module.scss

@@ -0,0 +1,78 @@
+.LoadingVideo {
+  width: 100%;
+  height: 100%;
+  .videoBox {
+    width: 100%;
+    height: 100%;
+    & > video {
+      width: 100%;
+      height: 100%;
+      object-fit: fill;
+    }
+  }
+}
+
+.loadingBox {
+  position: absolute;
+  z-index: 2;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-image: url('../../../assets/img/loading_bg.jpg');
+  background-size: 100% 100%;
+  background-repeat: no-repeat;
+  background-position: center center;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-direction: column;
+  gap: 3%;
+  opacity: 1;
+  transition: opacity 0.6s ease;
+  .loadingLogo {
+    width: 10%;
+    & > img {
+      width: 100%;
+      object-fit: contain;
+    }
+  }
+  .loading {
+    width: 33%;
+    height: 2.3%;
+    margin-bottom: 4%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    flex-direction: column;
+    position: relative;
+    .progress {
+      width: 100%;
+      height: 100%;
+      border: 1px solid rgba(255, 239, 197, 1);
+      border-radius: 0.5vw;
+      padding: 0.2vw;
+      .thumb {
+        width: 0;
+        height: 100%;
+        border-radius: 0.5vw;
+        background-color: rgba(255, 239, 197, 1);
+      }
+    }
+    .percent {
+      position: absolute;
+      top: 0;
+      right: -11%;
+      width: 8%;
+      height: 100%;
+      font-size: 1vw;
+      line-height: 1;
+      color: rgba(255, 239, 197, 1);
+    }
+  }
+}
+
+.loadingBoxExiting {
+  opacity: 0;
+  pointer-events: none;
+}

+ 138 - 0
project/src/pages/A3architecture/LoadingVideo/index.tsx

@@ -0,0 +1,138 @@
+import React, { useCallback, useEffect, useRef, useState, useMemo } from 'react'
+import styles from './index.module.scss'
+import MenuSider from '@/components/MenuSider'
+import { baseOssUrl } from '@/utils/http'
+
+const LOADING_MS = 20_000
+const EXIT_MS = 600
+
+type LoadingVideoProps = {
+  videoSrc: string
+}
+
+function LoadingVideo({ videoSrc }: LoadingVideoProps) {
+  const videoRef = useRef<HTMLVideoElement>(null)
+  const videoUrl = useMemo(() => (videoSrc ? baseOssUrl + videoSrc : ''), [videoSrc])
+  const [loadingProgress, setLoadingProgress] = useState(0)
+  const [showLoading, setShowLoading] = useState(true)
+  const [isLoadingExiting, setIsLoadingExiting] = useState(false)
+
+  useEffect(() => {
+    if (!videoUrl) {
+      setShowLoading(false)
+      return
+    }
+
+    setShowLoading(true)
+    setIsLoadingExiting(false)
+    setLoadingProgress(0)
+
+    let finished = false
+    let timer: ReturnType<typeof setTimeout> | undefined
+    const t0 = performance.now()
+
+    let progressInterval: ReturnType<typeof setInterval> | undefined
+
+    const reveal = () => {
+      if (finished) return
+      finished = true
+      if (timer !== undefined) clearTimeout(timer)
+      if (progressInterval !== undefined) clearInterval(progressInterval)
+      setLoadingProgress(100)
+      setIsLoadingExiting(true)
+      setTimeout(() => {
+        setShowLoading(false)
+        videoRef.current?.play().catch(console.warn)
+      }, EXIT_MS)
+    }
+
+    const tick = () => {
+      if (finished) return
+      const v = videoRef.current
+      const elapsed = performance.now() - t0
+      const timePct = Math.min(95, Math.floor((elapsed / LOADING_MS) * 95))
+      let bufPct = 0
+      if (v && v.duration && isFinite(v.duration) && v.buffered.length > 0) {
+        bufPct = Math.floor((v.buffered.end(v.buffered.length - 1) / v.duration) * 100)
+      }
+      setLoadingProgress(Math.min(99, Math.max(timePct, bufPct)))
+    }
+
+    // 进度不能只绑 video 事件:大文件可能很久才再次触发 progress,时间会停在第一次 tick(约 4%)
+    progressInterval = setInterval(tick, 200)
+
+    let removeListeners: (() => void) | undefined
+
+    const raf = requestAnimationFrame(() => {
+      const v = videoRef.current
+      if (!v) {
+        timer = setTimeout(reveal, LOADING_MS)
+        return
+      }
+      v.addEventListener('progress', tick)
+      v.addEventListener('loadedmetadata', tick)
+      v.addEventListener('canplay', tick)
+
+      timer = setTimeout(reveal, LOADING_MS)
+
+      removeListeners = () => {
+        v.removeEventListener('progress', tick)
+        v.removeEventListener('loadedmetadata', tick)
+        v.removeEventListener('canplay', tick)
+      }
+    })
+
+    return () => {
+      cancelAnimationFrame(raf)
+      if (timer !== undefined) clearTimeout(timer)
+      if (progressInterval !== undefined) clearInterval(progressInterval)
+      removeListeners?.()
+    }
+  }, [videoUrl])
+
+  const handleTogglePlay = useCallback(() => {
+    const v = videoRef.current
+    if (!v) return
+    if (v.paused || v.ended) v.play().catch(console.warn)
+    else v.pause()
+  }, [])
+
+  return (
+    <div className={styles.LoadingVideo}>
+      {showLoading && (
+        <div className={`${styles.loadingBox} ${isLoadingExiting ? styles.loadingBoxExiting : ''}`}>
+          <div className={styles.loadingLogo}>
+            <img src={require('../../../assets/img/loading_logo.png')} alt='loading logo' />
+          </div>
+          <div className={styles.loading}>
+            <div className={styles.progress}>
+              <div className={styles.thumb} style={{ width: `${loadingProgress}%` }} />
+            </div>
+            <div className={styles.percent}>{loadingProgress}%</div>
+          </div>
+        </div>
+      )}
+
+      <div className={styles.videoBox}>
+        <video
+          ref={videoRef}
+          src={videoUrl || undefined}
+          className='modal-video'
+          onClick={e => {
+            e.stopPropagation()
+            handleTogglePlay()
+          }}
+          loop
+          playsInline
+          preload='auto'
+        >
+          Your browser does not support the video tag.
+        </video>
+      </div>
+
+      <MenuSider isSidebarOpen={false} />
+    </div>
+  )
+}
+
+export default React.memo(LoadingVideo)

+ 68 - 53
project/src/pages/A3architecture/index.module.scss

@@ -1,63 +1,78 @@
 .A3architecture {
   width: 100%;
   height: 100%;
-  :global{
-    .custom-media-controller {
+  .videoBox {
+    width: 100%;
+    height: 100%;
+    & > video {
       width: 100%;
       height: 100%;
-      & > video {
-        width: 100%;
+      object-fit: fill;
+    }
+  }
+}
+
+.loadingBox {
+  position: absolute;
+  z-index: 2;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-image: url('../../assets/img/loading_bg.jpg');
+  background-size: 100% 100%;
+  background-repeat: no-repeat;
+  background-position: center center;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-direction: column;
+  gap: 3%;
+  opacity: 1;
+  transition: opacity 0.6s ease;
+  .loadingLogo {
+    width: 10%;
+    & > img {
+      width: 100%;
+      object-fit: contain;
+    }
+  }
+  .loading {
+    width: 33%;
+    height: 2.3%;
+    margin-bottom: 4%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    flex-direction: column;
+    position: relative;
+    .progress {
+      width: 100%;
+      height: 100%;
+      border: 1px solid rgba(255, 239, 197, 1);
+      border-radius: 0.5vw;
+      padding: 0.2vw;
+      .thumb {
+        width: 0;
         height: 100%;
-        object-fit: fill;
-      }
-      & > media-control-bar {
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        width: 70%;
-        gap: 10px;
-        margin: 0 auto;
-      }
-      media-play-button {
-        width: 40px;
-        height: 40px;
-        border-radius: 50%;
-        background-color: rgba(167, 31, 30, 1);
-        border: none;
-        display: flex;
-        align-self: center;
-      }
-      .progress {
-        width: 70%;
-        height: 50px;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        flex-direction: column;
-        & > media-time-range {
-          width: 100%;
-          height: 10px;
-          --media-range-bar-color: rgba(167, 31, 30, 1);
-          --media-range-track-background: rgba(230, 203, 160, 1); // 未播放部分背景
-          border-radius: 5px;
-          --media-range-track-fill-background: rgba(167, 31, 30, 1);
-        }
-        cursor: pointer;
-      }
-      .time {
-        display: flex;
-        justify-content: flex-end;
-        width: 80px;
-        color: rgba(230, 203, 160, 1);
-        align-items: center;
-        media-time-display,
-        media-duration-display {
-          font-size: 16px;
-          background: none;
-          color: rgba(230, 203, 160, 1);
-        }
+        border-radius: 0.5vw;
+        background-color: rgba(255, 239, 197, 1);
       }
     }
+    .percent {
+      position: absolute;
+      top: 0;
+      right: -11%;
+      width: 8%;
+      height: 100%;
+      font-size: 1vw;
+      line-height: 1;
+      color: rgba(255, 239, 197, 1);
+    }
   }
-  
+}
+
+.loadingBoxExiting {
+  opacity: 0;
+  pointer-events: none;
 }

+ 3 - 9
project/src/pages/A3architecture/index.tsx

@@ -1,20 +1,14 @@
 import React from 'react'
 import styles from './index.module.scss'
-import MenuSider from '@/components/MenuSider'
-import Zvideo from '@/components/Zvideo'
-function A3architecture() {
-  const currentVideoSrc = myDataTemp.architectureAnimation
+import LoadingVideo from './LoadingVideo'
 
+function A3architecture() {
   return (
     <div className={styles.A3architecture}>
-      {/* 第一次加载的时候给一个loading动画 */}
-      <div className={styles.loading}></div>
-      <Zvideo src={currentVideoSrc} isAutoPlay={true} isMuted={false} />
-      <MenuSider isSidebarOpen={false} />
+      <LoadingVideo videoSrc={myDataTemp.architectureAnimation} />
     </div>
   )
 }
 
 const MemoA3architecture = React.memo(A3architecture)
-
 export default MemoA3architecture

+ 1 - 1
project/src/pages/A4member/index.module.scss

@@ -116,7 +116,7 @@
             background: linear-gradient(180deg, #a56f2c 0%, #824e0f 41.35%, #4f310c 100%);
             background-clip: text;
             -webkit-text-fill-color: transparent;
-            font-family: syst;
+            // font-family: syst;
           }
         }
 

+ 6 - 3
project/src/pages/A6life/Record/index.tsx

@@ -1,22 +1,25 @@
 import React from 'react'
 import styles from './index.module.scss'
-import Zvideo from '@/components/Zvideo'
 import Zback from '@/components/Zback'
+import LoadingVideo from '@/pages/A3architecture/LoadingVideo'
 function Record({
   activeIndex,
   setActiveIndex,
-  setIsShowIntro
+  setIsShowIntro,
+  setIsShowRecord
 }: {
   activeIndex: number
   setActiveIndex: (index: number) => void
   setIsShowIntro: (isShow: boolean) => void
+  setIsShowRecord: (isShow: boolean) => void
 }) {
   return (
     <div className={styles.Record}>
-      <Zvideo src={myDataTemp.lifeList[activeIndex].videoSrc || ''} />
+      <LoadingVideo videoSrc={myDataTemp.lifeList[activeIndex].videoSrc || ''} />
       <Zback
         onBack={() => {
           setIsShowIntro(false)
+          setIsShowRecord(false)
         }}
       />
     </div>

+ 7 - 2
project/src/pages/A6life/Swiper/index.tsx

@@ -10,11 +10,13 @@ import { baseOssUrl } from '@/utils/http'
 function SwiperComponent({
   activeIndex,
   setActiveIndex,
-  setIsShowIntro
+  setIsShowIntro,
+  setIsShowRecord
 }: {
   activeIndex: number
   setActiveIndex: (index: number) => void
   setIsShowIntro: (isShow: boolean) => void
+  setIsShowRecord: (isShow: boolean) => void
 }) {
   const swiperRef = useRef<any>(null)
   // 是否点击bottom改变了activeIndex
@@ -72,7 +74,10 @@ function SwiperComponent({
                 <img className='itemImageImg' src={baseOssUrl + item?.imgSrc} alt='' />
                 <img
                   className='detailIcon'
-                  onClick={() => setIsShowIntro(true)}
+                  onClick={() => {
+                    if (index === listLength - 1) setIsShowRecord(true)
+                    else setIsShowIntro(true)
+                  }}
                   src={require('../../../assets/img/A6_life_look.png')}
                   alt=''
                 />

+ 12 - 4
project/src/pages/A6life/index.tsx

@@ -7,18 +7,25 @@ import Record from './Record'
 function A6life() {
   const [activeIndex, setActiveIndex] = useState(0)
   const [isShowIntro, setIsShowIntro] = useState(false)
+  const [isShowRecord, setIsShowRecord] = useState(false)
   const lifeList = myDataTemp.lifeList
+  const handleItemClick = (index: number) => {
+    setActiveIndex(index)
+    if (isShowIntro && index === lifeList.length - 1) setIsShowRecord(true)
+  }
   return (
     <div className={styles.A6life}>
       <div className={styles.lifeContent}>
         <div className={styles.top}>
-          {isShowIntro && activeIndex !== lifeList.length - 1 ? (
+          {isShowIntro && activeIndex !== lifeList.length - 1 && (
             <Intro activeIndex={activeIndex} setIsShowIntro={setIsShowIntro} />
-          ) : (
+          )}
+          {!isShowIntro && (
             <SwiperComponent
               activeIndex={activeIndex}
               setActiveIndex={setActiveIndex}
               setIsShowIntro={setIsShowIntro}
+              setIsShowRecord={setIsShowRecord}
             />
           )}
         </div>
@@ -33,7 +40,7 @@ function A6life() {
               <React.Fragment key={index}>
                 <div
                   className={`${styles.item} ${activeIndex === index ? styles.active : ''}`}
-                  onClick={() => setActiveIndex(index)}
+                  onClick={() => handleItemClick(index)}
                 >
                   <img
                     src={require(
@@ -63,10 +70,11 @@ function A6life() {
 
         <MenuSider isSidebarOpen={false} />
       </div>
-      {activeIndex === lifeList.length - 1 && isShowIntro ? (
+      {isShowRecord ? (
         <Record
           activeIndex={activeIndex}
           setActiveIndex={setActiveIndex}
+          setIsShowRecord={setIsShowRecord}
           setIsShowIntro={setIsShowIntro}
         />
       ) : null}