Jump to content
Search Community

How to tell MotionPathPlugin to update its path after window resize?

Maglr.com test
Moderator Tag

Go to solution Solved by Rodrigo,

Recommended Posts

Hi everyone,

 

I am wondering how i could tell an animation that uses the MotionPathPlugin to update its path after the window is resized. After some searching I have found that the suggested method is to kill and reinit the animation (like in the demo). In my case that causes a lot of overhead because I have a lot of animations (that might be nested) that are all relative transforms based on EM except for the motion paths that only accepts / translates to pixel values.

 

Is it possible to only tell the animations that use the MotionPathPlugin to get their new path by reading the resized SVG or by suppling a new path (array, string, etc.)?

 

Thanks in advance.

See the Pen WNPwVrV by jaap-maglr-com (@jaap-maglr-com) on CodePen

Link to comment
Share on other sites

  • Solution

Hi,

 

You could invalidate the GSAP instance in order to flush out the initial values so GSAP can record the new ones using a debounce method on the resize event:

const t = gsap.to("#div", {
  duration: 5, 
  repeat: 12,
  repeatDelay: 1,
  yoyo: true,
  ease: "power1.inOut",
  motionPath:{
    path: "#path",
    align: "#path",
    autoRotate: true,
    alignOrigin: [0.5, 0.5]
  }
});

const resizeHandler = () => {
  t.invalidate();
  t.play();
};

const timer = gsap.delayedCall(0.2, resizeHandler).pause();

window.addEventListener("resize", () => {
  timer.restart(true);
  t.pause();
});

Here is a simple demo:

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

 

Hopefully this helps.

Happy Tweening!

Link to comment
Share on other sites

Hi Rodrigo,

 

Thanks for the quick reply, after some tweaks to make it work in my own situation I think your solution is the better approach. So instead of killing and rebuilding all animations just let the specific motion paths update. A thing to note is that the align property is really important to make the resizing work, without it the motion path doesn't change.

 

Thanks again.

Link to comment
Share on other sites

  • 2 months later...

Invalidate() does the trick, however there is something that I can't wrap my head around.
In the following demo you will see that I have created an animation that sticks the green box to the top of the window and only translates x. We use this to, for example let a car follow a road or roll a marble down a pipe or track. The white borders mark the points that green box will pass on the initial scroll and all is great until we call the invalidate function after window resize (in the demo we call it on button-click). The result is that the first tween still ends up on the white border box but tweens after that have an offset (translate-y).

How can I achieve that all tween will have the same result as before the invalidate?

See the Pen qBvRRGm by jaap-maglr-com (@jaap-maglr-com) on CodePen

 

Link to comment
Share on other sites

Yeah, invalidate in this particular case is not needed because ScrollTrigger is doing that for you. In this case when you call the invalidate method on your timeline, flushing down the initially recorded values from the GSAP instances in your timeline is clearly conflicting with the way ScrollTrigger handles things.

 

Just out of curiosity, does the demo with ScrollTrigger without using the invalidate method is not working for you? If it does, which it seems to me at least, I'd keep it.

 

Finally keep in mind that you can also tell ScrollTrigger to invalidate the Timeline for you on the resize event. 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/Plugins/ScrollTrigger/#config-object

 

Hopefully this clear things up.

Happy Tweening!

Link to comment
Share on other sites

I created the following demo that resembles the setup that I am working on. It is a setup that uses css grid and sizes in em's to scale the entire grid based on the width of the viewport, with a maximum of 1000px. The paths are in the DOM as SVG's so they are also sized like the rest of the elements. If your viewport is less then 1000px everything will scale down in ratio including the animation path. Initially both animations are pretty good, sometimes the green box isn't as smooth (sticky at the top of the viewport) as I hope it would (sometimes it is a little "jumpy") be that is why I created pink box that is pinned and thus perfectly sticky at the top. 

Then when the viewport is scaled up or down both animations need to be invalidated because the recored values do not match the newly scaled pixel size. Whether the invalidate is done automatically or manually the result is the same, the green box will follow the correct new path but with the same issue as before that it is not perfectly stuck at the top and the pink box has an offset after the first tween.

See the Pen BabWZOr by jaap-maglr-com (@jaap-maglr-com) on CodePen

 

I think this demo clarifies my use of the invalidate method and I hope that you can put me in the right direction so that one of the solutions is both stuck at the top and has a resizeable path.

Link to comment
Share on other sites

Hi,

 

Yeah it seems that one approach is to kill the ScrollTrigger instances:

const invalidate = () => {
  ScrollTrigger.killAll();
  scrollLine.invalidate()
  scrollLinePinned.invalidate()
  ScrollTrigger.create(animationB)
  ScrollTrigger.create(animation)
}

You can actually setup GSAP MatchMedia for this so you don't have to do it manually:

const invalidate = () => {
  ScrollTrigger.killAll();
  scrollLine.invalidate()
  scrollLinePinned.invalidate()
  ScrollTrigger.create(animationB)
  ScrollTrigger.create(animation)
}

const mm = gsap.matchMedia();
mm.add(
  {
    large: "(min-width: 1000px)",
    small: "(max-width: 999px)"
  },
  (ctx) => {
    invalidate();
  }
);

Here is a fork of your demo with the MatchMedia approach:

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

 

Hopefully this helps.

Happy Tweening!

Link to comment
Share on other sites

Hi Rodrigo,
Thanks for the reply and advice. I ended up with a solution that looks similar to your first suggestion:

Quote

Yeah it seems that one approach is to kill the ScrollTrigger instances:

const invalidate = () => {
  ScrollTrigger.killAll();
  scrollLine.invalidate()
  scrollLinePinned.invalidate()
  ScrollTrigger.create(animationB)
  ScrollTrigger.create(animation)
}

I have a list with motionPaths that will be invalidated on resize and when there is a pinned scrollTrigger attached to it the trigger is killed and a new one is created.

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