Animating SVG with CSS and SMIL

Fronteers, Amsterdam, October 10th, 2014

sarasoueidan.com / @SaraSoueidan

Hello!

Freelance front-end web developer and writer.

 

Author & team member @ Codrops

 

 

Animating SVGs

  • Using CSS

  • Using SVG Animations (SMIL)

  • Using Javascript*

*We won't be covering JS in this talk

CSS'ing SVGs

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="300px" height="300px" viewBox="0 0 300 300">

  <polygon

    fill = "#FF931E"

    stroke = "#ED1C24"

    stroke-width = "5"

    points = "279.1,160.8 195.2,193.3 174.4,280.8   117.6,211.1 27.9,218.3 76.7,142.7 42.1,59.6 129.1,82.7 197.4,24.1 202.3,114 "/>

</svg>

Before CSS: SVG Presentation Attributes

star.svg

Shared with CSS

SVG-Only

font, font-family, font-size, font-size-adjust, font-stretch, font-style, font-variant, font-weight, direction, letter-spacing, text-decoration, unincode-bidi, word-spacing, visibility, text-rendering, writing-mode, clip-path, mask-opacity, filter, pointer-events, image-rendering, clip, color, cursor, display, overflow 

clip-rule, flood-color, flood-opacity, stop-opacity, kerning, tech-anchor, color-profile, color-rendering, fill, fill-opacity, fill-rule, marker, marker-end, marker-mid, marker-start, stroke, stroke-width, stop-color, lighting-color, enable-background, dominant-baseline, color-interpolation-filters, color-interpolation, glyph-orientation-horizontal, glyph-orientation-vertical, shape-rendering, baseline-shift, alignment-baseline, stroke-miterlimit, stroke-linejoin, stroke-linecap, stroke-dashoffset, stroke-dasharray, stroke-opacity

In SVG2, more presentation attributes will be added.

Full List

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" style="width: 300px; height: 300px;" viewBox="0 0 300 300">

  <polygon

    style="fill: #FF931E; stroke: #ED1C24; stroke-width: 5;"

    points = "279.1,160.8 195.2,193.3 174.4,280.8   117.6,211.1 27.9,218.3 76.7,142.7 42.1,59.6 129.1,82.7 197.4,24.1 202.3,114 "/>

</svg>

Inline Styles (style="...")

star.svg

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="300px" height="300px" viewBox="0 0 300 300">

    <style type="text/css">

        polygon { fill: ... ;}

    </style>

    <polygon points = "279.1,160.8 195.2,193.3 174.4,280.8   117.6,211.1 27.9,218.3 76.7,142.7 42.1,59.6 129.1,82.7 197.4,24.1 202.3,114 "/>

</svg>

Embedded Styles (<style>) Inside SVG

star.svg

<!DOCTYPE html> <html><head>...</head>
    <body>

        <style type="text/css">

                       svg { width: ...; }

                       polygon { fill: ... ; }

        </style>

        <svg version="1.1" viewBox="0 0 300 300">

            <!--SVG content-->

        </svg>

    </body>

</html>

Embedded Styles (<style>) Outside SVG

star.svg

External Style Sheets

<?xml version="1.0" standalone="no"?>

<?xml-stylesheet type="text/css" href="styles.css"?>

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="300px" height="300px" viewBox="0 0 300 300">

    <!-- SVG Content -->

</svg>

star.svg

Animating SVGs with CSS

Using CSS Transitions

Example: Iconic

Using CSS Animations & Transforms

transform-origin: SVG vs HTML

transform-origin (default value)
HTML Elements (div, ::before, etc.)
SVG Elements (circle, rect, etc.)

50% 50%

(the center of the element itself, calculated relative to its box model)

0 0

(top left corner of the SVG canvas, not of the element itself)

transform-origin: SVG vs HTML

Example: 45deg Rotation

<!DOCTYPE html>

<div style="width: 100px; height: 100px; background-color: orange"> </div>

<svg style="width: 150px; height: 150px; background-color: #eee">

        <rect width="100" height="100" x="25" y="25" fill="orange" />

</svg>

Setting transform-origin in SVG using CSS

  1. Using percentage values: The value is set relative to the element's bounding box, which includes the stroke used to draw its border.
  2. Using absolute values: The origin is set relative to the SVG canvas.

Setting transform-origin in SVG using CSS

Example

<!DOCTYPE html>

<style>

    div, rect { transform-origin: 50% 50%; }

</style>

Setting transform-origin in SVG using CSS

FIREFOX BUG

Setting transform-origin in percentages does not work as expected. Use absolute values instead.

Example Animations Demo

.wheel {

        -webkit-transform-origin: 50% 50%;

        transform-origin: 193px 164px;

        animation:  spin 4s cubic-bezier(.49,.05,.32,1.04) infinite alternate;

}

