【问题标题】:How to implement open curtain animation如何实现开帘动画
【发布时间】:2021-10-12 11:57:07
【问题描述】:

我正在尝试复制一个打开的窗帘动画,例如 following 中的一侧 divs 在滚动的背景图像上水平扩展。

我有一个使用以下代码的工作示例

import { useState, useEffect, useRef, useCallback } from "react";
import "./styles.css";

export default function App() {
  // check window scroll direction
  const [y, setY] = useState(null);
  const [scrollDirection, setScrollDirection] = useState("");

  const boxTwo = useRef(null);
  const boxTwoLeft = useRef(null);
  const boxTwoRight = useRef(null);
  const countRefTranslateX = useRef(0);

  // check window scroll direction https://stackoverflow.com/questions/62497110/detect-scroll-direction-in-react-js
  const handleScrollDirection = useCallback(
    (e) => {
      const window = e.currentTarget;
      if (y > window.scrollY) {
        setScrollDirection("up");
      } else if (y < window.scrollY) {
        setScrollDirection("down");
      }
      setY(window.scrollY);
    },
    [y]
  );

  const handleScroll = useCallback(() => {
    if (boxTwo.current) {
      let position = boxTwo.current.getBoundingClientRect();
      // checking for partial visibility and if 50 pixels of the element is visible in viewport
      if (
        position.top + 50 < window.innerHeight &&
        position.bottom >= 0 &&
        scrollDirection === "down"
      ) {
        countRefTranslateX.current = countRefTranslateX.current + 3;
        boxTwoLeft.current.style.transform = `translateX(-${countRefTranslateX.current}px)`;
        boxTwoRight.current.style.transform = `translateX(${countRefTranslateX.current}px)`;
      } else if (
        position.top + 50 < window.innerHeight &&
        position.bottom >= 0 &&
        scrollDirection === "up"
      ) {
        countRefTranslateX.current = countRefTranslateX.current - 3;
        boxTwoLeft.current.style.transform = `translateX(-${countRefTranslateX.current}px)`;
        boxTwoRight.current.style.transform = `translateX(${countRefTranslateX.current}px)`;
      } else {
        countRefTranslateX.current = 0;
        boxTwoLeft.current.style.transform = `translateX(-${countRefTranslateX.current}px)`;
        boxTwoRight.current.style.transform = `translateX(${countRefTranslateX.current}px)`;
      }
    }
  }, [scrollDirection]);

  useEffect(() => {
    window.addEventListener("scroll", handleScroll);
    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, [handleScroll]);

  useEffect(() => {
    setY(window.scrollY);
    window.addEventListener("scroll", handleScrollDirection);
    return () => {
      window.removeEventListener("scroll", handleScrollDirection);
    };
  }, [handleScrollDirection]);

  return (
    <div className="App">
      <div className="boxOne"></div>
      <div ref={boxTwo} className="boxTwo">
        <div ref={boxTwoLeft} className="boxTwoLeft"></div>
        <div ref={boxTwoRight} className="boxTwoRight"></div>
      </div>
      <div className="boxThree"></div>
    </div>
  );
}

我有两个问题。

  1. 当我向上滚动时,我的右白 div 一直向左移动

  2. 我无法获得固定的垂直窗口断点。在我想要开始/停止移动 divs 的点之后窗口滚动后,我的动画继续进行

我该如何解决这些问题?

我的codesandbox

【问题讨论】:

    标签: javascript html css reactjs


    【解决方案1】:

    我看过你做了什么,但是太复杂了。

    您只需要在内容顶部放置两个窗帘(面板)即可。 容器应该有position:relative,窗帘应该有position: absolute

    然后您声明它们应该在其间移动的 scrollLimits。在下面的示例中,0 是起点,window.innerHeight 是终点。考虑到其在页面中的垂直位置,将这些值替换为对您的部分有意义的值。您可以使用该部分的当前offsetTopclientHeight 根据当前窗口大小动态设置限制。

    然后您获取当前滚动位置并计算相对于限制的滚动百分比。

    然后您将percentage/2 + 50% 应用于每个窗帘的transform: translateX(),左侧为负值,右侧为正值。

    完成。

    document.addEventListener('scroll', revealSection);
    
    /* function revealSection() {
      const scrollLimits = [0, window.innerHeight];
      const currentScroll = window.scrollY;
      const percent = Math.min(currentScroll * 100 / (scrollLimits[1] - scrollLimits[0]), 100);
      document.querySelector('.l-panel').style.transform = `translateX(-${percent/2 + 50}%)`;
      document.querySelector('.r-panel').style.transform = `translateX(${percent/2 + 50}%)`;
    } */
    
    function revealSection() {
      const top = document.querySelector('section').getBoundingClientRect().top;
      const percent = Math.min(Math.max(0, (window.innerHeight - top) / window.innerHeight * 100), 100);
      document.querySelector('.l-panel').style.transform = `translateX(-${percent/2 + 50}%)`;
      document.querySelector('.r-panel').style.transform = `translateX(${percent/2 + 50}%)`;
    }
    body {
      margin: 0;
    }
    section {
      background-image: url('https://static.toss.im/assets/homepage/new tossim/section2_4_big.jpg');
      background-repeat: no-repeat;
      background-position: center;
      background-size: cover;
      width: 100%;
      height: 100vh;
      margin: 100vh 0;
      position: relative;
      overflow: hidden;
    }
    .l-panel, .r-panel{
      position: absolute;
      height: 100%;
      width: 100%;
      content: '';
      top: 0;
      background-color: white;
      left: 0;
    }
    <section>
      <div class="l-panel"></div>
      <div class="r-panel"></div>
    </section>

    注意:更改 CSS 选择器,使样式仅适用于您的部分。

    编辑:我想出了一个更通用的方法来设置滚动间隔,使用该部分的getBoundingClientRect().topwindow.innerHeight。它可能更有用,因为您不再需要担心该部分在页面中的位置。
    我保留了以前的版本,以供参考和/或任何喜欢它的人使用。

    【讨论】:

    • 上面的动画从完全关闭窗帘开始,然后在滚动时打开。如何更改它以使窗帘部分打开(如链接示例)并让它在滚动时完全打开?我一直在玩一些价值观,但似乎无法弄清楚。
    • @dev。每一半按以下百分比转换:50 + percentage / 2。右一正,左一负。或者,使用更直观的语法:50 + percentage * 0.5。如果你想在开始时给它 20% 的差距:translateX(${percent*.4 + 60}%)。同样:50% 初始差距:translateX(${percent*.25 + 75}%)
    • 对于任何好奇的人,我制作了上面答案的反应版本,从部分打开的窗帘开始codesandbox.io/s/open-curtain-animation2-fw7fx?file=/src/App.js
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-25
    • 1970-01-01
    • 2018-01-14
    • 1970-01-01
    • 1970-01-01
    • 2010-11-22
    • 1970-01-01
    相关资源
    最近更新 更多