You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Vestride_Shuffle/docs/js/animated-favicon.js

141 lines
3.7 KiB
JavaScript

class Sprite {
constructor(context, img, size) {
this.ctx = context;
this.img = img;
this.width = size;
this.height = size;
this.frameWidth = size;
this.frameHeight = size;
}
// Assuming horizontal sprite
getFrame(frame) {
return {
x: frame * this.frameWidth,
y: 0,
};
}
clearCanvas() {
this.ctx.clearRect(0, 0, this.width, this.height);
}
drawFrame(frameNumber) {
var frame = this.getFrame(frameNumber);
// Clear out the last frame
this.clearCanvas();
// Draw to the context. This method is really confusing...
this.ctx.drawImage(this.img, frame.x, frame.y, this.width, this.height, 0, 0, this.width, this.height);
}
}
class Favicon {
constructor(src, numFrames, framesPerAnimation, animationDelay) {
// Variables based on params
this.src = src;
this.numFrames = numFrames;
this.framesPerAnimation = framesPerAnimation;
this.animationDelay = animationDelay;
// Elements
this.canvas = document.createElement('canvas');
this.img = document.createElement('img');
this.html = document.documentElement;
// Calculations
this.size = window.devicePixelRatio > 1 ? 32 : 16;
// If it's not a data url, pick apart the filename and add @2x for retina
if (!this.src.match(/data:/) && window.devicePixelRatio > 1) {
var dot = this.src.lastIndexOf('.');
this.src = this.src.substring(0, dot) + '@2x' + this.src.substring(dot);
}
this.currentFrame = 0;
// Chrome chokes on this. It looks like it can handle 4 frames per second
this.fps = 24;
// No #favicon element, stop
if (!document.getElementById('favicon')) {
return;
}
// Save context
this.ctx = this.canvas.getContext('2d');
// Set canvas dimensions based on device DPI
this.canvas.height = this.canvas.width = this.size;
// Create a new sprite 32x32 size with 32x32 sprites
this.sprite = new Sprite(this.ctx, this.img, this.size);
// Bind the image load handler
this.img.onload = this.onSpriteLoaded.bind(this);
// Trigger image to load
this.img.src = this.src;
}
getData() {
return this.canvas.toDataURL('image/png');
}
// Clone the current #favicon and replace it with a new element
// which has the updated data URI href
setFavicon() {
var data = this.getData();
var originalFavicon = document.getElementById('favicon');
var clone = originalFavicon.cloneNode(true);
clone.setAttribute('href', data);
originalFavicon.parentNode.replaceChild(clone, originalFavicon);
}
// Request Animation Frame Loop
loop(timestamp) {
// If not enough time has elapse since the last call
// immediately call the next rAF
if (timestamp - this.lastExecuted < this.timeToElapse) {
return requestAnimationFrame(this.loop.bind(this));
}
// Increment current frame
this.currentFrame += 1;
if (this.currentFrame === this.numFrames) {
this.currentFrame = 0;
}
// Completed an animation state
this.timeToElapse = this.currentFrame % this.framesPerAnimation === 0 ? this.animationDelay : 1000 / this.fps;
// Draw current frame from sprite
this.sprite.drawFrame(this.currentFrame);
// Swap <link>
this.setFavicon();
// Set a timeout to draw again
this.lastExecuted = timestamp;
// Continue loop
return requestAnimationFrame(this.loop.bind(this));
}
// Sprite loaded
onSpriteLoaded() {
// Draw the first frame when the image loads
this.sprite.drawFrame(this.currentFrame);
// Swap <link>
this.setFavicon();
// Start loop
requestAnimationFrame(this.loop.bind(this));
}
}
new Favicon(window.site_url + '/img/favicon-sprite.png', 21, 7, 3000 * 1);