@keyframes spin {

   50% {

      transform: rotate(360deg);

   }

}

3D Transforms

They work!

But...

Bugs!

 

  1. perspective doesn't currently work in Chrome
  2. 3D transforms on SVG elements are (currently) not hardware-accelerated in Chrome. They have the same performance profile as SVG transform attributes.

Animating SVG with CSS: Current Limitations

Example: Motion Along a Path

Motion Along a Path in CSS

Example: Morphing Shapes

And animating other attributes that simply cannot be set/styled/changed via CSS..

Animating SVG with SMIL

Synchronized Multimedia Integration Language

  • Define an XML-based language that allows authors to write interactive multimedia presentations. 
  • Allow reusing of SMIL syntax and semantics in other XML-based languages, in particular those who need to represent timing and synchronization. For example, SMIL components are used for integrating timing into XHTML and into SVG.

What

Why

  • Declarative (Info)
  • Can animate attributes that CSS can't
  • Animations work when SVG is embedded as <img> or as background image in CSS
  • Event handling & animation synchronization capabilities

Current Browser Support

SMIL animations work in all browsers except IE and Opera Mini

Applying Animations:

SVG/SMIL Animation Elements

<animate> 

Used to animate scalar attributes and properties over a period of time.

<animateTransform>

Used to animate one of SVG's transformation attributes over time, such as the transform attribute.

<animateMotion>

Used to animate/move an element along a motion path.

 

<set>

Used to assign animation values to non-numeric attributes and properties (e.g visibility).

Animation Attributes: 

Naming Animations

<animate id="myAnim" 
         attributeName=" " 
         from=" " 
         to=" " 
         ...
/>
id

*You don't need to specify an animation name for the animation to work.

Animation Attributes: Specifying the target of the animation

<animate xlink:href="#myElement"  
         id="myAnim"
         attributeName=" " 
         from=" " 
         to=" " 
         ...
/>
xlink:href

*The target element must be part of the current SVG document fragment.

Nesting the animation element inside the target
<circle cx="50" cy="50" r="20">
    <animate 
         attributeName=" " 
         from=" " 
         to=" " 
         ... />
</circle>

Animation Attributes:

Specifying the target attribute of the animation

<animate xlink:href="#myCircle"   
         id="myAnim" 
         attributeName="cx" 
         from=" " 
         to=" " 
         ...
/>
attributeName

*Only one attribute name can be specified per animation element.

<animate xlink:href="#myCircle" 
         ID="myAnim"  
         attributeName="cx" 
         attributeType="XML" 
         from=" " 
         to=" " 
         ...
/>
attributeType

*The attributeType can be either "XML", "CSS", or "auto".

Animation Attributes:

Simple animation from one value to another, over a duration of time

<animate id="move" link:href="#myCircle" 
         attributeName="cx" attributeType="XML" 
         from="50" 
         to="450" 
         dur="5s"
/>

CSS Equivalent

animation-name: move; 
animation-duration: 5s;
@keyframes move { 
    from {/* ... */} 
    to { /* ... */ }
}

Animation Attributes:

Specifying the end state of the animation

<animate id="move" xlink:href="#myCircle" 
         attributeName="cx" attributeType="XML" 
         from="50" to="450" 
         dur="5s" 
         fill="freeze"
/>

CSS Equivalent

animation-name: move; 
animation-duration: 5s; 
animation-fill-mode: forwards;
@keyframes move { 
    from {/* ... */} 
    to { /* ... */ }
}

Animation Attributes:

Repeating Animations

<animate id="move" xlink:href="#myCircle" 
         attributeName="cx" attributeType="XML" 
         from="50" to="450" 
         dur="3s" fill="freeze" 
         repeatCount="indefinite" 
         repeatDur="00:30"
/>

CSS Equivalent

animation-name: move; 
animation-duration: 5s; 
animation-fill-mode: forwards; 
animation-iteration-count: infinite; 

@keyframes move { 
    from {/* ... */} 
    to { /* ... */ }
}

Animation Attributes:

Controlling animation begin time

<animate xlink:href="#myCircle" 
         attributeName="cx" attributeType="XML"             
         from="50" to="450" 
         dur="1s" 
         fill="freeze" 
         begin="click"
/>

CSS Equivalent

There is none.

Animation Attributes:

Synchronizing Animations

Examples of values

begin
click 
focus 
click + 2s 
click + 01:30 
click + 01:05:33 
1s 
15min 
indefinite (relies on JS) 
otherAnim.begin 
otherAnim.begin + 1s 
otherAnim.end - 1min 
otherAnim.repeat(1) 
otherAnim.repeat(1) + 5s
<circle id="orange-circle" r="30" cx="50" cy="50" fill="orange" />
<rect id="blue-rectangle" width="50" height="50" x="25" y="200" fill="#0099cc"></rect>
<animate xlink:href="#orange-circle" attributeName="cx" from="50" 

