FRONTEND MASTERS

Advanced SVG Animation 3

 

October, 2015

Sarah Drasner, Senior UX Engineer at Trulia

 

@sarah_edo : twitter || @sdras : codepen

7

DrawSVG and Motion Along a Path

DrawSVG

Plugin for GSAP, very simple code.

TweenMax.staggerFromTo($draw, 4,{ drawSVG:'0' }, { drawSVG: true }, 0.1);

Under the Hood

Done with stroke-dasharray and stroke-dashoffset

  • Path or shape has a stroke
  • The stroke is dashed
  • Use JS to .getTotalLength()
  • Dasharray the whole length of the shape
  • Animate dashoffset 
@keyframes dash {
  50% {
    stroke-dashoffset: 0;
  }
  100% {
    stroke-dashoffset: -274;
  }
}

Motion Along a Path

This is one of the coolest things about SMIL, but the promise of support has a longer tail with GreenSock.

  • Backwards compatibility and cross browser even IE! 
  • SMIL motion along a path will probably continue to be unsupported in IE, but support for this feature will move into CSS. However, this is down the line. In the meantime, use GSAP for the widest support.
  • Side note: vote for CSS support of motion along a path in Edge here.
TweenMax.to($firefly1, 6, {
bezier: {
  type:"soft", 
  values:[{x:10, y:30}, {x:-30, y:20}, {x:-40, y:10}, 
          {x:30, y:20}, {x:10, y:30}],
  autoRotate:true
},
ease:Linear.easeNone, repeat:-1}, "start+=3");

Motion along a path: Fireflies

Motion Along a Path: Curviness

  • Can just use paths as general coordinates and smooth out the motion between
  • Either set the type parameter to soft
  • Or for more control set the type to thru (this is the default), and define a curviness value. 0 defines no curviness, 1 is normal, 2 is twice as curvy, etc
function displayVals() {
    //get the value from the dropdown
    var singleValues = $("#single").val();
    //the timeline for the changing animation
    tl.to($('.s2'), 1.75, {
      rotation: 360,
      bezier: {
        type: 'thru',
        values: bezier,
        curviness: singleValues
      },
      ease: Power1.easeInOut
    });
  }
  displayVals();

Motion Along a Path:

Rotation

  • Position property 1 (typically "x")
  • Position property 2 (typically "y")
  • Rotational property (typically "rotation", but can also be “rotationX” or “rotationY”)
  • Number of degrees (or radians/Math.PI) to add to the new rotation at the onset (this is optional)
  • Boolean value indicating whether or not the rotational property should be defined in radians rather than degrees (default is false which results in degrees)

Either autoRotate: true OR

 autoRotate: ["x","y","rotation",0,false]

8

Animating Text and Relative Color Values

Split Text

  • Plugin- no dependencies, even on TweenMax
  • Support Back to IE8
  • Breaks into characters, words or lines
  • Honors natural line breaks
  • Option to create auto-incrementing classes, i.e.
.char1, .char2, .char3

Split Text

 var foo = new SplitText("#bar", {type:"words", 
   //optional
   wordsClass:"word"
 });
TweenLite.set(cont, {
  transformPerspective: 600,
  perspective: 300,
  transformStyle: "preserve-3d",
  autoAlpha: 1
});

Set perspective and preserve 3D

(you can also do this in CSS)

var tl = new TimelineLite,
  doSplitText = new SplitText(cont, {
    type: "words, chars"
  }),
  cWords = doSplitText.words,
  cChars = doSplitText.chars,
  numWords = doSplitText.words.length;

Define type of SplitText you need first

tl.add("start");
for (var i = 0; i < numWords; i++) {
  tl.from(cWords[i], 6, {
    z: totesRando(100, 500),
    opacity: 0,
    rotation: totesRando(360, -100),
    ease: Expo.easeOut
  }, "start+=" + Math.random() * 0.3);
}

Tween :)

With a for loop. 

You can even add different points in time to a relative label.

function totesRando(max, min) {
  return Math.floor(Math.random() * (1 + max - min) + min);
}

Helper function

HSL Relative Color

  • Hue
  • Saturation
  • Lightness

How would we turn a many element scene from day to night?

Incrementing

//button hue
function hued() {
  var ch1 = "hsl(+=110%, +=0%, +=0%)", 
  tl = new TimelineMax({
    paused: true
  });

  tl.add("hu");
  tl.to(mult, 1.25, {
      fill: ch1
    }, "hu");
  ...
  tl.to(body, 1.25, {
      backgroundColor: ch1
    }, "hu");

  return tl;
}

var hue = hued();

Putting it all together to tell a story

Exercise 5:

Combine 2 of the effects we just learned to tell a simple story with SVG animation

  • Draggable
  • DrawSVG
  • Motion Along a Path
  • SplitText
  • HSL Tween

9

MorphSVG

Main Principle:

Design everything first, slowly unveil things.

Point from one id to another

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

Convert to Path Data

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

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)

How was this done?

<svg xmlns="http://www.w3.org/2000/svg" width="265" height="400" viewBox="0 0 265 400">
  <defs>
    <filter id="Blur">
      <feGaussianBlur in="SourceGraphic" stdDeviation="9" result="blur"></feGaussianBlur>
      <feColorMatrix in="blur" mode="matrix" 
       values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 19 -7" result="goo"></feColorMatrix>
    </filter>
  </defs>
    
    ...
    
    <path id="f-stable" class="st7" d="M133.5 265.5s-29.5 12-28-60.5 23.5-91.5 23.5-91.5-10 
       23-5.5 52.5 25.5 60.5 10 99.5z" />
    <path id="f1" class="st9" d="M132.5 266.5s-29.5 12-28-60.5-1.2-108.3-1.2-108.3 17.8 39.8 
       22.3 69.3c4.6 29.5 22.4 60.5 6.9 99.5z" />
    <path id="f2" class="st9" d="M127.3 266.3s-24 11.8-22.5-60.7 24.2-93.3 24.2-93.3-7.8 
       25.2-3.3 54.7c4.5 29.5 33.3 64.3 1.6 99.3z" />
</svg>
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);
  }
 

Another Way

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
];

Exercise 6:

Either create an SVG or use one from here:
and create a shape morph.

This can be incorporated into your last pen.

Social Coding Sites Help You Learn

O'Reilly Book:

SVG Animations

coming in 2016

Resources

Thanks!

All pens made by me with the exception of two green and black pens by GreenSock, one by Lucas Bebber, one by Blake Bowen, and one by Anthony Dugois. Thanks also to Amelia Bellamy-Royds, Joni Trythall, Chris Gannon, Val Head, and Sara Soueidan for all their teaching resources. 

 

 

Follow me on codepen! 

@sdras

Follow me on twitter! 

@sarah_edo

Special thanks to Marc Grabinski and Frontend Masters for inviting me and Jack Doyle and Carl Schooff of GreenSock!