shaogen1995 11 maanden geleden
bovenliggende
commit
75942b94e7

+ 39 - 1
Code/public/myData.js

@@ -15,6 +15,44 @@ const myDataTemp = {
     // 开场动画视频名字
     videoSta: 'base.mp4',
     // 5个动画名字(这里暂时写3个,后面自己加/减)
-    video: ['1.mp4', '2.mp4', '3.mp4']
+    // 倒数第二个 是5个动画播放完之后的动画
+    // 最后一个 是点击开始之后的过度长动画
+    video: [
+      {
+        sort: 1, //排序
+        name: '1.mp4' //名字
+      },
+      {
+        sort: 2, //排序
+        name: '2.mp4' //名字
+      },
+      {
+        sort: 3, //排序
+        name: '3.mp4' //名字
+      },
+      // 倒数第二个
+      {
+        sort: 4, //排序
+        name: 'lastButOne.mp4' //名字
+      },
+      // 最后一个
+      {
+        sort: 5, //排序
+        name: 'end.mp4' //名字
+      }
+    ]
+  },
+
+  // 车骑拜谒
+  visit: {
+    // 开场动画视频名字
+    videoSta: 'base.mp4',
+    // 视频数组
+    video: [
+      {
+        sort: 1, //排序
+        name: '1.mp4' //名字
+      }
+    ]
   }
 }

+ 8 - 5
Code/src/App.tsx

@@ -9,6 +9,7 @@ import NotFound from '@/components/NotFound'
 import store, { RootState } from './store'
 import { useSelector } from 'react-redux'
 const A1home = React.lazy(() => import('./pages/A1home'))
