Jump to content
Search Community

ScrollTrigger and fixed header fade out/in on click of button

VIPStephan test
Moderator Tag

Go to solution Solved by ZachSaucier,

Recommended Posts

Hi, I’m new here and I’m trying to have a fixed header fade out upon scrolling down, with a button to have it fade in and out manually (if scrolled down). I’ve got the fade-on-scroll part working but I’m struggling with the fade toggle. I thought I would create some tweens and two independent timelines to which I add the tweens (with different timing in each timeline), and then I associate one timeline with ScrollTrigger and the other one will be executed on click of the button but somehow this doesn’t work. Note, I’m using this in combination with jQuery

 

This is the code I have so far:

var anim_header = gsap.timeline({
	scrollTrigger: {
		trigger: $('body'),
		scrub: true,
		//markers: true,
		start:'top top',
		end: '+='+$('body > header').outerHeight()*1.75+'px'
	}
}),
toggle_header = gsap.timeline().pause(),
fadeHeader = gsap.to($('body > header'), {
	opacity: 0
});
anim_header.add(fadeHeader);
toggle_header.add(fadeHeader);
toggleButton.click(function() {
	if(!$('html').hasClass('header_active')) {
		toggle_header.reverse().then(function() {
			$('html').addClass('header_active');
		});
	}
	else {
		toggle_header.play();
		$('html').removeClass('nav_active');
	}
});

I thought I could just add the same tween to different timelines and they would work independently but apparently this isn’t working; it seems like one timeline overrides the other one.

Link to comment
Share on other sites

Hey Stephan, welcome to the GreenSock forums.

 

18 hours ago, VIPStephan said:

I thought I could just add the same tween to different timelines and they would work independently

This is not true. You could create two equivalent tweens and to do something similar but you can't share one tween in multiple timelines and expect them to be independent - the variable reference is the same. 

 

It looks to me like you're overcomplicating things but it's pretty hard for us to know exactly what sort of setup you need without a minimal demo to see and edit ourselves. Can you please create one?

Link to comment
Share on other sites

Thanks Zach. I’m more a JSFiddle guy, so I’ve created a minimal example here: https://jsfiddle.net/q54n1ofx/

That’s what my original first implementation was. I know it’s still rough but my thinking was that it would fade out on scroll and if the “button” is clicked it will fade in or out depending on an “active” class added to the container by just reversing or playing the animation again. This kind of works but once we’re in the range of the scroll trigger again it behaves in a weird way.

Link to comment
Share on other sites

Thanks for the demo.

 

The issue is logical: You have a ScrollTrigger that's trying to set the value of the opacity every time it updates. But you also want the clicking to control the opacity. So when the scroll position is within the ScrollTrigger's top and end values and someone clicks the button, you have conflict. You have to decide: what do you want to happen when they are in conflict? Should the ScrollTrigger be disabled? Should the button clicking be ignored? Should the button's effect be prioritized but on the next update of the ScrollTrigger it gets control back? There are many ways you could get it working depending on what you want. What do you want?

  • Like 1
Link to comment
Share on other sites

Well, this was just the first implementation. My first step was to get it to fade out when scrolling down, and when clicking the button while being outside of the ScrollTrigger area, it’s supposed to fade in or out. Then, when scrolling up again, it should fade in if it’s invisible or it should just stay visible, whatever it was when the button had been clicked last time.

 

Eventually, this will be a header with a menu which will prevent scrolling the body altogether when active, so the only thing the script has to worry about is to fade in/out on click, and to fade in when scrolling up to the top. At the top it shouldn’t fade at all since it’s visible already, and if it’s half way opaque it should fade to full opacity upon activating or to the state of opacity determined by the scroll position upon deactivating the button.

 

I hope my explanations made sense.

Link to comment
Share on other sites

Hmm, well, for something like this I wouldn’t even need GSAP, I could do this with a simple class toggle and CSS transitions. I specifically like the fade connected to the scroll position.

 

OK, so, I’m thinking of working around this with a hybrid approach: have it fade out while scrolling, and onLeave reset it (to remove all inline styles) and set a class that takes over the hiding. Then, on click I can work with the class toggle and CSS transitions, and onEnterBack, set the ScrollTrigger animation again. Does that sound feasible? I’ve had some success with disable() onLeave but it won’t enable() again onEnterBack.

Link to comment
Share on other sites

1 hour ago, VIPStephan said:

Does that sound feasible?

Sure. As I said this is mostly a logical issue. Any solution that fixes the logic works :) 

 

1 hour ago, VIPStephan said:

