Designing Complex SVG Animations

 

Sarah Drasner

 Senior UX Engineer at Trulia

 

@sarah_edo : twitter || @sdras : codepen

SVG Summit, January 2016

 

Sarah Drasner

 Senior UX Engineer at Trulia

Why?

  • Animation is powerful to convey meaning
  • Can guide your users
  • Because otherwise we're not using the web to it's full potential
  • FUN!

Animation As Information

  • Animation must be designed
  • Helps with context-shifting
  • Invisible animation- check out Val Head's awesome "All the Right Moves"
  • Comply with branding
  • Animation for a company means having a motion design language, like Google Material Design
  • Consistent on mobile, or a fallback to a light version
  • Don't overdo it

UI/UX Animation

vs.

Standalone

UI/UX Animation

Pieces of the UI move to aid in an informative UX choreography. This can typically be done with well placed CSS, some JS to trigger interactions. Responsive can be achieved with good CSS media queries.

This pen made with Sara Soueidan's circulus.

UI/UX Animation: Context-Shifting

From this CSS-Tricks Article

Another good navigation example at Concrete Matter

This pen.

UI/UX Animation: Context-Shifting

Standalone

Used to illustrate concepts in the body of the page, either alongside or on it's own. Most of the basis of this talk is complex standalone animation. JavaScript is recommended for much longer implementations (explained later).

This pen.

If animation just feels like the sugar on top, that's because you treated it that way.

Motion on the Web:

Not one size fits all

Not one size fits all

Adhering to your branding means motion solves the problem for your users.

 

Questions to Ask:

  • Responsive?
  • Toggle on/off?
  • Easing structures
  • User flow
  • What happens EVERY TIME

Easing

Easing

Motion Design Language for Projects

  • Consider your tooling- loss leader- it was a lot of work at first that paid off along the way
  • Reuse resources like easing functions, animation keyframes in mixins, extends, variables, you name it!
  • If mobile is decoupled- easier to keep it consistent across both
  • Good animation branding guidelines means less communication down the line and reinventing the wheel.

Seems like a lot of work?

Main Principle:

Design everything first, slowly unveil things.

This pen.

Ugly storyboards save you time!

SVG!

  • Crisp on any display
  • Less HTTP requests to handle
  • Easily scalable for responsive
  • Small filesize if you design for performance
  • Easy to animate
  • Easy to make accessible
  • Fun! (there's a pattern here)

SVG!

This pen.

Before we get started:

Optimize!

Before we get started:

Animation Performance!

SVG Sprites

Start with this technique from Joe Harrison

Make it a Responsive SVG Animation Sprite

This pen.

Compare to using text with photos to illustrate an article.

8KB Gzipped.

That Whole Animation and SVG was

CSS Animation

[class^="star"] {
  animation: blink 2s ease-in-out infinite both;
}

[class^="dot"] {
  animation: blink 5s -3s ease-in-out infinite both;
}

@keyframes blink {
  50% { opacity: 0; }
}

No width and height for the SVG itself, instead define it in CSS

.initial { 
  width: 50%;
  float: left;
  margin: 0 7% 0 0;
}

We're using percentage here, but we could also use flexbox.

viewBox="0 0 490 474" preserveAspectRatio="xMidYMid meet"

Define smaller viewbox, put in preserveAspectRatio (though this is also the default)

Animation MEDIA QUERIES

+ Adjust initial object, affects animation

[class^="mountain"], [class^="grass"] {
  ...
  transform: skew(1.5deg);
}

@media screen and ( min-width: 500px ) {
  [class^="mountain"], [class^="grass"] { 
    transform: skew(2deg);
  }
}

!Important part

Animation MEDIA QUERIES

!Important part

You can do this. You know it already :)

ViewBox Shift with JavaScript

var shape = document.getElementById("svg");

// media query event handler
if (matchMedia) {
        var mq = window.matchMedia("(min-width: 500px)");
        mq.addListener(WidthChange);
        WidthChange(mq);
}
// media query change
function WidthChange(mq) {
        if (mq.matches) {
            shape.setAttribute("viewBox", "0 0 490 474");
        }
        else {
            shape.setAttribute("viewBox", "0 490 500 500");
        }
};

Acts like a window to show and hide the requisite parts of the sprite

Fallbacks

Options:

  • Fallback w/ modernizr
  • Fallback to a png equivalent
  • Fallback with the picture element (+ polyfill)
    Sara Soueidan's article
  • Recommended: look at your analytics

Drawing for Animation:

Sprites

3 of 21 frames.

Shooting on twos.

This pen.

Large Sprite and animate the background position.

Keep it simple.

@keyframes splashit {
        100% { background-position: 0 -3046px; }
}

.splash {
  background: url(‘splash-sprite2.svg’);
  animation: splashit 1.8s steps(21) infinite;
}

/* fallback */
.no-svg .splash {
  background: url(‘splash-sprite2.png’);
}

2 ways to make this.

