SVG can do THAT?!
Sarah Drasner
Sr. Developer Advocate at Microsoft
SVG!
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!
This pen.
SVG!
Positioning in CSS got you down?
We can do stuff like this, All fully responsive in every direction
SVG can mean conserving space in your UI until icons are needed
It doesn't neccessarily have to be fully fluid, either. Let's Implement some Responsive Design.
Like legos.
This pen.
//variable declaration for the global repeated animations
const gear = $("#gear1, #gear2, #gear3");
...
//animation that's repeated for all of the sections
function revolve() {
let tl = new TimelineMax();
tl.add("begin");
tl.to(gear, 4, {
transformOrigin: "50% 50%",
rotation: 360,
ease: Linear.easeNone
}, "begin");
...
return tl;
}
const repeat = new TimelineMax({repeat:-1});
repeat.add(revolve());
function paintPanda() {
let 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
const triggerPaint = new TimelineMax({ paused: true });
triggerPaint.add(paintPanda());
//this button kicks off the panda painting timeline
button.addEventListener("click", function() {
triggerPaint.restart();
});
...
We can use this stability for Page Transitions
In React
toggleShape() {
if (this.state.screen === 0) {
this.animFire(this.state.splitText)
} ...
this.setState({
screen: (this.state.screen + 1) % 3
})
}
animFire(splitText) {
const tl = new TimelineMax,
stA = 'start';
TweenMax.set([this.g1.childNodes, this.g2.childNodes], {
clearProps:'svgOrigin'
})
tl.add('start')
tl.staggerFromTo(this.g1.childNodes, dur, {
drawSVG: '68% 100%'
}, {
drawSVG: '27.75% 0%',
ease: Back.easeOut
}, stD, stA)
...
}
In Vue.js with Nuxt
JS Hooks
export default {
transition: {
mode: 'out-in',
css: false,
enter (el, done) {
let tl = new TimelineMax({ onComplete: done }),
spt = new SplitText('h1', {type: 'chars' }),
chars = spt.chars;
TweenMax.set(chars, {
transformPerspective: 600,
perspective: 300,
transformStyle: 'preserve-3d'
})
tl.add('start')
tl.from(el, 0.8, {
scale: 0.9,
transformOrigin: '50% 50%',
ease: Sine.easeOut
}, 'start')
...
tl.timeScale(1.5)
}
...
From Codrops
Position: fixed and morphed into pathdata:id
This pen by Sullivan Nolan
Use it FOR the layout
preserveAspectRatio="none"
SVG can be TINY
Before we get started:
Optimize!
Draggable for Responsive
That whole SVG is 2KB!
What's happening?
What the DOM actually sees
Entire filesize: 6KB!
Loaders!
SVG can make OTHER images smaller!
By Shaw (go follow him)
38kb => 16kb!
Make a Responsive SVG Animation Sprite
Viewbox Shift with a Sprite
This pen.
ViewBox Shift with JavaScript
var shape = document.getElementById("shape");
// 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
Compare to using text with photos to illustrate an article.
8KB Gzipped.
That Whole Animation and SVG was
SVG can be accessible
Title and associative aria tags:
<svg aria-labelledby="title"
id="svg"
role="presentation"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 765 587">
<title id="title"
lang="en">
Icons that illustrate Global Warming Solutions
</title>
Title for elements in the SVG DOM
Role to let the screen reader know whether to traverse
This resource, with support charts.
This article by Heather Migliorisi.
SVG can be styled like text
<svg class="icon" width="24" height="14" alt="Menu">
<use xlink:href="/svg/sprites.svg#icon-hamburger"></use>
</svg>
<!-- the svg -->
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<symbol id="icon-hamburger" viewBox="0 0 24 14">
<g fill="#FFF" fill-rule="evenodd">
<!-- I guess this is the part I need to style..
But I have tried everything..
literally, I tried styling * {fill: red} -->
<path d="M0 0h24v2H0zM0 6h24v2H0zM6 12h18v2H6z"/>
</g>
</symbol>
</svg>
The original question code
The secret: fill needs to be currentColor
SVG can BOUNCE
This pen.
data() {
return {
total: 200,
radius: 15,
}
},
methods: {
incrementHeight() {
this.total += 100
},
incrementRadius() {
this.radius += 1
},
bounceBall() {
this.vy += this.g; // gravity increases the vertical speed
this.x1 += this.vx; // horizontal speed inccrements horizontal position
this.y1 += this.vy; // vertical speed increases vertical position
if (this.y1 > this.total - this.radius) { // if ball hits the ground
this.y1 = this.total - this.radius; // reposition it at the ground
this.vy *= -0.8; // then reverse and reduce its speed
}
},
...
Vue.js
animateBall() {
//use rAF to animate but put a boundary on it so it doesn't run forever
let start,
vueThis = this;
this.running = true;
function step(timestamp) {
if (!start) start = timestamp;
var progress = timestamp - start;
if (progress < 13000) {
vueThis.bounceBall();
vueThis.req = window.requestAnimationFrame(step);
} else {
vueThis.x1 = this.radius;
vueThis.y1 = this.radius;
vueThis.running = false;
}
}
this.req = window.requestAnimationFrame(step);
},
<button @click="animateBall" v-if="!running">Start</button>
SVG can SNAP
ES6 Template Literals
Previously: String Concatenation
var stringie = "Hi, I ordered " + num1 + " " +
product1 + "s and " + num2 + " " + product2 + "s.";
Hi, I ordered 5 apples and 2 oranges.
const stringie = `Hi, I ordered ${num1}
${product1}s and ${num2} ${product2}s.`;
This pen by Anthony Dugois
M two points x,y
Q two points for the bezier handle x,y,
two points x,y
function plotter(points, startX) {
var pathArr = [],
pathpoints = [];
for (i = 0; i <= inc; i++) {
pathpoints.push(points + ((i * (points*2)) + points));
pathArr.push(` ${startX} ${pathpoints[i]}`);
}
return pathArr;
}
rope1.setAttribute("d", `M ${plotter(50, start1)}`.join(" L") );
Plot the points
M xcoord ycoord L xcoord ycoord
SVG can Distort
Distortion Filters
<filter id="turb">
<feTurbulence id="turbwave" type="fractalNoise" baseFrequency="0"
numOctaves="2" result="turbulence_3" data-filterId="3" />
<feDisplacementMap xChannelSelector="R" yChannelSelector="G"
in="SourceGraphic" in2="turbulence_3" scale="40" />
</filter>
function addFilter() {
var all = document.getElementById("all");
all.setAttribute("filter", "url(#turb)");
};
(function addFilterTimed() {
window.setTimeout(addFilter, 5000);
}());
Set the filter on a timer
let turb = document.querySelectorAll('#filter-ripple-2 feImage')[0],
feD = document.querySelectorAll('#filter-ripple-2 feDisplacementMap')[0];
function sceneOne() {
var tl = new TimelineMax();
tl.add('begin');
...
tl.fromTo(feD, 8, {
attr: {
scale: 30
}
}, {
attr: {
scale: 0,
transformOrigin: '50% 50%'
}
}, 'begin+=0.4');
}
Animate the filter
This pen.
This pen by Lucas Bebber (go follow him!)
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);
}
This pen.
SVG can do on-the-fly logo adjustment
SVG can increase KPIs
Revisiting old approaches
Responsive animated Infographic
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 query example element, mobile first */
@media (max-width: 825px) {
container {
width: 100%;
}
}
@media (min-width: 826px) {
.container {
width: 825px;
}
}
You already know this!
SVG can draw itself
Say you have a shape:
The shape has a stroke that is dashed.
But you make the dash REALLY LONG
The dash's offset is an animatable property!
This pen
This pen by Chris Gannon (go follow him!)
You can interact with an SVG
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);
}
createBigCircles() {
const svgNS = this.$refs.figure.namespaceURI;
this.$refs.patterngroup.innerHTML = '';
for (let i = 0; i < this.numLines/2; i++) {
let circ = document.createElementNS(svgNS, 'circle');
this.append(this.$refs.patterngroup, circ);
this.setAttributes(circ, {
'cx': this.size/2,
'cy': this.size/2,
'r': this.totesRando(this.size/2, 0),
'fill': 'none',
'stroke': this.gradients2[this.totesRando(1, 0)],
'stroke-width': 1
});
}
},
<div class="formarea">
<h3>Create Circles:</h3>
<button @click="createSmCircles">Make small circles</button>
<button @click="createBigCircles">Make big circles</button>
</div>
animation() {
let tl = new TimelineMax()
tl.add('begin')
tl.to('line', 2, {
rotation: 360,
repeat: -1,
transformOrigin: '50% 50%',
ease: Sine.easeOut
}, 'begin')
...
return tl;
},
pauseAnim() {
var tl = TimelineLite.exportRoot();
tl.pause(0);
},
<div class="formarea">
<h3>Animation:</h3>
<button @click="animation">Play Animation</button>
<button @click="pauseAnim">Stop Animation</button>
</div>
This pen.
<div id="app" @mousemove="coordinates">
coordinates(e) {
...
this.startArms.progress(1 - (e.clientX / walleCoords)).pause();
}
HTML (Vue Component)
JavaScript
SVG can be clipped and masked
This pen.
<svg class="svg" viewBox="0 0 500 500">
<defs>
<mask
id="mask"
maskunits="userSpaceOnUse"
maskcontentunits="userSpaceOnUse">
<image
xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/giphy%20(24).gif"
width="100%"
height="100%" />
</mask>
...
<rect width="100%" height="100%" fill="url(#checker)" mask="url(#mask)" />
</svg>
This reference pen by Yoksel
My CSS-Tricks article on the differences
This pen by Noel Delgado
SVG can signify something changing
This pen with Vue.js
methods: {
//this formats the hour info without a library
getCurrentHour(zone) {
let newhr = new Date().toLocaleTimeString('en', {
hour: '2-digit',
minute: '2-digit',
hour12: true,
timeZone: zone
})
return newhr
},
...
}
Side note: get rid of big libraries
You can use an SVG's viewBox like a camera
get bbox();
This pen.
This pen.
FLOW CHART
This pen by Andrey Sorokin
SVG can make a GAME
This pen.
How did we use SVG?
Three ways:
- Directly inline in the HTML, for loops
- Background images, for things like taco
- Inline in React
const App = React.createClass({
getInitialState: function() {
return {
score: 500,
startTime: 0,
finalScore: 0
};
},
_startGame: function(setGameToStart) {
this.setState({ isPlaying: setGameToStart,
score: 500,
startTime: Date.now()
});
},
_updateScore: function(scoreDelta) {
let score = Math.min(Math.max(0, this.state.score + scoreDelta), 1270);
this.setState({ score: score });
if (score === 0 || score === 1270) {
this.setState({ isPlaying: false,
finalScore: 10000 - Math.round((Date.now() - this.state.startTime) / 30) });
}
},
...
render() {
return (
<div>
...
<HeartMeter score={this.state.score}/>
...
</div>
)
}
});
const HeartMeter = React.createClass({
render() {
return (
<div>
<svg className="heartmeter" xmlns="http://www.w3.org/2000/svg" width="250" height="50" viewBox="0 0 1741.8 395.6">
<path d="M1741.8 197.7c0 109.3-89 197.8-198.8 197.8a198.6 198.6 0 0 1-158.5-78.4H11.2A11.2 11.2 0 0 1 0 305.9V89.5a11.2 11.2 0 0 1 11.2-11.1h1373.4A198.8 198.8 0 0 1 1543 0c109.8 0 198.8 88.5 198.8 197.7z" fill="#000"/>
<path d="M1591.8 127c-18.3 0-34.1 14.8-41.4 30.3-7.3-15.5-23.1-30.3-41.4-30.3a45.7 45.7 0 0 0-45.7 45.5c0 51.1 51.8 64.5 87.1 115.1 33.4-50.2 87.1-65.6 87.1-115.1a45.7 45.7 0 0 0-45.7-45.5z" fill="#b29968"/>
<rect x="68.2" y="140.8" width={this.props.score} height="101.55" fill="#9391aa"/>
</svg>
</div>
)
}
});
Request Animation Frame
(function getCoords() {
let tCoords = tC1.getBoundingClientRect(),
txCoords = txC1.getBoundingClientRect(),
mCoords = mC1.getBoundingClientRect(),
elCoords = ele1.getBoundingClientRect();
function intersectRect(a, b) {
return Math.max(a.left, b.left + 40) < Math.min(a.right, b.right - 40) &&
Math.max(a.top, b.top + 40) < Math.min(a.bottom, b.bottom - 40);
}
// can't do if/else because sometimes they both come out at once
// and one of them will be ignored
if (intersectRect(tCoords, elCoords)) {
getHitTestIncrease();
}
if (intersectRect(txCoords, elCoords)) {
getHitTestDecrease();
}
if (intersectRect(mCoords, elCoords)) {
getHitTestMargarita();
}
requestAnimationFrame(getCoords);
}());
All this, and we didn't even get to data vis!
O'Reilly Book
SVG Animation
with Val Head
🎉
Thank you!
@sarah_edo on twitter
SVG can do THAT?!
By sdrasner
SVG can do THAT?!
- 275,777