将固定内容移动到100vh组件的下方和上方

2022-03-10 00:00:00 reactjs javascript css css-animations
目前,我的屏幕中心有一个固定的图像,它在鼠标滚动时水平移动。我现在想在这个组件的上方和下方添加一个高度为100vh的屏幕。当我尝试这样做时,我的固定组件会自动移动到我想清空的屏幕顶部。我曾尝试向图像添加页边距顶部:100vh,但已将其移出屏幕。

这里有两件事我有问题。1是如何在100vh窗口之后将此组件放在屏幕中央,2是如何仅在用户到达该特定组件时启动动画。因为现在设置项目时,图像和文本会在用户滚动上移动,所以如果我在上面添加一个100vh的屏幕,则用户滚动已经开始,这将启动我的动画。

CodeSanbox(全屏查看更好的参考):https://codesandbox.io/s/interesting-bouman-lokt5?file=/src/App.js

编码:

{/* <div style={{height:"100vh"}}></div> */}
      <div className="App" ref={ref}>
        <h1 id="title" className="titles">
          Random Title
        </h1>
        <section id="block1" className="blocks"></section>
        <figure id="passport">
          <img
            alt="passport"
            src="https://cdn.britannica.com/87/122087-050-1C269E8D/Cover-passport.jpg"
          />
        </figure>
        <h2 id="text1" className="titles text1">
          Random Text 1
        </h2>
        <section id="block2" className="blocks"></section>
        <h2 id="text2" className="titles text2">
          Random Text 2
        </h2>
        <section id="block3" className="blocks"></section>
        <h2 id="text3" className="titles text3">
          Random Text 3
        </h2>
        <section id="block4" className="blocks"></section>
      </div>
      {/* Stop Scrolling Animation */}
      {/* <div>Content</div> */}

解决方案

此解决方案:

  1. 获取第一个元素的高度。
  2. 隐藏图像和第一个标题。
  3. 定义调用操作的时间。
  4. 显示或隐藏时添加其他检查。

沙盒示例link

