Jump to content
Search Community

Recalculate x Values in timeline / Scrolltrigger by Resize

Milenoi test
Moderator Tag

Recommended Posts

This ist my JS Code: 

 

document.querySelectorAll('.keytrigger-section').forEach(sectionContent => {
    // Animate Nav depending on Scrollprogress Sections
    const indicator = document.querySelector('.keytrigger-nav-indicator');
	  const keyTriggerNav = document.querySelector('.keytrigger-nav');

    let keyTriggerNavItemLength = keyTriggerNav.children.length - 1;
    let keyTriggerNavWidth = keyTriggerNav.clientWidth;
    let distraction = 40 / keyTriggerNavItemLength;
    let keyTriggerNavItemWidth = Math.round(keyTriggerNavWidth / keyTriggerNavItemLength) - distraction;
    let index = [...document.querySelector('.keytrigger-section-inner').children].indexOf(sectionContent);
    let yVal = (keyTriggerNavItemWidth * index) + keyTriggerNavItemWidth;
    let keyTriggerNavWidthClean = keyTriggerNavWidth - 40;
    xVal =  yVal >= keyTriggerNavWidthClean ? keyTriggerNavWidthClean : yVal;


  let tl = gsap.timeline({
      paused: true,
      reversed: true,
      scrollTrigger: {
          invalidateOnRefresh: true,
          trigger: sectionContent,
          start: 'center center', // Animate Nav 50% Section Contentbox hits center Browser
          end: 'bottom top',
          scrub: true,
      }
  });

  // Move Indicator
  tl.to(indicator, {
      x: xVal,
      duration: 1,
  }, 0);

});

 

HI, I have some sections that are scrolling.

Depending on when these appear in the viewport,
a small navigation should be animated in parallel.

My codepen is hopefully helpful;)
My problem is: I move the indicator dynamically.
On Resize, the entire calculation is no longer correct.
WHAT am I missing?

What is the easiest way to solve this?

Thank you very much!

See the Pen YzVWWZR by milenoi-the-sans (@milenoi-the-sans) on CodePen

Link to comment
Share on other sites

This is a step in the right direction.

You were doing a lot of calculations and declaring variables within your loop which was unnecessary, so I've moved some of those vars out of the loop and simplified it down a bit (e.g. - you don't need to find the nav element multiple times) 

You were already usinginvalidateOnRefresh: true which is great - but the values that need refreshing need to be functional for the values to get flushed out.

 

So.. this works for the most part..

If you scroll right back to the beginning and then resize it's ok. But I'm not quite sure how to handle this if you've scrolled partway through the timeline and then refresh. 🤔

See the Pen KKmMNdb?editors=0111 by svganimationworkshop (@svganimationworkshop) on CodePen

  • Like 1
Link to comment
Share on other sites

Dear Cassie, 

thank you very much for your help + efforts. 

Unfortunately it is a concrete project so the snap solution ist not the thing the customer desires to get ;)

And the left: 100%   proposal seems to be smart but it it does not behave in the way i thought.. 

There is also much more coming: a click event on the green bubbles to trigger the scroll on the exact position and with left: 100% this ist not working. 

 

Thank you for your previous codepen, which seemed to be the solution but the resize is still not working as you described: when the scrolling has begun and i resize the page the indicator disappears. 

Hmm... 

 

 

  • Like 1
Link to comment
Share on other sites

Right ok -

I've updated the first one, the issue was that your boxes are changing height base on the viewport width - so on the device I was looking at it was fine, but if the screen size changed the start and end triggers were overlapping and throwing it all off.
 

17 minutes ago, Milenoi said:

There is also much more coming: a click event on the green bubbles to trigger the scroll on the exact position and with left: 100% this ist not working. 

Not sure how the type of value being animated on the indicator would affect this?

You'll need to figure out a way to record the progress of the indicator on resize though... I'm not too sure on this but @OSUblake may be able to help

Link to comment
Share on other sites

Haha Cassie

 

i updated my pen, too with the click function and some onEnter, onLeave Function to demonstrate it better. 

But yours works better anyway... 😅

 

I would be so glad if anyone can figure out what my problem is and it this is possible with gsap anyway...

 

Thank you ;)

  • Like 1
Link to comment
Share on other sites

Welp... I'm thoroughly confused here from all these different demos. 

 

Having the indicator animation being part of different scroll triggers is not a good idea. A single scroll trigger like @Cassie did is much better. I just think she forgot to include the functions to refresh the start and end properties.

 

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

 

 

 

 

  • Like 3
Link to comment
Share on other sites

🙈 Yep. End of the day brain. Sorry.

 

7 minutes ago, OSUblake said:

Having the indicator animation being part of different scroll triggers is not a good idea

Glad you've said this because I was really struggling to wrap my brain around how to work out the position of the indicator on resize.

  • Like 1
  • Haha 2
Link to comment
Share on other sites

36 minutes ago, Milenoi said:

