惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Attack and Defense Labs
Attack and Defense Labs
N
News and Events Feed by Topic
L
LINUX DO - 热门话题
PCI Perspectives
PCI Perspectives
www.infosecurity-magazine.com
www.infosecurity-magazine.com
爱范儿
爱范儿
D
DataBreaches.Net
Simon Willison's Weblog
Simon Willison's Weblog
S
Secure Thoughts
S
SegmentFault 最新的问题
博客园 - 【当耐特】
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 叶小钗
P
Proofpoint News Feed
The Hacker News
The Hacker News
T
ThreatConnect
N
News and Events Feed by Topic
T
Threatpost
The Register - Security
The Register - Security
WordPress大学
WordPress大学
博客园 - Franky
Recorded Future
Recorded Future
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Project Zero
Project Zero
大猫的无限游戏
大猫的无限游戏
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
罗磊的独立博客
Stack Overflow Blog
Stack Overflow Blog
腾讯CDC
F
Future of Privacy Forum
F
Full Disclosure
Cyberwarzone
Cyberwarzone
J
Java Code Geeks
李成银的技术随笔
Schneier on Security
Schneier on Security
Know Your Adversary
Know Your Adversary
H
Hacker News: Front Page
人人都是产品经理
人人都是产品经理
博客园_首页
Scott Helme
Scott Helme
Google DeepMind News
Google DeepMind News
美团技术团队
Malwarebytes
Malwarebytes
Last Week in AI
Last Week in AI
T
Tailwind CSS Blog
T
The Exploit Database - CXSecurity.com
G
GRAHAM CLULEY
Recent Announcements
Recent Announcements
C
CXSECURITY Database RSS Feed - CXSecurity.com

CSS-Tricks

Revealing Text With CSS letter-spacing | CSS-Tricks Technical Writing in the AI Age | CSS-Tricks Cross-Document View Transitions: Scaling Across Hundreds of Elements | CSS-Tricks Cross-Document View Transitions: Scaling Across Hundreds of Elements | CSS-Tricks The State of CSS Centering in 2026 | CSS-Tricks Stack Overflow: When We Stop Asking | CSS-Tricks Cross-Document View Transitions: The Gotchas Nobody Mentions | CSS-Tricks What’s !important #11: 3D Voxel Scenes, Flying Focus, CSS Syntaxes, and More | CSS-Tricks Computing and Displaying Discounted Prices in CSS | CSS-Tricks rotateX() | CSS-Tricks rotateY() | CSS-Tricks rotateZ() | CSS-Tricks rotate() | CSS-Tricks Soon We Can Finally Banish JavaScript to the ShadowRealm | CSS-Tricks Using CSS corner-shape For Folded Corners | CSS-Tricks A Scrollytelling Gift for Mum on Mother’s Day 2026 | CSS-Tricks Google’s Prompt API | CSS-Tricks Making Zigzag CSS Layouts With a Grid + Transform Trick | CSS-Tricks Fixed-Height Cards: More Fragile Than They Look | CSS-Tricks What’s !important #10: HTML-in-Canvas, Hex Maps, E-ink Optimization, and More | CSS-Tricks The Importance of Native Randomness in CSS | CSS-Tricks contrast() | CSS-Tricks contrast-color() | CSS-Tricks Let’s Use the Nonexistent ::nth-letter Selector Now | CSS-Tricks Quick Hit #126 Recreating Apple’s Vision Pro Animation in CSS | CSS-Tricks Quick Hit #125 Enhancing Astro With a Markdown Component | CSS-Tricks Quick Hit #124 Markdown + Astro = ❤️ | CSS-Tricks Quick Hit #123 What’s !important #9: clip-path Jigsaws, View Transitions Toolkit, Name-only Containers, and More | CSS-Tricks A Well-Designed JavaScript Module System is Your First Architecture Decision | CSS-Tricks hypot() | CSS-Tricks The Radio State Machine | CSS-Tricks 7 View Transitions Recipes to Try | CSS-Tricks Quick Hit #122 Quick Hit #121 Selecting a Date Range in CSS | CSS-Tricks saturate() | CSS-Tricks justify-self | CSS-Tricks Quick Hit #120 Alternatives to the !important Keyword | CSS-Tricks Quick Hit #119 New CSS Multi-Column Layout Features in Chrome | CSS-Tricks Quick Hit #118 Making Complex CSS Shapes Using shape() | CSS-Tricks Quick Hit #117 Front-End Fools: Top 10 April Fools’ UI Pranks of All Time | CSS-Tricks Sniffing Out the CSS Olfactive API | CSS-Tricks What’s !important #8: Light/Dark Favicons, @mixin, object-view-box, and More | CSS-Tricks Quick Hit #116 Form Automation Tips for Happier User and Clients | CSS-Tricks Quick Hit #115 Generative UI Notes | CSS-Tricks Quick Hit #114 Quick Hit #113 Experimenting With Scroll-Driven corner-shape Animations | CSS-Tricks Quick Hit #112 JavaScript for Everyone: Destructuring | CSS-Tricks Quick Hit #111 Quick Hit #110 What’s !important #7: random(), Folded Corners, Anchored Container Queries, and More | CSS-Tricks 4 Reasons That Make Tailwind Great for Building Layouts | CSS-Tricks Quick Hit #109 Quick Hit #108 Abusing Customizable Selects | CSS-Tricks Quick Hit #107 The Value of z-index | CSS-Tricks Quick Hit #106 The Different Ways to Select <html> in CSS Quick Hit #105 Popover API or Dialog API: Which to Choose? Quick Hit #104 What’s !important #6: :heading, border-shape, Truncating Text From the Middle, and More Yet Another Way to Center an (Absolute) Element An Exploit ... in CSS?! Quick Hit #103 A Complete Guide to Bookmarklets Quick Hit #102 Loading Smarter: SVG vs. Raster Loaders in Modern Web Design Potentially Coming to a Browser :near() You Quick Hit #101 Distinguishing "Components" and "Utilities" in Tailwind Quick Hit #100 Spiral Scrollytelling in CSS With sibling-index() Interop 2026 Quick Hit #99 What’s !important #5: Lazy-loading iframes, Repeating corner-shape Backgrounds, and More Quick Hit #98 Making a Responsive Pyramidal Grid With Modern CSS Approximating contrast-color() With Other CSS Features Quick Hit #97 Trying to Make the Perfect Pie Chart in CSS Quick Hit #96 Quick Hit #95 CSS Bar Charts Using Modern Functions Quick Hit #94 No Hassle Visual Code Theming: Publishing an Extension Quick Hit #93
Morphing SVG With react-spring
CSS-Tricks · 2020-08-26 · via CSS-Tricks