Illustrator, with a template:

  • Object → Path → Split Into Grid
  • View → Guides → Make Guides
  • Draw and then copy paste and align, change drawing slightly
  • Can also easily export png fallback

Works with Sketch, too.

2 ways to make this.

In an SVG editor with Grunticon

  • Draw drawing in SVG editor, save off one by one
  • Can also draw on paper and scan
  • Use grunticon
  • It makes the fallback for you

Take the steps() out

The background rolls through...

Let's use this to our advantage.

/*--extend--*/
.area {
  width: 600px;
  height: 348px;
}

.fore, .mid, .bk, .container { @extend .area; }

Extend to keep it DRY

.fore {
  background: url(‘fore.svg’);
  animation: bk 7s -5s linear infinite;
}

.mid {
  background: url(‘mid.svg’);
  animation: bk 15s -5s linear infinite;
}

.bk {
  background: url(‘bkwalk2.svg’);
  animation: bk 20s -5s linear infinite;
}

@keyframes bk {
  100% { background-position: 200% 0; }
}

Z-index for parallax, consistent bk position, 

different length of animation in seconds.

Standalone as Illustration

Standalone SVG as an Illustration of Text

Very easy to implement. SVG stays a consistent size.

But Sarah, You Said COMPLEX

we want to do really complex stuff

OK COOL!

But we're going to use some JavaScript. GSAP, to be exact.

here's why...

 

(I don't work for them and they don't pay me.)

Solves Cross-Browser Inconsistencies

Bad transform origin bug on rotation, soon to be solved in Firefox.

More in this CSS-Tricks article.

Chrome

IE

Firefox

Safari (zoomed)

Timeline

  • stack tweens
  • set them a little before and after one another
  • change their placement in time
  • group them into scenes
  • add relative labels
  • animate the scenes!
  • make the whole thing faster, move the placement of the whole scene, nesting

All without recalculation!

The issue with longer CSS animations:

This pen.

Other Cool Things

for Complex Animation

  • Motion along a path (widest support)
  • Draggable
  • CSS Properties
  • Draw SVG- make an SVG look like it draws itself.
  • MorphSVG
  • Relative Color Tweening
  • CycleStagger

Relative Color Tweening

Relative Color Tweening

Motion Along a Path for Realism

Percentage-Based Transforms on SVG!

get excited.

This pen courtesy of GreenSock.

We can do stuff like this, All fully responsive in every direction

It doesn't neccessarily have to be fully fluid, either. Let's Implement some Responsive Design.

Designing Interaction in Responsive Animations

Like legos.

This pen.

//variable declaration for the global repeated animations
var gear = $("#gear1, #gear2, #gear3");
...

//animation that's repeated for all of the sections
function revolve() {
  var tl = new TimelineMax();

  tl.add("begin");
  tl.to(gear, 4, {
      transformOrigin: "50% 50%",
      rotation: 360,
      ease: Linear.easeNone
    }, "begin");
    ...

  return tl;
}

var repeat = new TimelineMax({repeat:-1});
repeat.add(revolve());
function paintPanda() {
  var tl = new TimelineMax();

  tl.to(lh, 1, {
    scaleY: 1.2,
    rotation: -5,
    transformOrigin: "50% 0",
    ease: Circ.easeOut
  }, "paintIt+=1");
  ...

  return tl;
}

//create a timeline but initially pause it so that we can control it via click
var triggerPaint = new TimelineMax({
  paused: true
});
triggerPaint.add(paintPanda());

//this button kicks off the panda painting timeline
$("#button").on("click", function(e) {
  e.preventDefault();
  triggerPaint.restart();
});

...

Atmosphere

Elemental Motion

  • Further away is less contrast, blurry
  • Does the air or environment effect movement
  • Reducing precision allows for understanding

Putting Techniques Together

MorphSVG from GSAP

  • Tween paths to paths
  • Tween shapes to paths
  • Make animation magic

Point from one id to another

TweenMax.to("#start", 1, {morphSVG:{shape:"#end"}, 
   ease:Linear.easeNone});

Use shapeIndex

