with
@RachelNabors
& Alice
devtoolschallenger.com
lightningdesignsystem.com/design/motion
It’strue.rachelthegreat.com
rachelnabors.com/alice-in-
videoland/book
History
GOSSIP
OMG!!!1!
Birth of SMIL, 1999
SMIL
Birth of CSS Anima5ons and Transi5ons, 2009
CSS
Transi-ons
CSS
Anima-ons
CSS
Transi-ons
CSS
Anima-ons
SMIL
Seriously?
Internet Explorer’s Reac5on
One API to Rule Them All
CSS
Transi-ons
CSS
Anima-ons
SMIL
?
Some years later…
?
Web
Anima-ons
API
CSS
Transi-ons
CSS
Anima-ons
SMIL
CSS
Transi-ons
CSS
Anima-ons
SMIL
?
Web
Anima-ons
API
la mort de SMIL, 2015
FirefoxDeveloperEdition
Core
Concepts
The Timing & Anima5on Models
The When The What
Timing
the Cheshire Cat’s 5meline
there
0 seconds
not all
there
8 seconds4 seconds2 61 3 5 7
“Time may be shi2ed backwards
and forwards, scaled, reversed,
paused, and repeated.”
–Web Animations API spec
Stateless Animation
Frame Rate Independent
Direc0on Agnos0c
Seek-able
In-synch no ma<er what
cdpn.io/MwYMJe
Animation
0 seconds 8 seconds4 seconds2 61 3 5 7
there not all
there
the Cheshire Cat’s 5meline
Keyframe Effect
Interac5on Development
Tradi5onal Anima5on
tweenskeyframe keyframe
key keyin-betweens
Do you evenremember me?
KeyframeEffect
Anima-on
Timing Anima-on
Web
Anima-ons
API
CSS
Transi-ons
CSS
Anima-ons
SMIL
Web Anima5ons API underlies both
CSS Anima5ons and Transi5ons
CSS
Animations
& Transitions
WEB
ANIMATIONS API
I’ve a
CSS AnimaCons
& TransiCons course…
rachelnabors.com/
css-animaCons-course
20% off with
LETSANIMATE
KeyframeEffect
Anima-on
KeyframeEffect
Constructor
0% 8 seconds4 seconds2 61 3 5 7
there not all
there
Keyframe Effect
cdpn.io/adVpaX
#rabbit {
transition: transform 3s;
}
#rabbit.interacted {

transform: translateY(100%);

}
cdpn.io/eJyWzm
new KeyframeEffect(
);
whiteRabbit,
[
{ transform: 'translateY(0%)' },
{ transform: 'translateY(100%)' }
],
{
duration: 3000,
fill: 'forwards'
}
var rabbitDownKeyframes = 

cdpn.io/eJyWzm
Familiar Keyframe timing options
duration = transition/animation-duration
delay = transition/animation-delay
fill = animation-fill-mode
iterations = animation-iteration-count
direction = animation-direction
easing = transition/animation-timing-function;
Defaults to linear.
Shiny Keyframe timing options
endDelay Milliseconds to delay aDer the end of an
anima0on.
iterationStart When the itera0on the anima0on should
start.
composite, iteration-composite
Animation
Constructor
var rabbitDownKeyframes = 

new KeyframeEffect(
whiteRabbit,
[
{ transform: 'translateY(0%)' },
{ transform: 'translateY(100%)' }
],
{
duration: 3000,
fill: 'forwards'
}
);
KeyframeEffect
Anima-on
new Animation(rabbitDownKeyframes,
document.timeline);
var rabbitDownAnimation = 

new Animation(rabbitDownKeyframes,
document.timeline);
new Animation(rabbitDownKeyframes,
document.timeline);
Animation Methods
Animation.pause()
Animation.play()
Animation.reverse()
Animation.finish()
Animation.cancel()
whiteRabbit.removeEventListener(
"click", downHeGoes);
function downHeGoes() {
}
whiteRabbit.addEventListener("click",
downHeGoes);
rabbitDownAnimation.play();
cdpn.io/eJyWzm
Fortunately,
we have a shortcut.
animate( )
Method
#rabbit {
transition: transform 3s;
}
#rabbit.interacted {
transform: translateY(100%);
}
#rabbit.interacted {
animation: downHeGoes 3s forwards;
}
@keyframes downHeGoes {
0% {
transform: translateY(0%);
}
100% {
transform: translateY(100%);
}
}
cdpn.io/QyOqqW
#alice {
animation: aliceTumbling infinite 3s linear;
}
@keyframes aliceTumbling {
0% {
color: #000;
transform: rotate(0) …;
}
30% {
color: #431236;
}
100% {
color: #000;
transform: rotate(360deg) …;
}
}
element.animate(
keyframes,
timingOptions
);
var aliceKeyframes = [
{
transform: 'rotate(0) …',
color: '#000'
},
{
color: '#431236',
},
{
transform: 'rotate(360deg) …',
color: '#000'
}
];
offset: 0.3
var aliceTiming = {
duration: 3000,
iterations: Infinity
}
Let’s put these together!
element.animate(
keyframes,
timingOptions
);
document.getElementById("alice").animate(
aliceTumbling,
aliceTiming
);
cdpn.io/rxpmJL
Playback
& Callbacks
Anima-on
Animation Attributes
onfinish promise
oncancel promise
ready promise
playState read-only, use methods
playbackRate more on you later…
effect points to…
KeyframeEffect
Anima-on
Animation Attributes
currentTime loca0on on 0meline
finished callback
goo.gl/Ek7T5h
var aliceChange =
document.getElementById('alice')

