FRONTEND MASTERS

Advanced SVG Animation 2

 

October, 2015

Sarah Drasner, Senior UX Engineer at Trulia

 

@sarah_edo : twitter || @sdras : codepen

4

Performance

Timeline Benchmarks

Timeline Benchmarks- Normalized

Visual Benchmarks

More tests soon!

Stay tuned...

+ Workflow:

GreenSock!

The issue with longer CSS animations:

More information in this CSS-Tricks article

Killer workflow features

  • Simple syntax
  • Timelines
  • Nested Timelines
  • Draggable
  • DrawSVG
  • Stagger
  • MorphSVG

Solves cross-browser inconsistencies

Historically bad transform origin bug on rotation in CSS,

now about to be resolved in Firefox.

More in this CSS-Tricks article.

Chrome

IE

Firefox

Safari (zoomed)

More!

Other neat GSAP goodies to come...

But First: GSAP Overview

5

GreenSock Overview

Syntax

TweenLite/TweenMax .to/.from/.fromTo ( variable, seconds { property:amount, ease });

TweenLite.to("div", 2, { 
    scaleY:0.75, scaleX:1.25, y:100, opacity:0.75, ease:Elastic.easeOut 
});

For help with easing: GSAP's Ease Visualizer

Stagger

.css
  %h2 CSS
  - @x = 5
  - @x.times do
    .bar
    
.sass
  %h2 SASS
  - @x = 5
  - @x.times do
    .bar
    
.gsap
  %h2 GSAP
  - @x = 5
  - @x.times do
    .bar

HAML

@keyframes staggerFoo {
  to {
    background: orange;
    transform: rotate(90deg);
 }
}

.css .bar:nth-child(1) { animation: staggerFoo 1s 0.1s ease-out both; }
.css .bar:nth-child(2) { animation: staggerFoo 1s 0.2s ease-out both; }
.css .bar:nth-child(3) { animation: staggerFoo 1s 0.3s ease-out both; }
.css .bar:nth-child(4) { animation: staggerFoo 1s 0.4s ease-out both; }
.css .bar:nth-child(5) { animation: staggerFoo 1s 0.5s ease-out both; }
.css .bar:nth-child(6) { animation: staggerFoo 1s 0.5s ease-out both; }

CSS

@keyframes staggerFoo {
  to {
    background: orange;
    transform: rotate(90deg);
 }
}

@for $i from 1 through 6 {
  .sass .bar:nth-child(#{$i} ) {
      animation: staggerFoo 1s ($i * 0.1s) ease-out both;
   }
 }

SASS

TweenMax.staggerTo(".gsap .bar", 1, {
  backgroundColor: "orange",
  rotation: 90,
  ease: Sine.easeOut
}, 0.1);

GSAP

Stagger

Things like stagger are really complicated to do in CSS animation, and in GSAP are one line of code.

TweenMax.staggerTo(".squares", 2, {
    y:100, backgroundColor:"#4f9d88", ease:Elastic.easeOut
}, 0.05);
TweenMax.staggerTo(".squares", 2, {
    rotation:200, delay:1, scale:0.5, backgroundColor:"#72b165", ease:Elastic.easeOut
}, 0.025);

Cycle Stagger

David Walsh Blog GSAP + SVG Article

Cycle Stagger

tl.staggerFrom(bP, 3, {
  cycle:{
    fill:["white", "yellow", "#e23e0c"],
    opacity:[0.8, 0.2, 0.5, 0.3],
  }, 
  ease:Elastic.easeOut
}, 0.001);

CSS Properties in GSAP

TweenMax.set($(".squares"), {
    css:{transformPerspective:200, perspective:200, transformStyle:"preserve-3d"}
}); 
TweenMax.to($(".squares"), 2.5, {
    css:{rotationX:230, z:-150}, y:180, opacity:0.2, ease:Power2.easeOut
}, "+=0.2");

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

This pen.

Simple Timeline

var tl = new TimelineLite();

//you can add a relative label (optional)
tl.add("start");
//or just add to the timeline without the label
tl.staggerFrom(bP, 3, {
  cycle:{
    fill:["white", "yellow", "#e23e0c"],
    opacity:[0.8, 0.2, 0.5, 0.3],
  }, 
  ease:Elastic.easeOut
}, 0.001);

Nesting Timelines

//set properties needed for animation
TweenMax.set($t1, {
  perspective: 400
});

