Jump to content
Search Community

ScrollTrigger + Lenis problem

Guest
Moderator Tag

Go to solution Solved by akapowl,

Recommended Posts

My goal:

  • Fixed header that fades out when scrolling down and fades in when scrolling up, very simple

My problem:

  • I have integrated Lenis in my project for smooth scroll. Following the guidelines provided here https://github.com/studio-freight/lenis

    Like suggested on github I've added this code for GSAP integration:

    lenis.on('scroll', ScrollTrigger.update)

    gsap.ticker.add((time)=>{
      lenis.raf(time * 1000)
    })

    gsap.ticker.lagSmoothing(0)

    The problem is that this piece of code causes the header to partially fade In or Out until the scroll animation is done (check codepen)
    I don't have the same problem if I completely remove this code, but that does not seem the right solution since this is provided as the code for GSAP integration

    What can I do? I want the header to fade as soon as the scroll movement starts, not to stop in between waiting for the scroll to end.

    Also, what does exactly this code do anyways? Is it fundamental for the perfect GSAP + Lenis setup?

See the Pen RwvOdjm by lucacigo (@lucacigo) on CodePen

Link to comment
Share on other sites

Hi @cigo that seems like a Lenis support question. Lenis is a third party plugin that is not supported by GSAP, we love helping with GSAP related questions, but sadly we don't have the resources to support every plugin that others build to work with GSAP on these free forums. Of course anyone else is welcome to post an answer if they'd like - we just want to manage expectations.

 

That said we have our own scroll smoother library which is aptly named ScrollSmoother and works perfect with all the GSAP tools https://gsap.com/docs/v3/Plugins/ScrollSmoother/

 

We don't want to force anyone to use our tools and you are free to use what ever you like, we just can't support third party tools on these forum. Hope it helps and happy tweening!  

Link to comment
Share on other sites

  • Solution
44 minutes ago, cigo said:

Like suggested on github I've added this code for GSAP integration:


I don't think you are supposed to add it to the usual Lenis setup, but use it instead of the usual Lenis setup.

When you add a simple console.log() of the direction in the onUpdate callback, you'll notice that the direction will flip from -1 to 1 to -1 to 1 [ and so forth] constantly.

See the Pen jOdRRzK by akapowl (@akapowl) on CodePen



That likely is the case because you call lenis.raf() in two ways over and over 'at the same time' - don't do that, you are creating conflicting behaviour.

If you just remove the usual call of the lenis.raf(), you'll see it work as it should, right?

I'm not too familiar with Lenis, but I think that should help; I hope it does.
 

// Don't use this portion anymore when you add the lenis.raf() to the gsap.ticker

function raf(time) {
  lenis.raf(time)
  requestAnimationFrame(raf)
}

requestAnimationFrame(raf)


See the Pen JjxVVMP by akapowl (@akapowl) on CodePen

  • Like 2
Link to comment
Share on other sites

 

13 minutes ago, cigo said:

It worked! I still have a question. I've noticed some people recommed to add this line:


ScrollTrigger.refresh();

 

What is this for? Should I use it?


Should you add it to the setup configuration for Lenis to work with ScrollTrigger in the first place? ...again; I'm not too familiar with Lenis, but I'd say that if it doesn't tell you to do so in the Lenis documentation (which apparently it does not), then I don't think you'd have to - adding it certainly won't hurt, though (unless you were to add it to the ticker - that would be a horrible idea). To me it looks like it also works just fine without it.

If you want to know what it does and better understand whether you'd need to use it at some point, the best would be to have a look at the documentation, and see what it tells you.

 

To keep it short;

ScrollTrigger.refresh() is a method you can call in certain cases neccessary, that...
 

Quote

