VOOZH about

URL: https://gsap.com/docs/v3/Plugins/MorphSVGPlugin/

⇱ MorphSVG | GSAP | Docs & Learning


Skip to main content
Detailed walkthrough

Description

MorphSVG morphs an SVG <path> by animating the data inside the d attribute. For example, you can morph a diamond into a lightning bolt with a single line of code:

gsap.to("#diamond",{duration:1,morphSVG:"#lightning"});

loading...

Features

Feature Highlights
  • Morph <path> data even if the number (and type) of points don't match between the start and end shapes!
  • Perfect shape fidelity. Most other morphing tools use a lossy approximation of your artwork that falls apart in complex shapes, or they require matching point quantities in the start/end shapes. MorphSVG is both super flexible and super accurate.
  • Natural point mapping that avoids strange twisting in other libraries caused by misaligned artwork point sequencing.
  • Optionally draw the resulting shape to a <canvas> (see MorphSVGPlugin.defaultRender).

Simple Examples

In its simplest form, you can just morph to another element or raw path data directly:

// selector string (grabs the data from the corresponding element's "d" attribute)
gsap.to("#diamond",{morphSVG:"#lightning"});

// an SVG element
const endShape =document.getElementById("lightning");
gsap.to("#diamond",{morphSVG: endShape });

// raw path data (string)
gsap.to("#diamond",{morphSVG:"M47.1,0.8 73.3,0.8 61.9,37.2 77.1,37.2 30.7,99.4 45.8,51.9 29,51.9z"});

// points for polygon or polyline elements:
gsap.to("#polygon",{morphSVG:"240,220 240,70 70,70 70,220"});
note

If the shape you pass in is a <circle>, <rect>, <ellipse>, <polygon>, <polyline>, or <line>, MorphSVG will internally create path data from those shapes.

Configuration

To configure the morph, use the object {...} syntax instead. shape is the only required property:

gsap.to("#diamond",{
duration:1,
ease:"power2.inOut",
morphSVG:{
shape:"#lightning",
smooth:{points:80,redraw:true}
}
});

Here are the various configuration options:

    Property

    Description

  • curveMode

    boolean(new in 3.14.0)

    Forces smooth anchors (on curves) to avoid kinks mid-morph by interpolating the angle and length of control point handles instead of their raw x/y coordinates. Normally you do not need to enable curveMode

  • map

    "size" | "position" | "complexity"

    If the sub-segments inside your path aren't matching up the way you hoped between the start and end shapes, you can use map to tell MorphSVG which algorithm to prioritize (see details below)

  • origin

    string

    Sets the origin of rotation. The default is 50% 50%. The format is either a string of two percentage values, or a string or four values if there are different values for the start and end shapes.

    To set your own origin:

    gsap.to("#shape1",{
    duration:2,
    morphSVG:{
    shape:"#shape2",
    type:"rotational",
    origin:"20% 60%",//or "20% 60%,35% 90%" if there are different values for the start and end shapes.
    },
    });

    sometimes the rotations around a point look odd, In cases like this, it's best to experiment and set your own custom origin to improve things even more. We created a findMorphOrigin() utility function to help with this...

  • precision

    number

    By default, MorphSVG will round values in the resulting d string to 2 decimal places in order to maximize performance and reduce string length but you can set precision to your preferred number of decimal places. For example, precision: 5 would round to 5 decimal places:

    gsap.to("#id",{morphSVG:{shape:"#other-id",precision:5}});
  • precompile

    Array

    Only for very advanced use cases where you're running into performance issues on the initial render, which is rare.

    Tell MorphSVG to run all of its initial calculations and return an array with the transformed strings, logging them to the console where you can copy and paste them back into your tween. That way, when the tween begins it can just grab all the values directly instead of doing expensive calculations.

    For more information see precompile

  • render

    function

    Define a render function that'll be called every time the path updates, typically for drawing to <canvas>. See Rendering to canvas

  • shape

    string | element

    The shape to morph to. You can use selector text like "#diamond" or a direct reference to the element like diamondEl or even raw path data like "M490.1,280.649c0,44.459-36.041,80..."

  • shapeIndex

    number

    The shapeIndex property controls how the points in the start shape are mapped to the ones in the end shape. Every closed path is drawn from a particular point on the path. For example, if you were drawing a circle with a pencil, you could start anywhere (1 o'clock, 9 o'clock, etc.). So if the starting shape begins its sequence of points in its upper left corner and it's mapped to an end shape whose points begin in the lower right corner (matching first with first, second with second, etc.) will result in the shape crossing over itself (visually inverting halfway through). Think of shapeIndex like an offset, so shapeIndex: 3 would match up the 3rd point from the start shape with the first point in the end shape:

    gsap.to("#square",{
    duration:1,
    morphSVG:{shape:"#star",shapeIndex:3},
    });

    For help finding the best shapeIndex, see the findShapeIndex() function

  • smooth

    number | "auto" | object(new in 3.14.0)

    Adds extra "smoothing" anchor points to the artwork. Think of it like increasing the resolution, inserting points to pull on during the morph. Normally this is not necessary, but if your original artwork has awkwardly-placed anchor points it can help make the morph look more natural.

    You can define a specific number of points, like smooth: 80 would redraw the path using 80 evenly-spaced anchor points (replacing all existing anchor points). Or smooth: "auto" would automatically choose a number of points based on the surface area. By default, smoothing the path will redraw it which is like tracing it at a certain resolution in order to distribute the anchor points evenly, so it loses some fidelity to the original artwork. You can use the object syntax to set redraw: false to avoid this (see details below). Keep in mind that if you use too many points, it could affect performance.

  • type

    "linear" | "rotational"

    By default, all of the anchors and control points in the shape are interpolated linearly (type: "linear") which is usually great but you can set type: "rotational" to make MorphSVG use rotation and length data for interpolation instead which can produce more natural morphs in some cases. It also eliminates kinks that may form in otherwise smooth anchors mid-tween (like curveMode: true). To tap into this alternative style of morphing, just set type: "rotational" in the object:

    gsap.to("#shape1",{
    duration:2,
    morphSVG:{
    shape:"#shape2",
    type:"rotational"
    }
    })

    The concept is best understood visually, so here are some videos and demos...