i updated my pen, too with the click function and some onEnter, onLeave Function to demonstrate it better. 

 

Just have your setIndicatorUi function tween the indicator.

 

Pseudo code.

 

function setIndicatorUi(xValue) {
  gsap.to(indicator, {
    x: xValue
  });
}

 

  • Like 2
Link to comment
Share on other sites

Guys, i thank you a lot and i appreciate this VERY much. 

This ist my very first approach to work with gsap so i have not figured out how to solve all this in the best not complicated way. 

 

Ok i understand: i should not use a scrolltrigger which has the 4 boxes as trigger and loop through these. 

It would be better to use a single container to animate the keytrigger navigation, is this correct?

Because then all the update and resizing works. 

Osublake, i did not understand your last post. 

My Plan is / was to do some design stuff to the keytrigger indicator. it should get another color when moving and ending. also Reverse. 

But i have no idea how i can do this when i have only 1 scrolltrigger...

 

Anyway i will try your last solutions to get a smooth and resize-resistent navigation sliding ;) And then the rest. 

 

 

 

  • Like 1
Link to comment
Share on other sites

@OSUblake's comment about tweening the indicator made me realise we could use callbacks to tween the indicator!

I think this may be a little closer. The only challenge left is to find out which section is in the viewport when the resize event fires and update the indicator position accordingly.

Right now I'm just killing all inline styles so it's reverting back to the start.

See the Pen LYyZjbo?editors=1011 by GreenSock (@GreenSock) on CodePen


 

  • Like 1
Link to comment
Share on other sites

It is quite close but not exakt the desired behaviour.  The indicator should show the exact scrollprogress.... it is hart to explain but it should visualize the exakt mouse position. But it is an interesting solution and a very small amount of code. 

 

Yesterday i got so much clever input from you guys. I wrote an animation function for the indicator and put it into the onUpdate function. 

My function uses self.progress  to calculate / controll the translate3d css property of the indicator. 

For me, this works "perfect", also (of course) onresize. 

I have no idea it this is technically the best solution but it matches my idea how the progressbar should behave. 

If you want i can make another codepen with my solution to show it the community and to discuss with you? ;)

Best wishes, Melanie

 

 

  • Like 1
Link to comment
Share on other sites

This is one of those challenges that got me curious...and inspired a helper function...

 

The goal was to take a bunch of separate ScrollTriggers (or animations that have ScrollTriggers) and merge their animations into ONE timeline where the spacing/timing correlates to the triggers so that when you scroll, the animations start/end at the correct spots. This helps in your scenario because the timeline includes the gaps inbetween so that when the playhead seeks there, the animations render in the proper state. You've got several animations that are all controlling the same element, so the order of rendering is critical. ScrollTriggers must get refreshed from top to bottom of the page because the ones above may affect the positioning of the ones below. However, if we render the animations in that order, it won't look right.

 

For example, if the first one animates the indicator from x: 0 to x: 100 and the second one animates from x: 100 to x: 200, imagine what'll happen if we scroll all the way to the top and render those from first to last - x will be at 100 instead of 0. 

 

Here's the helper function:

// feed in an Array of ScrollTriggers -OR- animations with ScrollTriggers and it'll merge all the associated animations into one timeline that has things spaced out accordingly, and a single ScrollTrigger. The original ScrollTriggers get disabled. Well, they temporarily get enabled while refreshing so that the start/end values can be calculated.
function mergeScrollTriggers(triggers, vars) {
	if (!triggers || !triggers.length) {
		return console.warn("No triggers found");
	}
	triggers = triggers.map(t => t.scrollTrigger || t); // to accommodate animations and ScrollTriggers
	vars = vars || {};
	let tl = gsap.timeline(),
		endPosition = 0,
		populate = () => {
			tl.progress(0).clear();
			triggers.sort((a, b) => a.start - b.start);
			let startPosition = triggers.length ? triggers[0].start : 0;
			triggers.forEach(trigger => {
				trigger.animation.progress(0).invalidate();
				tl.fromTo(trigger.animation, {progress: 0}, {progress: 1, ease: "none", duration: trigger.end - trigger.start, immediateRender: false}, trigger.start - startPosition);
			});
			endPosition = startPosition + tl.duration();
			tl.time(triggers[0].scroll() - startPosition);
			return startPosition;
		};
	vars.animation = tl;
	vars.refreshPriority = -100; // let all child ScrollTriggers update first so we can reposition correctly.
	vars.start = () => populate();
	vars.end = () => endPosition;
	vars.scrub = vars.scrub || true;
	vars.scroller = triggers[0].scroller;
	vars.onRefreshInit = () => triggers.forEach(t => t.enable(false, false));
	vars.onRefresh = () => triggers.forEach(t => t.disable(false, true));
	return ScrollTrigger.create(vars);
}

 

And here it is in action: 

See the Pen RwVGWOO?editors=0010 by GreenSock (@GreenSock) on CodePen

 

Is that more like the effect you were going for? 

  • Like 2
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...