Sr. Developer Advocate at Microsoft
This pen.
SVG can mean conserving space in your UI until icons are needed
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();
});
...
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
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"
What the DOM actually sees
By Shaw (go follow him)
This pen.
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.
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 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 secret: fill needs to be currentColor
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>
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") );
M xcoord ycoord L xcoord ycoord
<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);
}());
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');
}
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.
(one source example, The Whole Brain Group)
This pen.
Change the viewbox in JavaScript like we did before:
/* media query example element, mobile first */
@media (max-width: 825px) {
container {
width: 100%;
}
}
@media (min-width: 826px) {
.container {
width: 825px;
}
}
You already know this!
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!)
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
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
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
This pen.
This pen.
This pen by Andrey Sorokin
This pen.
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);
}());
with Val Head
@sarah_edo on twitter