FRONTEND MASTERS
Advanced SVG Animation 3
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
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
];
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
Repo with starters and cheatsheet:
Codepen collection:
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!
Follow me on twitter!
Special thanks to Marc Grabinski and Frontend Masters for inviting me and Jack Doyle and Carl Schooff of GreenSock!
Frontend Masters 3: Advanced SVG Animation
By sdrasner
Frontend Masters 3: Advanced SVG Animation
- 10,303