function App() {
  const [screen, setScreen] = React.useState(false);

  const headerRef = React.useRef(null);
  const mainRef = React.useRef(null);

  // Reduce value if want the image to be closer to the edges
  // otherwise to the center
  const setImageLimitMovement = 1;

  const setTextLimitMovement = 4;
  const opacityRange = 400;
  // Speed text movement
  const speed = 1; // .5

  React.useEffect(() => {
    window.addEventListener('resize', () => {
      if (window.innerWidth !== 0 || window.innerHeight !== 0) {
        setScreen(window.innerWidth);
      }
    });
  }, []);

  React.useEffect(() => {
    const app = [...mainRef.current.children];
    const titles = app.filter(el => el.matches('.titles') && el);
    const blocks = app.filter(el => el.matches('.blocks') && el);
    const img = app.find(el => el.matches('#passport') && el);

    const headerHeight = headerRef.current.getBoundingClientRect().height;

    // Get the center point of  blocks in an array
    const centerPoints = blocks.map((blockEl, idx) => {
      const blockindex = idx + 1;
      const blockHeight = Math.floor(blockEl.getBoundingClientRect().height);
      const blockHalf = blockHeight / 2;
      return blockHeight * blockindex - blockHalf;
    });

    const leftMoveLimitImg = -centerPoints[0] / setImageLimitMovement;
    const rightMoveLimitImg = centerPoints[0] / setImageLimitMovement;

    const textLimit = centerPoints[0] / setTextLimitMovement;
    // Hide when component mounted
    titles[0].style.transform = `scale(0)`;
    img.style.transform = `scale(0)`;

    const changeBackground = () => {
      const value = window.scrollY;

      // Init
      const startAction = headerHeight * 0.8;
      // const startToHideInFooter = centerPoints[3] + startAction;

      // Scale action to show content
      if (centerPoints[3] + startAction > value && startAction < value) {
        titles[0].style.transform = `scale(${
          (value - startAction) / 100 > 1 ? 1 : (value - startAction) / 100
        })`;
        // titles[0].style.display = 'block';
        img.style.transform = `scale(${
          (value - startAction) / 100 > 1 ? 1 : (value - startAction) / 100
        })`;
        // img.style.display = 'block';
      }

      // Hide image & title if header visible
      if (startAction > value) {
        titles[0].style.transform = `scale(0)`;
        img.style.transform = `scale(0)`;
      }

      // Hide first 'random text' when scroll above block
      if (headerHeight > value) {
        titles[1].style.transform = `translateX(0px)`;
        titles[1].style.opacity = 0;
      }

      if (headerHeight < value) {
        // Start scroll animation
        const mainValue = value - headerHeight; // reset scroll to start from 0
        titles[0].style.transform = `translateY(-${mainValue * speed}px)`;

        // IMAGE BOUNCE
        // Move to <==
        if (centerPoints[0] > mainValue) {
          img.style.transform = `translateX(-${
            mainValue * (1 / setImageLimitMovement)
          }px)`;

          titles[1].style.transform = `translateX( ${
            0 + mainValue / setTextLimitMovement
          }px)`;
          titles[1].style.opacity = mainValue / opacityRange;
          return;
        }

        // Move to ==>
        if (centerPoints[1] > mainValue) {
          const moveTextToRight =
            centerPoints[1] / setTextLimitMovement - textLimit;
          const hideText = centerPoints[0] / opacityRange;
          const checkDirection = Math.sign(
            textLimit + (textLimit - mainValue / setTextLimitMovement)
          );

          const moveImageToRight =
            (mainValue - centerPoints[0]) / setImageLimitMovement;
          img.style.transform = `translateX(${
            leftMoveLimitImg + moveImageToRight
          }px)`;

          if (checkDirection === -1) {
            titles[1].style.opacity = 0;
            titles[1].style.transform = `translateX(${0}px)`;

            titles[2].style.opacity =
              Math.abs(hideText - mainValue / opacityRange) - 1;
            titles[2].style.transform = `translateX(${
              moveTextToRight - mainValue / setTextLimitMovement
            }px)`;
            return;
          }
          if (checkDirection === 1) {
            titles[1].style.opacity = 1 + (hideText - mainValue / opacityRange);
            titles[1].style.transform = `translateX(${
              textLimit + (textLimit - mainValue / setTextLimitMovement)
            }px)`;

            titles[2].style.opacity = 0;
            titles[2].style.transform = `translateX(${0}px)`;
          }
          return;
        }

        // Move to <==
        if (centerPoints[2] > mainValue) {
          const moveTextToLeft =
            centerPoints[2] / setTextLimitMovement - textLimit;
          const hideText = centerPoints[1] / opacityRange;
          const checkDirection = Math.sign(
            moveTextToLeft - mainValue / setTextLimitMovement
          );

          const moveImageToLeft =
            (-mainValue + centerPoints[1]) / setImageLimitMovement;
          img.style.transform = `translateX(${
            rightMoveLimitImg + moveImageToLeft
          }px)`;

          if (checkDirection === -1) {
            titles[2].style.opacity = 0;
            titles[2].style.transform = `translateX(${0}px)`;

            titles[3].style.opacity =
              Math.abs(hideText - mainValue / opacityRange) - 1;
            titles[3].style.transform = `translateX(${Math.abs(
              moveTextToLeft - mainValue / setTextLimitMovement
            )}px)`;
          }

          if (checkDirection === 1) {
            titles[2].style.opacity = 1 + (hideText - mainValue / opacityRange);
            titles[2].style.transform = `translateX(-${
              moveTextToLeft - mainValue / setTextLimitMovement
            }px)`;

            titles[3].style.opacity = 0;
            titles[3].style.transform = `translateX(${0}px)`;
          }
          return;
        }

        // Move to ==>
        if (centerPoints[3] > mainValue) {
          const moveTextToRight =
            centerPoints[3] / setTextLimitMovement - textLimit;
          const hideText = centerPoints[2] / opacityRange;
          const checkDirection = Math.sign(
            moveTextToRight - mainValue / setTextLimitMovement
          );

          const moveImageToRight =
            (mainValue - centerPoints[2]) / setImageLimitMovement;
          img.style.transform = `translateX(${
            leftMoveLimitImg + moveImageToRight
          }px)`;

          if (checkDirection === -1) {
            titles[3].style.opacity = 0;
            titles[3].style.transform = `translateX(${0}px)`;

            // Hide image when scroll
            const reduceOpacity = Math.abs(
              (centerPoints[0] * 0.7 + (mainValue - centerPoints[3])) / 100
            );

            const checkReduceOpacity = Math.sign(
              (centerPoints[0] * 0.7 + (mainValue - centerPoints[3])) / 100
            );

            if (checkReduceOpacity === -1) {
              img.style.transform = `scale(${
                reduceOpacity > 1 ? 1 : reduceOpacity
              })`;
            }
            if (checkReduceOpacity === 1) {
              img.style.transform = `scale(0)`;
            }
          }
          //====

          if (checkDirection === 1) {
            titles[3].style.opacity = 1 + (hideText - mainValue / opacityRange);
            titles[3].style.transform = `translateX(${
              moveTextToRight - mainValue / setTextLimitMovement
            }px)`;
          }

          return;
        }
      }
    };
    window.addEventListener('scroll', changeBackground);

    return () => window.removeEventListener('scroll', changeBackground);
  }, [screen]);

  return (
      <div className="App">
        <div id="header" ref={headerRef}>
          Header
        </div>
        <main ref={mainRef}>
          <h1 id="title" className="titles">
            Random Title
          </h1>
          <section id="block1" className="blocks">
            block 1
          </section>
          <figure id="passport">
            <img
              alt="passport"
              src="https://cdn.britannica.com/87/122087-050-1C269E8D/Cover-passport.jpg"
            />
          </figure>
          <h2 id="text1" className="titles text1">
            Random Text 1
          </h2>
          <section id="block2" className="blocks">
            block
          </section>
          <h2 id="text2" className="titles text2">
            Random Text 2
          </h2>
          <section id="block3" className="blocks">
            block
          </section>
          <h2 id="text3" className="titles text3">
            Random Text 3
          </h2>
          <section id="block4" className="blocks">
            block 4
          </section>
        </main>
        {/* Stop Scrolling Animation */}
        <div id="footer">Footer</div>
      </div>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render( <
  App / > ,
  rootElement
);
* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
    Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}

.App {
  font-family: sans-serif;
  width: 100%;
  background-color: hsl(220, 65%, 16%);
}
#header,
#footer {
  height: 100vh;
  display: grid;
  place-items: center;
  font-size: 4em;
  color: aliceblue;
  background-color: hsl(220, 65%, 45%);
}
main {
  position: relative;
}
figure {
  width: 280px;
  height: max-content;
  position: fixed;
  inset: 0;
  margin: auto;
  z-index: 100;
}
img {
  width: 100%;
}

.blocks {
  height: 100vh;
  display: flex;
  position: relative;
  grid-column: 1 / -1;
  color: grey;
}

.titles {
  width: max-content;
  height: max-content;
  position: fixed;
  inset: 0;
  margin: auto;
  color: white;
  z-index: 99;
}

h1 {
  font-size: 3.5em;
}
h2 {
  display: flex;
  opacity: 0;
  font-size: 2.5em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="root"></div>

相关文章