/** * CoolClock 3.0.0-pre * Global Tick branch with https://github.com/henrahmagix/CoolClock/commit/00d48a01b1aeadbeb0b7d167aca892de78659b76 applied * Copyright (c) 2010-2013, Simon Baird. All rights reserved. * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name Simon Baird nor the names of other contributors may be used to endorse or promote products derived from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SIMON BAIRD BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Display an analog clock using canvas. * http://randomibis.com/coolclock/ * */ // Constructor for CoolClock objects window.CoolClock = function(options) { return this.init(options); } // Config contains some defaults, and clock skins CoolClock.config = { tickDelay: 1000, longTickDelay: 15000, defaultRadius: 85, renderRadius: 100, defaultSkin: "chunkySwiss", defaultFont: "15px sans-serif", skins: { // There are more skins in moreskins.js // Try making your own skin by copy/pasting one of these and tweaking it swissRail: { outerBorder: { lineWidth: 2, radius:95, color: "black", alpha: 1 }, smallIndicator: { lineWidth: 2, startAt: 88, endAt: 92, color: "black", alpha: 1 }, largeIndicator: { lineWidth: 4, startAt: 79, endAt: 92, color: "black", alpha: 1 }, hourHand: { lineWidth: 8, startAt: -15, endAt: 50, color: "black", alpha: 1 }, minuteHand: { lineWidth: 7, startAt: -15, endAt: 75, color: "black", alpha: 1 }, secondHand: { lineWidth: 1, startAt: -20, endAt: 85, color: "red", alpha: 1 }, secondDecoration: { lineWidth: 1, startAt: 70, radius: 4, fillColor: "red", color: "red", alpha: 1 } }, chunkySwiss: { outerBorder: { lineWidth: 4, radius:97, color: "black", alpha: 1 }, smallIndicator: { lineWidth: 4, startAt: 89, endAt: 93, color: "black", alpha: 1 }, largeIndicator: { lineWidth: 8, startAt: 80, endAt: 93, color: "black", alpha: 1 }, hourHand: { lineWidth: 12, startAt: -15, endAt: 60, color: "black", alpha: 1 }, minuteHand: { lineWidth: 10, startAt: -15, endAt: 85, color: "black", alpha: 1 }, secondHand: { lineWidth: 4, startAt: -20, endAt: 85, color: "red", alpha: 1 }, secondDecoration: { lineWidth: 2, startAt: 70, radius: 8, fillColor: "red", color: "red", alpha: 1 } }, chunkySwissOnBlack: { outerBorder: { lineWidth: 4, radius:97, color: "white", alpha: 1 }, smallIndicator: { lineWidth: 4, startAt: 89, endAt: 93, color: "white", alpha: 1 }, largeIndicator: { lineWidth: 8, startAt: 80, endAt: 93, color: "white", alpha: 1 }, hourHand: { lineWidth: 12, startAt: -15, endAt: 60, color: "white", alpha: 1 }, minuteHand: { lineWidth: 10, startAt: -15, endAt: 85, color: "white", alpha: 1 }, secondHand: { lineWidth: 4, startAt: -20, endAt: 85, color: "red", alpha: 1 }, secondDecoration: { lineWidth: 2, startAt: 70, radius: 8, fillColor: "red", color: "red", alpha: 1 } } }, // Test for IE so we can nurse excanvas in a couple of places isIE: !!document.all, // Will store (a reference to) each clock here, indexed by the id of the canvas element clockTracker: {}, // For giving a unique id to coolclock canvases with no id noIdCount: 0 } // Main tick handler. Refresh all the clocks then setup the next tick CoolClock.tick = function() { for (var clockId in CoolClock.config.clockTracker) { var clock = CoolClock.config.clockTracker[clockId]; if (clock.stillHere() && clock.active) { clock.refreshDisplay() } } this.tickTimeout = setTimeout(CoolClock.tick, CoolClock.config.tickDelay); }; // Define the CoolClock object's methods CoolClock.prototype = { // Initialise using the parameters parsed from the colon delimited class init: function(options) { // Parse and store the options this.canvasId = options.canvasId; this.skinId = options.skinId || CoolClock.config.defaultSkin; this.font = options.font || CoolClock.config.defaultFont; this.displayRadius = options.displayRadius || CoolClock.config.defaultRadius; this.renderRadius = options.renderRadius || CoolClock.config.renderRadius; this.showSecondHand = typeof options.showSecondHand == "boolean" ? options.showSecondHand : true; this.gmtOffset = (options.gmtOffset != null && options.gmtOffset != '') ? parseFloat(options.gmtOffset) : null; this.showDigital = typeof options.showDigital == "boolean" ? options.showDigital : false; this.showDigital24 = typeof options.showDigital24 == "boolean" ? options.showDigital24 : false; this.showDate = typeof options.showDate == "boolean" ? options.showDate : false; this.logClock = typeof options.logClock == "boolean" ? options.logClock : false; this.logClockRev = typeof options.logClock == "boolean" ? options.logClockRev : false; this.tickDelay = CoolClock.config[ this.showSecondHand ? "tickDelay" : "longTickDelay" ]; // Get the canvas element this.canvas = document.getElementById(this.canvasId); // Make the canvas the requested size. It's always square. this.canvas.setAttribute("width",this.displayRadius*2); this.canvas.setAttribute("height",this.displayRadius*2); this.canvas.style.width = this.displayRadius*2 + "px"; this.canvas.style.height = this.displayRadius*2 + "px"; // Determine by what factor to relate skin values to canvas positions. // renderRadius is the max skin positional value before leaving the // canvas. displayRadius is half the width and height of the canvas in // pixels. If they are equal, there is a 1:1 relation of skin position // values to canvas pixels. Setting both to 200 allows 100px of space // around clock skins to add your own things: this is due to current // skins maxing out at a positional value of 100. this.scale = this.displayRadius / this.renderRadius; // Initialise canvas context this.ctx = this.canvas.getContext("2d"); this.ctx.scale(this.scale,this.scale); // Keep track of this object CoolClock.config.clockTracker[this.canvasId] = this; // Start the clock going this.start(); return this; }, // Draw a circle at point x,y with params as defined in skin fullCircleAt: function(x,y,skin) { this.ctx.save(); this.ctx.globalAlpha = skin.alpha; this.ctx.lineWidth = skin.lineWidth; if (CoolClock.config.isIE) { // excanvas doesn't scale line width so we will do it here this.ctx.lineWidth = this.ctx.lineWidth * this.scale; } this.ctx.beginPath(); this.ctx.arc(x, y, skin.radius, 0, 2*Math.PI, false); if (CoolClock.config.isIE) { // excanvas doesn't close the circle so let's fill in the tiny gap this.ctx.arc(x, y, skin.radius, -0.1, 0.1, false); } if (skin.fillColor) { this.ctx.fillStyle = skin.fillColor this.ctx.fill(); } if (skin.color) { this.ctx.strokeStyle = skin.color; this.ctx.stroke(); } this.ctx.restore(); }, // Draw some text centered vertically and horizontally drawTextAt: function(theText,x,y,skin) { if (!skin) skin = this.getSkin(); this.ctx.save(); this.ctx.font = skin.font || this.font; var tSize = this.ctx.measureText(theText); // TextMetrics rarely returns a height property: use baseline instead. if (!tSize.height) { tSize.height = 0; this.ctx.textBaseline = 'middle'; } this.ctx.fillText(theText, x - tSize.width/2, y - tSize.height/2); this.ctx.restore(); }, lpad2: function(num) { return (num < 10 ? '0' : '') + num; }, tickAngle: function(second) { // Log algorithm by David Bradshaw var tweak = 3; // If it's lower the one second mark looks wrong (?) if (this.logClock) { return second == 0 ? 0 : (Math.log(second*tweak) / Math.log(60*tweak)); } else if (this.logClockRev) { // Flip the seconds then flip the angle (trickiness) second = (60 - second) % 60; return 1.0 - (second == 0 ? 0 : (Math.log(second*tweak) / Math.log(60*tweak))); } else { return second/60.0; } }, timeText: function(hour,min,sec,showAmPm,showSecs) { return '' + (showAmPm ? ((hour%12)==0 ? 12 : (hour%12)) : hour) + ':' + this.lpad2(min) + (showSecs ? ':' + this.lpad2(sec) : '') + (showAmPm ? (hour < 12 ? ' am' : ' pm') : '') ; }, // Draw a radial line by rotating then drawing a straight line // Ha ha, I think I've accidentally used Taus, (see http://tauday.com/) radialLineAtAngle: function(angleFraction,skin) { this.ctx.save(); this.ctx.translate(this.renderRadius,this.renderRadius); this.ctx.rotate(Math.PI * (2.0 * angleFraction - 0.5)); this.ctx.globalAlpha = skin.alpha; this.ctx.strokeStyle = skin.color; this.ctx.lineWidth = skin.lineWidth; if (CoolClock.config.isIE) // excanvas doesn't scale line width so we will do it here this.ctx.lineWidth = this.ctx.lineWidth * this.scale; if (skin.radius) { this.fullCircleAt(skin.startAt,0,skin) } else { this.ctx.beginPath(); this.ctx.moveTo(skin.startAt,0) this.ctx.lineTo(skin.endAt,0); this.ctx.stroke(); } this.ctx.restore(); }, render: function(hour,min,sec,date) { // Get the skin var skin = this.getSkin(); // Clear this.ctx.clearRect(0,0,this.renderRadius*2,this.renderRadius*2); // Draw the outer edge of the clock if (skin.outerBorder) this.fullCircleAt(this.renderRadius,this.renderRadius,skin.outerBorder); // Draw the tick marks. Every 5th one is a big one for (var i=0;i<60;i++) { (i%5) && skin.smallIndicator && this.radialLineAtAngle(this.tickAngle(i),skin.smallIndicator); !(i%5) && skin.largeIndicator && this.radialLineAtAngle(this.tickAngle(i),skin.largeIndicator); } // Write the time and or date if (this.showDigital || this.showDigital24 || this.showDate) { var text; if (this.showDigital) text = this.timeText(hour,min,sec,true,false); else if (this.showDigital24) text = this.timeText(hour,min,sec,false,true); else text = date; this.drawTextAt( text, this.renderRadius, this.renderRadius+this.renderRadius/2 ); } var hourA = (hour%12)*5 + min/12.0, minA = min + sec/60.0, secA = sec; // Draw the hands if (skin.hourHand) this.radialLineAtAngle(this.tickAngle(hourA),skin.hourHand); if (skin.minuteHand) this.radialLineAtAngle(this.tickAngle(minA),skin.minuteHand); if (this.showSecondHand && skin.secondHand) this.radialLineAtAngle(this.tickAngle(secA),skin.secondHand); // Hands decoration - not in IE if (!CoolClock.config.isIE) { if (skin.hourDecoration) this.radialLineAtAngle(this.tickAngle(hourA), skin.hourDecoration); if (skin.minDecoration) this.radialLineAtAngle(this.tickAngle(minA), skin.minDecoration); if (this.showSecondHand && skin.secondDecoration) this.radialLineAtAngle(this.tickAngle(secA),skin.secondDecoration); } if (this.extraRender) { this.extraRender(hour,min,sec); } }, // Check the time and display the clock refreshDisplay: function() { var now = new Date(); if (this.gmtOffset != null) { // Use GMT + gmtOffset var offsetNow = new Date(now.valueOf() + (this.gmtOffset * 1000 * 60 * 60)); this.render(offsetNow.getUTCHours(),offsetNow.getUTCMinutes(),offsetNow.getUTCSeconds(),offsetNow.toLocaleDateString()); } else { // Use local time this.render(now.getHours(),now.getMinutes(),now.getSeconds(),now.toLocaleDateString()); } }, // Check the canvas element hasn't been removed stillHere: function() { return document.getElementById(this.canvasId) != null; }, // Stop this clock stop: function() { this.active = false; }, // Start this clock start: function() { this.active = true; }, getSkin: function() { var skin = CoolClock.config.skins[this.skinId]; if (!skin) skin = CoolClock.config.skins[CoolClock.config.defaultSkin]; return skin; } }; // Find all canvas elements that have the CoolClock class and turns them into clocks CoolClock.findAndCreateClocks = function() { // (Let's not use a jQuery selector here so it's easier to use frameworks other than jQuery) var canvases = document.getElementsByTagName("canvas"); for (var i=0;i // If you do have jQuery and it's loaded already then we can do it right now if (window.jQuery) jQuery(document).ready(CoolClock.findAndCreateClocks);