to="450" dur="5s" fill="freeze" begin="click" id="circ-anim"/>
<animate xlink:href="#orange-circle" attributeName="fill" from="orange" 

to="#0099aa" dur="2s" fill="freeze" begin="circ-anim.begin + 5s" 

id="circ-color-anim"/>
<animate xlink:href="#blue-rectangle" attributeName="x" from="50" 

to="425" dur="5s" fill="freeze" begin="circ-anim.begin + 1s" id="rect-anim"/>

Example

Animation Attributes:

Restarting Animations

<animate id="move"
         link:href="#myCircle" 
         attributeName="cx" 
         attributeType="XML"             
         from="50" 
         to="450" 
         dur="1s" 
         fill="freeze" 
         begin="click" 
         restart="whenNotActive"
/>

CSS Equivalent

There is none.

Animation Attributes:

Controlling animation keyframe values and pacing

The CSS Animations Way

@keyframes bounce {
    0% {
        top: 0;
        animation-timing-function: ease-in;
    }
    15% {
        top: 200px;
        animation-timing-function: ease-out;
    }
    30% {
        top: 70px;
        animation-timing-function: ease-in;
    }

    /*...other keyframes...*/

    90% {
        top: 170px;
        animation-timing-function: ease-in;
    }
    100% {
        top: 200px;
        animation-timing-function: ease-out;
    }
}
<animate 
    id="bounce"
    xlink:href="#orange-circle"
    attributeName="cy"
    from="50"
    to="250" 
    dur="3s"
    begin="click" 
    fill="freeze"
    values="50; 250; 120;250; 170; 250; 210; 250"

    keyTimes="0; 0.15; 0.3; 0.45; 0.6;     
              0.75; 0.9; 1"

    keySplines=".42 0 1 1;
                 0 0 .59 1;
                 .42 0 1 1;
                 0 0 .59 1;
                 .42 0 1 1;
                 0 0 .59 1;
                 .42 0 1 1;
                 0 0 .59 1;"
    calcMode="spline" 
/>

The SMIL Animations Way

  • calcMode = linear | discrete | paced | spline
  • keyTimes are fractions, not percentages
  • nb. of keyTimes == nb. of values == nb. of keySplines + 1
  • keySplines use control points coordinates, not the bezier function syntax

Notes

keySplines control points: visual representation

Animation Attributes:

Additive and Accumulative Animations

  • Useful for repeating animations
  • Animation starts relative to current value.
  • Animation proceeds building up on the result of the previous iteration.
additive & accumulate
<animate 
    xlink:href="#orange-circle"
    attributeName="cx"
    from="0"
    to="100" 
    additive="sum"
    accumulate="sum"
    repeatCount="3"
    calcMode="spline"
    keyTimes="0;1"
    keySplines=".42 0 1 1"
    dur="1s"
    begin="click"
    fill="freeze" 
/>
<circle id="orange-circle" r="30" cx="50" cy="50" fill="orange" />
additive & accumulate
additive
accumulate

Animation Attributes:

Explicitly specifying an animation's end time

<animate 
    xlink:href="#orange-circle"
    attributeName="cx"
    from="50"
    to="450" 
    dur="1s"
    begin="click"
    fill="freeze" 
    id="move"
/>
<animate 
    xlink:href="#orange-circle"
    attributeName="fill"
    from="#0099CC"
    to="deepPink" 
    dur="5s"
    repeatCount="indefinite"
    begin="0s"
    end="move.begin"
    fill="freeze"
/>

Animation Attributes:

Specifying animation intervals

<animateTransform 
    xlink:href="#deepPink-rectangle"
    attributeName="transform" 
    attributeType="XML"
    type="rotate"
    from="0 75 75"
    to="360 75 75" 
    dur="2s" 
    fill="freeze"
    begin="click; 5s; 9s; 17s;"
    end="2s; 8s; 15s; 25s;" 
/>

Animation Attributes:

Restricting the entire active duration of an animation with `min` and `max`

<animate> animation example

Shape Tweening

<path fill="#1EB287">

    <animate 

             attributeName="d" 

             dur="1440ms" 

             repeatCount="indefinite"

             keyTimes="0; .0625; .208333333; .3125; .395833333; .645833333; .833333333; 1;"

             calcMode="spline" 

             keySplines="0,0,1,1; .42,0,.58,1; .42,0,1,1; 0,0,.58,1; .42,0,.58,1; .42,0,.58,1; .42,0,.58,1"

             values="M 0,0 

                     C 50,0 50,0 100,0

                     100,50 100,50 100,100

                     50,100 50,100 0,100

                     0,50 0,50 0,0

                     Z;



                     M 0,0 

                     C 50,0 50,0 100,0

                     100,50 100,50 100,100

                     50,100 50,100 0,100

                     0,50 0,50 0,0

                     Z;



                     M 50,0 

                     C 75,25 75,25 100,50 

                     75,75 75,75 50,100

                     25,75 25,75 0,50

                     25,25 25,25 50,0

                     Z;
                     ... "



    />


  </path>

