Framer's Magic Motion
nan.fyi
nan.fyi
I was clicking through on Firefox android and noticed a small number of minor things, incl. bounding box issues, animations that went off screen, and glitches when an example was toggled while in motion. Not sure if these are intentional (or expected). No doubt easily fixed. But they paint a picture for edge cases. I'm sure there's some amazing use cases for these techniques, but there's also a good case for simple CSS (and restraint!)
The version in Framer has stuff like infinitely deep scale correction and shared element transitions so because of the filesize overhead (~10kb) I would only use this for complex stuff that is otherwise prohibitively tough (shared element transitions, layout etc).
However I do want to make the point that this approach should be more robust and simpler to maintain for even simple examples.
If you imagine an iOS-style pill toggle. The "simplest" way to animate this would be to have `translateX(0)` when off and something like `translateX(100px)` when on.
However, if there were no animations, this isn't how you'd write it. You'd probably do something like switch `flex-start` to `flex-end` on the parent.
This is easier to change the design and also maintain between breakpoints. But it can't be animated. Except with these technique, where you'd simply write
<motion.div layout />
And it would be. Much more maintainable.
Agreed. The “magic” is just CSS used effectively, explained elegantly.
One thing I noticed that often isn't caught due to platform differences is when "Hide types" is pressed, a scrollbar will appear on most browsers on Windows 10. This draws the eye so harshly that most - if not all - of the magic of the animation is ruined.
I wish that Windows 10 had an option to "hide scrollbars until necessary" like macOS does, but sadly this continues to be a bit of an edge case for FE work.
Framer takes a slightly different approach in their docs which appears to be a mixture of pinning objects down and shoving margins around as well.
But one thing that has been confusing me is its performance. The same simple animation might be smooth 60fps 9 times out of 10, but chug significantly the 10th time, on the same device etc. Any advice on how to profile this?
Would love to hear what you find!
After looking at performance tab it turned out that dom modifications trigger uBlock to run it's internal checker(presumably to check in case you are adding some ads onto the page). It's not a problem in a normal circumstances, but when you are updating dom 10+ times a second it sometimes chugs. Rewrote rendering to use canvas and the problem went away.
Might me something similar here.
For those copying/getting inspired by the code, don't forget to add the missing empty-array for the useLayoutEffet dependency param.
You need to run it without the dependency array or add all layout-changing props and state to it.
https://svelte.dev/repl/a1628b3298eb4a91a840578aaf056574?ver...
Since it's using float: right to realign the box, add more items to the array to see what happens
[0]: https://www.framer.com/docs/layout-animations/###border-radi...
To take border from your example, in the Framer app we add correction that fixes border. But border is tricky because it only renders rounded values and triggers layout - exactly what we're trying to avoid. I always recommend a two element approach, one with an inset. More performant and visually nicer.
We also use this API to do more advanced stuff like this <LayoutCamera /> component for React Three Fiber/Framer Motion 3D. https://www.framer.com/docs/layoutcamera/
This camera renders the scene pre-distorted so when the layout transform is applied, everything looks correct.
Close, but still not perfect. Lines look blurry during the size animation, because the WebGL surface is rendered at the new resolution and then stretched to the current size. So if you animate from a 600x600 viewport to a 60x60 viewport it’s gonna look very bad when the animation starts. You should always render at max(old, new) resolution during the animation.
And again, this only works as long as the WebGL content commutes with affine transforms. If the WebGL content would contain 1 pixel screen-space hairlines, those would look bad when the framebuffer is stretched.
It is a very well-written and beautifully explained article though.