Tips

note

MorphSVG also stores the original path data on the target so that you can easily tween back to the original shape. (like data-original="M490.1,280.649c0,44.459-36.041,80...")

Morph into multiple shapes

Sequencing multiple morphs is a breeze with GSAP. Watch how easy it is to make that diamond morph into various other shapes and back again:

tl.to("#diamond",{duration:1,morphSVG:"#shape2"},"+=1")
.to("#diamond",{duration:1,morphSVG:"#shape3"},"+=1")
.to("#diamond",{duration:1,morphSVG:"#shape4"},"+=1")
.to("#diamond",{duration:1,morphSVG:"#diamond"},"+=1");// back to the original

loading...

Converting SVG shapes to paths

Feature runthrough

Technically it's only feasible to morph <path> elements or <polyline>/<polygon> elements, but what if you want to morph a <circle>, <rect>, <ellipse>, or <line>? No problem - just tap into the utility method and have the plugin do the conversion for you:

MorphSVGPlugin.convertToPath("#elementID");

You can pass in an element or selector text, so you could also have it convert ALL of those elements with one line:

MorphSVGPlugin.convertToPath("circle, rect, ellipse, line, polygon, polyline");

This literally swaps in a for each one directly in the DOM, and it should look absolutely identical. It'll keep the attributes, like the "id" attribute. So after the conversion, you should be able to target the elements pretty easily, just as you would previously.

// an svg <rect> Like this:
<rect id="square" width="100" height="100" fill="red"/>

// becomes
<path id="square" fill="red" d="M100,0 v100 h-100 v-100 h100z"></path>

findShapeIndex() utility

Experimenting with shapeIndex can be a bit of a guessing game. To make things easier we have created a stand-alone utility function called findShapeIndex() that you can use just during development to find the right number to plug into the final animation. This function provides an interactive user interface to help you visualize where the start point is, change it, and preview the animation.

You can load findShapeIndex() from this download link.

Once it's loaded you simply tell it which shapes to use.

findShapeIndex("#square","#star");

Or pass in raw data:

findShapeIndex(
"#square",
"M10 315 L 110 215 A 30 50 0 0 1 162.55 162.45 L 172.55 152.45 A 30 50 -45 0 1 215.1 109.9 L 315 10"
);
tip

The best way to get started is to drop your SVG into the pen and alter the IDs to match your svg.

loading...

Maximizing performance

Define a shapeIndex in advance

Performance tip: define a shapeIndex in advance

MorphSVGPlugin's default shapeIndex: "auto" does a bunch of calculations to reorganize the points so that they match up in a natural way but if you define a numeric shapeIndex (like shapeIndex: 5) it skips those calculations. Each segment inside a path needs a shapeIndex, so multiple values are passed in an array like shapeIndex:[5, 1, -8, 2]. But how would you know what numbers to pass in? The findShapeIndex() tool helps for single-segment paths, what about multi-segment paths? It's a pretty complex thing to provide a GUI for.

Precompile

While precompiling isn't usually necessary, it can really improve performance for very complex morphs. Precompiling involves having MorphSVG run all of its initial calculations and then spit out an array with the transformed strings, logging them to the console where you can copy and paste them back into your tween. That way, when the tween begins it can just grab all the values directly and skip the expensive startup calculations.

Rendering to canvas

SVG is fantastic, but sometimes developers prefer a canvas-based animation (often for rendering performance reasons). The MorphSVG plugin allows you to define a render function that'll be called every time the path updates.

loading...

Properties

MorphSVGPlugin.defaultRender : Function

Sets the default function that should be called whenever a morphSVG tween updates. This is useful if you're rendering to <canvas>.

MorphSVGPlugin.defaultType : String

Sets the default "type" for all MorphSVG animations. The default type is "linear" but you can change it to "rotational".

MorphSVGPlugin.defaultUpdateTarget : Boolean

Sets the default updateTarget value for all MorphSVG animations; if true, the original tween target (typically an SVG <path> element) itself gets updated during the tween.

Methods

MorphSVGPlugin.convertToPath( shape:[Element | String], swap:Boolean ) : Array

Converts SVG shapes like <circle>, <rect>, <ellipse>, or <line> into <path>

MorphSVGPlugin.rawPathToString( rawPath:Array ) : String

Converts a RawPath (array) into a string of path data, like "M0,0 C100,20 300,50 400,0..." which is what's typically found in the d attribute of a <path>.

MorphSVGPlugin.stringToRawPath( data:String ) : RawPath

Takes a string of path data (like "M0,0 C100,20 300,50 400,0...", what's typically found in the d attribute of a <path>), parses it, converts it into cubic beziers, and returns it as a RawPath which is just an array containing an array for each segment (each M command starts a new segment).

Demos

Check out the full collection of How-to demos and our favourite inspiring community demos on CodePen.