Jump to content
Search Community

Pinning not responsive

SWALKER test
Moderator Tag

Recommended Posts

I am going slightly mad. I have spent nearly a week trying to get this to work and I can't understand what's going on.

 

I have been trying to some basic pinning with a  clip mask and I just can't get it to behave responsibly.

Annoyingly on that codepen URL it's actually okay, but on my website, it's not, which I realise makes things difficult.

I have done another codepen with ALL my GSAP code (rather than it reduced to the animation in question which is linked above) and that one glitches more so there must be some offending code elsewhere, but I can't work out what, as it all seems fine to me.

See the Pen gOyxppY by shereewalker (@shereewalker) on CodePen

 

I am removing each piece of extra JS to see if I can work it out, but so far nothing is working. 

I am basically just trying to refresh the calculations on window resize for that animation - and actually for all of them

Any help would be amazing as I am at my wits end!

Thanks!

See the Pen KKYvKRJ by shereewalker (@shereewalker) on CodePen

Link to comment
Share on other sites

Hi,

 

Sorry to hear about the issues but I'm afraid I can't replicate the problem in neither demo, I resized both of them quite a few times and they keep working in the same way. Unfortunately in your first demo there is too much code (over 500 lines between CSS and JS) and we don't have the time resources to comb through all that. Also in the same first demo you're not including jquery and there are a lot of warnings that point to elements not being found by GSAP on the DOM, so clearly there are quite a few elements that are missing.

 

In your second demo I saw this:

// Function to refresh ScrollTrigger on resize
function refreshScrollTrigger() {
  ScrollTrigger.refresh();
}

// Event listener for window resize
window.addEventListener("resize", refreshScrollTrigger);

// Initialize ScrollTrigger
ScrollTrigger.create(scrollTriggerConfig);

// Initial setup
refreshScrollTrigger();

There is no need to manually call ScrollTrigger.refresh(), especially if nothing in the ScrollTrigger instances depends on something changing after some time when the user resizes the screen. ScrollTrigger calls the refresh method automatically when the screen is resized and does it with a debounce mechanism for better performance. You might need to call that if some content is added/removed asynchronously and you need for ScrollTrigger to run it's calculations again, so except for the create() method everything else in that code block is not really necessary IMHO.

 

As for the issue please be more specific about how it can be replicated and what exactly is happening or not happening.

 

Finally you might want to check GSAP MatchMedia:

https://gsap.com/docs/v3/GSAP/gsap.matchMedia()

 

Happy Tweening!

Link to comment
Share on other sites

HI there,

 

Thanks for your reply. It can be replicated but it has to be a very drastic move, from small to big which I should have pointed out and generally only works in the second demo. It's really bad on the live site but I don't suppose there's any point in posting a link as there would be too much to wade through.

Weirdly when I remove those lines you suggested, everything breaks.

Can you suggest a simplified way to correctly pin that section?

Thanks again

Link to comment
Share on other sites

So i have just tried switching it to the below and removing the code you suggested:

 

gsap.to(".row._1", {
  scrollTrigger: {
	  trigger: ".row._1",   
	  start: "top top",
	  scrub: true,
	  pin: true,
	  pinSpacing: false,
	  invalidateOnRefresh: true
	},
});

And actually it works better, I'm not sure why I didn't have it like this before. I think it was reconstructed to assist with the refresh. I'll keep my eye on it, but it's definitely better.

Thanks so much for your help

Link to comment
Share on other sites

Hi,

 

I just forked your second demo, removed all the code I suggested to remove and everything seems to keep working as expected after several resizes, I tried to resize as fast as I could, which is worth noticing that 99.99% of normal users don't do, just developers with an itch for unrealistic testing do that (nothing personal against neither you or other developers, just a personal opinion based on working on this area for over 13 years):

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

 

19 minutes ago, SWALKER said:

Actually one other question if you don't mind, if Scrolltrigger automatically refreshes, what purpose does  ' invalidateOnRefresh: true' serve?

From the ScrollTrigger docs:

invalidateOnRefresh
Boolean - If true, the animation associated with the ScrollTrigger will have its invalidate() method called whenever a refresh() occurs (typically on resize). This flushes out any internally-recorded starting values.

 

https://gsap.com/docs/v3/GSAP/Tween/invalidate()

 

