Jump to content
Search Community

Seeking Help with Vertical Parallax Animations Using GSAP and ScrollTrigger

Georges-jean test
Moderator Tag

Go to solution Solved by Rodrigo,

Recommended Posts

Title: Seeking Help with Vertical Parallax Animations Using GSAP and ScrollTrigger
 

Hello GSAP Community,
 

I'm working on a project involving vertical parallax animations with GSAP and ScrollTrigger, and I'm encountering some challenges in achieving the exact behavior I need. I have implemented two different methods, but each has its own set of issues.
 

Method 1: I'm using a GSAP timeline with a ScrollTrigger linked to a specific section (.starrySkySection). This method works well for triggering animations as the section comes into view, and it doesn't present any issues when resizing the window. However, the problem is that animations start even when individual elements are off-screen, which isn't the desired behavior. Here's a snippet of my code for this method:

const parallaxTimeline = gsap.timeline({
  scrollTrigger: {
    trigger: '.starrySkySection',
    start: 'top top',
    end: '+=1000',
    scrub: true,
    markers: true,
  },
});

Method 2: In this approach, I created a specific GSAP timeline for each element with an individual ScrollTrigger. This resolves the issue of triggering animations only when the specific elements come into view, which is great. However, I'm facing a problem when resizing the page: the page size changes, and the vertical parallax animations trigger prematurely. Here's how I've set it up:
 

elementsData.forEach((element) => {
  const elClassName = document.querySelector(`.${element.className}`);
  const parallaxTimeline = gsap.timeline({
    scrollTrigger: {
      trigger: elClassName,
      start: 'top bottom',
      end: '+=1000',
      scrub: true,
      markers: true,
    },
  });

I have a strong preference for Method 2, as it aligns more closely with the desired behavior of the animations. However, despite testing various configurations, I've been unable to resolve the resizing issue with this method. To aid in understanding and troubleshooting, I've prepared a minimal demo that includes both methods, with one commented out. I would greatly appreciate any insights or solutions that might help address the resizing issue in Method 2.

I'm seeking advice on how to effectively manage window resizing while ensuring that animations trigger only when specific elements come into view. Any help would be greatly appreciated!
 

Thank you in advance!

Link to comment
Share on other sites

hi @Georges-jean forgive me but I was unable to test directly inside your demo since it's not minimal and i usually get lost easily 

 

but since you want to go with the second option you don't need to create a new timeline inside forEach loop instead you can pass the scrollTrigger inside the tween  like this

 

 

gsap.utils.toArray('.element').forEach((e) => {
gsap.from(e, {
  ...
  scrollTrigger: { 
    trigger: e,
    start: 'top 70%',
    end: '+=500',
    scrub: true,
  },
})
})

 

 

for the resize problem you can try this code in a minimal demo and let's see what happens cuz, as far as I know, ScrollTrigger automatically listens for a resize event 

 

 

Link to comment
Share on other sites

One problem I see for sure is that you're animating the very same element that you're using as the trigger on your parallaxTimeline's ScrollTrigger. That's almost always a bad idea because you're contaminating the positioning of that element that's being used for scroll-based triggering. You're animating the rotation and x/y of the element, and all of those affect the bounding box used to calculate the intersection points (scroll-wise).  It's typically a much better idea to wrap that element with another <div> that you use for the trigger instead. I'd recommend giving that a try and let us know if you run into any trouble. 

Link to comment
Share on other sites

47 minutes ago, Toso said:

@GreenSock is it always bad to animate the trigger element or only when its pinned ?  

I'd say in the vast majority of the cases it's a bad idea. The logic challenge isn't limited to pinning.

 

48 minutes ago, Toso said:

but does the same problem happen in the loop version ?  

I didn't quite understand your question. Maybe give it a shot yourself and if something happens that you're not expecting, post a minimal demo back here and we'd be happy to take a peek. 👍

  • Like 1
Link to comment
Share on other sites

@Toso Perhaps I should have provided more context. The minimal demo I shared actually represents the most simplified version of what I am trying to achieve. In essence, I want to apply three independent animations to numerous objects on my page: a mouse-move effect, a vertical scrolling parallax effect, and rotation.

 

In this minimal demo, I am attempting to apply these three animations to several objects listed in elementsData. To achieve this, I loop through elementsData to create a timeline for each element.

 

@GreenSock I have considered the suggestion to add a wrapping <div> around each element I want to animate. Note that I had already wrapped the initial element to separate vertical parallax and mouse-move animations, thus avoiding conflicts with the y property, which I target using elClassName.parentNode in the tween.

 

So, if I understand correctly, for each element, I now have a <div> for the timeline, encompassing another <div> for vertical parallax, which itself encloses the element along with the mouse-move and rotation animations. The structure is essentially: bigPlanetTimeline>bigPlanetWrapper>bigPlanet. However, I am still facing the issue of repositioning during window resizing.
 

Here again is the minimal demo for reference.

Link to comment
Share on other sites

1 hour ago, Georges-jean said:

However, I am still facing the issue of repositioning during window resizing.

I'm a bit confused - wouldn't that be expected? I'm probably missing something obvious, sorry, but wouldn't things naturally reposition like that based on how you've set up your CSS and animations? Can you be super specific about exactly what you think SHOULD be happening (that isn't), or vice-versa? 

 

And yeah, it seems to me like what you're trying to accomplish has some inherently complex logic issues because when you resize the window, the ScrollTriggers of course recalculate to figure out where the start/end values should be, BUT you've got OTHER animations that are shifting around either those elements or their containers which would throw things off of course. So I guess you could add a "refreshInit" event listener to ScrollTrigger that'd temporarily revert those elements to where they'd naturally be (without the animations affecting them), and then move them back after the "refresh" event. 

  • Like 1
Link to comment
Share on other sites

 

Ok, no worries, I realize I wasn't very clear about the problem I'm facing. And often, the best way to explain a problem is through images!
 

The issue I'm encountering is shown in Images 1 and 2. When I resize the window (orange arrow), the ScrollTrigger is triggered and it anomalously moves the elements upwards (blue arrow), even though I haven't scrolled yet (notice the scrollbar on the right is still at the top). As a result, the element loses its positioning on the page, which should only be altered by the animations I've set.
 

What I'm aiming for is this: when I change the size of the window, I want the elements to maintain their relative positioning on the page through their positioning in VW (viewport width). I was able to achieve this behavior in Method 1 (which I've commented out in the demo). In Images 3 and 4, when I reduce the window size, the element 'bigPlanet' remains in its initial position, relative to the size of the page.