Forces the ScrollTrigger instance to re-calculate its start and end values (the scroll positions where it'll be activated).


When might that be neccessary? Whenever you change anything on your page in a way that will affect the ScrollPositions of elements you set up ScrollTriggers for.

E.g. when you change the dimensions of your page or elements in a way that will cause layout shifts; or e.g. add content dynamically so the scroll positions need to be (re-)calculated, or something else along those lines.


https://gsap.com/docs/v3/Plugins/ScrollTrigger/refresh()/

So unless Lenis does change something on the page, that would affect the ScrollPositions of the ScrollTriggers you set up, in hindsight, i don't think that would actually be neccessary - but as I already mentioned, I'm not familiar enough with Lenis to give you a recommendation about that. Maybe somebody else can give you a more thorough recommendation on that.
 

  • Like 2
Link to comment
Share on other sites

  • 4 weeks later...

I'm not sure if it's directly connected with your issue as I found your post when looking for a fix for my issue with Lenis causing issues with GSAP and thought to add it in case it is any help. But I just did as I mentioned, replaced the variable as it seems that Lenis was already using the default "t" so I tried a couple other letters that fixed it to varying degrees of success but finally everything seemed to work well when I changed it to "s".

Here's the chunk that I changed it in:
 

const contactIcons = gsap.utils.toArray("header .contact-icon");
 
contactIcons.forEach((icon) => {
gsap.set(icon, { xPercent: "-100", opacity: 0 });
const s = gsap
.to(icon, {
scale: 1.2,
transformOrigin: "center center"
})
.reverse();
icon.addEventListener("mouseenter", () => s.reversed(!s.reversed()));
icon.addEventListener("mouseleave", () => s.reversed(!s.reversed()));
});
const s = gsap.to(contactIcons, {
x: 0,
opacity: 1,
duration: 0.5,
stagger: 0.2,
delay: 1
});
 
ScrollTrigger.create({
start: "top+=50",
end: "+=1",
//markers: true,
onEnter: () => {
s.reversed(!s.reversed());
mainTimeline.reversed(!mainTimeline.reversed());
},
onLeaveBack: () => {
s.reversed(!s.reversed());
mainTimeline.reversed(!mainTimeline.reversed());
}
});
Link to comment
Share on other sites

Hm, that doesn't really help to only see a small excerpt like that. Where's the Lenis code? There has to be some kind of conflict created by using the same variable name somewhere in a shared scope, at least that's my guess. 🤷‍♂️

Link to comment
Share on other sites

The Lenis code is imported. How are you implementing the library in your build?

 

<script src="https://unpkg.com/@studio-freight/lenis@1.0.33/dist/lenis.min.js"></script>
 
<script>
 
const lenis = new Lenis()
 
lenis.on('scroll', (e) => {
console.log(e)
})
 
function raf(time) {
lenis.raf(time)
requestAnimationFrame(raf)
}
 
requestAnimationFrame(raf)
 
</script>
Link to comment
Share on other sites

No no, we need to see everything together so we can check what exactly is conflicting. Little individual excerpts aren't helpful - a minimal demo would be fabulous (one that shows the conflict). Like a Codepen. Maybe the Lenis code isn't properly wrapped in a protected scope and it's leaking out(?). Lenis isn't a GreenSock product, though, so we can't really support it here. I'm still curious about that variable renaming that you said fixed something which struck me as very odd. 

Link to comment
Share on other sites

Perhaps it's not pertinent as I fixed the issue as stated. But if you wish to look for a deeper issue it's more than I can show in a CodePen so below are links for the initial HTML dev page on the server I was working with to find the fix. marked the section of code on line 43 of assets/gsap.js on both. The broken code had the default "t" and the fixed has "s" (I also had tried "g" but that broke some stuff as well):

http://htmldev.radiologyhealthequity.com/example-broken/
http://htmldev.radiologyhealthequity.com/example-fixed/

Link to comment
Share on other sites

I noticed that you're loading 2 totally different versions of GSAP and ScrollTrigger (both files twice). Definitely don't do that 🙂

 

And I think the real problem is that the Lenis file is declaring a "t" variable that's loose in the global scope which is VERY strange. No library should do something like that. And then in your own code, you're declaring another "t" variable as a tween in the global scope which is overwriting the one that Lenis created. It's generally a very bad idea to declare variables on the global scope at all because they can be contaminated, as you experienced. 

 

View the source of the Lenis file and you'll see the very first thing is var t

 

🤕

 

So that's the real culprit, but you could also solve the problem by wrapping your code in its own scope so that it doesn't get contaminated or risk contaminating other code outside of yours. Sorta like: 

 

// BAD
const t = gsap.to(...);
...
                  
// GOOD (self-invoking function)
(function() {
  const t = gsap.to(...);
  ...
})();

Or put it inside a "load" event handler for the page. Or DOMContentLoaded. 

 

I hope that helps!

Link to comment
Share on other sites

Out of curiosity, I tried wrapping the GSAP script, as well as the Lenis script calls. Lastly, I did a dynamic load in the broken version and every attempt seems to only allow GSAP to work for the first few animations on load with a clear cache then everything after doesn't work, and fewer items function on reload. The last attempt is still on the "broken" version URL.

No need to look for a fix as I'm aware this isn't a Lenis support page and changing the variable seems to work fine, but in case you were curious about the solutions you offered. Thanks again for digging around though.

Link to comment
Share on other sites

No no, you didn't do what I suggested. All you did was add a "load" event listener that then loaded a script that ran in the global scope. Then that script executed and had exactly the same contamination problems because it ran in the global scope. 

 

All you had to do was wrap your code in a self-invoking function, or a "load" event handler which is effectively the same thing in terms of scoping: 

 

(function() {
  // your code here...
})();

Or 

window.addEventListener("load", function() {
  // your code here...
});

When I say "your code here..." I mean your actual code that was getting or causing contamination. If you declare a variable inside a function, it is scoped to that function! So...

 

let a = "one";

(function() {
  let a = "two";
})();

console.log(a); // "one"

And

(function() {
  let a = "two";
})();

console.log(a); // undefined

This is purely a scoping issue. Completely and totally unrelated to GSAP. Lenis is creating variables in the global scope (generally considered a big "no-no"). 

 

I hope that helps. 

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