SVG and GreenSock for Complex Animation
ForwardJS Summit, San Francisco
Sarah Drasner
Senior UX Engineer at Trulia
This pen.
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!
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!
- People expect mobile to be faster than web
- JankFree.org
- Advanced Performance Audits with DevTools by Paul Irish
- CSS-Tricks Article: Weighing SVG Animation Techniques with Benchmarks
- Above all else: test things yourself!
Fallbacks
Main Principle:
Design everything first, slowly unveil things.
This pen.
Ugly storyboards save you time
Standalone as Illustration
Viewbox Shift with a Sprite
This pen.
Compare to using text with photos to illustrate an article.
8KB Gzipped.
That Whole Animation and SVG was
Make it a Responsive SVG Animation Sprite
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
But Sarah, You Said COMPLEX
we want to do really complex stuff
OK COOL!
But we're going to need
some GSAP.
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)
Solves Other Transform-Origin Issues
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
Here's How to Build Tweens and Timelines
Relative Color Tweening
Relative Color Tweening
function saturation() {
var ch2 = "hsl(+=5%, +=2%, -=10%)",
tl = new TimelineMax({
paused: true
});
tl.add("sated");
tl.to(body, 1, {
backgroundColor:ch2
}, "sated");
tl.from(gauge, 2, {
rotation: "-=100",
transformOrigin: "50% 50%",
ease: Bounce.easeOut
}, "sated");
tl.to(mult, 2, {
fill:ch2
}, "sated");
return tl;
}
Motion Along a Path for Realism
tl.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");
David Walsh Blog article (and series)
Curviness
Autorotate
Scalable Graphic,
Scalable Animation
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
By Blake Bowen
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
];
This pen.
tl.add("begin");
for (var i = 1; i <= 6; i++) {
for (var j = 2; j <= 7; j++) {
tl.to("#t"+i+"-1", 0.5, {
morphSVG: {
shape: "#t"+i+"-"+j
},
y: -(j * 45),
scaleY: 1.4,
rotation: 5,
opacity: 1 - (0.14 * j),
ease: Linear.easeNone
}, "begin+=" + j*0.3);
}
}
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.
//balloon
function balloonGrow(){
var balloon = $("#balloon")[0];
var radius = balloon.getAttribute("r");
var cySet = balloon.getAttribute("cy");
balloon.setAttribute('r', parseInt(radius) + 10);
balloon.setAttribute('cy', parseInt(cySet) - 10);
if (parseInt(radius) > 125) {
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");
...
setTimeout(function(){
balloon.setAttribute("r", "45");
balloon.setAttribute("cy", "233.5");
}, 200);
}
}
//desktop keyboard
$(document).keydown(function(e) {
switch(e.which) {
case 70: // f
//balloon
balloonGrow();
ion.sound.play("hit4");
break;
...
default: return; // exit this handler for other keys
}
e.preventDefault(); // prevent scrolling with arrows
});
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
- I Hate Tomatoes (Petr Tichy)
- Lucas Bebber
- Rachel Smith
- Joni Trythall
- Jake Albaugh
- Blake Bowen
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_edo on twitter
@sdras on codepen
These Slides:
slides.com/sdrasner/svg-greensock
SVG and GreenSock for Complex Animation
By sdrasner
SVG and GreenSock for Complex Animation
Different ways to approach design and development for various types of complex movement with SVG and the GreenSock Animation API
- 11,627