CSS Animations
Orchestrated
CSS
IS
AWES...
text-overflow: ellipsis;
CSS Animations
What comes to mind?
Difficult to compose/sequence animations easily
David Khourshid
@davidkpiano
Codepen, Github, Twitter
Stages of Learning Music
Wow, this is fun!
Eh, maybe later.
Check it out, eyes closed!
I want to quit.
Time to get serious!
Stages of Learning CSS
Wow, that was easy!
I'll finish the styling later.
Look at all those Codepens!
Screw it, Bootstrap
Time to get serious!
- CSS Tooling: we're getting there
- Preprocessors: use them!
- Take notes
- Keep an open mind
- Relax: code in a thermal bath
Preparation
Principle: Powerful languages inhibit information reuse.
Good practice: Use the least powerful language suitable for expressing information, constraints or programs on the World Wide Web.
Rhythm
Melody
Harmony
Form
Durations & Delays
Effects and Easing Curves
Groups and Sequences
Themes and Storyboarding
Rhythm
- Proportional durations
- Human Processor Model: 230ms
- Match duration to experience
Communicate animation efficiently, yet effectively
Quickly, yet with meaning
$duration-slow: 1s;
$duration-medium: $duration-slow / 2;
// 500ms
$duration-fast: $duration-medium / 2;
// 250ms
Melody
- Single voice
- Pitch, timbre, texture, loudness
- Phrases and dynamics
Melody
Harmony
Melody in
Animation
- Timing functions (easing curves)
- Focal point and meaning
- Entrance, emphasis, exit
// Entrance easing
$easing-enter: cubic-bezier(0, 0, 0.5, 1);
// Emphasis/general easing
$easing: cubic-bezier(0.5, 0, 0.5, 1);
// Exit easing
$easing-exit: cubic-bezier(0.5, 0, 0, 1);
$easing: cubic-bezier(_, 0, _, 1);
Start
End
Harmony
- A collection of simultaneous notes
- Consonance and dissonance
- Gives context to melody
Harmony in
Animation
- Multiple simultaneous effects
- Gives context to focal point
- Consonance and dissonance
- Material Design - Motion & Choreography
body.active {
// Harmonic animations
.heading, .subheading {
animation-duration: $duration-normal;
animation-timing-function: $easing-enter;
animation-fill-mode: both;
}
.heading {
animation-name: slide-up;
}
.subheading {
animation-name: slide-down;
animation-delay: $duration-fast;
}
}
CSS
CONF 2016
Groups and Sequences
Composing Harmonic Animations
Single effect
Multiple keyframes
Multiple effects
Same starting time
Multiple effects
Starting time based on previous starting time
KeyframeEffect()
GroupEffect() *
SequenceEffect() *
Group
Sequence
Sequence
Group
Effects can be nested
This requires math
body.week-selected {
.label.-vacation,
.label.-walk,
.label.-fishing,
.label.-weekend,
.label.-custom {
animation-name: slide-right;
// Same delays, proportional durations
animation-delay: $duration-fast;
animation-duration: $duration-normal;
}
.label.-custom {
animation-duration: $duration-slow;
}
}
Group Effects
$animations: (
// ...
'nose': (
// resting position
(4s, 5s, 7s): rotateY(-4deg),
// nose down
4.5s: rotateY(-4deg)
rotateX(-3deg),
// fox looks left
(7.5s, 9s):
rotateX(-3deg)
rotateY(-28deg)
rotateZ(-11deg),
// fox looks right
(9.5s, 12s): rotateY(7deg),
// fox looks straight ahead
13s: rotateY(0),
),
// ...
);
Sequence Effects
@each $animation-name, $animation in $animations {
// keyframe declaration
@keyframes #{$animation-name} {
@each $offsets, $transform in $animation {
@each $offset in $offsets {
// offset declaration block
#{percentage($offset / $duration)} {
// transform property
transform: #{$transform};
}
}
}
}
}
...what?
from 0%
to 100%
50%
0ms
600ms
1400ms
2400ms
percentage($offset / $duration)
- 0ms / 2400ms = 0%
- 600ms / 2400ms = 25%
- 1400ms / 2400ms = 58.33333333333333333333333333%
- 2400ms / 2400ms = 100%
Total duration
Start delay
Current duration
End delay
- Start delay = sum of past (durations + start delays)
- End delay = total duration - (start delay + current duration)
4s
9s
12s
@keyframes orange {
#{percentage(4s / 12s)} {
transform: translateX(0);
}
#{percentage(9s / 12s)} {
transform: translateX(100px);
}
}
@keyframes orange {
0%, #{percentage(4s / 12s)} {
transform: translateX(0);
}
#{percentage(9s / 12s)} {
transform: translateX(100px);
}
}
@keyframes orange {
0%, #{percentage(4s / 12s)} {
transform: translateX(0);
}
#{percentage(9s / 12s)}, 100% {
transform: translateX(100px);
}
}
animation-delay
is not your friend
animation-delay: 4s .................
animation-delay: 9s ....................................................................................
This will work, with the right fill modes
.box {
animation:
first 2s backwards,
second 4s 4s none,
third 3s 9s forwards;
}
first 2s
second 4s
third 3s
animation-delay
is not your friend
animation-delay: 4s .................
animation-delay: 9s ....................................................................................
first 2s
second 4s
third 3s
Expectation
Reality
Delays do not repeat!
CSS
animation-delay
is not your friend
start-offset: 4s .........................
start-offset: 9s .............................................................................................
first 2s
second 4s
third 3s
$duration: 12s;
@keyframes first-second-third {
from {
// start first animation
}
#{percentage(2s / $duration)},
#{percentage(4s / $duration)} {
// end first animation
// begin second animation
}
// ... continued
// ... continued:
#{percentage(8s / $duration)},
#{percentage(9s / $duration)} {
// end second animation
// begin third animation
}
to {
// end third animation
}
}
animation-delay
is not your friend
start-offset: 4s .........................
start-offset: 9s .............................................................................................
first 2s
second 4s
third 3s
$duration: 12s;
@keyframes first-second-third {
from {
// start first animation
}
#{percentage(2s / $duration)},
#{percentage(4s / $duration)} {
// end first animation
// begin second animation
}
// ... continued
// ... continued:
#{percentage(8s / $duration)},
#{percentage(9s / $duration)} {
// end second animation
// begin third animation
}
to {
// end third animation
}
}
Staggers and Loops
.dot {
animation: up-down 2s $easing both infinite;
@for $i from 1 through 3 {
&:nth-child(#{$i}) {
animation-delay: 0.2s * ($i - 1);
}
}
}
@keyframes up-down {
from, 75%, to {
transform: translateY(0);
}
25%, 50% {
transform: translateY(400%);
}
}
Staggers and Loops
$duration: 2s;
$stagger: 0.2s;
.dot {
@for $i from 1 through 3 {
&:nth-child(#{$i}) {
$delay: percentage(
($stagger * ($i - 1)) / $duration);
animation:
dot-#{$i} $duration $easing infinite;
@keyframes dot-#{$i} {
from, to, 75%{
transform: translateY(0);
}
#{25% + $delay},
50% {
transform: translateY(400%);
}
}
}
}
}
Staggers and Loops
0%
100%
50%
Composition
.square {
animation: rotate 1s linear infinite;
}
.square-container {
animation: slide-left 2s $easing infinite;
}
Guidelines
For CSS Animation
Rhythm
Keep durations short yet meaningful
200ms - 500ms
Choose proportional durations
Use $variables
Melody
Choose natural timing functions (easings)
Accelerate from 0, decelerate to 0
Enter, Emphasis, Exit
cubic-bezier(_, 0, _, 1);
Tweak in @keyframes
Harmony
Identify the focal (main) effect
Avoid superfluous effects
Similar durations, similar easings
Groups, Sequences, Staggers, Loops
Animation-delay is not your friend
Harmony
Start-offset, duration, end-offset
percentage($offset / $duration)
Use @for loops
Use @each loops with $maps
Composition
Always keep transforms together
Independent transforms: use layers (for now)
Separate animations for color, etc.
CSS Animations are Awesome
Experiment & Never Stop Learning
Thank you!
@davidkpiano
CSS Animations, Orchestrated
By David Khourshid
CSS Animations, Orchestrated
- 28,855