lanxin 5 giorni fa
parent
commit
a0c992327b

+ 8 - 1
project/public/index.html

@@ -18,6 +18,13 @@
     <noscript>You need to enable JavaScript to run this app.</noscript>
     <div id="root" style="width: 100%; height: 100vh"></div>
   </body>
+  <style>
+    @font-face {
+      font-family: 'syst';
+      src: url('./syst.otf') format('opentype');
+      font-display: swap;
+    }
+  </style>
   <script src="./jsmpeg.min.js"></script>
-<script src="./f-video.js"></script>
+  <script src="./f-video.js"></script>
 </html>

BIN
project/public/syst.otf


File diff suppressed because it is too large
+ 8 - 8
project/public/three/data.js


+ 2 - 2
project/src/components/MenuSider/index.module.scss

@@ -2,7 +2,7 @@
   position: fixed;
   top: 0;
   left: 0;
-  z-index: 1;
+  z-index: 999;
   width: 11%;
   height: 100%;
   background-image: url('../../assets/img/A1_sider_bg.png');
@@ -55,7 +55,7 @@
   left: 1.5vw;
   width: 4vw;
   height: 4vw;
-  z-index: 1;
+  z-index: 1000;
   & > img {
     width: 100%;
     object-fit: contain;

+ 35 - 1
project/src/components/MenuSider/index.tsx

@@ -1,9 +1,10 @@
-import React, { useEffect, useState } from 'react'
+import React, { useEffect, useRef, useState } from 'react'
 import styles from './index.module.scss'
 import history from '@/utils/history'
 function MenuSider({ isSidebarOpen = true }: { isSidebarOpen?: boolean }) {
   const [activeIndex, setActiveIndex] = useState(0)
   const [isOpen, setIsOpen] = useState(true)
+  const idleTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
   useEffect(() => {
     if (!isSidebarOpen) {
       setIsOpen(false)
@@ -15,6 +16,39 @@ function MenuSider({ isSidebarOpen = true }: { isSidebarOpen?: boolean }) {
       setActiveIndex(index)
     }
   }, [isSidebarOpen])
+
+  useEffect(() => {
+    const clearIdleTimer = () => {
+      if (idleTimerRef.current) {
+        clearTimeout(idleTimerRef.current)
+        idleTimerRef.current = null
+      }
+    }
+
+    const startIdleTimer = () => {
+      clearIdleTimer()
+      idleTimerRef.current = setTimeout(() => {
+        setIsOpen(false)
+      }, 10000)
+    }
+
+    if (!isOpen) {
+      clearIdleTimer()
+      return
+    }
+
+    const handleUserActivity = () => {
+      startIdleTimer()
+    }
+
+    startIdleTimer()
+    window.addEventListener('mousemove', handleUserActivity)
+
+    return () => {
+      clearIdleTimer()
+      window.removeEventListener('mousemove', handleUserActivity)
+    }
+  }, [isOpen])
   const handleClick = (index: number, path: string) => {
     const pathRes = path === 'view' ? 'view/three' : path
     history.push(`/${pathRes}`)

+ 37 - 15
project/src/components/Zvideo/index.tsx

@@ -2,7 +2,19 @@ import React, { useRef, useCallback } from 'react'
 import styles from './index.module.scss'
 import 'media-chrome'
 import { baseOssUrl } from '@/utils/http'
-function Zvideo({ src, objFit = 'fill' ,style}: { src: string; objFit?: string,style?: React.CSSProperties }) {
+function Zvideo({
+  src,
+  objFit = 'fill',
+  style,
+  isAutoPlay = false,
+  isMuted = false
+}: {
+  src: string
+  objFit?: string
+  style?: React.CSSProperties
+  isAutoPlay?: boolean
+  isMuted?: boolean
+}) {
   const videoRef = useRef<HTMLVideoElement>(null)
 
   const handleTogglePlay = useCallback(() => {
@@ -19,23 +31,33 @@ function Zvideo({ src, objFit = 'fill' ,style}: { src: string; objFit?: string,s
     <div
       className={styles.Zvideo}
       style={style}
-      onKeyDown={(e) => { if (e.key === ' ' || e.key === 'Enter') { e.preventDefault(); handleTogglePlay() } }}
-      role="button"
+      onKeyDown={e => {
+        if (e.key === ' ' || e.key === 'Enter') {
+          e.preventDefault()
+          handleTogglePlay()
+        }
+      }}
+      role='button'
       tabIndex={0}
     >
       {/* <media-controller class='custom-media-controller' gesturesdisabled={true}> */}
-        <video
-          ref={videoRef}
-          slot='media'
-          src={baseOssUrl + src}
-          className='modal-video'
-          style={{ objectFit: objFit as any }}
-          onClick={(e) => {
-            e.stopPropagation()
-            handleTogglePlay()
-          }}
-        ></video>
-        {/* <media-control-bar>
+      <video
+        ref={videoRef}
+        slot='media'
+        className='modal-video'
+        style={{ objectFit: objFit as any }}
+        onClick={e => {
+          e.stopPropagation()
+          handleTogglePlay()
+        }}
+        loop
+        preload='auto'
+        autoPlay={isAutoPlay}
+        muted={isMuted}
+      >
+        <source src={baseOssUrl + src} type='video/mp4' />
+      </video>
+      {/* <media-control-bar>
           <media-play-button></media-play-button>
           <div className='progress'>
             <media-time-range></media-time-range>

+ 18 - 10
project/src/pages/A0base/index.tsx

@@ -5,7 +5,7 @@ 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, envFlag } from '@/utils/http';
+import { baseOssUrl } from '@/utils/http';
 
 /** 以「页面上真实 img」为准:load + decode + 双 rAF,避免与独立 new Image() 不同步 */
 function useDomImageReady(
@@ -51,9 +51,9 @@ function useDomImageReady(
   }, [ref]);
 }
 
-/** 与首页 F_Video('siji.ts') 同源,便于浏览器缓存命中 */
-function getIntroVideoTsUrl(): string {
-  return new URL('siji.ts', !envFlag ? "https://houseoss.4dkankan.com/project/gmlx/" : window.location.origin).href;
+/** 与首页背景视频同源,直接预拉取 mp4 以便快速命中缓存 */
+function getIntroVideoMp4Url(): string {
+  return `${baseOssUrl}myData/media/siji.mp4`;
 }
 
 function A0base() {
@@ -67,6 +67,7 @@ function A0base() {
   const slide2Ref = useRef<HTMLImageElement>(null);
   const logoRef = useRef<HTMLImageElement>(null);
   const pendingRef = useRef(4);
+  const hasNavigatedRef = useRef(false);
 
   const markOneReady = useCallback(() => {
     if (pendingRef.current <= 0) return;
@@ -82,29 +83,36 @@ function A0base() {
   useEffect(() => {
     if (!assetsReady) return;
 
-    const url = getIntroVideoTsUrl();
+    const url = getIntroVideoMp4Url();
     const xhr = new XMLHttpRequest();
     xhr.open('GET', url, true);
     xhr.responseType = 'blob';
 
     xhr.onprogress = (e) => {
       if (e.lengthComputable && e.total > 0) {
-        setProgress(Math.min(100, Math.round((e.loaded / e.total) * 100)));
+        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');
+        }
       }
     };
 
     xhr.onload = () => {
       setProgress(100);
-      setTimeout(() => {
+      if (!hasNavigatedRef.current) {
+        hasNavigatedRef.current = true;
         history.push('/home');
-      }, 1500);
+      }
     };
 
     xhr.onerror = () => {
       setProgress(100);
-      setTimeout(() => {
+      if (!hasNavigatedRef.current) {
+        hasNavigatedRef.current = true;
         history.push('/home');
-      }, 1500);
+      }
     };
 
     xhr.send();

+ 4 - 48
project/src/pages/A1home/index.tsx

@@ -17,6 +17,7 @@ function A1home() {
 
   useEffect(() => {
     window.gotoScene = () => {
+      console.log('gotoScene')
       history.push('/scene')
     }
     window.setActiveHotId = (id: string) => {
@@ -28,46 +29,6 @@ function A1home() {
     }
   }, [])
 
-  // 初始视频:进入全景时及时销毁,避免继续占用解码资源
-  useEffect(() => {
-    const dom = document.querySelector('.baseVideo')
-    if (!dom) return
-    const container = dom as HTMLElement
-    container.style.width = '100vw'
-    container.style.height = '100vh'
-    container.style.overflow = 'hidden'
-
-    if (isOpenPano) {
-      if (videoRef.current?.destroy) {
-        videoRef.current.destroy()
-      }
-      videoRef.current = null
-      dom.innerHTML = ''
-      return
-    }
-
-    const params = {
-      objectFit: 'cover', // 视频的object-fit样式, 默认 cover
-      loop: true, // 是否循环, 默认false
-      autoplay: true, // 自动播放, 默认false
-      audio: false, // 关闭音频解码,降低 CPU 压力
-      videoBufferSize: 8 * 1024 * 1024, // 适当增大缓冲,减少卡顿
-      onSourceEstablished: () => {} //有足够的数据可以播放了
-    }
-
-    const videoInit = F_Video('siji.ts', params)
-    videoRef.current = videoInit
-    dom.append(videoInit.domElement)
-
-    return () => {
-      if (videoRef.current?.destroy) {
-        videoRef.current.destroy()
-      }
-      videoRef.current = null
-      dom.innerHTML = ''
-    }
-  }, [isOpenPano])
-
   const handleItemClick = (item: any, index: number) => {
     setCurrentSceneIndex(index)
     callIframeFu('setCurrentScene', item.id)
@@ -162,14 +123,9 @@ function A1home() {
       ) : (
         <>
           {/* 背景视频 */}
-          {/* <video
-            src={`${baseOssUrl}myData/media/siji.mp4`}
-            autoPlay
-            loop
-            muted
-            className={styles.bgVideo}
-          /> */}
-          <div className='baseVideo'></div>
+          <video autoPlay loop muted className={styles.bgVideo} preload='auto'>
+            <source src={`${baseOssUrl}myData/media/siji.mp4`} type='video/mp4' />
+          </video>
 
           {/* 跳过按钮 */}
           <div className={styles.skipBtn} onClick={() => setIsOpenPano(true)}>

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

@@ -7,7 +7,9 @@ function A3architecture() {
 
   return (
     <div className={styles.A3architecture}>
-      <Zvideo src={currentVideoSrc} />
+      {/* 第一次加载的时候给一个loading动画 */}
+      <div className={styles.loading}></div>
+      <Zvideo src={currentVideoSrc} isAutoPlay={true} isMuted={false} />
       <MenuSider isSidebarOpen={false} />
     </div>
   )

+ 42 - 33
project/src/pages/A4member/index.module.scss

@@ -25,19 +25,21 @@
         position: relative;
 
         .nameBox {
-          margin-top: 7%;
+          margin-top: 25%;
           margin-left: 4%;
-          width: 12%;
+          width: 17%;
           height: fit-content;
           transition: opacity 0.3s ease;
-
+          display: flex;
+          align-items: center;
+          flex-direction: column;
           .up,
           .down {
             width: 100%;
             height: 3.5vh;
             transition: opacity 0.3s ease;
 
-            &>img {
+            & > img {
               width: 100%;
               height: 100%;
               object-fit: contain;
@@ -45,14 +47,15 @@
           }
 
           .name {
-            width: 100%;
+            width: 70%;
             height: fit-content;
             text-align: center;
             font-weight: 700;
-            font-size: 1.5vw;
-            white-space: pre-line;
+            font-size: 2vw;
             color: rgba(195, 172, 153, 1);
-            transition: color 0.35s ease, text-shadow 0.35s ease;
+            transition:
+              color 0.35s ease,
+              text-shadow 0.35s ease;
             text-shadow:
               0 0 3px rgba(78, 47, 23, 0.9),
               0 0 6px rgba(78, 47, 23, 0.7),
@@ -97,7 +100,9 @@
               width: auto;
               max-width: 100%;
               object-fit: contain;
-              transition: opacity 0.35s ease, right 0.35s ease-out;
+              transition:
+                opacity 0.35s ease,
+                right 0.35s ease-out;
             }
           }
 
@@ -108,17 +113,18 @@
             height: auto;
             font-size: 1.8vw;
             font-weight: bold;
-            background: linear-gradient(180deg, #A56F2C 0%, #824E0F 41.35%, #4F310C 100%);
+            background: linear-gradient(180deg, #a56f2c 0%, #824e0f 41.35%, #4f310c 100%);
             background-clip: text;
             -webkit-text-fill-color: transparent;
+            font-family: syst;
           }
         }
 
         .imgBox1 {
           // bottom: 14%;
           transform: translateY(14%);
-          .imgWrap{
-            img{
+          .imgWrap {
+            img {
               left: 56%;
             }
           }
@@ -127,8 +133,8 @@
         .imgBox2 {
           // bottom: 20%;
           transform: translateY(-5%);
-          .imgWrap{
-            img{
+          .imgWrap {
+            img {
               left: 54.6%;
             }
           }
@@ -137,8 +143,8 @@
         .imgBox3 {
           // bottom: 14%;
           transform: translateY(20%);
-          .imgWrap{
-            img{
+          .imgWrap {
+            img {
               left: 45%;
             }
           }
@@ -147,8 +153,8 @@
         .imgBox4 {
           // bottom: 20%;
           transform: translateY(0);
-          .imgWrap{
-            img{
+          .imgWrap {
+            img {
               left: 38%;
             }
           }
@@ -157,8 +163,8 @@
           height: 58%;
           // bottom: 8%;
           transform: translateY(-5%);
-          .imgWrap{
-            img{
+          .imgWrap {
+            img {
               left: 66.5%;
             }
           }
@@ -168,8 +174,8 @@
           height: 58%;
           // bottom: 19%;
           transform: translateY(-24%);
-          .imgWrap{
-            img{
+          .imgWrap {
+            img {
               left: 69%;
             }
           }
@@ -179,8 +185,8 @@
           height: 62%;
           // bottom: 8%;
           transform: translateY(-8%);
-          .imgWrap{
-            img{
+          .imgWrap {
+            img {
               left: 71%;
             }
           }
@@ -190,8 +196,8 @@
           height: 40%;
           // bottom: 20%;
           transform: translateY(20%);
-          .imgWrap{
-            img{
+          .imgWrap {
+            img {
               left: 23%;
             }
           }
@@ -212,16 +218,20 @@
           height: 22%;
           // bottom: 25%;
           transform: translateY(95%);
-          .imgWrap{
-            img{
+          .imgWrap {
+            img {
               left: 43%;
             }
           }
         }
       }
 
-      .itemAc{
-        background: linear-gradient(rgba(255, 243, 197, 0.4), rgba(255, 243, 197, 0.6), rgba(255, 243, 197, 0.8));
+      .itemAc {
+        background: linear-gradient(
+          rgba(255, 243, 197, 0.4),
+          rgba(255, 243, 197, 0.6),
+          rgba(255, 243, 197, 0.8)
+        );
         transition: background 0.35s ease;
         .nameBox {
           .name {
@@ -229,7 +239,6 @@
             height: fit-content;
             text-align: center;
             font-weight: 700;
-            font-size: 1.5vw;
             color: rgba(255, 243, 197, 1);
             text-shadow:
               0 0 3px rgba(78, 47, 23, 0.9),
@@ -260,7 +269,7 @@
         min-width: 2px;
         height: 100%;
 
-        &>img {
+        & > img {
           width: 100%;
           height: 100%;
           object-fit: contain;
@@ -278,4 +287,4 @@
     z-index: 100;
     background-color: rgba(0, 0, 0, 0.9);
   }
-}
+}

+ 1 - 0
project/src/pages/A4member/index.tsx

@@ -112,6 +112,7 @@ function A4Member() {
           <Zvideo
             src={myDataTemp.memberList[currentIndex].videoSrc}
             objFit='contain'
+            isAutoPlay={true}
           />
           <Zback onBack={() => setIsShowVideo(false)} />
         </div>

+ 7 - 14
project/src/pages/A6life/index.module.scss

@@ -54,7 +54,7 @@
             text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
             line-height: 1.4;
             position: relative;
-
+            color: rgba(231, 227, 211, 1);
             .textTop {
               position: absolute;
               top: 0;
@@ -63,7 +63,6 @@
               white-space: nowrap;
               font-size: 1.2vw;
               font-weight: 700;
-              color: rgba(231, 227, 211, 1);
               // 描边 + 发光组合
               text-shadow:
                 -0.05vw -0.05vw 0 rgba(123, 72, 14, 1),
@@ -71,7 +70,7 @@
                 -0.05vw 0.05vw 0 rgba(123, 72, 14, 1),
                 0.05vw 0.05vw 0 rgba(123, 72, 14, 1),
                 // 整体投影
-                0.08vw 0.08vw 0.1vw rgba(123, 72, 14, 0.5);
+                0.08vw 0.08vw 0.1vw rgba(123, 72, 14, 1);
             }
 
             .textBottom {
@@ -81,19 +80,20 @@
               transform: translateX(-50%);
               white-space: nowrap;
               font-size: 1vw;
-              color: rgba(231, 227, 211, 1);
               text-shadow:
                 -0.05vw -0.05vw 0 rgba(123, 72, 14, 1),
                 0.05vw -0.05vw 0 rgba(123, 72, 14, 1),
                 -0.05vw 0.05vw 0 rgba(123, 72, 14, 1),
                 0.05vw 0.05vw 0 rgba(123, 72, 14, 1),
                 // 整体投影
-                0.08vw 0.08vw 0.1vw rgba(123, 72, 14, 0.5);
+                0.08vw 0.08vw 0.1vw rgba(123, 72, 14, 1);
             }
           }
+          .textWrapAc {
+            color: rgba(249, 211, 109, 1);
+          }
         }
 
-
         .connector {
           width: 5vw;
           height: auto;
@@ -102,14 +102,7 @@
           position: relative;
           top: 12%;
         }
-
       }
     }
-
   }
-
-
-
-
-
-}
+}

+ 3 - 1
project/src/pages/A6life/index.tsx

@@ -42,7 +42,9 @@ function A6life() {
                     alt=''
                     className={styles.flower}
                   />
-                  <div className={styles.textWrap}>
+                  <div
+                    className={`${styles.textWrap} ${activeIndex === index ? styles.textWrapAc : ''}`}
+                  >
                     <div className={styles.textTop}>{item.name}</div>
                     <div className={styles.textBottom}>{item.time}</div>
                   </div>

BIN
静态文件/myData/img/Atest2.png


BIN
静态文件/myData/media/jianzhu.mp4


BIN
静态文件/myData/media/record.mp4


BIN
静态文件/myData/media/siji.mp4