+const A2visit = React.lazy(() => import('./pages/A2visit'))
 
 export default function App() {
   // 根元素
@@ -35,10 +36,10 @@ export default function App() {
       //横屏
       isHH = true
       // 最大宽度1200px
-      if (width >= 1200) {
-        width = 1200
-        height = 675
-      }
+      // if (width >= 1200) {
+      //   width = 1200
+      //   height = 675
+      // }
     } else {
       // 竖屏
       width = width >= 800 ? 800 : width
@@ -47,7 +48,8 @@ export default function App() {
     store.dispatch({ type: 'layout/isHH', payload: isHH })
     clearTimeout(timeRef.current)
     timeRef.current = window.setTimeout(() => {
-      if (height < moBaseObj.current && width < 1200) {
+      // && width < 1200
+      if (height < moBaseObj.current) {
         // 打开了键盘
         height = moBaseObj.current
       }
@@ -72,6 +74,7 @@ export default function App() {
         <React.Suspense fallback={<SpinLoding />}>
           <Switch>
             <Route path='/' component={A1home} exact />
+            <Route path='/visit' component={A2visit} exact />
             <Route path='*' component={NotFound} />
           </Switch>
         </React.Suspense>

+ 2 - 0
Code/src/assets/styles/base.css

@@ -3,6 +3,7 @@
   padding: 0;
   box-sizing: border-box;
   word-wrap: break-word;
+  -webkit-tap-highlight-color: transparent;
 }
 /* 全局css变量 */
 :root {
@@ -25,6 +26,7 @@ body {
 #root {
   overflow: hidden;
   margin: auto;
+  position: relative;
 }
 #root > div {
   width: 100%;

+ 2 - 0
Code/src/assets/styles/base.less

@@ -3,6 +3,7 @@
   padding: 0;
   box-sizing: border-box;
   word-wrap: break-word;
+  -webkit-tap-highlight-color: transparent;
 }
 
 /* 全局css变量 */
@@ -30,6 +31,7 @@ body {
 #root {
   overflow: hidden;
   margin: auto;
+  position: relative;
 
   & > div {
     width: 100%;

+ 1 - 1
Code/src/components/ownUse/UseDataUrl.tsx

@@ -3,7 +3,7 @@ import { baseURLTemp } from '@/utils/http'
 import { useCallback, useEffect, useState } from 'react'
 import { useSelector } from 'react-redux'
 
-export default function UseDataUrl(url: string) {
+export default function useDataUrl(url: string) {
   // 是否是横屏,默认是false
   const isHH = useSelector((state: RootState) => state.A0Layout.isHH)
 

+ 63 - 0
Code/src/components/ownUse/useMove.tsx

@@ -0,0 +1,63 @@
+import { useCallback, useRef, useState } from 'react'
+
+export default function useMove() {
+  const [flag, setFlag] = useState(false)
+  const [staX, setStaX] = useState(0)
+  const [moveX, setMoveX] = useState(0)
+  // pc端是否触发了滑动(避免和点击事件冲突)
+  const [isPcMoveFlag, setIsPcMoveFlag] = useState(true)
+
+  // 手指按住屏幕
+  const touchstart = useCallback((x: number) => {
+    setStaX(x)
+    setFlag(true)
+  }, [])
+
+  // 手指移动事件
+  const touchmove = useCallback(
+    (x: number) => {
+      if (flag) {
+        setMoveX(x)
+      }
+    },
+    [flag]
+  )
+
+  // pc端是否触发了滑动(避免和点击事件冲突)
+  const time = useRef(-1)
+  const isPcFu = useCallback(() => {
+    clearTimeout(time.current)
+    time.current = window.setTimeout(() => {
+      setIsPcMoveFlag(true)
+    }, 200)
+  }, [])
+
+  // 手指抬起事件
+  const touchend = useCallback(
+    (fu: (val: number) => void, dev: 'pc' | 'mobile') => {
+      if (moveX !== 0) {
+        if (staX > moveX && staX - moveX >= 20) {
+          if (dev === 'pc') setIsPcMoveFlag(false)
+          fu(1)
+
+          if (dev === 'pc') isPcFu()
+        } else if (moveX > staX && moveX - staX >= 20) {
+          if (dev === 'pc') setIsPcMoveFlag(false)
+          fu(-1)
+          if (dev === 'pc') isPcFu()
+        }
+        setFlag(false)
+        setStaX(0)
+        setMoveX(0)
+      }
+    },
+    [isPcFu, moveX, staX]
+  )
+
+  return {
+    touchstart,
+    touchmove,
+    touchend,
+    isPcMoveFlag
+  }
+}

+ 87 - 0
Code/src/pages/A1home/index.module.scss

@@ -1,5 +1,92 @@
 .A1home {
   :global {
+    video {
+      width: 100%;
+      height: 100%;
+      object-fit: fill;
+    }
+    .A1base {
+      width: 100%;
+      height: 100%;
+      position: absolute;
+      z-index: 10;
+      opacity: 1;
+      transition: opacity 0.5s;
+      & > img {
+        position: absolute;
+        top: 56px;
+        width: 280px;
+        left: 50%;
+        transform: translateX(-50%);
+      }
+      .A1loding {
+        position: absolute;
+        bottom: 20px;
+        left: 50%;
+        transform: translateX(-50%);
+        color: #733c00;
+      }
+    }
+    .A1baseHide {
+      opacity: 0;
+      pointer-events: none;
+    }
+
+    .A1video {
+      width: 100%;
+      height: 100%;
+      position: relative;
+      & > img {
+        cursor: pointer;
+        position: absolute;
+        z-index: 10;
+        bottom: 20px;
+        left: 50%;
+        transform: translateX(-50%);
+        width: 30px;
+        height: 30px;
+      }
+      .A1videoBtnLogo {
+        position: absolute;
+        pointer-events: none;
+        top: 56px;
+        width: 280px;
+        height: auto;
+        left: 50%;
+        transform: translateX(-50%);
+      }
+      .A1videoBtn {
+        position: absolute;
+        width: 50px;
+        height: 50px;
+        bottom: 30px;
+        left: 50%;
+        transform: translateX(-50%);
+        cursor: pointer;
+      }
+    }
+
+    .A1videoLast {
+      width: 100%;
+      height: 100%;
+      position: relative;
+      .A1videoLastBtn {
+        bottom: 20px;
+        right: 20px;
+        position: absolute;
+        width: 50px;
+        height: 50px;
+        cursor: pointer;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        color: #733c00;
+        background-image: url();
+        background-size: 100% 100%;
+        font-weight: 700;
+      }
+    }
+
     // 竖屏
     @media all and (orientation: portrait) {
     }

+ 208 - 5
Code/src/pages/A1home/index.tsx

@@ -1,12 +1,215 @@
-import React, { useRef } from 'react'
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
 import styles from './index.module.scss'
-import { Button } from 'antd'
-import UseDataUrl from '@/components/ownUse/UseDataUrl'
+import { myData } from '@/utils/http'
+import { domDelOwnFu, videoLodingNumFu } from '@/utils/xhrVideo'
+import classNames from 'classnames'
+import useMove from '@/components/ownUse/useMove'
+import useDataUrl from '@/components/ownUse/useDataUrl'
+import history from '@/utils/history'
 
 function A1home() {
-  const { dataUrl, dataUrlSame } = UseDataUrl('home')
+  const { dataUrlSame } = useDataUrl('home')
 
-  return <div className={styles.A1home}></div>
+  const { touchstart, touchmove, touchend } = useMove()
+
+  // 动画视频的ref
+  const videoRef = useRef<HTMLVideoElement>(null)
+
+  // 当前视频索引
+  const [videoInd, setVideoInd] = useState(-1)
+
+  useEffect(() => {
+    if (videoInd > -1) {
+      setTimeout(() => {
+        if (videoRef.current) {
+          videoRef.current.currentTime = 0
+          videoRef.current.play()
+        }
+      }, 100)
+    }
+  }, [videoInd])
+
+  const [loding, setLoding] = useState(0)
+
+  useEffect(() => {
+    if (loding >= 100) {
+      setVideoInd(0)
+      // 0.5s之后删除自己
+      setTimeout(() => {
+        domDelOwnFu('.A1base')
+      }, 500)
+    }
+  }, [loding])
+
+  const lodingIng = useRef(0)
+
+  // 5个动画视频的总长度
+  const lodingNumRef = useRef(0)
+
+  // 加载完成之后的bolo地址
+  const videoSrcArrRef = useRef<{ sort: number; name: string }[]>([])
+
+  const [videoSrcArr, setVideoSrcArr] = useState<string[]>([])
+
+  // 通过blob预加载 src为原视频的视频地址
+  const videoLodingFu = useCallback((src: string, sort: number) => {
+    return new Promise((resolve, reject) => {
+      const req = new XMLHttpRequest()
+      req.open('GET', src, true)
+      req.responseType = 'blob'
+
+      req.onprogress = function (event) {
+        if (lodingNumRef.current) {
+          const num = lodingIng.current + event.loaded
+
+          let percent = (num / lodingNumRef.current) * 100
+
+          percent = Number(percent.toFixed(0))
+
+          setLoding(percent)
+        }
+      }
+
+      req.onload = function (event) {
+        lodingIng.current += event.total
+        if (this.status === 200) {
+          const videoBlob = this.response
+          const blobSrc = URL.createObjectURL(videoBlob)
+          videoSrcArrRef.current.push({ sort, name: blobSrc })
+          if (videoSrcArrRef.current.length === myData.home.video.length) {
+            videoSrcArrRef.current = videoSrcArrRef.current.sort((a, b) => a.sort - b.sort)
+            setVideoSrcArr(videoSrcArrRef.current.map(v => v.name))
+          }
+        }
+      }
+      req.send()
+    })
+  }, [])
+
+  const getVideoNum = useCallback(() => {
+    myData.home.video.forEach(async v => {
+      const temp = await videoLodingNumFu(`${dataUrlSame}/${v.name}`)
+      lodingNumRef.current += temp
+
+      await videoLodingFu(`${dataUrlSame}/${v.name}`, v.sort)
+    })
+  }, [dataUrlSame, videoLodingFu])
+
+  useEffect(() => {
+    if (dataUrlSame) {
+      getVideoNum()
+    }
+  }, [dataUrlSame, getVideoNum])
+
+  // 视频播完 或者 点击下一步
+  const videoEndFu = useCallback(
+    (num: number) => {
+      if (videoInd === myData.home.video.length - 2 && num === 1) return
+      if (videoInd === 0 && num === -1) return
+      setVideoInd(videoInd + num)
+    },
+    [videoInd]
+  )
+  const onSwipeChange = useCallback(
+    (val: number) => {
+      videoEndFu(val)
+    },
+    [videoEndFu]
+  )
+
+  // 是否滑到了倒数第二个
+  const isLastButOne = useMemo(() => {
+    let flag = false
+    if (videoInd === myData.home.video.length - 2) flag = true
+    return flag
+  }, [videoInd])
+
+  // 是否到了最后一个
+  const isLastVideo = useMemo(() => {
+    let flag = false
+    if (videoInd === myData.home.video.length - 1) flag = true
+    return flag
+  }, [videoInd])
+
+  return (
+    <div className={styles.A1home}>
+      {/* 加载页面 */}
+      <div className={classNames('A1base', videoInd < 0 ? '' : 'A1baseHide')}>
+        <img src={`${dataUrlSame}/logo.png`} alt='' />
+        <video
+          src={`${dataUrlSame}/${myData.home.videoSta}`}
+          playsInline
+          muted
+          webkit-playsinline='true'
+          x5-video-player-type='h5'
+          autoPlay
+          loop
+        />
+
+        <div className='A1loding'>{loding}%</div>
+      </div>
+
+      {/* 5个视频动画页面 */}
+      {videoInd > -1 && videoSrcArr.length ? (
+        <>
+          {isLastVideo ? (
+            <div className='A1videoLast'>
+              <video
+                ref={videoRef}
+                src={videoSrcArr[videoSrcArr.length - 1]}
+                playsInline
+                muted
+                webkit-playsinline='true'
+                x5-video-player-type='h5'
+                onEnded={() => history.push('/visit')}
+              />
+              <div
+                className='A1videoLastBtn'
+                style={{ backgroundImage: `url(${dataUrlSame}/quan.png)` }}
+                onClick={() => history.push('/visit')}
+              >
+                跳 过
+              </div>
+            </div>
+          ) : (
+            <div
+              className='A1video'
+              onTouchStart={e => touchstart(e.touches[0].pageY)}
+              onTouchMove={e => touchmove(e.touches[0].pageY)}
+              onTouchEnd={() => touchend(val => onSwipeChange(val), 'mobile')}
+              onMouseDown={e => touchstart(e.pageY)}
+              onMouseMove={e => touchmove(e.pageY)}
+              onMouseUp={() => touchend(val => onSwipeChange(val), 'pc')}
+            >
+              <video
+                ref={videoRef}
+                src={videoSrcArr[videoInd]}
+                playsInline
+                muted
+                webkit-playsinline='true'
+                x5-video-player-type='h5'
+                onEnded={() => videoEndFu(1)}
+                loop={videoInd === videoSrcArr.length - 2}
+              />
+              {isLastButOne ? (
+                <>
+                  <img className='A1videoBtnLogo' src={`${dataUrlSame}/logo.png`} alt='' />
+                  <img
+                    className='A1videoBtn'
+                    onClick={() => setVideoInd(videoSrcArr.length - 1)}
+                    src={`${dataUrlSame}/nextLast.png`}
+                    alt=''
+                  />
+                </>
+              ) : (
+                <img onClick={() => videoEndFu(1)} src={`${dataUrlSame}/next.png`} alt='' />
+              )}
+            </div>
+          )}
+        </>
+      ) : null}
+    </div>
+  )
 }
 
 const MemoA1home = React.memo(A1home)

+ 92 - 0
Code/src/pages/A2visit/index.module.scss

@@ -0,0 +1,92 @@
+.A2visit {
+  :global {
+    video {
+      width: 100%;
+      height: 100%;
+      object-fit: fill;
+    }
+    .A2base {
+      width: 100%;
+      height: 100%;
+      position: absolute;
+      z-index: 10;
+      opacity: 1;
+      transition: opacity 0.5s;
+      & > img {
+        position: absolute;
+        top: 100px;
+        width: 280px;
+        left: 50%;
+        transform: translateX(-50%);
+      }
+      .A2baseBtn {
+        position: absolute;
+        bottom: 40px;
+        left: 50%;
+        transform: translateX(-50%);
+        cursor: pointer;
+        width: 100px;
+        height: auto;
+        z-index: 10;
+        & > img {
+          width: 100%;
+        }
+        .A2Btxt {
+          position: absolute;
+          top: 0;
+          left: 0;
+          color: #fffddc;
+          font-weight: 700;
+          width: 100%;
+          height: 100%;
+          display: flex;
+          justify-content: center;
+          align-items: center;
+        }
+        .A2Bxian {
+          position: absolute;
+          bottom: -10px;
+          left: 0;
+          width: 100%;
+          padding: 0 10px;
+          height: 2px;
+          & > div {
+            width: 100%;
+            height: 100%;
+            background-color: rgba(231, 214, 142, 0.6);
+            & > div {
+              width: 0%;
+              height: 100%;
+
+              background-color: #fffddc;
+            }
+          }
+        }
+      }
+    }
+    .A2baseHide {
+      opacity: 0;
+      pointer-events: none;
+    }
+
+    // 过度视频
+    .A2base2 {
+      width: 100%;
+      height: 100%;
+      position: absolute;
+      z-index: 10;
+      opacity: 1;
+      transition: opacity 0.5s;
+    }
+    .A2baseHide2 {
+      opacity: 0;
+      pointer-events: none;
+    }
+
+    .A2panoVideo {
+      width: 100%;
+      height: 100%;
+      background-color: red;
+    }
+  }
+}

+ 162 - 0
Code/src/pages/A2visit/index.tsx

@@ -0,0 +1,162 @@
+import React, { useCallback, useEffect, useRef, useState } from 'react'
+import styles from './index.module.scss'
+import classNames from 'classnames'
+import useDataUrl from '@/components/ownUse/useDataUrl'
+import { myData } from '@/utils/http'
+import { domDelOwnFu, videoLodingNumFu } from '@/utils/xhrVideo'
+
+function A2visit() {
+  const { dataUrlSame } = useDataUrl('visit')
+
+  // 动画视频的ref
+  const videoRef = useRef<HTMLVideoElement>(null)
+
+  // 当前视频索引
+  const [videoInd, setVideoInd] = useState(-1)
+
+  const [loding, setLoding] = useState(0)
+
+  const lodingIng = useRef(0)
+
+  // 动画视频的总长度
+  const lodingNumRef = useRef(0)
+
+  // 加载完成之后的bolo地址
+  const videoSrcArrRef = useRef<{ sort: number; name: string }[]>([])
+
+  const [videoSrcArr, setVideoSrcArr] = useState<string[]>([])
+
+  // 通过blob预加载 src为原视频的视频地址
+  const videoLodingFu = useCallback((src: string, sort: number) => {
+    return new Promise((resolve, reject) => {
+      const req = new XMLHttpRequest()
+      req.open('GET', src, true)
+      req.responseType = 'blob'
+
+      req.onprogress = function (event) {
+        if (lodingNumRef.current) {
+          const num = lodingIng.current + event.loaded
+
+          let percent = (num / lodingNumRef.current) * 100
+
+          percent = Number(percent.toFixed(0))
+
+          setLoding(percent)
+        }
+      }
+
+      req.onload = function (event) {
+        lodingIng.current += event.total
+        if (this.status === 200) {
+          const videoBlob = this.response
+          const blobSrc = URL.createObjectURL(videoBlob)
+          videoSrcArrRef.current.push({ sort, name: blobSrc })
+          if (videoSrcArrRef.current.length === myData.visit.video.length) {
+            videoSrcArrRef.current = videoSrcArrRef.current.sort((a, b) => a.sort - b.sort)
+            setVideoSrcArr(videoSrcArrRef.current.map(v => v.name))
+          }
+        }
+      }
+      req.send()
+    })
+  }, [])
+
+  const getVideoNum = useCallback(() => {
+    myData.visit.video.forEach(async v => {
+      const temp = await videoLodingNumFu(`${dataUrlSame}/${v.name}`)
+      lodingNumRef.current += temp
+
+      await videoLodingFu(`${dataUrlSame}/${v.name}`, v.sort)
+    })
+  }, [dataUrlSame, videoLodingFu])
+
+  useEffect(() => {
+    if (dataUrlSame) {
+      getVideoNum()
+    }
+  }, [dataUrlSame, getVideoNum])
+
+  // 开始之后的过度动画
+  const [overVideo, setOverVideo] = useState(true)
+
+  useEffect(() => {
+    if (!overVideo) {
+      // 0.5s之后删除过度视频
+      setTimeout(() => {
+        domDelOwnFu('.A2base2')
+      }, 500)
+      // 打开全景页面
+    }
+  }, [overVideo])
+
+  // 点击开始
+  const btnStart = useCallback(() => {
+    if (loding >= 100) {
+      // 播放已经加载好的视频
+      setVideoInd(0)
+      setTimeout(() => {
+        if (videoRef.current) {
+          videoRef.current.play()
+        }
+      }, 100)
+      // 0.5s之后删除初始视频
+      setTimeout(() => {
+        domDelOwnFu('.A2base')
+      }, 500)
+    }
+  }, [loding])
+
+  return (
+    <div className={styles.A2visit}>
+      {/* 加载页面 */}
+      <div className={classNames('A2base', videoInd < 0 ? '' : 'A2baseHide')}>
+        <img src={`${dataUrlSame}/mulu.png`} alt='' />
+        <div className='A2baseBtn'>
+          <img src={`${dataUrlSame}/btn.png`} alt='' />
+          <div className='A2Btxt' onClick={btnStart}>
+            {loding >= 100 ? '点击开始' : '加载中'}
+          </div>
+          {loding >= 100 ? null : (
+            <div className='A2Bxian'>
+              <div>
+                <div style={{ width: loding + '%' }}></div>
+              </div>
+            </div>
+          )}
+        </div>
+
+        <video
+          src={`${dataUrlSame}/${myData.visit.videoSta}`}
+          playsInline
+          muted
+          webkit-playsinline='true'
+          x5-video-player-type='h5'
+          autoPlay
+          loop
+        />
+      </div>
+
+      {/* 过度动画页面 */}
+      {videoInd === 0 ? (
+        <div className={classNames('A2base2', overVideo ? '' : 'A2baseHide2')}>
+          <video
+            ref={videoRef}
+            src={videoSrcArr[videoInd]}
+            playsInline
+            muted
+            webkit-playsinline='true'
+            x5-video-player-type='h5'
+            onEnded={() => setOverVideo(false)}
+          />
+        </div>
+      ) : null}
+
+      {/* 全景视频 */}
+      {overVideo ? null : <div className='A2panoVideo'>全景视频</div>}
+    </div>
+  )
+}
+
+const MemoA2visit = React.memo(A2visit)
+
+export default MemoA2visit

Code/src/pages/初始化组件 copy/index.module.scss → Code/src/pages/初始化组件 copy 2/index.module.scss


Code/src/pages/初始化组件 copy/index.tsx → Code/src/pages/初始化组件 copy 2/index.tsx


+ 5 - 1
Code/src/types/declaration.d.ts

@@ -16,6 +16,10 @@ declare const myDataTemp: MyDataType
 type MyDataType = {
   home: {
     videoSta: string
-    video: string[]
+    video: { sort: number; name: string }[]
+  }
+  visit: {
+    videoSta: string
+    video: { sort: number; name: string }[]
   }
 }

+ 29 - 0
Code/src/utils/xhrVideo.ts

@@ -0,0 +1,29 @@
+/**
+ *
+ * @param src 视频路径
+ * @returns Promise - 视频的总长度
+ */
+
+export const videoLodingNumFu = (src: string): Promise<number> => {
+  return new Promise((resolve, reject) => {
+    const req = new XMLHttpRequest()
+    req.open('HEAD', src, true)
+    req.onreadystatechange = function () {
+      if (req.readyState === 4) {
+        if (req.status === 200) {
+          const contentLength = req.getResponseHeader('Content-Length')
+          if (contentLength) {
+            const videoLength = parseInt(contentLength, 10)
+            resolve(videoLength)
+          }
+        }
+      }
+    }
+    req.send()
+  })
+}
+
+export const domDelOwnFu = (classNmae: string) => {
+  const dom = document.querySelector(classNmae)
+  if (dom) dom.remove()
+}

BIN
资源/staticData/HH/home/1.mp4


BIN
资源/staticData/HH/home/2.mp4


BIN
资源/staticData/HH/home/3.mp4


BIN
资源/staticData/HH/home/end.mp4


BIN
资源/staticData/HH/home/lastButOne.mp4


BIN
资源/staticData/HH/home/logo.png


BIN
资源/staticData/HH/home/next.png


BIN
资源/staticData/HH/home/nextLast.png


BIN
资源/staticData/HH/home/quan.png


BIN
资源/staticData/HH/visit/1.mp4


BIN
资源/staticData/HH/visit/base.mp4


BIN
资源/staticData/HH/visit/btn.png


BIN
资源/staticData/HH/visit/mulu.png