SVG Cubic Morph

The component that also covers SVG morphing, with similar functionality as for the SVG Morph component, but with a different implementation for value processing and animation setup.

Overview

Animate SVG paths with cubic-bezier path commands and improved performance.

The KUTE.js SVG Cubic Morph component enables animation for the d (description) presentation attribute and is the latest in all the SVG components.

The component will process paths and out of the box will provide the closest possible interpolation by default. It also implements a series of solutions from Paper.js to determine paths draw direction and automatically reverse one of them for most accurate presentation and as a result the previously featured options reverseFirstPath and reverseSecondPath have been deprecated.

The main difference with the SVG Morph component is the fact that this components is converting all path commands to cubicBezierTo, giving it the upper hand over the original morph component. While the other component will spend time to process the path data and create polygons, this component should deliver the animation faster and using considerably less power.

All path processing is powered by our SVGPathCommander starting KUTE.js version 2.0.14, which aims to modernize and improve the path processing and enable transition from closed to and from un-closed shapes.

Basic Example

The first morphing animation example is a transition from a rectangle into a star, just like for the other component.

<svg id="morph-example" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 600">
<path id="rectangle" class="bg-lime" d="M38.01,5.653h526.531c17.905,0,32.422,14.516,32.422,32.422v526.531 c0,17.905-14.517,32.422-32.422,32.422H38.01c-17.906,0-32.422-14.517-32.422-32.422V38.075C5.588,20.169,20.104,5.653,38.01,5.653z"/>
<path id="star" style="visibility:hidden" d="M301.113,12.011l99.25,179.996l201.864,38.778L461.706,380.808 l25.508,203.958l-186.101-87.287L115.01,584.766l25.507-203.958L0,230.785l201.86-38.778L301.113,12.011"/>
</svg>

Now we can apply both .to() and fromTo() methods:

// the fromTo() method
var tween = KUTE.fromTo('#rectangle', {path: '#rectangle' }, { path: '#star' }).start();
// OR
// the to() method will take the path's d attribute value and use it as start value
var tween = KUTE.to('#rectangle', { path: '#star' }).start();
// OR
// simply pass in a valid path string without the need to have two paths in your SVG
var tween = KUTE.to('#rectangle', { path: 'M301.113,12.011l99.25,179.996l201.864,38.778L461.706,380.808l25.508,203.958l-186.101-87.287L115.01,584.766l25.507-203.958L0,230.785l201.86-38.778L301.113,12.011' }).start();

By default, the component will process the paths as authored and deliver its best without any option required, like for the first red rectangle below which applies to any of the above invocations:

Some takeaways:

In this example we focus on experimentation to discover ways to optimize the morph animation so that the points travel optimal distance. Keep in mind that the Z path command is actually a shorthand for L (line path command), sometimes it's required to close the shape, our shapes here happen to be OK without it.

Each morph animation as well as each pair of shapes can have its own quirks. You can use the technique on your shapes to optimize the animation to your style. Chris Coyier wrote an excelent article in which he explains the terminology and workflow on how SVG morphing animation works with simple examples.

Morphing Unclosed Shapes To Closed Shapes

The next set of morphing animations is a transition from a line into a circle and showcases the component's behavior when both shapes are closed (they have the Z path command) or one or another is not, but first let's create the necessary markup and scripting:

<svg id="morph-example" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 600">
<path id="line" fill="transparent" stroke="orange" stroke-linejoin="round" stroke-width="10" d="M10 300 L580 300"/>
<path id="star" style="visibility:hidden" d="M10,300a290,290 0 1,0 580,0a290,290 0 1,0 -580,0z"/>
</svg>
// the fromTo() method
var tween = KUTE.fromTo('#line', {path: '#line' }, { path: '#circle' }).start();
// OR
// the to() method will take the path's d attribute value and use it as start value
var tween = KUTE.to('#line', { path: '#circle' }).start();

As you can see, the functionality of this component is very different from the svgMorph component in the sense that it will morph shapes as authored. If you replay the above animations, here are a few takeaways:

As you can see, this is another case where easy steps can be made to optimize the visual presentation. Keep in mind that stroke attributes like stroke-linejoin such can have a small impact on your animation, particularly on start and end.

Subpath Example

In other cases, you may want to morph paths that have subpaths. Let's have a look at the following SVG:

<svg id="multi-morph-example" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
<path id="w1" d="M412.23 511.914c-47.708-24.518-94.086-36.958-137.88-36.958-5.956 0-11.952 0.18-17.948 0.708-55.88 4.624-106.922 19.368-139.75 30.828-8.708 3.198-17.634 6.576-26.83 10.306l-89.822 311.394c61.702-22.832 116.292-33.938 166.27-33.938 80.846 0 139.528 30.208 187.992 61.304 22.962-77.918 78.044-266.09 94.482-322.324-11.95-7.284-24.076-14.57-36.514-21.32z 
  m116.118 79.156l-90.446 314.148c26.832 15.372 117.098 64.05 186.212 64.05 55.792 0 118.252-14.296 190.834-43.792l86.356-301.976c-58.632 18.922-114.876 28.52-167.464 28.52-95.95 0-163.114-31.098-205.492-60.95z 
  m-235.526-222.28c77.118 0.798 134.152 30.208 181.416 60.502l92.752-317.344c-19.546-11.196-70.806-39.094-107.858-48.6-24.386-5.684-50.02-8.616-77.204-8.616-51.796 0.976-108.388 13.946-172.888 39.8l-88.44 310.596c64.808-24.436 120.644-36.34 172.086-36.34 0.046 0.002 0.136 0.002 0.136 0.002z 
  m731.178-170.666c-58.814 22.832-116.208 34.466-171.028 34.466-91.686 0-159.292-31.802-203.094-62.366l-91.95 318.236c61.746 39.708 128.29 59.878 198.122 59.878 56.948 0 115.94-13.68 175.462-40.688l-0.182-2.222 3.734-0.886 88.936-306.418z"/>
<path id="w2" d="M0 187.396l367.2-50.6v354.798h-367.2v-304.2z 
  m0 649.2v-299.798h367.2v350.398z 
  m407.6 56v-355.798h488.4v423.2z 
  m0-761.2l488.4-67.4v427.6h-488.4v-360.2z"/>
</svg>

Similar to the svgMorph component, this component doesn't provide multipath processing so we should split the sub-paths into multiple <path> shapes, analyze and associate them in a way that corresponding shapes are close and their points travel the least possible distance.

Now since we've worked with these paths before, the first example below showcases how the svgCubicMorph component handles it by default. The following example was made possible by editing the shapes with a vector graphics editor. The last example showcases a creative use of association between starting and end shapes. Let's have a look:

Make sure to check the markup here as well as the svgCubicMorph.js for a full code review.

Intersecting Paths Example

The same preparation apply here, however the outcome is a bit different with cubic-bezier path commands, as shown in the first example. For the next two examples, the shapes have been edited for a better outcome. Let's have a look:

So the technique involves creating <mask> elements, splitting multipath shapes into multiple <path> shapes, matching the amount of starting and ending shapes by duplicating an existing shape or by sampling another shape for the same purpose, editing shapes for more accurate point-to-point animation, as well as various options to optimize the visual presentation.

That's it, you now mastered the SVG Cubic Morph component.

Notes