当另一个元素的高度发生变化时,GSAP Scrolltrigger 动画开始/结束位置不会更新

发布于 2025-01-16 22:07:22 字数 2759 浏览 1 评论 0原文

我有两个区域,当页面滚动时,使用 GSAP ScrollTrigger 在 h1 元素上向上滚动 h3 动画。在这两个区域之间有一个元素,即笔中的绿色框,它可以通过单击按钮和 GSAP 动画来更改高度。

问题是,当绿色框高度减小时,第二个标题区域的开始/结束位置不会改变(如页脚区域中页面下方的开始/结束标记所示)。因此,当 h3 元素按预期从底部进入屏幕时,滚动动画不会开始。

我打算尝试使用计时器来观察 window.document.documentElement.scrollHeight 的变化,并以某种方式重置或刷新动画,但这看起来很黑客。我缺少 GSAP 的某种方法来处理这种情况吗?

Codepen,这不是实际用例,但演示了问题。

let { Button, Container } = ReactBootstrap

if (typeof window !== undefined) {
  gsap.registerPlugin(ScrollTrigger)
}


const App = () => {
  const box = React.useRef(null),
        titleContainer = React.useRef(null),
        titleScroller1 = React.useRef(null),
        titleScroller2 = React.useRef(null),
        title = React.useRef(null)
  
  let boxFullSize = true
  
  function resizeBox() {
    if (boxFullSize) {
      gsap.to(box.current, {
        height: "500px"
      })
      boxFullSize = false
    } else {
      gsap.to(box.current, {
        height: "1000px"
      })
      boxFullSize = true
    }
  }
  
  React.useEffect(() => {
    [titleScroller1, titleScroller2].forEach( x => {
      gsap.to(x.current, {
        y: "-100px",
        scrollTrigger: {
          trigger: x.current,
          start: "top bottom",
          scrub: true,
          markers: true
        }
      })
    })
  })
  
  return (
    <div className="app">
      <Container className="hero" fluid="true">
        <h1>Scroll</h1>
      </Container>
      <Container>
        <Button onClick={resizeBox}>Resize Box</Button>
        <div className="titleContainer" ref={titleContainer}>
          <h1 className="lgHeading">Big Heading 1</h1>
          <div ref={titleScroller1} className="titleScroller">
            <h3 className="title" ref={title}>Small Heading 1</h3>
            <div className="lineAfter"></div>
          </div>
        </div>
        <div className="box" ref={box}>
          Box
        </div>
      </Container>
      <Container>
        <div className="titleContainer" ref={titleContainer}>
          <h1 className="lgHeading">Big Heading 2</h1>
          <div ref={titleScroller2} className="titleScroller">
            <h3 className="title" ref={title}>Small Heading 2</h3>
            <div className="lineAfter"></div>
          </div>
        </div>
      </Container>
      <footer>
        <p>Footer</p>
      </footer>
    </div>
  )
}

ReactDOM.render(<App />, document.getElementById("app"))

I have two areas where as the page is scrolled a h3 animates, using GSAP ScrollTrigger, up over an h1 element. Between these two areas is an element, the green box in the pen, that changes height with a button click and GSAP animation.

The problem is that when the green box height is reduced, the start/end positions for the second heading area do not change (as indicated by the start/end markers being further down the page in the footer area). So the scroll animation does not start when the h3 element enters the screen from the bottom as expected.

I was going to try with a timer to watch for a change in window.document.documentElement.scrollHeight and reset or refresh the animation somehow but that seems very hackish. There must be a way with GSAP I am missing to handle this situation?

Codepen, it's not the actual use case but demonstrates the issue.

let { Button, Container } = ReactBootstrap

if (typeof window !== undefined) {
  gsap.registerPlugin(ScrollTrigger)
}


