gemercheung 1 tahun lalu
induk
melakukan
0af0895caa
5 mengubah file dengan 296 tambahan dan 59 penghapusan
  1. 77 54
      src/App.jsx
  2. 60 0
      src/action.jsx
  3. 154 3
      src/components/TimeLineText.jsx
  4. 2 2
      src/components/Viewer.jsx
  5. 3 0
      src/index.css

+ 77 - 54
src/App.jsx

@@ -1,7 +1,11 @@
 import { useRef, useEffect } from "react";
 import { gsap, ScrollTrigger } from "gsap/all";
+import { Action } from "./action";
 import Opening from "./components/Opening";
 import Viewer from "./components/Viewer";
+import TimeLineText from "./components/TimeLineText";
+import { css } from "@emotion/react";
+
 import { useControls } from "leva";
 // import "./App.css";
 // gsap.registerPlugin(useGSAP);
@@ -20,20 +24,27 @@ function App() {
   const handlerResize = () => {
     clearTimeout(resizeTimer);
     resizeTimer = setTimeout(function () {
-      var height = 0.01 * window.innerHeight;
-      var width = ((16 * window.innerHeight) / 9) * 0.01;
-      var n = 0;
-      100 * height < window.innerWidth &&
-        (n = 0.5 * (window.innerWidth - 100 * height));
+      let height = 0.01 * window.innerHeight;
+      let sHeight = ((16 * window.innerHeight) / 9) * 0.01;
+      let cHeight = 0;
+
+      if (100 * sHeight < window.innerWidth) {
+        cHeight = 0.5 * (window.innerWidth - 100 * sHeight);
+      }
+
       document.documentElement.style.setProperty(
         "--vh",
         "".concat(height, "px")
       ),
         document.documentElement.style.setProperty(
           "--cw",
-          "".concat(width, "px")
+          "".concat(sHeight, "px")
         ),
-        document.documentElement.style.setProperty("--cl", "".concat(n, "px"));
+        document.documentElement.style.setProperty(
+          "--cl",
+          "".concat(cHeight, "px")
+        );
+        debugger
       console.log("fresh");
       ScrollTrigger.refresh();
       // ScrollTrigger.getAll().forEach((ST) => ST.refresh(true));
@@ -45,27 +56,6 @@ function App() {
     document.documentElement.style.overflow = "auto";
   });
 
-  const pinForExit = {
-    onLeave: function (e) {
-      // console.error("pinForExit-onLeave", e.trigger);
-      gsap.set(e.trigger, {
-        autoAlpha: 0,
-      });
-    },
-    onEnterBack: function (e) {
-      // console.error("pinForExit-onEnterBack", e.trigger);
-      gsap.set(e.trigger, {
-        autoAlpha: 1,
-      });
-    },
-  };
-  const crossFadeIn = {
-    duration: 0.5,
-    from: {
-      autoAlpha: 0,
-    },
-  };
-
   return (
     <div ref={container}>
       <Opening></Opening>
@@ -78,7 +68,40 @@ function App() {
         pause={{
           298: 40,
         }}
-      />
+      >
+        <TimeLineText
+          trigger={Action.scrubPin}
+          parentHeight={"1300vh"}
+          verticalOffset={"33vh"}
+          keyframes={[
+            {
+              ...Action.visible,
+              ...{
+                start: 0,
+                end: 0,
+              },
+            },
+            {
+              ...Action.fadeDown,
+              ...{
+                start: 0.02,
+                end: 0.03,
+              },
+            },
+          ]}
+        >
+          <div
+            css={css`
+              font-weight: 300;
+              font-size: 2.75vw;
+              width: 100%;
+              text-align: center;
+            `}
+          >
+            对古代美索不达米亚人来说,礼拜是日常生活的一部分。
+          </div>
+        </TimeLineText>
+      </Viewer>
 
       <Viewer
         height={"400vh"}
@@ -86,8 +109,8 @@ function App() {
         path="vessel-steadi-10fps-873-rev1"
         frameCount={64}
         debug={debug}
-        enterTween={crossFadeIn}
-        exitTween={pinForExit}
+        enterTween={Action.crossFadeIn}
+        exitTween={Action.pinForExit}
       />
 
       <Viewer
@@ -96,8 +119,8 @@ function App() {
         path="vessel-render-10fps-873-rev1"
         frameCount={125}
         debug={debug}
-        enterTween={crossFadeIn}
-        exitTween={pinForExit}
+        enterTween={Action.crossFadeIn}
+        exitTween={Action.pinForExit}
       />
 
       <Viewer
@@ -106,8 +129,8 @@ function App() {
         path="michaux-steadi-10fps-873-rev1"
         frameCount={98}
         debug={debug}
-        enterTween={crossFadeIn}
-        exitTween={pinForExit}
+        enterTween={Action.crossFadeIn}
+        exitTween={Action.pinForExit}
       />
 
       <Viewer
@@ -115,8 +138,8 @@ function App() {
         name="Michaux Render"
         path="michaux-render-10fps-873-rev1"
         frameCount={158}
-        enterTween={crossFadeIn}
-        exitTween={pinForExit}
+        enterTween={Action.crossFadeIn}
+        exitTween={Action.pinForExit}
       />
 
       <Viewer
@@ -124,16 +147,16 @@ function App() {
         name="Gudea Architect Steadi"
         path="gudea-architect-steadi-10fps-873-rev1"
         frameCount={84}
-        enterTween={crossFadeIn}
-        exitTween={pinForExit}
+        enterTween={Action.crossFadeIn}
+        exitTween={Action.pinForExit}
       />
       <Viewer
         height={"800vh"}
         name="Gudea Architect Render"
         path="gudea-architect-render-10fps-873-rev1"
         frameCount={201}
-        enterTween={crossFadeIn}
-        exitTween={pinForExit}
+        enterTween={Action.crossFadeIn}
+        exitTween={Action.pinForExit}
       />
 
       <Viewer
@@ -141,8 +164,8 @@ function App() {
         name="Dictionary Steadi"
         path="dictionary-steadi-10fps-873-rev1"
         frameCount={258}
-        enterTween={crossFadeIn}
-        exitTween={pinForExit}
+        enterTween={Action.crossFadeIn}
+        exitTween={Action.pinForExit}
       />
 
       <Viewer
@@ -150,8 +173,8 @@ function App() {
         name="Dictionary Render"
         path="dictionary-steadi-10fps-873-rev1"
         frameCount={45}
-        enterTween={crossFadeIn}
-        exitTween={pinForExit}
+        enterTween={Action.crossFadeIn}
+        exitTween={Action.pinForExit}
       />
 
       <Viewer
@@ -168,7 +191,7 @@ function App() {
             y: 500,
           },
         }}
-        exitTween={pinForExit}
+        exitTween={Action.pinForExit}
       />
 
       <Viewer
@@ -176,8 +199,8 @@ function App() {
         name="Cone Steadi"
         path="cone-steadi-10fps-873-rev1"
         frameCount={256}
-        enterTween={crossFadeIn}
-        exitTween={pinForExit}
+        enterTween={Action.crossFadeIn}
+        exitTween={Action.pinForExit}
       />
 
       <Viewer
@@ -185,8 +208,8 @@ function App() {
         name="Cone Render"
         path="cone-render-10fps-873-rev1"
         frameCount={50}
-        enterTween={crossFadeIn}
-        exitTween={pinForExit}
+        enterTween={Action.crossFadeIn}
+        exitTween={Action.pinForExit}
       />
 
       <Viewer
@@ -194,8 +217,8 @@ function App() {
         name="Gudea Steadi"
         path="gudea-steadi-10fps-873-rev1"
         frameCount={50}
-        enterTween={crossFadeIn}
-        exitTween={pinForExit}
+        enterTween={Action.crossFadeIn}
+        exitTween={Action.pinForExit}
       />
 
       <Viewer
@@ -203,8 +226,8 @@ function App() {
         name="Gudea Render"
         path="gudea-render-10fps-873-rev1"
         frameCount={184}
-        enterTween={crossFadeIn}
-        exitTween={pinForExit}
+        enterTween={Action.crossFadeIn}
+        exitTween={Action.pinForExit}
       />
     </div>
   );

+ 60 - 0
src/action.jsx

@@ -0,0 +1,60 @@
+import gsap from "gsap";
+
+export const Action = {
+  scrubPin: {
+    pin: true,
+    scrub: true,
+  },
+  crossFadeIn: {
+    duration: 0.5,
+    from: {
+      autoAlpha: 0,
+    },
+  },
+  pinForExit: {
+    onLeave: function (e) {
+      // console.error("pinForExit-onLeave", e.trigger);
+      gsap.set(e.trigger, {
+        autoAlpha: 0,
+      });
+    },
+    onEnterBack: function (e) {
+      // console.error("pinForExit-onEnterBack", e.trigger);
+      gsap.set(e.trigger, {
+        autoAlpha: 1,
+      });
+    },
+  },
+  visible: {
+    type: "set",
+    animation: {
+      autoAlpha: 1,
+    },
+  },
+  fadeIn: {
+    type: "from",
+    animation: {
+      autoAlpha: 0,
+    },
+  },
+  fadeOut: {
+    type: "to",
+    animation: {
+      autoAlpha: 0,
+    },
+  },
+  fadeUp: {
+    type: "from",
+    animation: {
+      y: 75,
+      autoAlpha: 0,
+    },
+  },
+  fadeDown: {
+    type: "to",
+    animation: {
+      y: -75,
+      autoAlpha: 0,
+    },
+  },
+};

+ 154 - 3
src/components/TimeLineText.jsx

@@ -1,5 +1,156 @@
-import { useEffect } from "react";
+import { Component, createRef } from "react";
+import gsap from "gsap";
+import PropTypes from "prop-types";
+import { css } from "@emotion/react";
 
-export default function TimeLineText() {
-  return <></>;
+export default class TimeLineText extends Component {
+  constructor(props) {
+    super();
+    this.containerRef = createRef();
+    this.timeline = false;
+    this.top = 0;
+    this.init(props);
+  }
+  init(props) {
+    console.log("props", props);
+  }
+  componentWillUnmount() {
+    console.error("remove-timeline");
+    if (this.timeline) {
+      this.timeline.kill(true);
+      this.timeline = false;
+    }
+  }
+  componentDidMount() {
+    if (this.props.keyframes && this.props.trigger) {
+      // var n = this.props.keyframes[0].start;
+      // this.setTop(n);
+      this.createTimeLine();
+    }
+  }
+  setTop(height) {
+    const parentHeight = parseFloat(this.props.parentHeight);
+    const lastHeight =
+      ((height * window.innerHeight * ((parentHeight - 100) / 100)) /
+        ((window.innerHeight * parentHeight) / 100)) *
+      100;
+    // debugger;
+    this.top = lastHeight + "%";
+    console.log("this.top", this.top);
+  }
+
+  createTimeLine() {
+    if (!this.timeline) {
+      const start = this.props.keyframes[0].start;
+      const end = this.props.keyframes[0].end;
+      const dif = end - start;
+      const lastS = this.props.keyframes[this.props.keyframes.length - 1].start;
+      const lastE = this.props.keyframes[this.props.keyframes.length - 1].end;
+      const lDif = lastE - lastS;
+      const gapDiff = lastS - end;
+      const timeRes =
+        (lastE - start) * (parseFloat(this.props.parentHeight) - 100);
+      console.log("timeRes", timeRes);
+
+      this.timeline = gsap.timeline({
+        scrollTrigger: {
+          ...{
+            trigger: this.containerRef.current,
+            start: "top top",
+            end: "+=" + timeRes + "%",
+            onEnter: () => {
+              console.warn("onEnter");
+            },
+          },
+          ...this.props.trigger,
+        },
+      });
+      for (var i = 0; i < this.props.keyframes.length; i += 1) {
+        const cFrame = this.props.keyframes[i];
+        1 === i &&
+          gapDiff > 0.001 &&
+          this.timeline.to(this.containerRef.current, {
+            duration: gapDiff,
+          });
+        if (cFrame.type === "set") {
+          this.timeline.set(this.containerRef.current, cFrame.animation);
+        }
+        if (cFrame.type === "from") {
+          this.timeline.from(this.containerRef.current, {
+            ...cFrame.animation,
+            ...{
+              duration: dif,
+            },
+          });
+        } else {
+          this.timeline.to(this.containerRef.current, {
+            ...cFrame.animation,
+            ...{
+              duration: lDif,
+            },
+          });
+        }
+      }
+    }
+  }
+
+  render() {
+    const top =
+      "calc(var(--vh, 1vh) *" + parseFloat(this.props.verticalOffset) + ")";
+
+    return (
+      <div
+        css={css`
+          top: ${this.top};
+          position: absolute;
+          width: 100%;
+          text-align: center;
+          pointer-events: none;
+        `}
+        ref={this.containerRef}
+      >
+        <div
+          css={css`
+            position: relative;
+            display: flex;
+            margin-left: var(--cl);
+            max-width: calc(var(--cw) * 100);
+            z-index: 20;
+            direction: ltr;
+            direction: var(--text-direction);
+            text-align: left;
+            text-align: var(--alignment);
+          `}
+        >
+          <div
+            css={css`
+              position: absolute;
+              width: 100%;
+            `}
+          >
+            <div
+              className="text-wrapper"
+              css={css`
+                margin-top: ${top};
+              `}
+            >
+              {this.props.children}
+            </div>
+          </div>
+        </div>
+      </div>
+    );
+  }
 }
+
+TimeLineText.propTypes = {
+  verticalOffset: PropTypes.string,
+  children: PropTypes.object,
+  trigger: PropTypes.object,
+  parentHeight: PropTypes.string,
+  keyframes: PropTypes.arrayOf(PropTypes.object),
+};
+
+TimeLineText.defaultProps = {
+  verticalOffset: "100vh",
+};

+ 2 - 2
src/components/Viewer.jsx

@@ -11,7 +11,7 @@ console.log("isDebug", isDebug);
 export default function Viewer({ height, ...props }) {
   return (
     <LazyLoad height={height || "500vh"}>
-      <ViewerInner {...props} />
+      <ViewerInner height={height} {...props} />
     </LazyLoad>
   );
 }
@@ -26,7 +26,7 @@ Viewer.propTypes = {
   canvasSize: PropTypes.array,
   pause: PropTypes.object,
   children: PropTypes.object,
-  debug:PropTypes.bool
+  debug: PropTypes.bool,
 };
 
 class ViewerInner extends Component {

+ 3 - 0
src/index.css

@@ -1,5 +1,7 @@
 :root {
   font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
+  --text-direction: ltr;
+  --alignment: left;
   /* line-height: 1.5;
   font-weight: 400;
 
@@ -12,6 +14,7 @@
   /* font-synthesis: none;
   text-rendering: optimizeLegibility;
 
+
   -moz-osx-font-smoothing: grayscale; */
 }