Maglr.com Posted November 1, 2023 Share Posted November 1, 2023 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 More sharing options...
Solution Rodrigo Posted November 1, 2023 Solution Share Posted November 1, 2023 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 More sharing options...
Maglr.com Posted November 2, 2023 Author Share Posted November 2, 2023 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 More sharing options...
Maglr.com Posted January 16 Author Share Posted January 16 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 More sharing options...
Rodrigo Posted January 16 Share Posted January 16 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 More sharing options...
Maglr.com Posted January 19 Author Share Posted January 19 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 More sharing options...
Rodrigo Posted January 19 Share Posted January 19 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 More sharing options...
Maglr.com Posted January 26 Author Share Posted January 26 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. 1 Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now