// the first scene
function sceneOne() {
  var tl = new TimelineMax();

  tl.add("label");
  tl.to(elem, 1, {vars}, "label");

  tl.timeScale(1.2);

  return tl;
}

// Create a master timeline
var master = new TimelineMax({options});
// Add the scene function to the master
master.add(sceneOne(), "labelOnMaster");

//use this while you're working to get to a place in time
//master.seek("labelOnMaster+=2");

No momentary display

//set back in js
TweenMax.set(".foo", {
  visibility: "visible"
});
//set to hide in CSS
.foo { visibility: hidden; }

Position Parameter

var tl = new TimelineLite();
tl.to(".orange", 1, {x:750})
  //this just follows the first
  .to(".green", 1, {x:750})
  //there is a one second gap between these two tweens
  .to(".blue", 1, {x:750}, "+=1")
  //this goes to two seconds in
  .to(".red", 1, {x:750}, "2")
  // add a relative label at this part of the timeline
  .add("newLabel")
  // tween at 3 seconds past the relative label
  .to(".purple", 1, {x:750}, "newlabel+=3");
  

Percentage based transforms on SVG!

get excited.

This pen courtesy of GreenSock.

var playBtn = document.getElementById("play"),
    tl = new TimelineMax({repeat:1, yoyo:true, paused:true});

tl.staggerTo(".box", 0.5, {x:"100%"}, 0.4);

play.onclick = function() {
  tl.restart();
}

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

Exercise 3:

Take an SVG, animate it with GSAP, and make it responsive. Use a timeline and the position parameter or labels.

There are starter pens for reference in the repo:

https://github.com/artemist/frontendmasters-svganimation

6

UI/UX Animation

vs.

Standalone

+ Interaction

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 Animation Rocks

Another good navigation example at Concrete Matter

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.

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 branding means solving the problem for your users.

 

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 reinventing the wheel and communication down the line.

Seems like a lot of work?

Motion Design Language for Projects

  • time
  • experience
  • performance budget
  • color
  • composition
  • user’s time

Standalone SVG as an illustration of text

Very easy to implement. SVG stays a consistent size.

UI/UX Animation with Interaction

Helps with context-shifting.

UI/UX Animation: Context-Shifting

jQuery or Vanilla JS for Interaction

Since jQuery 3.0, we can now do class operations on SVG. You can also do so with vanilla JS.

 $("#close").on("click", function() {

    if ($(this).hasClass("contactOpen")) {
      contactOut.restart();
    } else {
      master.reverse();
      master.timeScale(1.8);
    }

 });

JS Detection for CSS Animation

Hook into 

  • animationstart
  • animationiteration
  • animationend
var lil = $(".lilguy"),
  logIt = $(".logIt"),
  log = $(".term-content");

logIt.on('click', function() {
  lil.addClass('restart');
});

lil.on('animationstart webkitAnimationStart oanimationstart MSAnimationStart', 
function() {
  log.empty();
  log.append("<p>Animation has started.</p>");
});

lil.on('animationiteration webkitAnimationIteration oanimationiteration MSAnimationIteration', 
function() {
  log.append("<p>An iteration fired.");
});

lil.on('animationend webkitAnimationEnd oanimationend MSAnimationEnd', 
function() {
  log.append("<p>Animation has completed.</p>");
  lil.removeClass("restart");
});

Useful functions for Interaction + GSAP Timeline

tl.pause(); // Pause timeline
tl.resume(); // Continue playback
tl.restart(); // Restart the timeline
tl.play(X); // Play from Xs
tl.play(-X); // Play Xs from end
tl.seek(X); // Go to Xs or 'label'
tl.reverse(); // Reverse playback anytime
tl.timeScale(x); // Speed up/slow down timeline
tl.progress(0.5); // Skip to halfway

Also in the repo.

Interaction with Responsive:

build like legos

//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();
});

...

with jQuery UI

with jQuery UI

var master = new TimelineMax();
master.add(sceneOne(), "scene1")
      .add(sceneTwo(), "scene2")
      .add(scThree, "scene3");

//jQueryUI Slider
function updateSlider(){
  $slider.slider("value", scThree.progress() * 100);
}

$slider.slider({
    range: false,
    min: 0,
    max: 100,
    step:.1,
    slide: function ( event, ui ) {
    scThree.pause();
    scThree.progress( ui.value/100 );
  }
});

Draggable