.animate(
[
{ transform: 'scale(.5) …' },
{ transform: 'scale(2) …' }
], aliceTimingOptions);
aliceChange.pause();
goo.gl/Ek7T5h
var aliceChange =
document.getElementById('alice')

.animate(
[
{ transform: 'scale(.5) …' },
{ transform: 'scale(2) …' }
], aliceTimingOptions);
aliceChange.pause();
aliceChange.currentTime =
aliceChange.effect.timing.duration / 2;
Setting the
Controls
var shrinkAlice = function() {
aliceChange.playbackRate = -1;
aliceChange.play();
// bottle’s animation
drinking.play();
}
drinking.addEventListener("mousedown", shrinkAlice);
var shrinkAlice = function() {
aliceChange.playbackRate = -1;
aliceChange.play();
// bottle’s animation
drinking.play();
}
drinking.addEventListener("mousedown", shrinkAlice);
var shrinkAlice = function() {
aliceChange.playbackRate = -1;
aliceChange.play();
// bottle’s animation
drinking.play();
}
drinking.addEventListener("mousedown", shrinkAlice);
var shrinkAlice = function() {
aliceChange.playbackRate = -1;
aliceChange.play();
// bottle’s animation
drinking.play();
}
drinking.addEventListener("mousedown", shrinkAlice);
var shrinkAlice = function() {
aliceChange.playbackRate = -1;
aliceChange.play();
// bottle’s animation
drinking.play();
}
drinking.addEventListener("mousedown", shrinkAlice);
var shrinkAlice = function() {
aliceChange.reverse();
// bottle’s animation
drinking.play();
}
drinking.addEventListener("mousedown", shrinkAlice);
goo.gl/Ek7T5h
var growAlice = function() {
aliceChange.playbackRate = 1;
aliceChange.play();
// play cake’s animation
nommingCake.play();
}
cake.addEventListener("mousedown", growAlice);
var stopPlayingAlice = function() {
aliceChange.pause();
nommingCake.pause();
drinking.pause();
};
bottle.addEventListener("mouseup", stopPlayingAlice);
cake.addEventListener("mouseup", stopPlayingAlice);
var stopPlayingAlice = function() {
aliceChange.pause();
nommingCake.pause();
drinking.pause();
};
bottle.addEventListener("mouseup", stopPlayingAlice);
cake.addEventListener("mouseup", stopPlayingAlice);
var stopPlayingAlice = function() {
aliceChange.pause();
nommingCake.pause();
drinking.pause();
};
bottle.addEventListener("mouseup", stopPlayingAlice);
cake.addEventListener("mouseup", stopPlayingAlice);
Game Over:
Two Endings
cdpn.io/bEPdQr
cdpn.io/EPJdJx
cdpn.io/PNYGZQ
// When the cake or runs out...
nommingCake.onfinish = endGame;
drinking.onfinish = endGame;
// ...or Alice reaches the end of her
animation
aliceChange.onfinish = endGame;
// When the cake or runs out...
animation.onfinish = endGame;
drinking.onfinish = endGame;
// ...or Alice reaches the end of her
animation
aliceChange.onfinish = endGame;
// When the cake or runs out...
nommingCake.onfinish = endGame;
drinking.onfinish = endGame;
// ...or Alice reaches the end of her
animation
aliceChange.onfinish = endGame;
var endGame = function() {
var alicePlayhead =
aliceChange.currentTime;
var aliceTimeline =
aliceChange.effect.activeDuration;
var aliceHeight =
alicePlayhead/aliceTimeline;
}
var endGame = function() {
var alicePlayhead =
aliceChange.currentTime;
var aliceTimeline =
aliceChange.effect.activeDuration;
var aliceHeight =
alicePlayhead/aliceTimeline;
}
var endGame = function() {
var alicePlayhead =
aliceChange.currentTime;
var aliceTimeline =
aliceChange.effect.activeDuration;
var aliceHeight =
alicePlayhead/aliceTimeline;
}
var endGame = function() {
var alicePlayhead =
aliceChange.currentTime;
var aliceTimeline =
aliceChange.effect.activeDuration;
var aliceHeight =
alicePlayhead/aliceTimeline;
}
var aliceHeight =
alicePlayhead/aliceTimeline;
if (aliceHeight <= .333){
// Alice got smaller!
…
} else if (aliceHeight >= .666) {
// Alice got bigger!
…
} else {
// Alice didn't change significantly
…
}
var aliceHeight =
alicePlayhead/aliceTimeline;
if (aliceHeight <= .333){
// Alice got smaller!
…
} else if (aliceHeight >= .666) {
// Alice got bigger!
…
} else {
// Alice didn't change significantly
…
}
var endGame = function() {
stopPlayingAlice();
var alicePlayhead = aliceChange.currentTime;
var aliceTimeline = aliceChange.effect.activeDuration;
var aliceHeight = alicePlayhead/aliceTimeline;
if (aliceHeight <= .333){
// Alice got smaller!
…
} else if (aliceHeight >= .666) {
// Alice got bigger!
…
} else {
// Alice didn't change significantly
…
}
}
var endGame = function() {
stopPlayingAlice();
var alicePlayhead = aliceChange.currentTime;
var aliceTimeline = aliceChange.effect.activeDuration;
var aliceHeight = alicePlayhead/aliceTimeline;
if (aliceHeight <= .333){
// Alice got smaller!
…
} else if (aliceHeight >= .666) {
// Alice got bigger!
…
} else {
// Alice didn't change significantly
…
}
}
Bonus:
Randomizing
Animation
cdpn.io/EPJdJx
var getRandomMsRange = function(min, max) {
return Math.random() * (max - min) + min;
}
tears.forEach(function(tear) {
tear.animate(
tearsFalling,
{
delay: getRandomMsRange(-1000, 1000),
duration: getRandomMsRange(2000, 6000),
iterations: Infinity,
easing: "cubic-bezier(0.6, 0.04, 0.98, 0.335)"
});
});
var getRandomMsRange = function(min, max) {
return Math.random() * (max - min) + min;
}
tears.forEach(function(tear) {
tear.animate(
tearsFalling,
{
delay: getRandomMsRange(-1000, 1000),
duration: getRandomMsRange(2000, 6000),
iterations: Infinity,
easing: "cubic-bezier(0.6, 0.04, 0.98, 0.335)"
});
});
var getRandomMsRange = function(min, max) {
return Math.random() * (max - min) + min;
}
tears.forEach(function(tear) {
tear.animate(
tearsFalling,
{
delay: getRandomMsRange(-1000, 1000),
duration: getRandomMsRange(2000, 6000),
iterations: Infinity,
easing: "cubic-bezier(0.6, 0.04, 0.98, 0.335)"
});
});
var getRandomMsRange = function(min, max) {
return Math.random() * (max - min) + min;
}
tears.forEach(function(tear) {
tear.animate(
tearsFalling,
{
delay: getRandomMsRange(-1000, 1000),
duration: getRandomMsRange(2000, 6000),
iterations: Infinity,
easing: "cubic-bezier(0.6, 0.04, 0.98, 0.335)"
});
});
var getRandomMsRange = function(min, max) {
return Math.random() * (max - min) + min;
}
tears.forEach(function(tear) {
tear.animate(
tearsFalling,
{
delay: getRandomMsRange(-1000, 1000),
duration: getRandomMsRange(2000, 6000),
iterations: Infinity,
easing: "cubic-bezier(0.6, 0.04, 0.98, 0.335)"
});
});
Playback
Rate
The Red Queen’s Race
cdpn.io/GZpREz
var redQueen_alice =
redQueen_alice_sprite.animate(
spriteFrames,
{
easing: 'steps(6, end)',
direction: "reverse",
duration: 600,
iterations: Infinity,
playbackRate: 1
});
var redQueen_alice =
redQueen_alice_sprite.animate(
spriteFrames,
{
easing: 'steps(6, end)',
direction: "reverse",
duration: 600,
iterations: Infinity,
playbackRate: 1
});
setInterval( function() {
if (redQueen_alice.playbackRate > .4) {
redQueen_alice.playbackRate *= .9;
}
}, 3000);
setInterval( function() {
if (redQueen_alice.playbackRate > .4) {
redQueen_alice.playbackRate *= .9;
}
}, 3000);
setInterval( function() {
if (redQueen_alice.playbackRate > .4) {
redQueen_alice.playbackRate *= .9;
}
}, 3000);
var goFaster = function() {
redQueen_alice.playbackRate *= 1.1;
}
document.addEventListener("click", goFaster);
document.addEventListener("touchstart", goFaster);
Running to
Stay in Place
cdpn.io/PNGGaV
What’s
Next
github.com/web-animations/web-
animations-js
Support for the Web Anima5ons API
Let’s do this.
Totes.
status.microsoftedge.com
uservoice.microso2edge.com
Spirit.js
ChromeCanary
GroupingandSequencing

goo.gl/1zH64t
Timing Anima-on
Web
Anima-ons
API
CSS
Transi-ons
CSS
Anima-ons
Web Anima5ons API
opens the door to future anima5on specs
?
Ace Folks
Alex Miller
Opal Essence
Chris Mills
Brian Birtles
@RachNabo
RachelNabors.com/waapi
WebAnimaConWeekly.com
slack.AnimaConAtWork.com
Here for all your animaCon needs.

Alice in Web Animations API Land