So I’m using React/Pose to animate a Showcase
component like the Postmates fleet website seen here:
I’m almost there but I can’t figure out the last bit. Basically, I have an array of items:
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" } ];
And a handleScroll(e)
method which does a few things:
- Detects when scrolling through the component
- When scrolling through a chapter, it updates
this.state.content
andthis.state.title
with the correct chapter content fromchapters
:
—
// 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"); } }
The problem is that my animation is always firing because this.state.animate
is always changing on scroll.
I need to fire the animation only when the chapter changes, not all the time that is scrolling, but I cannot figure out how.
I asked a question a few hours ago, which did not have the animations, but I think the question only really makes sense in the context of animating.
Any help is really appreciated!
Advertisement
Answer
https://codesandbox.io/s/youthful-leftpad-33j4y should be the effect you are looking for.
There are number of things I changed to get this into a usable state and assumptions I had to make.
Once the exit animation is finished, you want to start the enter
animation of the next chapter. The problem you had was, you already lost track of the previous chapter, as you had already updated the state
to the next chapter, whilst also triggering the animation repeatedly.
I’ve resolved this by now tracking, the currentChapter (the chapter which is currently visible) and the nextChapter (the chapter you wish to transition into).
<Text pose={animate ? "enter" : "exit"} onPoseComplete={this.onPoseComplete} >
https://popmotion.io/pose/api/posed/#posed-props-onposecomplete
When an animation is finished, pose
will callback on onPoseComplete
. If the animation was false, set the currentChapter to be the nextChapter e.g.
When we exit Chapter 0, going into 1:
{ animate: false, currentChapter: 0, nextChapter: 1, }
This starts the Exit animation on the currentChapter. When the animation finishes, onPoseComplete
is called and, if the animate was false i.e. exit animation is now finished, update the state to:
{ animate: true, currentChapter: 1, nextChapter: 1, }
Which will now starts the enter
animation on currentChapter, which was now changed to 1.
Now that we are tracking the chapter index, we don’t need the title
, content
state which I’ve removed.
I’ve simplified the handleScroll logic to what I think is equivalent. By dividing the scrollY with the window.innerHeight
, we can get the index of the chapter you want visible.
style={{ height: `${100 * chapters.length}vh` }}
We know how many chapters there are, so simply multiplied the number of chapters with 100vh
onPoseComplete = () => {
As you are using create-react-app
, you have transform-class-properties
which allows you to create an arrow function rather than .bind
.