I’ve been intrigued by the morphing effect ever since I was a little kid. There’s something about a shape-shifting animation that always captures my attention. The first time I saw morphing left me wondering  “ Wow, how did they do that?” Since then, I’ve created demos and written an article about the effect.

There are a lot of options when it comes to different animation libraries that support morphing. Many of them are good and provide numerous features. Lately, I’ve been absorbed by react-spring. React-spring is a nifty physics-enabled animation library built on React. Adam Rackis recently posted a nice overview of it. The library comes with many features including, among a lot of others, SVG morphing. In fact, the beauty of react-spring lies in how it supports morphing. It allows you to do so directly in the markup where you define your SVG paths descriptors. This is good from a bookkeeping perspective. The SVG path descriptors are where you typically would expect them to be.

Here’s a video of what we’re looking into in this article:

It’s a morphing effect in an onboarding sequence. Here, it’s used as a background effect. It’s meant to complement the foreground animation; to make it stand out a bit more rather than taking over the scene.

Creating the SVG document

The first thing we want to do is to create the underlying model. Usually, once I have a clear picture of what I want to do, I create some kind of design. Most of my explorations start with a model and end with a demo. In most cases, it means creating an SVG document in my vector editor. I use Inkscape to draw my SVGs.

When I create SVG documents, I use the exact proportions. I find that it’s better to be precise. For me, it helps my perception of what I want to create when I use the same coordinate system in the vector editor as in the browser and the code editor. For example, let’s say you’re about to create a 24px ✕ 30px SVG icon, including padding. The best approach is to use the exact same size — an SVG document that is 24 pixels wide by 30 pixels tall. Should the proportions turn out to be wrong, then they can always be adjusted later on. SVG is forgiving in that sense. It’s scalable, no matter what you do.

The SVG document we’re creating is 256 pixels wide and 464 pixels high.

Drawing the models

When creating models, we need to think about where we place the nodes and how many nodes to use. This is important. This is where we lay the groundwork for the animation. Modeling is what morphing is all about. We have one set of nodes that transforms into another. These collections of nodes need to have the exact same number of nodes. Secondly, these sets should somehow correlate.

If the relation between the vector shapes is not carefully thought through, the animation won’t be perfect. Each and every node will affect the animation. Their position and the curvature need to be just right. For more detailed information on how nodes in SVG paths are constructed, refer to the explanation of Bézier curves on MDN.

Secondly, we need to take both shapes into account. One of the vectors may contain parts, which cannot be found in the other. Or, there may be other differences between the two models. For these cases, it can be a good idea to insert extra nodes in certain places. The best is to form strategies. Like, this corner goes there, this straight line bulges into a curve and so on.

I’ve put together a pen to illustrate what it looks like when sets correlate bad versus when accurately designed. In the below example, in the morphing effect on the left, nodes are randomly placed. The nodes that make up the number one and two have no relation. In the right example, the placement of the nodes is more carefully planned. This leads to a more coherent experience. 

The first model

The line tool is what we use to draw the first vector shape. As the model we’re creating is more abstract, it’s slightly more forgiving. We still need to think about the placement and the curvature, but it allows for more sloppiness.

As for vectors and sizing, the same goes for creating the models for morphing. It’s an iterative process. If it’s not right the first time, we can always go back and adjust. It usually requires an iteration or two to make the animation shine. Here’s what the model looks like when completed.

The result is a smooth abstract SVG shape with eight nodes.

The second and third models