<animateTransform> animation  example

<animateTransform 
           xlink:href="#deepPink-rectangle"
           attributeName="transform" 
           attributeType="XML"
           type="rotate"
           from="0 75 75"
           to="360 75 75" 
           dur="2s"
           begin="0s"
           repeatCount="indefinite"
           fill="freeze" 
/>

<set> animation  example

<set 
   xlink:href="#deepPink-rectangle"     
   attributeName="fill"
   to="#0099AA" 
   begin="click" 
   dur="3s" 
/>

Animations along arbitrary paths: 

<animateMotion>

Specifying a motion path

<animateMotion 
           xlink:href="#circle"
           dur="1s"
           begin="click"
           fill="freeze"
           path="M0,0c3.2-3.4,18.4-0.6,23.4-0.6c5.7,0.1,10.8,0.9,16.3,2.3    c13.5,3.5,26.1,9.6,38.5,16.2c12.3,6.5,21.3,16.8,31.9,25.4c10.8,8.7,21,18.3,31.7,26.9c9.3,7.4,20.9,11.5,31.4,16.7
    c13.7,6.8,26.8,9.7,41.8,9c21.4-1,40.8-3.7,61.3-10.4c10.9-3.5,18.9-11.3,28.5-17.8c5.4-3.7,10.4-6.7,14.8-11.5
    c1.9-2.1,3.7-5.5,6.5-6.5"
/>

OR

<animateMotion xlink:href="#circle" dur="1s" begin="click" fill="freeze">
    <mpath xlink:href="#motionPath" />
</animateMotion>

1

2

<path id="motionPath" d="..." />

1

2

Setting the orientation

without changing orientation

<animateMotion 
    xlink:href="#car"
    dur="3s"
    begin="0s"
    fill="freeze"
    repeatCount="indefinite"
    rotate="auto">
   <mpath xlink:href="#motionPath" />
</animateMotion>
<animateMotion 
    xlink:href="#car"
    dur="3s"
    begin="0s"
    fill="freeze"
    repeatCount="indefinite"
    rotate="auto-reverse">
   <mpath xlink:href="#motionPath" />
</animateMotion>

Setting the orientation

flipping the car with transforms

Controlling the animation distance along the motion path 

Animationing <text> along arbitrary paths

Laying text along a path

<path id="myPath" fill="none" stroke="#000000" stroke-miterlimit="10" d="M91.4,104.2c3.2-3.4,18.4-0.6,23.4-0.6c5.7,0.1,10.8,0.9,16.3,..."/>
<text>
    <textpath xlink:href="#myPath">
      Text laid out along a path.
    </textpath>
</text>

Animating the text offset

<text>
    <textpath xlink:href="#myPath">
      Text laid out along a path.

      <animate attributeName="startOffset" from="0%" to ="100%" begin="0s" dur="5s" repeatCount="indefinite" keyTimes="0;1" calcMode="spline" keySplines="0.1 0.2 .22 1"/>
    </textpath>
</text>

Animatable elements & attributes:

Embedding SVGs

Embedding Technique

CSS Animations

<img src="mySVG.svg" alt=".." />

CSS Interactions

Yes, only if inside <svg>

No

.el {background: url(mySVG.svg);}

No

Yes, only if inside <svg>

<object type="image/svg+xml" data="mySVG.svg"><!--fallback--></object>

Yes, only if inside <svg>

Yes, only if inside <svg>

<embed type="image/svg+xml" src="mySVG.svg" />

Yes, only if inside <svg>

Yes, only if inside <svg>

<iframe src="mySVG.svg"><!--fallback--></iframe>

Yes, only if inside <svg>

Yes, only if inside <svg>

<svg><!--SVG content--></svg>

Yes

Yes

Embedding Technique

SMIL Animations

<img src="mySVG.svg" alt=".." />

SMIL Interactions

Yes

No

.el {background: url(mySVG.svg);}

No

Yes

<object type="image/svg+xml" data="mySVG.svg"><!--fallback--></object>

Yes

Yes

<embed type="image/svg+xml" src="mySVG.svg" />

Yes

Yes

<iframe src="mySVG.svg"><!--fallback--></iframe>

Yes

Yes

<svg><!--SVG content--></svg>

Yes

Yes

Thank You!