gemercheung преди 1 година
родител
ревизия
5664b43f87
променени са 9 файла, в които са добавени 247 реда и са изтрити 105 реда
  1. 2 1
      .env
  2. 2 1
      .env.development
  3. 2 1
      .env.production
  4. 0 42
      src/App.css
  5. 29 3
      src/App.jsx
  6. 16 15
      src/components/Opening.jsx
  7. 46 0
      src/components/ProcessBar.jsx
  8. 144 37
      src/components/Viewer.jsx
  9. 6 5
      src/index.css

+ 2 - 1
.env

@@ -1 +1,2 @@
-VITE_APP_SOURCE=https://houseoss.4dkankan.com/project/nanjinbwg-demo/getty-image/
+VITE_APP_SOURCE=https://houseoss.4dkankan.com/project/nanjinbwg-demo/getty-image/
+VITE_APP_DEBUG=0

+ 2 - 1
.env.development

@@ -1 +1,2 @@
-VITE_APP_SOURCE=https://houseoss.4dkankan.com/project/nanjinbwg-demo/getty-image/
+VITE_APP_SOURCE=https://houseoss.4dkankan.com/project/nanjinbwg-demo/getty-image/
+VITE_APP_DEBUG=0

+ 2 - 1
.env.production

@@ -1 +1,2 @@
-VITE_APP_SOURCE=https://houseoss.4dkankan.com/project/nanjinbwg-demo/getty-image/
+VITE_APP_SOURCE=https://houseoss.4dkankan.com/project/nanjinbwg-demo/getty-image/
+VITE_APP_DEBUG=1

+ 0 - 42
src/App.css

