Shape Morphing

There are a number of ways we can animate or transition one shape to another on the web. I’m not talking about rotating an arrow or enlarging a checkmark, I’m talking about watching a shape move the very points it is made from to new places.

There are lots of motion possibilities on the web. You can animate any element’s opacity, color, and transform properties (like translate, scale, and rotate), to name a few, all pretty easily. For example:

.kitchen-sink {
  opacity: 0.5;
  background-color: orange;
  transform: translateX(-100px) scale(1.2) rotate(1deg);
}
.kitchen-sink:hover {
  opacity: 1;
  background-color: black;
  transform: translateX(0) scale(0) rotate(0);
}

By the way, animating the transform and opacity properties are ideal because the browser can do it “cheaply” as they say. It means that the browser has much less work to do to make the movement happen and can take advantage of “hardware acceleration”).

Lesser known is the fact that you can animate the actual shape of elements! I’m not just talking about animating border-radius or moving some pseudo-elements around (although that can certainly be useful to), I mean quite literally morphing the vector shape of an element.

For our first trick, let’s create the vector shape by way of clip-path. We can cut away parts of an element at % coordinates like this:

.moving-arrow {
  width: 200px;
  height: 200px;
  background: red;
  clip-path: polygon(100% 0%, 75% 50%, 100% 100%, 25% 100%, 0% 50%, 25% 0%);
}

Clippy is an incredible tool for generating polygon() shape data. Firefox DevTools also has pretty good built-in tooling for manipulating it once it has been applied.

Then we can change that clip-path on some kind of state change. It could be the change of a class, but let’s use :hover here. While we’re at it, let’s add a transition so we can see the shape change!

.moving-arrow {
  ...
  transition: clip-path 0.2s;
  clip-path: polygon(100% 0%, 75% 50%, 100% 100%, 25% 100%, 0% 50%, 25% 0%);
}
.moving-arrow:hover {
  clip-path: polygon(75% 0%, 100% 50%, 75% 100%, 0% 100%, 25% 50%, 0% 0%);
}

Because the polygon() has the exact same number of coordinates, the transition works.

Using clip-path to draw vector shapes is fine, but it’s perhaps not the perfect tool for the job of drawing vector shapes. Drawing vector shapes is really the parlance of SVG. SVG’s elements have attributes designed for drawing. The powerhouse is the element that has its own special syntax for drawing.

You might see a path like this:

<svg viewBox="0 0 20 20">
  <path d="
     M 8 0
     L 12 0
     L 12 8
     L 20 8
     L 20 12
     L 12 12
     L 12 20
     L 8 20
     L 8 12
     L 0 12
     L 0 8
     L 8 8
     L 8 0
  "></path>
</svg>

Which draws a “+” shape.

That value for the d attribute may look like gibberish, but it really just commands the movement of a virtual pen. Move the pen here, draw a line this far in this direction, etc.

In the example above, there are only two commands in use, which you can see from the letters that precede each line:

  • M: Move the pen to these exact coordinates (without drawing)
  • L: Draw a line from the pen’s current coordinates to these exact coordinates

SVG itself has a language for altering those coordinates if we wanted to, including animation. It’s called SMIL, but the big problem with it is that it’s old and was never particularly well supported.

The good news is that some browsers support some of what SMIL can do right in CSS. For example, we can alter the path on :hover in CSS like this:

svg:hover path {
    d: path("
      M 10 0 
      L 10 0
      L 13 7
      L 20 10
      L 20 10
      L 13 13
      L 10 20
      L 10 20
      L 7 13
      L 0 10
      L 0 10
      L 7 7
      L 10 0
    ");
}
path {
  transition: 0.2s;
}

That turns our plus shape into a throwing star shape, and the transition is possible because it’s the same number of points.

If you’re seriously into the idea of morphing shape and want an extremely powerful tool for helping do it, check out Greensock’s MorphSVG plugin. It allows for a ton of control over how the shape morphs and isn’t limited to same-number-of-points transitions.