Jump to content
Search Community

Hero Animation with chain animaton after (ScrollTrigger + scrub + pin)

Wild West Design test
Moderator Tag

Recommended Posts

Hello,

 

I'm building a website that will have hero animation that will trigger .section__hero-banner and once this animation is finished some of the element within .section__hero-banner will fade away and that will be contacted to the scrollTrigger (pin + scrub). Code below:

//* Hero section animation
  if (document.querySelectorAll(".section__hero-banner").length > 0) {
    document.querySelectorAll(".section__hero-banner").forEach((section) => {
      const heroLhs = section.querySelector(".hero-lhs");
      const heroLhsText = section.querySelector(".lhs-column-title");
      const heroRhsText = section.querySelector(".rhs-column-title");
      const heroRhs = section.querySelector(".hero-rhs");
      if (heroLhs && heroRhs) {
        const lhsTimeline = gsap.timeline({
          scrollTrigger: {
            trigger: section,
            // markers: {startColor: "green", endColor: "red", fontSize: "18px", fontWeight: "bold", indent: 20},
            start: "top center",
          },
        });
        lhsTimeline
          .to(heroRhs, {
            width: "50%",
            duration: 1.5,
            delay: 0.3,
            opacity: 1,
            autoAlpha: 1,
            ease: "expo.inOut",
          })
          .to(heroLhsText, {
            autoAlpha: 1,
            opacity: 1,
            duration: 1.2,
            delay: -0.7,
            ease: "power3.inOut",
          })
          .to(heroRhsText, {
            autoAlpha: 1,
            opacity: 1,
            delay: -0.2,
            duration: 1.2,
            ease: "power3.inOut",
          })
        var tl = gsap.timeline({
          scrollTrigger: {
            markers: {startColor: "green", endColor: "red", fontSize: "18px", fontWeight: "bold", indent: 20},
            toggleActions: "play none none reverse",
            trigger: section,
            scrub: true,
            pin: true,
            start: "clamp(top+=10% top)",
            end: "+=50%",
          },
        });
        tl.to(heroRhsText, {autoAlpha: 0, ease: "power2"})
      }
    });
  }

So in overall, i will have some sort of a couple of stages: initial reveal lhsTimeline will reveal the background and two titles and after that, once the user starts scrolling these two titles will fade away with scrolling.. but i found a bug - when i start scrolling immediately it breaks the whole thing and weird stuff is happening.. i guess this is a very common scenario. What would be the best way to handle this? Because I will add even more animations with scrub and pin after these two titles fade away...
I think like disabling scrolling until animation first animation is finished is a bit odd to do also what if user comes with the URL that automatically scroll to the element that is placed on the bottom of page, for example footer?

Thanks guys!


I

Link to comment
Share on other sites

Without a minimal demo, it's very difficult to troubleshoot; the issue could be caused by CSS, markup, a third party library, a 3rd party script, etc. Would you please provide a very simple CodePen or Stackblitz that illustrates the issue? 

 

Please don't include your whole project. Just some colored <div> elements and the GSAP code is best. See if you can recreate the issue with as few dependencies as possible. Start minimal and then incrementally add code bit by bit until it breaks. Usually people solve their own issues during this process! If not, at least we have a reduced test case which greatly increases your chances of getting a relevant answer.

 

See the Pen aYYOdN by GreenSock (@GreenSock) on CodePen

that loads all the plugins. Just click "fork" at the bottom right and make your minimal demo

 

Using a framework/library like React, Vue, Next, etc.? 

CodePen isn't always ideal for these tools, so here are some Stackblitz starter templates that you can fork and import the gsap-trial NPM package for using any of the bonus plugins: 

 