const App = () => {
  const box = React.useRef(null),
        titleContainer = React.useRef(null),
        titleScroller1 = React.useRef(null),
        titleScroller2 = React.useRef(null),
        title = React.useRef(null)
  
  let boxFullSize = true
  
  function resizeBox() {
    if (boxFullSize) {
      gsap.to(box.current, {
        height: "500px"
      })
      boxFullSize = false
    } else {
      gsap.to(box.current, {
        height: "1000px"
      })
      boxFullSize = true
    }
  }
  
  React.useEffect(() => {
    [titleScroller1, titleScroller2].forEach( x => {
      gsap.to(x.current, {
        y: "-100px",
        scrollTrigger: {
          trigger: x.current,
          start: "top bottom",
          scrub: true,
          markers: true
        }
      })
    })
  })
  
  return (
    <div className="app">
      <Container className="hero" fluid="true">
        <h1>Scroll</h1>
      </Container>
      <Container>
        <Button onClick={resizeBox}>Resize Box</Button>
        <div className="titleContainer" ref={titleContainer}>
          <h1 className="lgHeading">Big Heading 1</h1>
          <div ref={titleScroller1} className="titleScroller">
            <h3 className="title" ref={title}>Small Heading 1</h3>
            <div className="lineAfter"></div>
          </div>
        </div>
        <div className="box" ref={box}>
          Box
        </div>
      </Container>
      <Container>
        <div className="titleContainer" ref={titleContainer}>
          <h1 className="lgHeading">Big Heading 2</h1>
          <div ref={titleScroller2} className="titleScroller">
            <h3 className="title" ref={title}>Small Heading 2</h3>
            <div className="lineAfter"></div>
          </div>
        </div>
      </Container>
      <footer>
        <p>Footer</p>
      </footer>
    </div>
  )
}

ReactDOM.render(<App />, document.getElementById("app"))

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

请爱~陌生人 2025-01-23 22:07:22

不是最好的......但有效......

//Init first GSAP
setTimeout(function (){
    //Init second GSAP
}, 100);

Not best.... But works...

//Init first GSAP
setTimeout(function (){
    //Init second GSAP
}, 100);
物价感观 2025-01-23 22:07:22

使用refresh()函数刷新所有滚动触发器。 这里是文档

这是一个示例:

例如,如果单击按钮时元素展开,导致高度发生变化,则您需要做的是在发生这种情况时刷新滚动触发位置。

例如,对于多个扩展元素

buttons.forEach((button) => {

    button.addEventListener("click", () => {
      otherElement.classList.toggle("open"); // this causes the height change
      ScrollTrigger.refresh(); // This refreshes the scroll triggers
    });
  });

这仅在高度变化是即时的情况下才有效,如果您有过渡,请参见下文。

如果改变高度的元素有一个过渡

在这种情况下,元素改变高度并有一个过渡属性,页面的高度不会立即改变,因此滚动触发器将计算错误。

如果是这样,请为 transitionend 使用事件侦听器,如下所示(此示例是如果您有多个可能更改高度的元素,例如手风琴):

const accordions = document.querySelectorAll(".accordion");

accordions.forEach((accordion) => {
    accordion.addEventListener("transitionend", () => {
      ScrollTrigger.refresh();
    });
  });

现在,每当手风琴完成高度更改时,所有元素都会滚动触发器将刷新。

确保您正在收听的元素是实际正在转换的元素!例如,如果 .accordion__body 正在改变高度,请听它,而不是它的 transitionend 的父级

Use the refresh() function to refresh all of your scroll triggers. Here are the docs.

Here's an example:

If, for example, when you click a button an element expands, causing the height to change, what you need to do is refresh the scroll trigger positions when that happens.

E.g. for multiple expanding elements

buttons.forEach((button) => {

    button.addEventListener("click", () => {
      otherElement.classList.toggle("open"); // this causes the height change
      ScrollTrigger.refresh(); // This refreshes the scroll triggers
    });
  });

This only works if the height change is instant, if you have a transition, see below.

If the elements changing height have a transition

In this case where the elements change height and have a transition property, the height of the page doesn't change instantly, so the scroll triggers will calculate incorrectly.

If so, use an event listener for transitionend like so (this example is if you have multiple elements which may change height, like an accordion for example):

const accordions = document.querySelectorAll(".accordion");

accordions.forEach((accordion) => {
    accordion.addEventListener("transitionend", () => {
      ScrollTrigger.refresh();
    });
  });

Now everytime an accordion has finished changing height, all scroll triggers will refresh.

Make sure the element you are listening to is the one that is actually transitioning! E.g. if the .accordion__body is what is changing height, listen to that, not it's parent for the transitionend

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文