Once the first model is complete, it’s time to draw the second model (which we can also think of as a state). This is the shape the first set will morph into. This could be the end state, i.e., a single morphing effect. Or it could be a step on the way, like a keyframe. In the case we’re looking at, there are three steps. Again, each model must correlate to the previous one. One way to make sure the models match up is to create the second vector as a duplicate of the first. This way, we know the models have the same numbers of nodes and the same look and feel.

Be careful with the editor. Vector editors typically optimize for file size and format. It might very well make the models incompatible when saving changes. I’ve made a habit of inspecting the SVG code after saving the file. It also helps if you’re familiar with the path descriptor format. It is a bit cryptic if you’re not used to it. It could also be a good idea to disable optimization in the vector editor’s preferences.

Repeat the above process for the third shape. Copy, relocate all of the nodes, and set the third color.

Lights, camera… action!

Once the models are created, we’ve done most of the work. Now it’s time to look at the animation part. React-spring comes with a set of hooks that we can use for animation and morphing. useSpring is a perfect candidate for the effect in this example. It’s meant to be used for single animations like the one we’re creating. Here’s how to initiate animations with the useSpring hook.

const [{ x }, set] = useSpring(() => ({
  x: 0,
}));

The above gives us an animation property, x, to build our morphing effect around. A great thing about these animation properties is that we can alter them to create almost any kind of animation. If the value is off, we can change it through interpolation.

The second parameter, the set function, allows us to trigger updates. Below is a snippet of code showing its use. It updates the animation value x with a gesture handler useDrag from the react-use-gesture library. There are many ways in which we can trigger animations in react-spring.

const bind = useDrag(
  ({ movement: [x] }) => {
    // ...
    set({ x });
  },
);

We now have everything set up to combine our models, the path descriptors, with the markup. By adding the animated keyword to the JSX code, we activate react-spring’s animation system. With the interpolation call to, previously named interpolate, we convert drag distances to morphed shapes. The output array contains the models already discussed. To get them in place, we simply copy the path descriptors from the SVG file into the markup. Three different descriptors, d, from three different path elements copied from three different SVG files are now combined into one. Here’s what the JSX node looks like when powered with a react-spring animation.

<svg ...>
  <animated.path
    d={x.to({
      range: [-400, -200, 0],
      output: [
        // First model
        "M 157.81292,131.16918 C 128.33979,127.45582 59.004493,121.76045 53.287478,168.06051 47.570462,214.36057 86.454799,213.14326 77.881699,234.66986 69.308599,256.19646 59.042495,268.13837 67.634107,288.98209 76.225718,309.82581 103.27857,320.05328 138.34249,312.55156 173.40641,305.04984 204.93111,298.87002 208.02612,279.75926 211.12113,260.6485 189.48716,257.88808 188.5557,229.54606 187.62424,201.20404 212.01456,174.45091 200.8528,155.7634 189.69104,137.07589 187.28605,134.88254 157.81292,131.16918 Z",
        // Second model
        "M 157.81292,131.16918 C 128.33979,127.45582 48.756902,138.1566 53.287478,168.06051 57.818054,197.96442 75.182448,197.77187 73.782662,224.42227 72.382877,251.07266 70.314846,257.89078 72.757903,278.7345 75.20096,299.57822 88.114636,303.32873 113.94876,307.60312 139.78288,311.87751 159.84171,314.24141 176.25858,295.13065 192.67546,276.01989 203.83379,256.86332 190.60522,228.5213 177.37665,200.17928 205.866,189.8223 211.10039,171.13479 216.33478,152.44728 187.28605,134.88254 157.81292,131.16918 Z",
        // Third model
        "M 157.81292,131.16918 C 128.33979,127.45582 86.672992,124.83473 71.733144,166.01099 56.793295,207.18725 69.033893,203.92043 80.955976,230.57083 92.87806,257.22123 55.968217,259.9403 59.436033,279.75926 62.90385,299.57822 94.985717,299.83924 132.0922,306.16316 169.19868,312.48708 186.48544,320.38997 198.80328,288.98209 211.12113,257.57422 199.73475,245.59097 195.72902,217.24895 191.72328,188.90693 209.96504,178.54995 215.19943,159.86244 220.43382,141.17493 187.28605,134.88254 157.81292,131.16918 Z",
      ],
    })}
  />
</svg>

Let’s look at the differences between a standard JSX path element and what we currently have. To get the morphing animation in place, we have:

  • added the animated keyword to make the JSX path element animate with React spring,
  • changed the descriptor d from a string to a React spring interpolation, and
  • converted the distance x to a keyframe animation between three path descriptors.

Development environment

I have yet to find the perfect development environment for working with SVG. Currently, I go back and forth between the vector editor, the IDE, and the browser. There’s a bit of copying and some redundancy involved. It’s not perfect, but it works. I have experimented a bit, in the past, with scripts that parse SVGs. I have still not found something that I can apply to all scenarios. Maybe it’s just me who got it wrong. If not, it would be great if web development with SVGs could be a bit more seamless.

Here we go!

Last but not least, the demo!

Thanks for reading!