Please share the StackBlitz link directly to the file in question (where you've put the GSAP code) so we don't need to hunt through all the files. 

 

Once we see an isolated demo, we'll do our best to jump in and help with your GSAP-specific questions. 

Link to comment
Share on other sites

Oh sorry yes,this codepen should describe the problem:

See the Pen rNXMEqq by Maciej-Koson (@Maciej-Koson) on CodePen

 

So first problem is when will start scrolling immediately the whole flow will be broken - is there any way to make it work if the user starts scrolling early and fast and also when user comes with anchor "#id-to-section".
And also is there any good practice/example how animations should be build specially the ones that are pinned and connected to the scroll?

thanks!

 

Link to comment
Share on other sites

You can block the scroll when animating and then release it when onComplete of the timeline. 

 

Or another solution could be to wrap your elements in a sacrificial div and then in the scroll animation target that sacrificial div than you solve the issue of targeting the same element in two different timelines which is always going to cause issues.

 

For the scroll to  anchor "#id-to-section". I would look into the ScrollTo https://gsap.com/docs/v3/Plugins/ScrollToPlugin/

 

Below a reply by Jack with some great helper functions that could help you. Hope it helps and happy tweening! 

 

 

Link to comment
Share on other sites

Oh okay so there are options thats great! thanks for sharing this with me.

 

Could you please explain this option a bit more in the detail:
"

Or another solution could be to wrap your elements in a sacrificial div and then in the scroll animation target that sacrificial div than you solve the issue of targeting the same element in two different timelines which is always going to cause issues.

" - how would that help me? 

Link to comment
Share on other sites

This is great, thank you for the explanation!

I have updated my animation chain and it works great!

See the Pen mdNOyrW by Maciej-Koson (@Maciej-Koson) on CodePen



However now i need to somehow pin and scrub #our-vision section to the "two polygon" so after they finish growing/scaling the #our-vision will fade in there.
Please note that these two sections don't have a wrapper div and are standalone and there is more sections added after but they will be part of different animation chain. Do you know what the best way to do that would be?

thank you!

Link to comment
Share on other sites

No, I don't think I fully understand what it is you want.

 

Keep in mind that ScrollTrigger is just animating something on scroll so first create the animation before you start thinking about ScrollTrigger. I would just build all the tweens you want an then add it all to one timeline and have ScrollTrigger control that timeline.

 

You can move things on the y-axis (from the bottom) to create the illusion of scrolling that is in most cases the easiest way to have things work on scroll, but then nothing is really scrolling, just animating.

 

It is always a good idea when things start becoming more complex to get out pen and paper and first sketch in a story board before jumping in to coding everything from scratch, at least that always helps me.

Link to comment
Share on other sites

Sorry i migght sound a bit chaotic... so long story short after the:
 

  tl.fromTo(
          ".hero-logo-rhs-curtain",
          {
            scaleX: 1,
          },
          {
            scaleX: 0,
            ease: "none",
          }
        );
        tl.fromTo(
          ".hero-logo-lhs-curtain",
          {
            scaleX: 1,
          },
          {
            scaleX: 0,
            ease: "none",
          },
          "<"
        );

these animations (big polygon objects appearing) i want to fade in #our-vision section on top of the #by-founders-for-founders so it looks like one section is covering the next one - so its like stacked layer and it should also be pined and scrub.

Link to comment
Share on other sites

Yes, then just make that section stack on top of the current section and animate it from off screen to where you want it. Eg something like this, I've set your trigger to the <main> element and add a .from() animation to the #our-vision section to animate from off screen to on screen. Hope it helps and happy tweening! 

 

See the Pen PoMbPNw?editors=0010 by mvaneijgen (@mvaneijgen) on CodePen

  • Like 1
Link to comment
Share on other sites

This is perfect! thank you so much. Is there any way to make the last animation have a bit more scrolling? I guess this is set by end: "+=150%" but bascily what i want is once the text have faded in i want to keep this section in the view for like 2 seconds and they move to the next section

Link to comment
Share on other sites

Hi,

 

You can indeed add more scrolling and also you can add a dummy instance at the end of the Timeline as well:

var tl = gsap.timeline({
  scrollTrigger: {
    markers: true,
    toggleActions: "play none none reverse",
    trigger: "main",
    scrub: true,
    pin: true,
    start: "clamp(top top)",
    end: "+=200%",
    preventOverlaps: true,
    fastScrollEnd: true
  }
});

tl.to(".titles-wrapper", { opacity: 0, ease: "none" });
tl.fromTo(
  ".hero-logo-rhs-curtain",
  {
    scaleX: 1
  },
  {
    scaleX: 0,
    ease: "none"
  }
);
tl.fromTo(
  ".hero-logo-lhs-curtain",
  {
    scaleX: 1
  },
  {
    scaleX: 0,
    ease: "none"
  },
  "<"
);
tl.to(heroLogoLhs, { autoAlpha: 1, ease: "none" }, "<");
tl.from("#our-vision", {
  yPercent: 100,
  opacity: 0,
  ease: "none"
})
  // Dummy instance at the end
  .to({},{ duration: 0.25 })

That extends the duration of the Timeline but with no animation happening though. Here is a fork of Mitchel's last demo:

See the Pen abeBdor by GreenSock (@GreenSock) on CodePen

 

Hopefully this helps

Happy Tweening!

Link to comment
Share on other sites

this is brilliant, thank you so much guys for all help!

 

One more question.. sorry guys.. is nesting scrollTriggers with pin to true will cause some problems? Bascily i have more sections inside <main> and want to do some separate scrollTrigger animations with pining and scrubbing..

  • Like 1
Link to comment
Share on other sites

Sorry please see the full codepen:

See the Pen abeBVqR by Maciej-Koson (@Maciej-Koson) on CodePen



Re. question above i think i have figured out... but i have few glitches like the .fb-rich-aside is fired few times if you scroll down and back up (the animation is like reversing back not sure why normally i don't even need to use once: true to work but this one is playing again). 

 

also another issue is URLs with "#" - #team for example does not scroll to the section... is there any way to support it?


Also, are these timelines built with some sort of good practice, or could something be improved? 

Link to comment
Share on other sites

Hi,

 

Sorry but there is far too much code in there to isolate this (over 1400 lines in HTML). Are you referring to this section here?

P19rax7.png

I see that entire section being pinned, the text fading in from below and then being unpinned, nothing wrong there, maybe I'm missing something obvious here 🤷‍♂️

 

If possible reduce the demo to a bare minimum and be super specific about what should happen and what is actually happening right now.

 

Finally nesting pinned sections should be avoided as much as possible but you can resort to either mix the pin type (fixed/transform) or use the pinnedContainer config option as well:

https://gsap.com/docs/v3/Plugins/ScrollTrigger/?page=1#pinType

https://gsap.com/docs/v3/Plugins/ScrollTrigger/?page=1#pinnedContainer

 

Hopefully this helps

Happy Tweening!

Link to comment
Share on other sites

Oh sorry, i think i have resolved most of the issues, the only problem left to resolve is that pined sections are flashing (for like a milisecond but it is not clear) behind if you come with the hash in the URL. Do you know if there is any specific way how pined elements should be hidden in the initial load? 

 

Additionally I'm using:
 

 
  preventOverlaps: true,
        fastScrollEnd: true,

 

 

To ease the scrollTo animation when user comes with # in the URL and that is working well - based on this example 

See the Pen ZEyXPGj by GreenSock (@GreenSock) on CodePen

 this help to end animation if scrolling is fast but the animation flash a bit anyway so i guess that's why I'm seeing previous pinned section flashed - is there any way to trick this?

 

Thanks guys!

Link to comment
Share on other sites

Hello, thank you for sharing this with me, i have already tested switching autoalpha and that works as expected, however if the scrollTrigger is scrubbed and pinned that results that opacity is bonded with scroll which result in fading section in with scroll. I think ideally if user comes with # in the url (scroll to some section below) should not even trigger this pined section? I'm just not sure how this should be handled... do you have any ideas/advice?

thanks!

Link to comment
Share on other sites

Hi,

 

Without a minimal demo (emphasis on minimal) that clearly illustrates the issue there is not much we can do.

 

Normally the best way to deal with this is to set the opacity to 0 and visibility to hidden in the CSS and just use a to() instance that fades in the element in the Timeline or Tween that is controlled by ScrollTrigger. If fade in is not part of that animation you can set autoAlpha to 1 after creating the other instances, like this:

gsap.to(element, {
  x: 200,
  rotation: 360,
});

//  After creating all the GSAP instances, show the element
gsap.set(element, { autoAlpha: 1, });

Another option is to create a second ScrollTrigger instance that shows the element immediately using either toggleActions or the onEnter/Leave/EnterBack/LeaveBack callbacks:

https://gsap.com/docs/v3/Plugins/ScrollTrigger/?page=1#toggleActions

https://gsap.com/docs/v3/Plugins/ScrollTrigger/?page=1#onEnter

 

Hopefully this helps

Happy Tweening!

Link to comment
Share on other sites

Hello, thank you for your response!

That is really helpful - just to clarify so normally you would use this to avoid layout gsap.set(element, { autoAlpha: 1, }); to hide all animated elements and that declarations should be at the end of the script after all gsap instances (if that fade in is not part of animation because other wise is just easy to set these in CSS visibility hidden and opacity 0)?

Also another question in what event ideally gsap instances should be created/fired? 
 

window.addEventListener("load", (event) => {
});
//or

addEventListener("DOMContentLoaded", (event) => {});

 

Link to comment
Share on other sites

2 hours ago, Wild West Design said:

to hide all animated elements and that declarations should be at the end of the script after all gsap instances

Nope, that would be necessary only when a from() instance is above the fold, otherwise there is not really any need for that.

2 hours ago, Wild West Design said:

Also another question in what event ideally gsap instances should be created/fired? 
 

window.addEventListener("load", (event) => {
});
//or

addEventListener("DOMContentLoaded", (event) => {});

That really depends, if you're using images with fluid height and that layout shift creates issues with ScrollTrigger instances present after those images are loaded, in that case use the load event. Otherwise the DOMContentLoaded event should be good enough.

 

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

Ohh okay that makes a lot of sense!

Normally for example when I'm programming "hero section" with 100vh image in 4k they are quite big and i animate that also I'm trying to make this as efficient as possible to meet FCP (First Contentful Paint), LCP (Largest Conetentful Paint) and Cumulative Layout Shift.. also many times I'm using lazy loading for images..

Just to summarize it..  correct me if I'm wrong please! If they animated image/section causes issues would be wise to to put it into "load" event to wait until image is downloaded.. but normally try to keep animations in DOMContentLoaded. I understand that every case is different but just trying to find a way of dealing with these..

Link to comment
Share on other sites

If your image or the image container has a fixed height (you mentioned that the hero section has 100vh, so that's a fixed height) there won't be a layout shift. A layout shift is when you load an image and you use either a fluid height or the natural height of the image, because before the image is loaded it's height will be 0 pixels and after is loaded it will have a different height (fluid or natural), that will cause a layout shift for every element below that image in the page.

 

Hopefully this clear things up

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...