Search code examples
svgsvg-animate

reuse and combine SVG path d strings in animate values


I'm animating a path through several d values and am constantly reusing the same shape. In the simplified example below I'm morphing a main triangle M 1,5 L 9,1 9,9 Z into 3 smaller triangles. To do this, I've had to write the d value for the main triangle into the <animate/>'s values attribute 4 times.

<svg width="100" height="100" viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <path fill="black" stroke="none">
    <animate attributeName="d" values="M 1,5 L 9,1 9,9 Z;
                                       M 2,2 L 5,8 8,2 Z;
                                       M 1,5 L 9,1 9,9 Z;
                                       M 7,3 L 3,3 5,7 Z;
                                       M 1,5 L 9,1 9,9 Z;
                                       M 6,4 L 6,6 4,5 Z;
                                       M 1,5 L 9,1 9,9 Z" dur="3s" repeatCount="indefinite" />
  </path>
</svg>

With more complicated animations and shapes the SVG gets large, error-prone, and generally difficult to work with. Ideally I could do something like: <animate values="${a};${b};${a};${c};${a};${d};${a}" ... />. Is there a way to do this without javascript?

I thought I got close with CSS variables and a data URI as the content property of ::before ...but data URIs aren't strings (and what I really want isn't a hack but a "right way" that I missed)


Solution

  • In SVG2, the d attribute has become a presentational attribute and can therefore be set by CSS.
    We can thus use it in css animations, with css-variables.

    But the browser support is still very low (only Blink as of today).

    <svg width="100" height="100" viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
      <style>
    :root {
      --def-A: path("M 1,5 L 9,1 9,9 Z");
      --def-B: path("M 2,2 L 5,8 8,2 Z");
      --def-C: path("M 7,3 L 3,3 5,7 Z");
      --def-D: path("M 6,4 L 6,6 4,5 Z");
    }
    path {
      animation: path-change 3s linear infinite;
    }
    @keyframes path-change {
      0%   { d: var(--def-A); }
      17%  { d: var(--def-B); }
      33%  { d: var(--def-A); }
      50%  { d: var(--def-C); }
      66%  { d: var(--def-A); }
      83%  { d: var(--def-D); }
      100% { d: var(--def-A); }
    }
      </style>
      <path fill="black" stroke="none"></path>
    </svg>

    So given it's just a one time thing, you can also just consider using the replace-all method of your text-editor to make the edition easy.