Sometimes you need GSAP to record again the initial values of the element being animated and dump the previous ones, because the new screen size (since this is triggered on ScrollTrigger's refresh method) could change those values and GSAP instances are mostly read-only, so GSAP only records the initial and final values once and then iterates between them over a specific amount of time, one of the many micro optimizations that makes GSAP super performant.

 

Hopefully this helps.

Happy Tweening!

Link to comment
Share on other sites

Hi there,

 

On the live site. You don’t have to resize quickly, it’s considerably worse. Enough that I think a simple device orientation change would cause it. A quick resize was the only way I could get the demo to do it. 

It’s working a lot better now. It’s not perfect but it’s better.

 

I read the docs for invalidation on refresh but it sounded like the same thing to me, which is why I asked. Thanks for that extra info. So much to learn!

 

I will take a look at your fork tomorrow. Thanks again for your help, much appreciated

Link to comment
Share on other sites

Here's a simple code example of invalidateOnRefresh

 

gsap.to(".el", {
  xPercent: () => window.innerWidth / 2, // here's a functional value that will update if the window size changes, (it will 'invalidate' on 'refresh')
  rotation: window.innerWidth / 2, // this value won't update
  scrollTrigger: {
   invalidateOnRefresh: true
    ...
  }
})

 

  • Like 2
Link to comment
Share on other sites

Ugh, so after FINALLY thinking this was working, it now ONLY works with markers on.

Without them, the animation is all over the place and all the other ones are jumpy. With them on, it's perfect. It makes no sense to me!

I can't seem to do a demo to replicate it which I know leaves me in a situation

 

This URL is here, maybe something will jump out

https://staging-chfp.shereewalker.com/

At the moment, I have it working by having markers:true and the hiding them with CSS

VERY hacky but I only have a a few days until launch

Link to comment
Share on other sites

Hi,

 

Sorry about the issues but yeah, without a demo that clearly illustrates the problem there is not a lot we can do 🤷‍♂️

 

The only thing I can think of is that somehow the markers are pushing the height of the document element so everything works as expected. You should review your setup in order to see if something in your HTML/CSS could be causing something that the markers are actually fixing, which would be quite weird TBH.

 

Another option is that something else (like another JS package or CSS library) is interfering with this and causing the problem. Try removing some of them one by one and see if that fixes anything.

 

As I was about to submit this I checked your site again and opened devtools and I noticed that you're using wordpress and that your body element has scroll-behavior: smooth, that could be a source of problems (linke 92):

https://staging-chfp.shereewalker.com/wp-content/themes/CHFP/style.css?ver=1.0

 

Try overriding that in  a custom CSS that is loaded after the theme's styles in order to see if it helps.

 

Hopefully this helps.

Happy Tweening!

Link to comment
Share on other sites

Hey there,

 

Yeah I know that lack of demo doesn't help sorry :(- I do actually have one, but I can't replicate the issue. I searched other forums and I'm not the first person who has experienced this so i thought maybe there was a generic answer. Most other forums imply it has to do with a lack of height on the page, which I don't have - there is more than enough top and bottom.

I have a gross solution for now which is that I have turned markers on, and then hid them with CSS. It's not a long term solution (at the moment!) but it will do until I can work it out. 

I have tried stripping out all my other css and JS but no joy. Oddly if I move the section with the pinning, its okay - it's almost like the combination of the pinning animation AND the section above which has VW and VH calculations is too much for it to pinpoint the starting and ending? It's weird. And again, if I add markers - it's fine.

I will try removing that scroll behaviour - and see if it makes a difference. - thanks very much for that suggestion.

Link to comment
Share on other sites

  • 3 weeks later...

So I THINK i have worked this out - after a huge amount of time!

I noticed that the section worked fine, a long as there was NO other blocks on the page. But no single block seemed to be the issue - it was ANY block on the page.

The only common denominator they had was the padding (which I ruled out) and they each had a heading - which was, believe it or not, the issue.

It was cause by the css
 

  transition: all .3s ease-in-out;


Which had been applied, to every heading. When resizing quickly (for example on ipad orientation change), scroll trigger calculates WHILE the headings are still in the process of resizing - thus, throwing off the calcultaions.

Fingers crossed this is it, but it appears to have fixed the issue


Edit - false alarm..... it has not :(

Link to comment
Share on other sites

Hi,

 

Sorry about the issues, but as mentioned without a demo that clearly reproduces the problem there is not a lot we can do.

 

The only advice I can give you is to keep looking into anything that could change the height of the document when resizing, maybe images with src set, maybe different screen sizes will use different images with different heights, that could load the image after the ScrollTrigger instance is created.

 

Happy Tweening!

Link to comment
Share on other sites

Hi there

I have looked and I have even removed almost everything off the page except a basic text block.

I use src set but the images use object-fit so their containers dont change. Some elements on the page are based on VH but I can't change this.

I know that without a broken demo it's near impossible but are you able to just tell me how to correctly kill an animation on resize?

Sorry i have confused things by opening another thread. I thought maybe this  was too old now

Link to comment
Share on other sites

4 hours ago, SWALKER said:

tell me how to correctly kill an animation on resize?

Again, it's tough to advise without seeing a minimal demo, but I guess the most generic answer I can give would be: "call kill() on that animation from inside a 'resize' event handler". But if you want a more targeted answer for your scenario, please provide a minimal demo that clearly illustrates the problem and we'd be happy to take a peek. 

Link to comment
Share on other sites

Hi there,

 

I just can't get the demo to replicate it sorry.

I have done this - but it's not perfect. Would you recommed a better way to "call kill() on that animation from inside a 'resize' event handler". 

 

/ Function to invalidate and remake specific animations
  function invalidateAndRemakeAnimations() {
	// Kill the first animation
	ScrollTrigger.getAll().forEach(trigger => {
	  if (trigger.vars.trigger === ".row._1") {
		trigger.kill();
	  }
	});
  
	// Remake the first animation
	gsap.to(".row._1", {
	  scrollTrigger: {
		trigger: ".row._1",
		start: "top top",
		scrub: true,
		pin: true,
		pinSpacing: false,
		invalidateOnRefresh: true,
		markers: true
	  }
	});
  
	// Kill the second animation
	ScrollTrigger.getAll().forEach(trigger => {
	  if (trigger.vars.trigger === ".row._3") {
		trigger.kill();
	  }
	});
  
	// Remake the second animation
	gsap.to("h2.text", {
	  scrollTrigger: {
		trigger: ".row._3",
		start: "top top",
		scrub: true,
		invalidateOnRefresh: true
	  },
	  position: "relative"
	});
  }
  
  // Initial application of animations
  invalidateAndRemakeAnimations();
  
  // Listen for orientation change event
  window.addEventListener("orientationchange", function() {
	// When orientation changes, invalidate and remake specific animations
	invalidateAndRemakeAnimations();
  });


 

Link to comment
Share on other sites

I assume maybe you mean something like this?: 

let tween1, tween2;
function invalidateAndRemakeAnimations() {
	// Kill existing animations
	tween1 && tween1.revert();
	tween2 && tween2.revert();

	// Remake the first animation
	tween1 = gsap.to(".row._1", {
		scrollTrigger: {
			trigger: ".row._1",
			start: "top top",
			scrub: true,
			pin: true,
			pinSpacing: false,
			invalidateOnRefresh: true,
			markers: true
		}
	});

	// Remake the second animation
	tween2 = gsap.to("h2.text", {
		scrollTrigger: {
			trigger: ".row._3",
			start: "top top",
			scrub: true,
			invalidateOnRefresh: true
		},
		position: "relative"
	});
}

 

Link to comment
Share on other sites

I've actually just rebuilt the whole thing Without pinning but the markers still off on orientation change!

To apply the above to the new animations, is this correct because it does not work, i think maybe I have modified it incorrectly

 

let tween1, tween2, tween3 ;
function invalidateAndRemakeAnimations() {
	// Kill existing animations
	tween1 && tween1.revert();
	tween2 && tween2.revert();
	tween3 && tween3.revert();

	// Remake the first animation
	tween1 = gsap.to(".fixed-wrap", {
	  		scrollTrigger: {
		  		trigger: ".initial-row",   
		  		start: "top top",
		  		scrub: true,
		  		invalidateOnRefresh: true,
		  		markers: true
				},
				position:'fixed'
	});

	// Remake the second animation
	tween2 = gsap.to(".last-text .text-holder .text", {
	  		scrollTrigger: {
		  		trigger: ".last-text",   
		  		start: "top top",
		  		scrub: true,
		  		invalidateOnRefresh: true,
		  		markers: true
				},
				position:'relative'
	});
	
	tween3 = gsap.to(".fixed-wrap", {
	  		scrollTrigger: {
		  		trigger: ".last-text",   
		  		start: "top top",
		  		scrub: true,
		  		invalidateOnRefresh: true,
		  		markers: true
				},
				position:'relative'
	});
}


Alternatively,  do you know how to use function based start/end values? 

The docs just say this as an example , but I don't really know what this means


 

end: () => `+=${elem.offsetHeight}` // will be updated



Thank you!

Link to comment
Share on other sites

Function based values mean that when the ScrollTrigger instance is refreshed that end value will be whatever number/string that function returns. If oyu're doing some sort of calculation whose result might change that calculation will be run again. In the particular snippet you added the end point is based on the height of an element, if a screen resize changes the height of that element then the end point will be recalculated as well.

 

Hopefully this clear things up.

 

Finally it seems that you have created two different threads for the same issue, please let's keep the discussion in just one thread. That will make easier to follow and focus our efforts, thanks!

 

Happy Tweening!

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
×
×
  • Create New...