CSS animations used to be quite limited.
Dealing with anything other than a very basic effect usually meant long and complex @keyframes
declarations.
Furthermore, moving elements through a path required a masterful use of simultaneous translation and rotation: exactly the kind of thing that browser discrepancies would turn into nothing short of a nightmare.
To fix that hassle, developers have been turning to JS solutions since the dawn of web animation (or at least, since we decided to drop Flash, but I like to pretend that Flash never happened.)
Back in the day, moving things was one of the great uses for the good ol’ jQuery, but things could get resource-intensive in a pinch.
Then GSAP came out and gave us unlimited animation power with far better performance, which quickly turned it into an industry standard.
But CSS has been progressing too, and the recent release of Firefox 72 and Chromium-powered Edge means we can start implementing the CSS Motion Path specification that comes to solve exactly these kind of scenarios.
But does this mean we can get away with pure CSS animations and dump GSAP?
The core of the Motion Path Module is the offset-path
property. It takes a path()
function as its value, allowing us to define an SVG path for elements to be positioned through.
.container{ offset-path: path('M 0 100 L 200 150 L 300 150'); }
If you ever used CSS clip-path, this should look familiar. Essentially, it defines the points that the line goes through and the different ways it gets there.
Next up, the offset-distance
property allows us to define the position of the element on its offset-path
. It can take whatever CSS <length>
unit (%
, px
, etc).
In most cases, using percentages will be the better approach.
For instance, the following code will position an element right at the middle of its offset-path
.
.element{ offset-distance: 50%; }
Here’s an example with various elements positioned throughout a path:
Motion Path #1 – Positioning elements through a path
No Description
Despite what the name seems to imply, there’s no motion involved when using the properties of the Motion Path module on themselves: that part is still handled by animating the different motion path properties via transitions, CSS animations, or the Web Animations API.
Therefore, to actually move the element on the path we can use a @keyframes
declaration that shifts the offset-distance
.
.element{ offset-path: path('M0,0 C40,160 60,160 100,0'); animation: move 2000ms; } @keyframes move { 100% { offset-distance: 100%; } }
Motion Path #2: animating offset-distance
No Description
We can also use the offset-rotate
property to control how the element rotation should behave as it goes through its defined path.
The default auto
value will make sure that the element always faces in the direction of the path, automatically rotating as needed. If we want to position the element through the path facing in a given direction, we can use a CSS <degree>
value instead.
The following code will make sure that the element keeps it’s original orientation as it goes through the path.
.element{ offset-rotate: 0deg; }
Similar to what we did with offset-position
, we can control the offset-rotate
throughout the keyframe declaration and have the element direction adjust accordingly.
Keep in mind that for the rotation to smoothly transition, we must declare all of them in angles; changing from a degree to auto
will result in jumpy motion.
Combining both, we can get results as follows:
Motion Path #3 – offset-distance + offset-rotate
No Description
:hover
and transitionsRemember that we can also use simple transitions to animate the motion path properties
.element:hover{ offset-distance: 100%; offset-rotate: 360deg; }
In the following example, all elements start positioned at the beginning of their path and are animated out on hover.
Motion Path #4 – Animating on hover
No Description
If we try to apply the motion path properties to a text element, we’ll see that the whole text block is treated as a single piece. If what we want is for the text to wrap around the path (and potentially be animated through it) we need to make each letter behave as an individual element.
The first approach to this is actually splitting the text with an utility such as splitting.js. While this works great, it will pollute the dom with a <span>
for each letter, and result in screen readers spelling out the words.
The screen-reader-friendly option is to use an actual SVG text with a textPath element.
Motion Path #5 – Wrapping text through a path
No Description
Just like the fun stuff we do with clip-path
, the path declaration of an offset-path
can be animated.
To do so, we must make sure to have the same amount of nodes in every step of the animation for the browser to be able to smoothly transition between them.
If we provide different amounts of nodes in any step, the browser won’t be able to guess the in-betweens and will simply jump from one step to the next without a transition.
Motion Path #6 – Animating the path itself
No Description
The browsers’ current implementation of offset-path
only allows us to declare a path()
function for the elements to follow. According to the working draft of the spec, we should be able to use a <basic-shape>
(such as circle, polygon, etc) too. So we can probably expect those in the near future.
It will also allow us to use an SVG path id as the value (e.g. offset-path: url(#my-path)
), which will help us animate things through a drawn path in our HTML and let it scale accordingly.
There’s also an additional property that only Firefox has implemented so far: the offset-anchor
property allows us to define the anchoring point of the element relative to it’s offset-path.
It’s default setting is 50 %/50 %
, which means the element is centered on the path. By changing the offset-anchor
, we can manipulate which part of the element stays fixed to the path, similarly to what we do with transform-origin
.
I’ll be the first to admit that it’s tempting to use this new tool everywhere, but animations should be used purposely and responsibly.
Animations can trigger nausea in people with vestibular disorders, be distracting for anyone with attention disorders, or simply annoying for some users that might prefer to disable it.
So consider implementing this in a prefers-reduced-motion media query, to keep those users safe.
.element{ offset-path: path('M0,0 C40,160 60,160 100,0'); animation: move 2s ease-out; } @keyframes move { 100% { offset-distance: 100%; } } @media screen and (prefers-reduced-motion: reduce){ .element{ animation-duration: 1ms; /* takes it immediately to the ending position */ } }
The Motion Path module has just made CSS animations a hundred times more powerful, with many use cases that we’re just beginning to discover.
But it still has some downsides: for starters, and to address the elephant in the room, Safari doesn’t support the spec yet, which can be a deal breaker for compatibility.
It also doesn’t change the quirks of CSS animation itself, such as the complexity of writing and maintaining keyframes (specially when we need to chain and synchronize them together), and the limitation to two bezier handlers for their easing functions, which make things like bounce effects much more difficult to create and maintain than in external libraries that support multiple beziers.
So GSAP is here to stay, at least for a good while.
What Motion Path does, in my opinion, is moving the threshold at which we would switch from CSS to JS for animations quite a bit.
As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.
Modernize how you debug web and mobile apps — start monitoring for free.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.
One Reply to "CSS Motion Path: The end of GSAP?"
Hmm… It doesn’t replace GSAP at in my opinion. GSAP is much more powerful and has much more features than just animating something along a certain trajectory.