Friday, May 22, 2015

Morphing Between SVG Shapes

In order to animate between two shapes in SVG you need an equal number of nodes. This is because the node coordinates are what get adjusted during a transition. If one shape has more then another you get some really weird behaviour.

One approach is to manually edit one of the paths so the number of nodes matches the other. But that is pretty time consuming. I opt for the lazy approach of stuffing it through a filter.

I wrote polymorph to make the calculation between two shapes. Essentially transposing the nodes from one shape onto the path of another.

Input Shape
This is a little over simplified... But you start with a complex shape with lots of nodes.

Destination Shape
Then say you want to animate it into a simple circle like this.

Note the destination path contains arcs. Polymorph can handle any type of SVG path for the destination. The source, and more complex, path must always contain linear lines. See SVG Path for more details.

var inputShape = "m 14.108932,44.306697 33.663417,-22.772311 49.257499,-6.435654 4.702982,24.504988 -13.613886,30.693115 42.326796,10.14853 44.80204,-10.14853 8.91091,30.940645 -3.46535,31.18816 -28.46539,23.51489 L 99.752625,177.72274 87.128843,144.55437 97.277373,118.56423 75.000112,97.029549 30.44559,94.059247 44.059472,65.841383 14.108932,44.306697"
var destinationShape = "m 191.90169,310.3161 a 51.514591,51.514591 0 1 1 -103.029181,0 51.514591,51.514591 0 1 1 103.029181,0 z"
var newShape = polymorph.transpose(inputShape,destinationShape);

New Shape
The nodes are obviously enlarged for the purposes of this post. But you end up with a shape that is almost identical to the Destination Shape. However, the node numbers match.

You can transpose nodes in any direction. However it makes most sense to convert from high to equal or less complexity.

Now use one of the handy dandy svg libraries, such as snapvelocity, or d3, to transform between the two. The following example uses d3 to transform from geographic boundaries (just svg paths) into circles, and back again.


Unknown said...

Thank you very much for your interesting post, i am trying to make it work with Snap, but cannot...
The polymorph transformation seems strange, here's my log output :
Animate between path : m770 2679c-128-9-160-15-160-29 0-19 82-25 345-25 206 0 279 3 310 14 38 14 39 14 15 23-28 10-255 28-340 26-30 0-107-5-170-9z
and path : m758.52 262.02-23.327 22.575 0.7525 16.555 25.585 50.417 19.565 106.1 9.03 15.802 14.297 3.01 8.2775-15.05-7.525-138.46-4.515-42.892-6.7725-9.7825z
plymorphed as path : M758.5196533203125,262.02032470703125Z

Any idea ?

Unknown said...

By the way i am using Snap to animate the path, but it fails, if you have any code example, it would be great!

Jamie Popkin said...

Thanks. Glad you liked the post.
How are you finding Snap.js? Been meaning to play around with it.

At the moment polymorph only works with simple lines. ie No curves. So you would have to do something like this M43 43 L 54 32 L 43 65Z.

Would be an interesting enhancement if I could ever figure it out. ☺

Unknown said...

Thanks Jamie for your help!
I've just played very little with Snap.js, seems to be interesting, but i am far to be an expert.
Ok for simple lines.
Good luck for everything!

Jamie Popkin said...

No problem Max. Good luck with your project.