Draggable.create(".box", {type:"x,y", edgeResistance:0.65, 
  bounds:"#container", throwProps:true});
  • Device-enabled for touchscreen
  • Impose bounds- containing units or pixel parameters bounds:{top:100, left:0, width:1000, height:800}
  • Momentum: If you have ThrowPropsPlugin you can set throwProps:true
  • Draggable.hitTest() to sense if elements touch eachother (more on this in a moment)
  • Honors transform-origin
  • Still works on transformed elements
  • Lock movement to an axis lockAxis:true
  • GPU-accelerated and requestAnimationFrame-driven
  • more
Draggable.create(circ, {
  type: "x,y",
  edgeResistance: 0.5,
  bounds: "#container",
  throwProps: true
});

TweenMax.staggerTo(circ, 3, {
  rotation: 360,
  scale: 0.85,
  repeat: -1,
  yoyo: true,
  transformOrigin: "50% 50%",
  ease: Elastic.easeInOut
}, 0.35);
  • onPress
  • onDragStart
  • onDrag
  • onDragEnd
  • onRelease
  • onLockAxis
  • onClick

Rich Callback System and Event Dispatching

Callbacks:

"this" refers to the Draggable instance itself, so you can easily access its "target" or bounds.

If you prefer event listeners, Draggable dispatches events:

 yourDraggable.addEventListener("dragend", yourFunc);
var droppables = $(".box");
var overlapThreshold = "50%"; 

//we'll call onDrop() when a Draggable is dropped on top of one of the "droppables"
function onDrop(dragged, dropped) {
  TweenMax.fromTo(dropped, 0.1, {opacity:1}, {opacity:0, repeat:3, yoyo:true});
}

Draggable.create(droppables, {
  bounds:window,
  onDrag: function(e) {
    var i = droppables.length;
		 while (--i > -1) {
       if (this.hitTest(droppables[i], overlapThreshold)) {
         $(droppables[i]).addClass("highlight");
       } else {
         $(droppables[i]).removeClass("highlight");
       }
    }
  },
  onDragEnd:function(e) {
		var i = droppables.length;
		while (--i > -1) {
			if (this.hitTest(droppables[i], overlapThreshold)) {
				onDrop(this.target, droppables[i]);
			}
		}
	}
});

jQuery 3.0

Draggable to Control a Timeline Interaction

Draggable to Control a Timeline Interaction

TweenMax.set($("#flowers1, #radio, #burst, #magnolia, #flowers2, #starfish, #berries1, #berries2, #berries3, #skulls, #tv, #glitch, #shadow, #lights"), {
  visibility: "visible"
});

// the first scene
function sceneOne() {
  var tl = new TimelineMax();

  tl.add("start");
  tl.staggerFromTo($f1, 0.3, {
    scale: 0
  }, {
    scale: 1,
    ease: Back.easeOut
  }, 0.05, "start");
  ...
  
  return tl;
}

var master = new TimelineMax({paused:true});
master.add(sceneOne(), "scene1");

Draggable to Control a Timeline Interaction

Draggable.create($gear, {
  type: "rotation",
  bounds: {
    minRotation: 0,
    maxRotation: 360
  },
  onDrag: function() {
    master.progress((this.rotation)/360 );
  }
});

Draggable and SVGOrigin for Responsive

TweenMax.set(cow, {
  svgOrigin:"321.05, 323.3", 
  rotation:50
});

what's happening?

Problem with hitTest() for SVG

Extra: clipPath

<defs>
  <ellipse id="SVGID_3_" class="st2" cx="276" cy="147" rx="272" ry="147"/>
</defs>

<clipPath id="SVGID_4_">
  <use xlink:href="#SVGID_3_" />
</clipPath>
.st4 { 
  clip-path: url(#SVGID_4_); 
}

SVG

CSS

Example of output from Illustrator

<defs>
  <clipPath id="clippy">
    <ellipse cx="276" cy="147" rx="272" ry="147"/>
  </clipPath>
</defs>
.img { 
  clip-path: url(#clippy); 
}

SVG

CSS

Better

-webkit-clip-path: ellipse(25% 35% at 50% 36%);
clip-path: ellipse(25% 35% at 50% 36%);

Support in SVG

Support in CSS

Source: MDN

You can animate it

Exercise 4:

Take the your last animation and make it interactive with any of the techniques we just covered.

Frontend Masters 2: Advanced SVG Animaton

By sdrasner

Frontend Masters 2: Advanced SVG Animaton

SVG Animation Performance, GreenSock Overview, Adding Interaction

  • 10,303