I hope I've made myself clearer this time. I've added refreshInit to the ScrollTrigger parameters, but I had already tried that without success. Thank you, anyway, for the effort and help provided! It's probably something obvious, and I'm trying to keep my code as simple as possible to achieve what I want to do. I didn't expect that implementing these combined animations would make things so challenging.
 


Image 1

image.thumb.png.001fa6b8ee6b656a3c1b70bc3f8e253a.png
Image 2

image.thumb.png.9c8366efc9e5f09b867eb98e51572dc6.png
Image 3

image.png.54bc5c974fb4fe46ad23191b91058b3a.png
Image 4

image.png.57932e83119313b817af2a2339f9724c.png

Link to comment
Share on other sites

  • Solution

Hi,

 

I've been fiddling with your demo for some time and I find it a bit confusing to be honest. The way you're using selectors and classes doesn't really make a lot of sense to me as I think is quite convoluted.

 

Then all your elements have the same start and end positions (check your ScrollTrigger markers), so that is not going to create a lot of differences since all the elements will be animating at the same time. That is because of this:

.bigPlanetTimeline,
.bigPlanet2Timeline,
.bigPlanet3Timeline,
.bigPlanet4Timeline {
  position: absolute;
}

Those are the trigger elements of ScrollTrigger and they are all at the top of the viewport.

 

I forked your demo and removed a bunch of stuff that is not needed to show this behaviour I'm mentioning right now, that could or could not influence the end result:

https://stackblitz.com/edit/react-gui4a9?file=src%2FApp.js

 

Finally I added the clamp feature for the start values:

 

I think most of your issues stem from HTML/CSS more than GSAP. I'd strongly recommend you to create a more robust HTML/CSS in order to have this working the way you intend. Unfortunately this is beyond the scope of what we do in these free forums, since we try to keep things related to GSAP around here.

 

Good luck with your project!

Happy Tweening!

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

Ok, thank you for the explanations. It's true that when I'm deep in coding, I sometimes don't realize how my code gradually becomes convoluted.
 

However, there's still something I don't understand about the fork you shared. If I understand correctly, this code creates a vertical parallax animation for each element with the .planet class. When these elements enter the visible area of the window, their children with the .wrapper class will move vertically by -1000 pixels.
 

What I don't understand is why, under the first animation (so when the first '.planet' is off-screen), the other animations stop. It seems that there is animation only when the first planet is visible.
 

Nonetheless, I'll work on strengthening my CSS and adding clarity to my code. In any case, I appreciate your comment and the effort, thank you!

Link to comment
Share on other sites

Hi,

On 12/23/2023 at 12:44 PM, Georges-jean said:

What I don't understand is why, under the first animation (so when the first '.planet' is off-screen), the other animations stop. It seems that there is animation only when the first planet is visible.

 

Because of this

On 12/22/2023 at 2:07 PM, Rodrigo said:

Then all your elements have the same start and end positions (check your ScrollTrigger markers), so that is not going to create a lot of differences since all the elements will be animating at the same time. That is because of this:

.bigPlanetTimeline,
.bigPlanet2Timeline,
.bigPlanet3Timeline,
.bigPlanet4Timeline {
  position: absolute;
}

Those are the trigger elements of ScrollTrigger and they are all at the top of the viewport.

 

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...