@@ -1,42 +0,0 @@
-#root {
-  max-width: 1280px;
-  margin: 0 auto;
-  padding: 2rem;
-  text-align: center;
-}
-
-.logo {
-  height: 6em;
-  padding: 1.5em;
-  will-change: filter;
-  transition: filter 300ms;
-}
-.logo:hover {
-  filter: drop-shadow(0 0 2em #646cffaa);
-}
-.logo.react:hover {
-  filter: drop-shadow(0 0 2em #61dafbaa);
-}
-
-@keyframes logo-spin {
-  from {
-    transform: rotate(0deg);
-  }
-  to {
-    transform: rotate(360deg);
-  }
-}
-
-@media (prefers-reduced-motion: no-preference) {
-  a:nth-of-type(2) .logo {
-    animation: logo-spin infinite 20s linear;
-  }
-}
-
-.card {
-  padding: 2em;
-}
-
-.read-the-docs {
-  color: #888;
-}

+ 29 - 3
src/App.jsx

@@ -42,6 +42,21 @@ function App() {
     handlerResize();
   });
 
+  const pinForExit = {
+    onLeave: function (e) {
+      console.warn("pinForExit-onLeave");
+      gsap.set(e.trigger, {
+        autoAlpha: 0,
+      });
+    },
+    onEnterBack: function (e) {
+      console.warn("pinForExit-onLeave");
+      gsap.set(e.trigger, {
+        autoAlpha: 1,
+      });
+    },
+  };
+
   return (
     <div ref={container}>
       <Opening></Opening>
@@ -57,14 +72,25 @@ function App() {
         />
       </LazyLoad>
       <LazyLoad height={"400vh"}>
-        <Viewer name="222" height={"400vh"} />
+        <Viewer
+          name="Vessel Steadicam"
+          path="vessel-steadi-10fps-873-rev1"
+          frameCount={64}
+          enterTween={{
+            from: {
+              autoAlpha: 0,
+              y: 0,
+            },
+          }}
+          exitTween={pinForExit}
+        />
       </LazyLoad>
-      <LazyLoad height={"1300vh"}>
+      {/* <LazyLoad height={"1300vh"}>
         <Viewer name="333" height={"1300vh"} />
       </LazyLoad>
       <LazyLoad height={"1300vh"}>
         <Viewer name="444" height={"1300vh"} />
-      </LazyLoad>
+      </LazyLoad> */}
     </div>
   );
 }

+ 16 - 15
src/components/Opening.jsx

@@ -12,6 +12,7 @@ const TopTitleWrapper = styled.div({
 export default function Opening() {
   const containerRef = useRef();
   const TitleRef = useRef();
+  const height = window.innerHeight;
   //   const containerRef = useRef();
 
   useEffect(() => {
@@ -23,24 +24,24 @@ export default function Opening() {
       autoAlpha: 1,
       delay: 0.77,
     });
-    // gsap.to(containerRef.current.children, {
-    //   autoAlpha: 0,
-    //   y: -125,
-    //   stagger: 0.08,
-    //   scrollTrigger: {
-    //     scrub: true,
-    //     pin: true,
-    //     pinSpacing: false,
-    //     ease: "none",
-    //     trigger: containerRef.current,
-    //     start: "top top",
-    //     end: "center top",
-    //   },
-    // });
+    gsap.to(containerRef.current.children, {
+      autoAlpha: 0,
+      y: -125,
+      stagger: 0.08,
+      scrollTrigger: {
+        scrub: true,
+        pin: true,
+        pinSpacing: false,
+        ease: "none",
+        trigger: containerRef.current,
+        start: "top top",
+        end: "center top",
+      },
+    });
   });
 
   return (
-    <div ref={containerRef} style={{ height: "911px" }}>
+    <div ref={containerRef} style={{ height: height }}>
       <TopTitleWrapper ref={TitleRef}>美索不达米亚</TopTitleWrapper>
     </div>
   );

+ 46 - 0
src/components/ProcessBar.jsx

@@ -0,0 +1,46 @@
+import { css } from "@emotion/react";
+import { useEffect } from "react";
+import { createRef } from "react";
+
+// eslint-disable-next-line react/prop-types
+export default function ProcessBar({ progressing }) {
+  const processBarStyle = css`
+    position: fixed;
+    top: calc(100vh - 4px);
+    // top: calc(var(--vh, 1vh) * 100 - 4px);
+    left: 0;
+    width: 100vw;
+    max-width: 100%;
+    height: 4px;
+    background-color: rgba(51, 51, 68, 0.6);
+    z-index: 9;
+  `;
+  const processGoingStyle = css`
+    position: absolute;
+    width: auto;
+    height: 4px;
+    left: 0;
+    bottom: 0;
+    z-index: 10;
+  `;
+  const preProcessGoingStyle = css`
+    position: absolute;
+    width: auto;
+    height: 4px;
+    left: 0;
+    bottom: 0;
+    z-index: 10;
+  `;
+  useEffect(() => {
+    console.debug("progressing", progressing);
+  });
+
+  const processingRef = createRef();
+  const preProcessingRef = createRef();
+  return (
+    <div css={processBarStyle}>
+      <div css={processGoingStyle} ref={processingRef}></div>
+      <div css={preProcessGoingStyle} ref={preProcessingRef}></div>
+    </div>
+  );
+}

+ 144 - 37
src/components/Viewer.jsx

@@ -1,9 +1,9 @@
 import { Component, createRef } from "react";
 import PropTypes from "prop-types";
 import { gsap, ScrollTrigger } from "gsap/all";
-import styled from "@emotion/styled";
 import { css } from "@emotion/react";
-
+const isDebug = Number(import.meta.env.VITE_APP_SOURCE) === 1;
+console.log("isDebug", isDebug);
 export default class Viewer extends Component {
   constructor() {
     super();
@@ -14,18 +14,22 @@ export default class Viewer extends Component {
     this.canvasContainerRef = createRef(null);
     this.loadingWrap = createRef(null);
     this.viewerOffsetRef = createRef(null);
-    this.canvasRef = createRef();
-
+    this.canvasRef = createRef(null);
+    this.processingRef = createRef(null);
+    this.preProcessingRef = createRef(null);
     this.sequence = [];
     this.loadedRenderPool = [];
     this.enterTimeline = false;
     this.exitTimeline = false;
     this.loadComplete = false;
 
+    this.playBarTween = false;
+    this.playPreBarTween = false;
     this.loadedCount = 0;
     this.progress = 0;
     this.lastFrame = -1;
     this.floatFrame = 0;
+    this.frame = 0;
     this.loadedRenderTimeout = null;
     this.poolAnimateDelay = 40;
 
@@ -41,22 +45,31 @@ export default class Viewer extends Component {
     height: PropTypes.string,
     path: PropTypes.string,
     frameCount: PropTypes.number,
-    enterTween: PropTypes.func,
-    exitTween: PropTypes.func,
+    startFrame: PropTypes.number,
+    enterTween: PropTypes.object,
+    exitTween: PropTypes.object,
     canvasSize: PropTypes.array,
     pause: PropTypes.object,
     children: PropTypes.object,
   };
-
-  state = {};
-
   componentWillUnmount() {
-    this.timeline && this.timeline.kill(true);
+    console.error("remove-timeline");
+    if (this.timeline) {
+      this.timeline.kill(true);
+    }
   }
 
   componentDidMount() {
+    this.fullFrameCount = this.props.frameCount;
+    this.frame = this.props.startFrame || 0;
+    if (this.props.pause) {
+      Object.keys(this.props.pause).forEach((index) => {
+        this.fullFrameCount += this.props.pause[index];
+      });
+    }
     this.loadAssets();
     this.canvasRef.current && this.initializeCanvas();
+
     if (!this.timeline) {
       this.initializeTimeline();
       this.setTimeline();
@@ -64,12 +77,15 @@ export default class Viewer extends Component {
     if (!this.enterTimeline && this.props.enterTween) {
       this.initializeEnterTween();
     }
+    if (!this.exitTimeline && this.props.exitTween) {
+      this.initializeExitTween();
+    }
     ScrollTrigger.refresh();
   }
 
   loadImage(index) {
     const img = new Image();
-    // console.log("index", this.getSourcePath(index));
+
     img.retried = 0;
     img.src = this.getSourcePath(index);
     img.ogSrc = img.src;
@@ -86,6 +102,7 @@ export default class Viewer extends Component {
       if (this.frame > index && this.timeline.scrollTrigger.isActive) {
         this.poolNewFrames(index - 1);
       }
+      // var t = 100 - (this.frame / this.fullFrameCount) * 100 + "%";
       this.loadedCount += 1;
       if (this.loadedCount === parseFloat(this.props.frameCount) - 1) {
         this.loadingComplete();
@@ -142,7 +159,7 @@ export default class Viewer extends Component {
         start: "top top",
         end: "bottom bottom",
         ease: "none",
-        markers: true,
+        markers: isDebug,
         onUpdate: function (n) {
           //处理processloading
         },
@@ -173,13 +190,15 @@ export default class Viewer extends Component {
     });
   }
   setTimeline() {
-    console.log("this.fullFrameCount", this.fullFrameCount);
     this.timeline.to(this, {
       floatFrame: this.fullFrameCount - 1,
       ease: "none",
       onUpdate: () => {
         this.frame = Math.floor(this.floatFrame);
-        if (this.lastFrame === this.frame || this.loadedRenderPool.length > 0) {
+        if (
+          this.lastFrame === this.frame ||
+          this.loadedRenderPool.length === 0
+        ) {
           this.renderImageToCanvas(this.frame);
         }
       },
@@ -187,21 +206,61 @@ export default class Viewer extends Component {
   }
 
   initializeEnterTween() {
-    console.log("initializeEnterTween");
+    const duration = this.props.enterTween.duration || 1;
+    console.log("initializeEnterTween", duration, this.props.enterTween.pin);
+    gsap.set(this.viewerRef.current, {
+      yPercent: -100 * duration,
+    });
+    if (this.props.enterTween.pin !== 0) {
+      console.warn("tutu", this.props.enterTween.pin);
+      this.enterTimeline = gsap.timeline({
+        scrollTrigger: {
+          trigger: this.viewerRef.current,
+          scrub: true,
+          pin: this.props.enterTween.pin,
+          start: function () {
+            return "top top";
+          },
+          end: function () {
+            return "top top-=" + window.innerHeight * duration;
+          },
+        },
+      });
+    }
+  }
+  initializeExitTween() {
+    console.log(this.props.path, "initializing exit tween ");
+    console.warn("this.props.exitTween", this.props.exitTween);
+    this.exitTimeline = gsap.timeline({
+      scrollTrigger: {
+        scrub: true,
+        trigger: this.containerRef.current,
+        pin: this.viewerRef.current,
+        onLeave: this.props.exitTween.onLeave,
+        onLeaveBack: this.props.exitTween.onLeaveBack,
+        onEnterBack: this.props.exitTween.onEnterBack,
+        start: function () {
+          return "bottom bottom";
+        },
+        end: function () {
+          return "bottom top";
+        },
+      },
+    });
   }
 
   poolNewFrames(index) {
-    console.log("poolNewFrames", index);
     this.loadedRenderPool.unshift(index);
-    this.loadedRenderPool.sort(function (e, t) {
-      return t - e;
+    this.loadedRenderPool.sort(function (a, b) {
+      return b - a;
     });
     this.animatePool();
   }
+
   animatePool() {
     if (!this.loadedRenderTimeout && this.loadedRenderPool.length) {
       this.loadedRenderTimeout = setTimeout(() => {
-        this.loadedRenderTimeout = true;
+        this.loadedRenderTimeout = false;
         var poolFrame = this.loadedRenderPool[this.loadedRenderPool.length - 1];
         if (poolFrame <= this.frame) {
           var remainFrame = this.loadedRenderPool.pop();
@@ -217,39 +276,88 @@ export default class Viewer extends Component {
   renderImageToCanvas(index) {
     if (this.sequence[index]) {
       if (this.context.drawImage) {
-        console.log("renderImageToCanvas", index);
         this.context.drawImage(this.sequence[index], 0, 0);
         this.lastFrame = index;
+        this.handleSyncProessBar(index);
       } else {
         this.initializeCanvas();
       }
     }
   }
 
-  render() {
-    // process props
-    this.fullFrameCount = this.props.frameCount;
-    if (this.props.pause) {
-      Object.keys(this.props.pause).forEach((index) => {
-        this.fullFrameCount += this.props.pause[index];
-      });
-    }
-    const Wrapper = styled.div({
-      position: "relative",
-      margin: "auto",
-      textAlign: "center",
-      pointerEvents: "none",
-      maxWidth: "100vw",
+  handleSyncProessBar(index) {
+    const progressingPreload =
+      100 - (this.frame / this.fullFrameCount) * 100 + "%";
+    const progressing = 100 - (index / this.fullFrameCount) * 100 + "%";
+    this.playPreBarTween = gsap.to(this.preProcessingRef.current, {
+      duration: 0.05,
+      right: progressingPreload,
+      ease: "none",
     });
+    this.playBarTween = gsap.to(this.processingRef.current, {
+      duration: 0.05,
+      right: progressing,
+      ease: "none",
+    });
+  }
+
+  render() {
     return (
       <>
-        <Wrapper
+        <div
+          css={css`
+            position: "relative";
+            margin: "auto";
+            textalign: "center";
+            pointerevents: "none";
+            maxwidth: "100vw";
+          `}
           ref={this.containerRef}
           style={{ height: this.props.height || "500vh" }}
         >
           <div>{this.props.name}</div>
           <div className="loading-wrap" ref={this.loadingWrap}></div>
 
+          <div
+            css={css`
+              position: fixed;
+              top: calc(100vh - 4px);
+              // top: calc(var(--vh, 1vh) * 100 - 4px);
+              left: 0;
+              width: 100vw;
+              max-width: 100%;
+              border-top: 1px solid rgba(33, 33, 44, 0.6);
+              background-color: rgba(17, 17, 34, 0.6);
+              height: 4px;
+              z-index: 9;
+            `}
+          >
+            <div
+              css={css`
+                position: absolute;
+                width: auto;
+                height: 4px;
+                left: 0;
+                bottom: 0;
+                z-index: 10;
+                background-color: rgba(120, 120, 163, 0.33);
+              `}
+              ref={this.preProcessingRef}
+            ></div>
+            <div
+              css={css`
+                position: absolute;
+                width: auto;
+                height: 4px;
+                left: 0;
+                bottom: 0;
+                z-index: 10;
+                background-color: hsla(0, 0%, 79.2%, 0.5);
+              `}
+              ref={this.processingRef}
+            ></div>
+          </div>
+
           <div ref={this.viewerRef}>
             <div style={{ overflow: "hidden" }}>
               <canvas
@@ -265,9 +373,8 @@ export default class Viewer extends Component {
               ></canvas>
             </div>
           </div>
-
           <>{this.props.children}</>
-        </Wrapper>
+        </div>
       </>
     );
   }

+ 6 - 5
src/index.css

@@ -1,16 +1,17 @@
 :root {
   font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
-  line-height: 1.5;
+  /* line-height: 1.5;
   font-weight: 400;
 
   color-scheme: light dark;
-  color: rgba(255, 255, 255, 0.87);
+  color: rgba(255, 255, 255, 0.87); */
   background-color: #000;
-
-  font-synthesis: none;
+  font-size: 1rem;
+  color-scheme: light dark;
+  /* font-synthesis: none;
   text-rendering: optimizeLegibility;
   -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
+  -moz-osx-font-smoothing: grayscale; */
 }
 
 a {