I’ve had some success with disable() onLeave but it won’t enable() again onEnterBack.

There's no way that onEnterBack could work because you disabled it...

 

 

Link to comment
Share on other sites

15 hours ago, ZachSaucier said:

There's no way that onEnterBack could work because you disabled it...

Ah, right. Is there a way to reset everything to “unspecified” (removing all inline styles applied by ScrollTrigger) while still having ScrollTrigger enabled and ready to react onEnterBack?

 

Hm, thinking about this a little more, it sounds like it’s overcomplicating things, too. I guess the best is to calculate the point of re-entry manually and then just enable() the ScrollTrigger again.

Link to comment
Share on other sites

1 hour ago, VIPStephan said:

Is there a way to reset everything to “unspecified” (removing all inline styles applied by ScrollTrigger)

Sure, but the best way of how to do so likely depends on exactly what you're trying to do. Please create a minimal demo of what you're trying to do and where you're getting stuck if you'd like additional help. 

Link to comment
Share on other sites

OK, after a bit of fiddling around I changed the approach a bit. So, the overall goal is to have the header fade out on scroll, and past a certain point all inline styles should be removed and instead be controlled by a CSS class in the stylesheet for better separation of concerns. Eventually, a click on the toggle button should just change the class name and CSS transitions will do the rest.

 

Now, I managed to disable and enable the ScrollTrigger instance by creating another ScrollTrigger instance that does just that, as shown in this minimal demo: https://jsfiddle.net/8kpt53wq/2/

However, I thought that ScrollTrigger.disable() will revert everything to unspecified but it will revert everything by setting new inline styles to the initial computed values which defeats the purpose because my CSS class will be overridden by these inline styles. How can I remove all inline styles onLeave and re-add them onEnterBack?

Link to comment
Share on other sites

  • 2 weeks later...

I’m sorry to keep on bothering you with questions but I’m trying things as we go here since it’s the first time I’m implementing something like this. Although it has been helpful, I felt like I was getting nowhere with the previous approach, so I changed it yet again. Basically, I created two timelines, one for the animation while scrolling and one for the animation on click of the toggle button; here’s the demo:

https://jsfiddle.net/dfp2z0s7/1/

 

It works alright on its own, i. e. scrolling down/up, the header fades out/in, and while scrolled down, clicking the button fades in/out the header. The tricky part is if the document is just scrolled so much that the header is just half transparent: it should fade in completely from where it currently is and should also fade out to its current half-transparent state if the button is clicked again. At the moment it looks like the first time it works but for consecutive clicks, if it’s scrolled a bit it will start from the CSS state before the scroll. Any idea?

Link to comment
Share on other sites

Hey Stephan. It's quite okay to keep asking questions, but I'm afraid my answer is pretty much the same: it's a logical issue :) 

 

The timeline is the same after the first click which is not what you want. You'll have to create a new animation each time that the toggle is clicked if you want to retain the scrubbing. However if the user clicks to make the nav fully opaque and then scrolls again you again have a logical issue of which one should be in control. So you'd need to either disable the ScrollTrigger if the user has clicked the nav is "open" or you could switch the state of the clicking if the user scrolls while the nav is "open".

  • Like 1
Link to comment
Share on other sites

OK, eventually this whole thing will open a navigation and scrolling will be disabled in the “active” state, so scrubbing isn’t an issue there. It’s really only the fade-in from wherever it currently is (i. e. at the very top there will be no animation, a little bit down there will be an animation from, say, 50% opacity to 100% and back to 50% upon deactivating, and further down there will be an animation from 0 to 100% opacity and back). So, I think it’s not as difficult as it seems. Here is an adjusted demo with scrolling disabled in the active state: https://jsfiddle.net/dfp2z0s7/2/

 

Scroll down until the header is fully transparent, click the button at the top right two times and it will fade in and out nicely. Then scroll back up to the top and click the button again. Why does it animate from zero opacity to one, not from where it currently is? In the timeline only a “to” method is specified, so I would expect it to calculate the current value everytime the animation is called, or where am I thinking wrongly?

Link to comment
Share on other sites

  • Solution
1 hour ago, VIPStephan said:

Why does it animate from zero opacity to one, not from where it currently is? In the timeline only a “to” method is specified, so I would expect it to calculate the current value everytime the animation is called, or where am I thinking wrongly?

I answered this in my last post:

5 hours ago, ZachSaucier said:

The timeline is the same after the first click which is not what you want. You'll have to create a new animation each time that the toggle is clicked if you want to retain the scrubbing.

 

Putting that logic into a demo: https://jsfiddle.net/4v5ar7m2/

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