<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript" src="TimeCircles.js"></script>
<link href="TimeCircles.css" rel="stylesheet">
</head>
<body>
<div id="DateCountdown" data-date="2014-01-24 18:00:00" style=" margin:9em 0 0; width: 1000px; height: 300px; padding: 0px; box-sizing: border-box; background-color: #25282b"></div>
<script>
$("#DateCountdown").TimeCircles();
$("#CountDownTimer").TimeCircles({ time: { Days: { show: false }, Hours: { show: false } }});
$("#PageOpenTimer").TimeCircles();
var updateTime = function(){
var date = $("#date").val();
var time = $("#time").val();
var datetime = date + ' ' + time + ':00';
$("#DateCountdown").data('date', datetime).TimeCircles().start();
}
$("#date").change(updateTime).keyup(updateTime);
$("#time").change(updateTime).keyup(updateTime);
// Start and stop are methods applied on the public TimeCircles instance
$(".startTimer").click(function() {
$("#CountDownTimer").TimeCircles().start();
});
$(".stopTimer").click(function() {
$("#CountDownTimer").TimeCircles().stop();
});
// Fade in and fade out are examples of how chaining can be done with TimeCircles
$(".fadeIn").click(function() {
$("#PageOpenTimer").fadeIn();
});
$(".fadeOut").click(function() {
$("#PageOpenTimer").fadeOut();
});
</script>
</body>
TimeCircles.js :
/**
* Basic structure: TC_Class is the public class that is returned upon being called
*
* So, if you do
* var tc = $(".timer").TimeCircles();
*
* tc will contain an instance of the public TimeCircles class. It is important to
* note that TimeCircles is not chained in the conventional way, check the
* documentation for more info on how TimeCircles can be chained.
*
* After being called/created, the public TimerCircles class will then- for each element
* within it's collection, either fetch or create an instance of the private class.
* Each function called upon the public class will be forwarded to each instance
* of the private classes within the relevant element collection
**/
(function($) {
var debug = (location.hash === "#debug");
function debug_log(msg) {
if(debug) {
console.log(msg);
}
}
var allUnits = ["Days", "Hours", "Minutes", "Seconds"];
var nextUnits = {
Seconds: "Minutes",
Minutes: "Hours",
Hours: "Days",
Days: "Years"
};
var secondsIn = {
Seconds: 1,
Minutes: 60,
Hours: 3600,
Days: 86400,
Years: 31536000
};
/**
* Converts hex color code into object containing integer values for the r,g,b use
* This function (hexToRgb) originates from:
* http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
* @param {string} hex color code
*/
function hexToRgb(hex) {
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, function(m, r, g, b) {
return r + r + g + g + b + b;
});
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
/**
* Function s4() and guid() originate from:
* http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
*/
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
/**
* Creates a unique id
* @returns {String}
*/
function guid() {
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
}
function parse_date(str) {
var match = str.match(/^[0-9]{4}-[0-9]{2}-[0-9]{2}\s[0-9]{1,2}:[0-9]{2}:[0-9]{2}$/);
if (match !== null && match.length > 0) {
var parts = str.split(" ");
var date = parts[0].split("-");
var time = parts[1].split(":");
return new Date(date[0], date[1] - 1, date[2], time[0], time[1], time[2]);
}
// Fallback for different date formats
var d = Date.parse(str);
if(!isNaN(d)) return d;
d = Date.parse(str.replace(/-/g, '/').replace('T', ' '));
if(!isNaN(d)) return d;
// Cant find anything
return new Date();
}
function parse_times(diff, old_diff, super_unit, units) {
var raw_time = {};
var raw_old_time = {};
var time = {};
var pct = {};
var old_time = {};
var greater_unit = null;
for(var i in units) {
var unit = units[i];
if(greater_unit === null) greater_unit = super_unit;
var maxUnits = secondsIn[greater_unit] / secondsIn[unit];
var curUnits = (diff / secondsIn[unit]);
var oldUnits = (old_diff / secondsIn[unit]);
if(unit !== "Days"){
curUnits = curUnits % maxUnits;
oldUnits = oldUnits % maxUnits;
}
raw_time[unit] = curUnits;
time[unit] = Math.abs(curUnits);
raw_old_time[unit] = oldUnits;
old_time[unit] = Math.abs(oldUnits);
pct[unit] = Math.abs(curUnits) / maxUnits;
greater_unit = unit;
}
return {
raw_time: raw_time,
raw_old_time: raw_old_time,
time: time,
old_time: old_time,
pct: pct
};
}
var TC_Instance_List = {};
// Try fetch/share instance
if(window !== window.top && typeof window.top.TC_Instance_List !== "undefined") {
TC_Instance_List = window.top.TC_Instance_List;
}
else {
window.top.TC_Instance_List = TC_Instance_List;
}
var TC_Instance = function(element, options) {
this.element = element;
this.container;
this.timer = null;
this.data = {
prev_time: null,
drawn_units: [],
super_unit: null,
text_elements: {
Days: null,
Hours: null,
Minutes: null,
Seconds: null
},
attributes: {
canvas: null,
context: null,
item_size: null,
line_width: null,
radius: null,
outer_radius: null
},
state: {
fading: {
Days: false,
Hours: false,
Minutes: false,
Seconds: false
}
}
};
this.config = null;
this.setOptions(options);
this.initialize();
};
TC_Instance.prototype.initialize = function() {
// Initialize drawn units
this.data.super_unit = null;
this.data.drawn_units = [];
for(var unit in this.config.time) {
if(this.config.time[unit].show){
this.data.drawn_units.push(unit);
if(this.data.super_unit === null) this.data.super_unit = nextUnits[unit];
}
}
// Avoid stacking
$(this.element).children('div.time_circles').remove();
this.listeners = { all: [], visible: [] };
this.container = $("<div>");
this.container.addClass('time_circles');
this.container.appendTo(this.element);
this.data.attributes.canvas = $("<canvas>");
this.data.attributes.context = this.data.attributes.canvas[0].getContext('2d');
var height = this.element.offsetHeight;
var width = this.element.offsetWidth;
if(height === 0) height = $(this.element).height();
if(width === 0) width = $(this.element).width();
if(height === 0 && width > 0) height = width / this.data.drawn_units.length;
else if(width === 0 && height > 0) width = height * this.data.drawn_units.length;
this.data.attributes.canvas[0].height = height;
this.data.attributes.canvas[0].width = width;
this.data.attributes.canvas.appendTo(this.container);
this.data.attributes.item_size = Math.min(this.data.attributes.canvas[0].width / this.data.drawn_units.length, this.data.attributes.canvas[0].height);
this.data.attributes.line_width = this.data.attributes.item_size * this.config.fg_width;
this.data.attributes.radius = ((this.data.attributes.item_size * 0.8) - this.data.attributes.line_width) / 2;
this.data.attributes.outer_radius = this.data.attributes.radius + 0.5 * Math.max(this.data.attributes.line_width, this.data.attributes.line_width * this.config.bg_width);
// Prepare Time Elements
var i = 0;
for (var key in this.data.text_elements) {
if(!this.config.time[key].show) continue;
var textElement = $("<div>");
textElement.addClass('textDiv_' + key);
textElement.css("top", Math.round(0.35 * this.data.attributes.item_size));
textElement.css("left", Math.round(i++ * this.data.attributes.item_size));
textElement.css("width", this.data.attributes.item_size);
textElement.appendTo(this.container);
var headerElement = $("<h4>");
headerElement.text(this.config.time[key].text); // Options
headerElement.css("font-size", Math.round(0.07 * this.data.attributes.item_size));
headerElement.css("line-height", Math.round(0.07 * this.data.attributes.item_size) + "px");
headerElement.appendTo(textElement);
var numberElement = $("<span>");
numberElement.css("font-size", Math.round(0.21 * this.data.attributes.item_size));
numberElement.css("line-height", Math.round(0.07 * this.data.attributes.item_size) + "px");
numberElement.appendTo(textElement);
this.data.text_elements[key] = numberElement;
}
if (this.config.start && this.timer === null)
this.start();
};
TC_Instance.prototype.updateArc = function() {
var diff, old_diff;
var prevDate = this.data.prev_time;
var curDate = new Date();
this.data.prev_time = curDate;
if(prevDate === null) prevDate = curDate;
// If not counting past zero, and time < 0, then simply draw the zero point once, and call stop
if (!this.config.count_past_zero) {
if(curDate > this.data.attributes.ref_date) {
for (var i in this.data.drawn_units) {
// TODO: listeners!
var key = this.data.drawn_units[i];
// Set the text value
this.data.text_elements[key].text(Math.floor(time[key]));
var x = (i * this.data.attributes.item_size) + (this.data.attributes.item_size / 2);
var y = this.data.attributes.item_size / 2;
var color = this.config.time[key].color;
this.drawArc(x, y, color, 0);
}
this.stop();
return;
}
}
// Compare current time with reference
diff = (this.data.attributes.ref_date - curDate) / 1000;
old_diff = (this.data.attributes.ref_date - prevDate) / 1000;
var visible_times = parse_times(diff, old_diff, this.data.super_unit, this.data.drawn_units);
var all_times = parse_times(diff, old_diff, "Years", allUnits);
var i = 0;
var j = 0;
var lastKey = null;
var cur_shown = this.data.drawn_units.slice();
for (var i in allUnits) {
var key = allUnits[i];
// Notify (all) listeners
if(Math.floor(all_times.raw_time[key]) !== Math.floor(all_times.raw_old_time[key])) {
this.notifyListeners(key, Math.floor(all_times.time[key]), Math.floor(diff), "all");
}
if(cur_shown.indexOf(key) < 0) continue;
// Notify (visible) listeners
if(Math.floor(visible_times.raw_time[key]) !== Math.floor(visible_times.raw_old_time[key])) {
this.notifyListeners(key, Math.floor(visible_times.time[key]), Math.floor(diff), "visible");
}
// Set the text value
this.data.text_elements[key].text(Math.floor(Math.abs(visible_times.time[key])));
var x = (j * this.data.attributes.item_size) + (this.data.attributes.item_size / 2);
var y = this.data.attributes.item_size / 2;
var color = this.config.time[key].color;
if (lastKey !== null) {
if (Math.floor(visible_times.time[lastKey]) > Math.floor(visible_times.old_time[lastKey])) {
this.radialFade(x, y, color, 1, key);
this.data.state.fading[key] = true;
}
else if (Math.floor(visible_times.time[lastKey]) < Math.floor(visible_times.old_time[lastKey])) {
this.radialFade(x, y, color, 0, key);
this.data.state.fading[key] = true;
}
}
if (!this.data.state.fading[key]) {
this.drawArc(x, y, color, visible_times.pct[key]);
}
lastKey = key;
j++;
}
};
TC_Instance.prototype.drawArc = function(x, y, color, pct) {
var clear_radius = Math.max(this.data.attributes.outer_radius, this.data.attributes.item_size / 2);
this.data.attributes.context.clearRect(
x - clear_radius,
y - clear_radius,
clear_radius * 2,
clear_radius * 2
);
if (this.config.use_background) {
this.data.attributes.context.beginPath();
this.data.attributes.context.arc(x, y, this.data.attributes.radius, 0, 2 * Math.PI, false);
this.data.attributes.context.lineWidth = this.data.attributes.line_width * this.config.bg_width;
// line color
this.data.attributes.context.strokeStyle = this.config.circle_bg_color;
this.data.attributes.context.stroke();
}
var startAngle = (-0.5 * Math.PI);
var endAngle = (-0.5 * Math.PI) + (2 * pct * Math.PI);
var counterClockwise = false;
this.data.attributes.context.beginPath();
this.data.attributes.context.arc(x, y, this.data.attributes.radius, startAngle, endAngle, counterClockwise);
this.data.attributes.context.lineWidth = this.data.attributes.line_width;
// line color
this.data.attributes.context.strokeStyle = color;
this.data.attributes.context.stroke();
};
TC_Instance.prototype.radialFade = function(x, y, color, from, key) {
// TODO: Make fade_time option
var rgb = hexToRgb(color);
var _this = this; // We have a few inner scopes here that will need access to our instance
var step = 0.2 * ((from === 1) ? -1 : 1);
var i;
for (i = 0; from <= 1 && from >= 0; i++) {
// Create inner scope so our variables are not changed by the time the Timeout triggers
(function() {
var rgba = "rgba(" + rgb.r + ", " + rgb.g + ", " + rgb.b + ", " + (Math.round(from * 10) / 10) + ")";
window.top.setTimeout(function() {
_this.drawArc(x, y, rgba, 1);
}, 50 * i);
}());
from += step;
}
window.top.setTimeout(function() {
_this.data.state.fading[key] = false;
}, 50 * i);
};
TC_Instance.prototype.timeLeft = function() {
var now = new Date();
return ((this.data.attributes.ref_date - now) / 1000);
};
TC_Instance.prototype.start = function() {
// Check if a date was passed in html attribute or jquery data
var attr_data_date = $(this.element).data('date');
if (typeof attr_data_date === "undefined") {
attr_data_date = $(this.element).attr('data-date');
}
if (typeof attr_data_date === "string") {
this.data.attributes.ref_date = parse_date(attr_data_date);
}
// Check if this is an unpause of a timer
else if (typeof this.data.attributes.timer === "number") {
this.data.attributes.ref_date = (new Date()).getTime() + (this.data.attributes.timer * 1000);
}
else {
// Try to get data-timer
var attr_data_timer = $(this.element).data('timer');
if (typeof attr_data_timer === "undefined") {
attr_data_timer = $(this.element).attr('data-timer');
}
if (typeof attr_data_timer === "string") {
attr_data_timer = parseFloat(attr_data_timer);
}
if(typeof attr_data_timer === "number") {
this.data.attributes.ref_date = (new Date()).getTime() + (attr_data_timer * 1000);
}
else {
// data-timer and data-date were both not set
// use config date
this.data.attributes.ref_date = this.config.ref_date;
}
}
// Start running
var _this = this;
this.timer = window.top.setInterval(function() { _this.updateArc(); }, this.config.refresh_interval * 1000);
};
TC_Instance.prototype.restart = function() {
this.data.attributes.timer = null;
this.start();
};
TC_Instance.prototype.stop = function() {
if (typeof this.data.attributes.timer === "number") {
this.data.attributes.timer = this.timeLeft(this);
}
// Stop running
window.top.clearInterval(this.timer);
};
TC_Instance.prototype.destroy = function() {
this.stop();
this.container.remove();
$(this.element).removeData('tc-id');
};
TC_Instance.prototype.setOptions = function(options) {
if(this.config === null) {
this.default_options.ref_date = new Date();
this.config = $.extend(true, {}, this.default_options);
}
$.extend(true, this.config, options);
};
TC_Instance.prototype.addListener = function(f, context, type) {
if(typeof f !== "function") return;
if(typeof type === "undefined") type = "visible";
this.listeners[type].push({func: f, scope: context});
};
TC_Instance.prototype.notifyListeners = function(unit, value, total, type) {
for(var i = 0; i < this.listeners[type].length; i++) {
var listener = this.listeners[type][i];
listener.func.apply(listener.scope, [unit, value, total]);
}
};
TC_Instance.prototype.default_options = {
ref_date: new Date(),
start: true,
refresh_interval: 0.1,
count_past_zero: true,
circle_bg_color: "#60686F",
use_background: true,
fg_width: 0.1,
bg_width: 1.2,
time: {
Days: {
show: true,
text: "Days",
color: "#b5dec9"
},
Hours: {
show: true,
text: "Hours",
color: "#facaca"
},
Minutes: {
show: true,
text: "Minutes",
color: "#ea4d79"
},
Seconds: {
show: true,
text: "Seconds",
color: "#b5dec9"
}
}
};
// Time circle class
var TC_Class = function(elements, options) {
this.elements = elements;
this.options = options;
this.foreach();
};
TC_Class.prototype.getInstance = function(element) {
var instance;
var cur_id = $(element).data("tc-id");
if (typeof cur_id === "undefined") {
cur_id = guid();
$(element).attr("data-tc-id", cur_id);
}
if (typeof TC_Instance_List[cur_id] === "undefined") {
var element_options = $(element).data('options');
var options = this.options;
if(typeof element_options === "object") {
options = $.extend(true, {}, this.options, element_options);
}
instance = new TC_Instance(element, options);
TC_Instance_List[cur_id] = instance;
}
else {
instance = TC_Instance_List[cur_id];
if (typeof this.options !== "undefined") {
instance.setOptions(this.options);
}
}
return instance;
};
TC_Class.prototype.foreach = function(callback) {
var _this = this;
this.elements.each(function() {
var instance = _this.getInstance(this);
if (typeof callback === "function") {
callback(instance);
}
});
return this;
};
TC_Class.prototype.start = function() {
this.foreach(function(instance) {
instance.start();
});
return this;
};
TC_Class.prototype.stop = function() {
this.foreach(function(instance) {
instance.stop();
});
return this;
};
TC_Class.prototype.restart = function() {
this.foreach(function(instance) {
instance.restart();
});
return this;
};
TC_Class.prototype.rebuild = function() {
this.foreach(function(instance) {
instance.initialize();
});
return this;
};
TC_Class.prototype.getTime = function() {
return this.getInstance(this.elements[0]).timeLeft();
};
TC_Class.prototype.addListener = function(f, type) {
if(typeof type === "undefined") type = "visible";
var _this = this;
this.foreach(function(instance) {
instance.addListener(f, _this.elements, type);
});
return this;
};
TC_Class.prototype.destroy = function() {
this.foreach(function(instance) {
instance.destroy();
});
return this;
};
TC_Class.prototype.end = function() {
return this.elements;
};
$.fn.TimeCircles = function(options) {
return new TC_Class(this, options);
};
}(jQuery));
timercircle.css :
/*
A few CSS properties are set by the javascript
This is in order to allow us to set the size of
the text relative to the size of the circles
and the element they inhabit.
Time circles was fully programmed and designed
by Wim Barelds
So long as you do not remove this message, you
are free to use this on your website. However
please do not redistribute without being given
direct permission from the author
Contact me at: Wim.Barelds@GMail.com
Version: 1.0, 2013/07/25
Todo:
jQuery plugin
[html property] data-ref-date="yyyy/mm/dd hh:mm:ss"
[function] start
[function] stop
[function] destroy
Option:
Days: { Show: true, text: "Days" }
Use of absolute time instead of ref-date
*/
/**
* This element is created inside your target element
* It is used so that your own element will not need to be altered
**/
.time_circles {
position: relative;
width: 100%;
height: 100%;
}
/**
* This is all the elements used to house all text used
* in time circles
**/
.time_circles > div {
position: absolute;
text-align: center;
}
/**
* Titles (Days, Hours, etc)
**/
.time_circles > div > h4 {
margin: 0px;
padding: 0px;
text-align: center;
text-transform: uppercase;
font-family: 'Century Gothic';
}
/**
* Time numbers, ie: 12
**/
.time_circles > div > span {
display: block;
width: 100%;
text-align: center;
font-family: 'Century Gothic';
font-size: 300%;
margin-top: 0.4em;
font-weight: bold;
}
No comments:
Post a Comment