KEMBAR78
Randomising css animations | PDF
RANDOMISING CSS
ANIMATIONS
ADAM BANKIN
Front End Engineer at Shutterstock
14 years experience with HTML, CSS and JS
AUSTRALIAN
(z === "zed"); // true
"ize".replace(/z/, "s");
(r === "ahhhhhh"); // true
$("#shrimp").on("barbie", function () {
console.error("racial stereotype");
console.error("and frankly, I'm offended");
return false;
});
RANDOMISING CSS ANIMATIONS
Why? Is this difficult?
Shutterstock labs home page
THE APPROACH
Respect the separation of concerns
Minimal interaction with the DOM
Keep it "jank"-free
DAFT JANK
Addy Osmani's post on jank busting
SEPARATION OF CONCERNS
Don't write HTML Elements with JavaScript
Use CSS to change appearance
Use JavaScript for the rest
THE APPROACH (AGAIN)
Respect the separation of concerns
Minimal interaction with the DOM
Keep it "jank"-free
Can we achieve randomised CSS animations with this
approach?
HTML
Keep it simple
PREDEFINE YOUR DOM ELEMENTS
<div id="bubble_source">
<div id="bubble_0" class="bubble"></div>
<div id="bubble_1" class="bubble"></div>
<div id="bubble_2" class="bubble"></div>
<div id="bubble_3" class="bubble"></div>
<div id="bubble_4" class="bubble"></div>
<div id="bubble_5" class="bubble"></div>
<div id="bubble_6" class="bubble"></div>
…
<div id="bubble_15" class="bubble"></div>
</div>
HTML… Done
CSS
The syntax of CSS Keyframe Animations
AT-RULE AND NAME
@keyframes myAnimationName {...}
… WITH PREFIXES
@-webkit-keyframes myAnimationName {...}
@-moz-keyframes myAnimationName {...}
@keyframes myAnimationName {...}
WITH KEYFRAMES
@keyframes myAnimationName {
from {
background-position: 0 0;
}
to {
background-position: -162px 0;
}
}
MDN has a list of animatable properties
CHOOSE THE RIGHT PROPERTIES TO ANIMATE
top/left | translate
Paul Irish's post on using translate instead of top/left
WITH PERCENTAGE-BASED KEYFRAMES
@keyframes myAnimationName {
0% {
background-position: 0 0;
}
100% {
background-position: -162px 0;
}
}
MIXED VALUES IN KEYFRAMES
@keyframes myAnimationName {
0% {
background-position: 0 0;
}
23% {
opacity: 0;
background-position: -162px 0;
}
100% {
opacity: 1;
}
}
TARGETING PREFIXED PROPERTIES
@-webkit-keyframes myAnimationName {
0% {-webkit-transform-origin: 0 0;}
100% {-webkit-transform-origin: 25px 0;}
}
@-moz-keyframes myAnimationName {
0% {-moz-transform-origin: 0 0;}
100% {-moz-transform-origin: 25px 0;}
}
@keyframes myAnimationName {
0% {transform-origin: 0 0;}
100% {transform-origin: 25px 0;}
}
ATTACHING A DEFINED KEYFRAME ANIMATION
.classWithAnimation {
animation: myAnimationName 1.2s linear 0s infinite;
}
... WITH PREFIXES
.classWithAnimation {
-webkit-animation: myAnimationName 1.2s linear 0s infinite;
-moz-animation: myAnimationName 1.2s linear 0s infinite;
animation: myAnimationName 1.2s linear 0s infinite;
}
SHORTHAND PROPERTIES EXPANDED
/* shorthand */
.bubble {
animation: myAnimationName 1.2s linear 0s infinite;
}
/* expanded */
.bubble {
animation-name: myAnimationName;
animation-duration: 1.2s;
animation-timing-function: linear;
animation-delay: 0s;
animation-iteration-count: infinite;
}
and that's just some of them
… at MDNthere are more
*AHEM*… PREFIXED
/* let's now call it 'bubble' */
.bubble {
-webkit-animation-name: myAnimationName;
-webkit-animation-duration: 1.2s;
-webkit-animation-timing-function: linear;
-webkit-animation-delay: 0s;
-webkit-animation-iteration-count: infinite;
}
ARE WE THERE YET?
CSS Keyframe Animations, they:
define a start
… the inbetween frames
… and the end?
THE END IS NOT THE END
"omit the to or 100% declaration from the
@keyframe…"
"then you call the animation on the progress
bar…"
"and just like that, the progress bar will
animate itself up to the value set by the inline
style."
Chris Coyer's post on animating to an inline style
"omit the to or 100% declaration from the
@keyframe…"
@keyframes myAnimationName {
0% {background-position: 0 0;}
}
"…then you call the animation on the progress
bar…"
.bubble {
animation: myAnimationName 1.2s linear 0s infinite;
}
"…and just like that, the progress bar will
animate itself up to the value set by the inline
style."
<div id="bubble_0" class="bubble" style="background-position: -162px 0;"></div>
DOES IT NEED TO BE INLINE?
What if the properties were set in a class ruleset?
/* css */
@keyframes myAnimationName {
0% {background-position: 0 0;}
}
.bubble {
animation: myAnimationName 1.2s linear 0s infinite;
}
.background-move {
background-position: -162px 0;
}
Then the class was dynamically applied to the DOM Element?
<!-- html -->
<div id="bubble_0" class="bubble background-move"></div>
RULESET AS INLINE STYLE
/* Keyframe Animation definitions */
@-webkit-keyframes lightBlue {
0% {
color: #2ed0e5;
-webkit-transform: translate(0, 50px) scale(.2, .2);
-webkit-transform-origin: 20px 0;
}
14% {
color: #2ed0e5;
-webkit-transform: translate(0, -100px) scale(.3, .3);
-webkit-transform-origin: 43px 0;
}
}
@-moz-keyframes lightBlue {
0% {
color: #2ed0e5;
-moz-transform: translate(0, 50px) scale(.2, .2);
-moz-transform-origin: 20px 0;
}
14% {
color: #2ed0e5;
-moz-transform: translate(0, -100px) scale(.3, .3);
-moz-transform-origin: 43px 0;
}
}
@keyframes lightBlue {
0% {
color: #2ed0e5;
JAVASCRIPT
Make it work
WHAT DOES JAVASCRIPT GIVE US?
requestAnimationFrame/cancelAnimationFrame methods
Event-type "animationend"
… WITH PREFIXES
requestAnimationFrame, msRequestAnimationFrame,
mozRequestAnimationFrame,
webkitRequestAnimationFrame, oRequestAnimationFrame
animationend, MSAnimationEnd, webkitAnimationEnd,
oAnimationEnd
CLASSY JAVASCRIPT
var ANIMATION_CLASSES = [
// colors
['color_dark_red', 'color_red', 'color_yellow', 'color_green', 'col
// translations
['translateA', 'translateB', 'translateC', 'translateD', 'translate
// origins
['originA', 'originB', 'originC', 'originD', 'originE', 'originF'],
// durations
['time_1500', 'time_1800', 'time_2000', 'time_2500', 'time_2700'],
// easings
['easing_1', 'easing_2', 'easing_3'],
// names
['light_blue', 'mid_blue']
];
GAME ON
When the page loads we:
1. Load custom classes "Bubble" and "BubbleController"
2. BubbleController puts all ".bubble" divs into an Array
3. It then makes two 'object pools'
4. One has "Bubble" instances, the other is ids from each div in
the Array
OBJECT POOLS ON HTML5ROCKS
Colt McAnlis's post on Object Pools in JS
GAME ON…
Then:
1. BubbleController runs a loop creating setTimeouts at
+200ms
2. The interval calls BubbleController's 'createBubble' method
3. This method pops a Bubble instance from its pool
4. It also pops an id from the id pool
5. It creates a listener for completion of the Bubble's 'init'
method
6. It creates a listener for completion of the Bubble's 'hide'
method
7. It runs the Bubble's 'init' method, with the id property
OBJECT POOL CODE IN BUBBLECONTROLLER
// `bubbles` is an Array of Dom Elements
// `bubbleClass` is Bubble
function createObjectPools (bubbles, bubbleClass) {
var j = bubbles.length;
var bubbleArgs = { delegateName: NAME };
while (j--) {
// populate the individual bubble's ids into an Array
idPool[j] = bubbles[j].id;
// use an Object Pool to create enough instances to control each bubble
bubblePool[j] = new bubbleClass(bubbleArgs);
}
}
'CREATEBUBBLE' METHOD IN
BUBBLECONTROLLER
function createBubble () {
// get new bubble instance id form the pools
var bubble = bubblePool.pop();
var id = idPool.pop();
// listen for the end of the new bubble's init phase
$.subscribe(NAME + EVENTS.INIT + STATUS.COMPLETE + id, bubbleInitCompleteHandle
// call init on the bubble instance
bubble.init({ id: id });
}
GAME ON… ALMOST THERE
Finally, the Bubble's 'init' method:
1. The Bubble instance gets the DOM Element via the id
2. Bubble creates a listener for the 'animationend' event
3. It then gets a random class from each sub-Array in
ANIMATION_CLASSES
4. It saves these as a String, but doesn't set its className
5. It fires off its 'init complete' Event
6. BubbleController reacts to this and calls
requestAnimationFrame.
7. requestAnimationFrame calls the Bubble instances 'show'
method
8. The 'show' method attaches the classlist, starting the
animation
'INIT' METHOD IN BUBBLE
Bubble.prototype.init = function (args) {
// add inited variables
this.id = args.id;
this.node = doc.getElementById(args.id);
this.$node = $(this.node);
this.classList = 'bubble on';
// add listeners
addListeners(this);
setClassList(this);
$.publish(this.delegateName + EVENTS.INIT + STATUS.COMPLETE + this.id, [this]);
};
RANDOMISING THROUGH THE
ANIMATION_CLASSES ARRAY
function setClassList (instance) {
var j = ANIMATION_CLASSES.length;
while (j--) {
instance.classList += (' ' + randomValue(ANIMATION_CLASSES[j]));
}
}
function randomValue (arr) {
var length = arr.length;
var idx = (Math.random() * length) | 0;
return arr[idx];
}
CALLING REQUESTANIMATIONFRAME IN
BUBBLECONTROLLER
function bubbleInitCompleteHandler (e, bubble) {
$.unsubscribe(e.type, bubbleInitCompleteHandler);
win.requestAnimationFrame(function () {
bubble.show();
});
}
'SHOW' METHOD IN BUBBLE
Bubble.prototype.show = function () {
// appending the classes makes the bubble appear and the animation run
this.node.className = this.classList;
};
GAME OFF
'animationend' event
1. The Bubble reacts to the DOM Element's 'animationend'
event
2. It changes the className of the DOM Element to "splat"
3. It runs its 'hide' method and fires off 'hide complete'
4. Inside the 'hide complete' Event is the id as a String
5. BubbleController reacts to this
6. It 'pools' the Bubble instance and 'pools' the id
7. It then runs its 'createBubble' method to start all over again
RE-'POOLING' THE BUBBLE AND ID IN
BUBBLECONTROLLER
function bubbleAnimationCompleteHandler (e, bubble, id) {
// add the completed bubble and its id back into the pools
bubblePool.unshift(bubble);
idPool.unshift(id);
createBubble();
}
SO!!!
Did it work?
Labs home page
TIME ESCAPES ME
The bubbles are done with :before and :after
They're webfonts - that's a story in itself
You can pause the bubbles
'animation-timing-function' easing is done with cubic-
beziers
THEY'RE CLICKABLE!
THANKS!
abankin@shutterstock.com
@adambankin
DID I MENTION WE'RE
HIRING?!

Randomising css animations

  • 1.
  • 2.
    ADAM BANKIN Front EndEngineer at Shutterstock 14 years experience with HTML, CSS and JS
  • 3.
    AUSTRALIAN (z === "zed");// true "ize".replace(/z/, "s"); (r === "ahhhhhh"); // true $("#shrimp").on("barbie", function () { console.error("racial stereotype"); console.error("and frankly, I'm offended"); return false; });
  • 4.
  • 6.
  • 7.
    THE APPROACH Respect theseparation of concerns Minimal interaction with the DOM Keep it "jank"-free
  • 8.
    DAFT JANK Addy Osmani'spost on jank busting
  • 9.
    SEPARATION OF CONCERNS Don'twrite HTML Elements with JavaScript Use CSS to change appearance Use JavaScript for the rest
  • 11.
    THE APPROACH (AGAIN) Respectthe separation of concerns Minimal interaction with the DOM Keep it "jank"-free Can we achieve randomised CSS animations with this approach?
  • 12.
  • 13.
    PREDEFINE YOUR DOMELEMENTS <div id="bubble_source"> <div id="bubble_0" class="bubble"></div> <div id="bubble_1" class="bubble"></div> <div id="bubble_2" class="bubble"></div> <div id="bubble_3" class="bubble"></div> <div id="bubble_4" class="bubble"></div> <div id="bubble_5" class="bubble"></div> <div id="bubble_6" class="bubble"></div> … <div id="bubble_15" class="bubble"></div> </div> HTML… Done
  • 14.
    CSS The syntax ofCSS Keyframe Animations
  • 15.
    AT-RULE AND NAME @keyframesmyAnimationName {...}
  • 16.
    … WITH PREFIXES @-webkit-keyframesmyAnimationName {...} @-moz-keyframes myAnimationName {...} @keyframes myAnimationName {...}
  • 17.
    WITH KEYFRAMES @keyframes myAnimationName{ from { background-position: 0 0; } to { background-position: -162px 0; } } MDN has a list of animatable properties
  • 18.
    CHOOSE THE RIGHTPROPERTIES TO ANIMATE top/left | translate Paul Irish's post on using translate instead of top/left
  • 19.
    WITH PERCENTAGE-BASED KEYFRAMES @keyframesmyAnimationName { 0% { background-position: 0 0; } 100% { background-position: -162px 0; } }
  • 20.
    MIXED VALUES INKEYFRAMES @keyframes myAnimationName { 0% { background-position: 0 0; } 23% { opacity: 0; background-position: -162px 0; } 100% { opacity: 1; } }
  • 21.
    TARGETING PREFIXED PROPERTIES @-webkit-keyframesmyAnimationName { 0% {-webkit-transform-origin: 0 0;} 100% {-webkit-transform-origin: 25px 0;} } @-moz-keyframes myAnimationName { 0% {-moz-transform-origin: 0 0;} 100% {-moz-transform-origin: 25px 0;} } @keyframes myAnimationName { 0% {transform-origin: 0 0;} 100% {transform-origin: 25px 0;} }
  • 22.
    ATTACHING A DEFINEDKEYFRAME ANIMATION .classWithAnimation { animation: myAnimationName 1.2s linear 0s infinite; }
  • 23.
    ... WITH PREFIXES .classWithAnimation{ -webkit-animation: myAnimationName 1.2s linear 0s infinite; -moz-animation: myAnimationName 1.2s linear 0s infinite; animation: myAnimationName 1.2s linear 0s infinite; }
  • 24.
    SHORTHAND PROPERTIES EXPANDED /*shorthand */ .bubble { animation: myAnimationName 1.2s linear 0s infinite; } /* expanded */ .bubble { animation-name: myAnimationName; animation-duration: 1.2s; animation-timing-function: linear; animation-delay: 0s; animation-iteration-count: infinite; } and that's just some of them … at MDNthere are more
  • 25.
    *AHEM*… PREFIXED /* let'snow call it 'bubble' */ .bubble { -webkit-animation-name: myAnimationName; -webkit-animation-duration: 1.2s; -webkit-animation-timing-function: linear; -webkit-animation-delay: 0s; -webkit-animation-iteration-count: infinite; }
  • 26.
    ARE WE THEREYET? CSS Keyframe Animations, they: define a start … the inbetween frames … and the end?
  • 28.
    THE END ISNOT THE END "omit the to or 100% declaration from the @keyframe…" "then you call the animation on the progress bar…" "and just like that, the progress bar will animate itself up to the value set by the inline style." Chris Coyer's post on animating to an inline style
  • 30.
    "omit the toor 100% declaration from the @keyframe…" @keyframes myAnimationName { 0% {background-position: 0 0;} }
  • 31.
    "…then you callthe animation on the progress bar…" .bubble { animation: myAnimationName 1.2s linear 0s infinite; }
  • 32.
    "…and just likethat, the progress bar will animate itself up to the value set by the inline style." <div id="bubble_0" class="bubble" style="background-position: -162px 0;"></div>
  • 34.
    DOES IT NEEDTO BE INLINE? What if the properties were set in a class ruleset? /* css */ @keyframes myAnimationName { 0% {background-position: 0 0;} } .bubble { animation: myAnimationName 1.2s linear 0s infinite; } .background-move { background-position: -162px 0; } Then the class was dynamically applied to the DOM Element? <!-- html --> <div id="bubble_0" class="bubble background-move"></div>
  • 36.
    RULESET AS INLINESTYLE /* Keyframe Animation definitions */ @-webkit-keyframes lightBlue { 0% { color: #2ed0e5; -webkit-transform: translate(0, 50px) scale(.2, .2); -webkit-transform-origin: 20px 0; } 14% { color: #2ed0e5; -webkit-transform: translate(0, -100px) scale(.3, .3); -webkit-transform-origin: 43px 0; } } @-moz-keyframes lightBlue { 0% { color: #2ed0e5; -moz-transform: translate(0, 50px) scale(.2, .2); -moz-transform-origin: 20px 0; } 14% { color: #2ed0e5; -moz-transform: translate(0, -100px) scale(.3, .3); -moz-transform-origin: 43px 0; } } @keyframes lightBlue { 0% { color: #2ed0e5;
  • 37.
  • 38.
    WHAT DOES JAVASCRIPTGIVE US? requestAnimationFrame/cancelAnimationFrame methods Event-type "animationend"
  • 39.
    … WITH PREFIXES requestAnimationFrame,msRequestAnimationFrame, mozRequestAnimationFrame, webkitRequestAnimationFrame, oRequestAnimationFrame animationend, MSAnimationEnd, webkitAnimationEnd, oAnimationEnd
  • 40.
    CLASSY JAVASCRIPT var ANIMATION_CLASSES= [ // colors ['color_dark_red', 'color_red', 'color_yellow', 'color_green', 'col // translations ['translateA', 'translateB', 'translateC', 'translateD', 'translate // origins ['originA', 'originB', 'originC', 'originD', 'originE', 'originF'], // durations ['time_1500', 'time_1800', 'time_2000', 'time_2500', 'time_2700'], // easings ['easing_1', 'easing_2', 'easing_3'], // names ['light_blue', 'mid_blue'] ];
  • 41.
    GAME ON When thepage loads we: 1. Load custom classes "Bubble" and "BubbleController" 2. BubbleController puts all ".bubble" divs into an Array 3. It then makes two 'object pools' 4. One has "Bubble" instances, the other is ids from each div in the Array
  • 42.
    OBJECT POOLS ONHTML5ROCKS Colt McAnlis's post on Object Pools in JS
  • 43.
    GAME ON… Then: 1. BubbleControllerruns a loop creating setTimeouts at +200ms 2. The interval calls BubbleController's 'createBubble' method 3. This method pops a Bubble instance from its pool 4. It also pops an id from the id pool 5. It creates a listener for completion of the Bubble's 'init' method 6. It creates a listener for completion of the Bubble's 'hide' method 7. It runs the Bubble's 'init' method, with the id property
  • 44.
    OBJECT POOL CODEIN BUBBLECONTROLLER // `bubbles` is an Array of Dom Elements // `bubbleClass` is Bubble function createObjectPools (bubbles, bubbleClass) { var j = bubbles.length; var bubbleArgs = { delegateName: NAME }; while (j--) { // populate the individual bubble's ids into an Array idPool[j] = bubbles[j].id; // use an Object Pool to create enough instances to control each bubble bubblePool[j] = new bubbleClass(bubbleArgs); } }
  • 45.
    'CREATEBUBBLE' METHOD IN BUBBLECONTROLLER functioncreateBubble () { // get new bubble instance id form the pools var bubble = bubblePool.pop(); var id = idPool.pop(); // listen for the end of the new bubble's init phase $.subscribe(NAME + EVENTS.INIT + STATUS.COMPLETE + id, bubbleInitCompleteHandle // call init on the bubble instance bubble.init({ id: id }); }
  • 47.
    GAME ON… ALMOSTTHERE Finally, the Bubble's 'init' method: 1. The Bubble instance gets the DOM Element via the id 2. Bubble creates a listener for the 'animationend' event 3. It then gets a random class from each sub-Array in ANIMATION_CLASSES 4. It saves these as a String, but doesn't set its className 5. It fires off its 'init complete' Event 6. BubbleController reacts to this and calls requestAnimationFrame. 7. requestAnimationFrame calls the Bubble instances 'show' method 8. The 'show' method attaches the classlist, starting the animation
  • 48.
    'INIT' METHOD INBUBBLE Bubble.prototype.init = function (args) { // add inited variables this.id = args.id; this.node = doc.getElementById(args.id); this.$node = $(this.node); this.classList = 'bubble on'; // add listeners addListeners(this); setClassList(this); $.publish(this.delegateName + EVENTS.INIT + STATUS.COMPLETE + this.id, [this]); };
  • 49.
    RANDOMISING THROUGH THE ANIMATION_CLASSESARRAY function setClassList (instance) { var j = ANIMATION_CLASSES.length; while (j--) { instance.classList += (' ' + randomValue(ANIMATION_CLASSES[j])); } } function randomValue (arr) { var length = arr.length; var idx = (Math.random() * length) | 0; return arr[idx]; }
  • 50.
    CALLING REQUESTANIMATIONFRAME IN BUBBLECONTROLLER functionbubbleInitCompleteHandler (e, bubble) { $.unsubscribe(e.type, bubbleInitCompleteHandler); win.requestAnimationFrame(function () { bubble.show(); }); }
  • 51.
    'SHOW' METHOD INBUBBLE Bubble.prototype.show = function () { // appending the classes makes the bubble appear and the animation run this.node.className = this.classList; };
  • 52.
    GAME OFF 'animationend' event 1.The Bubble reacts to the DOM Element's 'animationend' event 2. It changes the className of the DOM Element to "splat" 3. It runs its 'hide' method and fires off 'hide complete' 4. Inside the 'hide complete' Event is the id as a String 5. BubbleController reacts to this 6. It 'pools' the Bubble instance and 'pools' the id 7. It then runs its 'createBubble' method to start all over again
  • 53.
    RE-'POOLING' THE BUBBLEAND ID IN BUBBLECONTROLLER function bubbleAnimationCompleteHandler (e, bubble, id) { // add the completed bubble and its id back into the pools bubblePool.unshift(bubble); idPool.unshift(id); createBubble(); }
  • 54.
  • 55.
    TIME ESCAPES ME Thebubbles are done with :before and :after They're webfonts - that's a story in itself You can pause the bubbles 'animation-timing-function' easing is done with cubic- beziers THEY'RE CLICKABLE!
  • 59.