动画总是触发,因为状态总是在 React 滚动时发生变化

Animation always firing because state always changes on scroll in React

所以我正在使用 React/Pose to animate a Showcase component like the Postmates fleet website 在这里看到:

了,但我想不通最后一点。基本上,我有一系列项目:

const chapters = [
  {
    content: "1",
    title: "Create your profile"
  },
  {
    content: "2",
    title: "See their rating"
  },
  {
    content: "3",
    title: "Forgot annoying requests"
  },
  {
    content: "4",
    title: "Block them"
  },
  {
    // tslint:disable-next-line:max-line-length
    content: "5",
    title: "Make your decision"
  },
  {
    content: "6",
    title: "Time elapses"
  },
  {
    content: "7",
    title: "Complete"
  }
];

还有一个 handleScroll(e) 方法可以做一些事情:

  1. 滚动组件时检测
  2. 滚动浏览章节时,它会使用来自 chapters:
  3. 的正确章节内容更新 this.state.contentthis.state.title

--

// loop through the chapters
for (let index = 0; index < chapters.length; index++) {
  const chapterHeight = componentHeight;
  topOfChapter.push(componentTopY + chapterHeight * index);
  bottomOfChapter.push(componentTopY * 2 + chapterHeight * index);

  // when scrolling through the component
  if (
    scrollY >= component.offsetTop &&
    scrollY <= component.offsetHeight + window.innerHeight
  ) {
    // when scrolling through a chapter
    if (
      scrollY >= topOfChapter[index] &&
      scrollY <= bottomOfChapter[index]
    ) {
      // tslint:disable-next-line:no-console
      console.log(`Scrolling through chapter ${index + 1}`);
      this.setState({
        animate: !this.state.animate,
        content: chapters[index].content,
        title: chapters[index].title
      });
      // tslint:disable-next-line:no-console
      console.log(topOfChapter[index], "topOfChapter[index]");
      // tslint:disable-next-line:no-console
      console.log(bottomOfChapter[index], "bottomOfChapter[index]");
    }
  } else {
    // tslint:disable-next-line:no-console
    console.log("exited the component");
  }
}

问题是我的动画总是在触发,因为 this.state.animate 总是在滚动时发生变化。

我只需要在章节更改时触发动画,而不是一直滚动,但我不知道如何触发。

几个小时前我问了一个没有动画的问题,但我认为这个问题只有在动画的背景下才真正有意义。

非常感谢任何帮助!

Codesandbox

也许你可以用这个https://www.react-reveal.com/examples/common/Fade

所以您不需要自定义实现?

只有当页面从数据库加载章节数据以及用户调整屏幕大小时(因为组件的高度发生变化),您才应该通过将章节的位置保存为组件状态的数组来缓存章节的位置发生这种情况时)

然后在滚动时将 scrollY 与该章节位置数组进行比较,以确定您所在的位置:

this.state.chapters.forEach( (chapter, index) => {
  if ( scrollY >= chapter.top && scrollY <= chapter.bottom && currentScrolledChapter !== chapter ) {
    currentScrolledChapter = chapter;
  }
});

并根据它所在的位置进行以下检查:

if ( currentScrolledChapter.title !== this.state.title ) {
  this.setState( {
    animate: !this.state.animate,
    content: chapters[index].content,
    title: chapters[index].title
  } );
}

https://codesandbox.io/s/youthful-leftpad-33j4y应该就是你要的效果。

为了使它进入可用状态,我做了很多更改,并且我必须做出一些假设。


退出动画结束后,想开始下一章的enter动画。你遇到的问题是,你已经忘记了上一章,因为你已经将 state 更新到下一章,同时还反复触发动画。

我现在通过跟踪 currentChapter(当前可见的章节)和 nextChapter(您希望过渡到的章节)解决了这个问题。


    <Text
        pose={animate ? "enter" : "exit"}
        onPoseComplete={this.onPoseComplete}
    >

https://popmotion.io/pose/api/posed/#posed-props-onposecomplete

当动画结束时,pose 将回调 onPoseComplete。如果动画为 false,则将 currentChapter 设置为下一章,例如

当我们退出第 0 章,进入第 1 章时:

{
   animate: false,
   currentChapter: 0,
   nextChapter: 1,
}

这将在当前章节启动退出动画。当动画完成时,调用 onPoseComplete,如果动画为 false,即退出动画现在已完成,则将状态更新为:

{
   animate: true,
   currentChapter: 1,
   nextChapter: 1,
}

现在将在 currentChapter 上启动 enter 动画,现在已更改为 1。


现在我们正在跟踪章节索引,我们不需要我删除的 titlecontent 状态。

我已将 handleScroll 逻辑简化为我认为等效的逻辑。把scrollY除以window.innerHeight,我们就可以得到你想要显示的章节的索引。


  style={{ height: `${100 * chapters.length}vh` }}

我们知道有多少章,所以简单地将章数乘以100vh

onPoseComplete = () => {

当您使用 create-react-app 时,您有 transform-class-properties 允许您创建箭头函数而不是 .bind