TweenMax.to("#start", 1, {morphSVG:{shape:"#end", 
   shapeIndex:"1"}});
  • Default is shapeIndex: "auto"
  • Load the extra plugin, and a GUI will come up
  • Usually auto will be correct, but you can pick
  • Use findShapeIndex(#start, #end)
MorphSVGPlugin.convertToPath("ellipse");

function flame() {
  var tl = new TimelineMax();

  tl.add("begin");
  tl.fromTo(blurNode, 2.5, {
    attr: {
      stdDeviation: 9
    }
  }, {
    attr: {
      stdDeviation: 3
    }
  }, "begin");
  var num = 9;
  for (var i = 1; i <= num; i++) {
    tl.to(fStable, 1, {
      morphSVG: {
        shape: "#f" + i
      },
      opacity: ((Math.random() * 0.7) + 0.7),
      ease: Linear.easeNone
    }, "begin+=" + i);
  }
 

More than one way of working

function solve(data) {

  var size = data.length;
  var last = size - 4;    

  var path = "M" + [data[0], data[1]];

  for (var i = 0; i < size - 2; i +=2) {

    var x0 = i ? data[i - 2] : data[0];
    var y0 = i ? data[i - 1] : data[1];

    var x1 = data[i + 0];
    var y1 = data[i + 1];

    var x2 = data[i + 2];
    var y2 = data[i + 3];

    var x3 = i !== last ? data[i + 4] : x2;
    var y3 = i !== last ? data[i + 5] : y2;

    var cp1x = (-x0 + 6 * x1 + x2) / 6;
    var cp1y = (-y0 + 6 * y1 + y2) / 6;

    var cp2x = (x1 + 6 * x2 - x3) / 6;
    var cp2y = (y1 + 6 * y2 - y3) / 6;
   
    path += "C" + [cp1x, cp1y, cp2x, cp2y, x2, y2];
  } 

  return path;
}

Catmull-Rom Spline

Article about history in computer science.

var poly = document.querySelector("polyline");
var path = document.querySelector("path");

var points = [
  100,350,  
  200,100,
  300,350,
  400,150,
  500,350,
  600,200,
  700,350
];

poly.setAttribute("points", points);
path.setAttribute("d", solve(points));
var points = [
  100,350,  
  200,150,
  300,350,
  400,120,
  500,350,
  600,180,
  700,350
];
var points = [
  100,350,  
  200,100,
  300,350,
  400,150,
  500,350,
  600,200,
  700,350
];

Design + Information + Animation

Revisiting old approaches

Responsive animated Infographic

Three Sources with Detailed Analysis Showing Lead Improvements

Conversion

(one source example, The Whole Brain Group)

  • increased traffic to their website by over 400%
  • lead increase by almost 4500%
  • the number of new visitors to their site to almost 78%

Problems

  • Not responsive- tipping point: Tim Kadlec
  • Not updated to current context
  • ^ Especially design

All posts older than 2 years.

What Happened?

This pen.

Change the viewbox in JavaScript like we did before:

Responsive:

Responsive:

Media queries for layout, and fallback with Modernizr:

/* media query example element, mobile first */
@media (max-width: 825px) {
  container {
    width: 100%;
  }
}
@media (min-width: 826px) {
  .container {
    width: 825px;
  }
}

/* fallback */
.inlinesvg .fallback {
  display: none;
}

.no-inlinesvg .fallback { 
  width: 500px;
  height: 500px;
  display: block;
}

Accessibility

Title and associative aria tags: (WIP)

<svg aria-labelledby="title" id="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 765 587">
<title id="title" lang="en">Circle of icons that illustrate Global Warming Solutions</title>

You can also add a title for elements in the SVG DOM

This resource, with support charts.

Also, this article by Dudley Storey.

Last Fun Thing: 

Interactivity!

This pen.

SVG is really good at SHAPES

//balloon
function balloonGrow(){
  var balloon = $("#balloon");
  var radius = balloon.getAttribute("r");
  var cySet = balloon.getAttribute("cy");
  balloon.setAttribute('r', parseInt(radius) + 10);
  balloon.setAttribute('cy', parseInt(cySet) - 10);
  //all of the poppingness
  if (parseInt(radius) > 125) {
    //play the sound for the popping
    ion.sound.play("balloon-pop3");
    var balloonTl = new TimelineMax();
    balloonTl.add("pop");
    balloonTl.to("#balloon", 0.05, {
      scale: 5,
      opacity: 0,
      transformOrigin: "50% 50%",
      ease: Circ.easeOut
    }, "pop");
    ... //wait and then put it back
    setTimeout(function(){
      balloon.setAttribute("r", "45");
      balloon.setAttribute("cy", "233.5");
    }, 200);
  }
}

Social Coding Sites Help You Learn

Fun. Remember fun?

(I don't work for them and they don't pay me)

  • Codepen
  • JS Fiddle
  • Dabblet

People You Should Know About

  • Val Head
  • Sara Soueidan
  • Rachel Nabors
  • Tiffany Rayside
  • Chris Gannon
  • CJ Gammon
  • LegoMushroom
  • Ana Tudor
  • David Walsh Blog
  • Gregor Adams
  • Diaco ML
  • Amelia Bellamy-Royds
  • Taylor Hunt
  • Dudley Storey
  • GreenSock
  • Blake Bowen
  • I Hate Tomatoes (Petr Tichy)
  • Lucas Bebber
  • Rachel Smith
  • Joni Trythall
  • Jake Albaugh
  • Louis Hoegbregts

More!

SVG Immersion Podcast and Web Animation Weekly

More Technical Information:

Frontend Masters Course

Advanced SVG Animation

O'Reilly Book

SVG Animation

Thank You!

Sarah Drasner

@sarah_edo on twitter

@sdras on codepen

These Slides:

slides.com/sdrasner/svg-summit