diff --git a/_includes/head.html b/_includes/head.html
index 61d1a58..cf9536d 100644
--- a/_includes/head.html
+++ b/_includes/head.html
@@ -27,7 +27,7 @@
-
+
diff --git a/img/favicon-sprite.png b/img/favicon-sprite.png
new file mode 100644
index 0000000..8d66332
Binary files /dev/null and b/img/favicon-sprite.png differ
diff --git a/img/favicon-sprite@2x.png b/img/favicon-sprite@2x.png
new file mode 100644
index 0000000..a7aa791
Binary files /dev/null and b/img/favicon-sprite@2x.png differ
diff --git a/js/page.js b/js/page.js
index 22962cc..675e119 100644
--- a/js/page.js
+++ b/js/page.js
@@ -40,6 +40,14 @@ Modules.Support = (function() {
return dfd.promise();
};
+ // Fill rAF
+ var rAF = window.requestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.msRequestAnimationFrame;
+
+ window.requestAnimationFrame = rAF;
+
return self;
}());
@@ -188,6 +196,176 @@ Modules.Nav = (function( $ ) {
}( jQuery ));
+Modules.Favicon = (function( doc ) {
+ 'use strict';
+
+ var Favicon = function( src, numFrames, framesPerAnimation, animationDelay ) {
+ var self = this;
+
+ // Variables based on params
+ self.src = src;
+ self.numFrames = numFrames;
+ self.framesPerAnimation = framesPerAnimation;
+ self.animationDelay = animationDelay;
+
+ // Elements
+ self.canvas = doc.createElement('canvas');
+ self.img = doc.createElement('img');
+ self.html = doc.documentElement;
+
+ // Calculations
+ self.size = window.devicePixelRatio > 1 ? 32 : 16;
+
+ // If it's not a data url, pick apart the filename and add @2x for retina
+ if ( !self.src.match(/data:/) && window.devicePixelRatio > 1 ) {
+ var dot = self.src.lastIndexOf('.');
+ self.src = self.src.substring( 0, dot ) + '@2x' + self.src.substring( dot );
+ }
+
+ self.currentFrame = 0;
+ self.thirtyFPS = 1000 / 30;
+
+ self.init();
+ };
+
+ Favicon.prototype.init = function() {
+ var self = this;
+
+ // No #favicon element or browser doesn't support canvas or < IE9, stop
+ if ( !doc.getElementById('favicon') || !self.canvas.getContext || self.html.className.indexOf('lt-ie9') > -1 ) {
+ return;
+ }
+
+ // Save context
+ self.ctx = self.canvas.getContext('2d');
+
+ // Set canvas dimensions based on device DPI
+ self.canvas.height = self.canvas.width = self.size;
+
+ // Create a new sprite 32x32 size with 32x32 sprites
+ self.sprite = new Sprite( self.ctx, self.img, self.size );
+
+ // Bind the image load handler
+ self.img.onload = self.onSpriteLoaded.bind( self );
+
+ // Trigger image to load
+ self.img.src = self.src;
+ };
+
+ Favicon.prototype.getData = function() {
+ return this.canvas.toDataURL('image/png');
+ };
+
+ // Clone the current #favicon and replace it with a new element
+ // which has the updated data URI href
+ Favicon.prototype.setFavicon = function() {
+ var self = this,
+ data = self.getData(),
+ originalFavicon = doc.getElementById('favicon'),
+ clone = originalFavicon.cloneNode( true );
+
+ clone.setAttribute( 'href', data );
+ originalFavicon.parentNode.replaceChild( clone, originalFavicon );
+ };
+
+ // Request Animation Frame Loop
+ Favicon.prototype.loop = function( timestamp ) {
+ var self = this,
+ lastCall = self.lastTimestamp;
+
+ // If not enough time has elapse since the last call
+ // immediately call the next rAF
+ if ( timestamp - lastCall < self.timeToElapse ) {
+ return requestAnimationFrame( self.loop.bind( self ) );
+ }
+
+ // Increment current frame
+ self.currentFrame += 1;
+ if ( self.currentFrame === self.numFrames ) {
+ self.currentFrame = 0;
+ }
+
+ // Completed an animation state
+ self.timeToElapse = self.currentFrame % self.framesPerAnimation === 0 ?
+ self.animationDelay :
+ self.thirtyFPS;
+
+ // Draw current frame from sprite
+ self.sprite.drawFrame( self.currentFrame );
+
+ // Swap
+ self.setFavicon();
+
+ // Set a timeout to draw again
+ self.lastTimestamp = timestamp;
+
+ // Continue loop
+ return requestAnimationFrame( self.loop.bind( self ) );
+ };
+
+ // Sprite loaded
+ Favicon.prototype.onSpriteLoaded = function() {
+ var self = this;
+
+ // Draw the first frame when the image loads
+ self.sprite.drawFrame( self.currentFrame );
+
+ // Swap
+ self.setFavicon();
+
+ // Start loop
+ requestAnimationFrame( self.loop.bind( self ) );
+ };
+
+
+ var Sprite = function( context, img, size ) {
+ var self = this;
+ self.ctx = context;
+ self.img = img;
+ self.width = size;
+ self.height = size;
+ self.frameWidth = size;
+ self.frameHeight = size;
+ };
+
+ // Assuming horizontal sprite
+ Sprite.prototype.getFrame = function( frame ) {
+ return {
+ x: frame * this.frameWidth,
+ y: 0
+ };
+ };
+
+ Sprite.prototype.clearCanvas = function() {
+ this.ctx.clearRect( 0, 0, this.width, this.height );
+ };
+
+ Sprite.prototype.drawFrame = function( frameNumber ) {
+ var self = this;
+
+ var frame = self.getFrame( frameNumber );
+
+ // Clear out the last frame
+ self.clearCanvas();
+
+ // Draw to the context. This method is really confusing...
+ self.ctx.drawImage(
+ self.img,
+ frame.x,
+ frame.y,
+ self.width,
+ self.height,
+ 0,
+ 0,
+ self.width,
+ self.height
+ );
+ };
+
+ return Favicon;
+}( document ));
+
+
// Analytics
var _gaq = [ ['_setAccount', 'UA-39355642-1'], ['_trackPageview'] ];
@@ -222,4 +400,15 @@ $(document).ready(function() {
Modules.Nav.init();
Modules.Polyfill.init();
-});
\ No newline at end of file
+
+ var src = '/img/favicon-sprite.png';
+ new Modules.Favicon( src, 21, 7, 3000 * 1 );
+});
+
+
+
+
+
+
+
+