Skip to content

Instantly share code, notes, and snippets.

@magcks magcks/clock.js
Last active Aug 29, 2015

Embed
What would you like to do?
"use strict";
/**
* Canvas Clock (GPLv2)
*
* @author Max (http://m9x.de)
* @version 1.0
*
* Original version and design by http://www.board3.de/
* JavaScript clone by Max (m9x.de)
*/
/**
* A wrapper class emulating a vectorized context for a canvas.
*
* @param ctx The canvas 2d context.
* @param virtual_width The virtual width of the canvas (default: 100).
* @param virtual_height The virtual height of the canvas (default:100).
*/
function VecCtx(ctx, virtual_width, virtual_height) {
this.ctx = ctx;
this.virtual_width = virtual_width || 100;
this.virtual_height = virtual_height || 100;
}
/**
* Computes the absolute coordinates from the relative ones.
*
* @param x The x value.
* @param y The y value (If the y value is a string ("x" or "y") only the x value will be computed as a x or y value (specified by the string).
* @return An array of the absolute coordinates (It will be no array if only one value is given).
*/
VecCtx.prototype.rel2abs = function(x, y) {
var single = null;
if (typeof y == 'string') {
single = Number(y == "x");
if (single == 'x')
y = 0;
else {
y = x;
x = 0;
}
}
var res = [ (x / this.virtual_width) * this.ctx.canvas.width, (y / this.virtual_height) * this.ctx.canvas.height, ];
if (single !== null)
return res[single];
else
return res;
};
/**
* Executes an method to the canvas context.
*
* @param method The method name.
* @param ... The arguments of the method.
* @return Return value of the executed method.
*/
VecCtx.prototype.exec = function(method) {
var args = Array.prototype.slice.call(arguments, 1);
var parsed = [];
for (var i in args) {
if (args[i] instanceof VecCtx.Rel) {
var val = this.rel2abs(args[i].x, args[i].y);
if (Array.isArray(val)) {
parsed.push(val[0]);
parsed.push(val[1]);
}
else
parsed.push(val);
}
else
parsed.push(args[i]);
}
return this.ctx[method].apply(this.ctx, parsed);
};
/**
* An class to signalize the exec method that this is a relative value.
*
* @param x The x coordinate.
* @param y The y coordinate.
*/
VecCtx.Rel = function(x, y) {
this.x = x;
this.y = y;
};
/**
* This class draws the clock to the canvas.
*
* @param canvas The canvas to which the clock should be drawn.
* @param refreshTime The time of a refresh cycle.
*/
function Clock(canvas, refreshTime) {
this.vec = new VecCtx(canvas.getContext("2d"), 100, 100);
this.refreshTime = refreshTime || 10;
this.interval = null;
}
/**
* Starts the clock interval.
*/
Clock.prototype.start = function() {
var self = this;
this.interval = setInterval(function() {
self.refresh();
}, this.refreshTime);
this.refresh();
};
/**
* Stops the clock interval.
*/
Clock.prototype.stop = function() {
clearInterval(this.interval);
this.interval = null;
};
/**
* This method is called during a refresh cycle.
*/
Clock.prototype.refresh = function() {
this.vec.ctx.clearRect(0, 0, this.vec.ctx.canvas.width, this.vec.ctx.canvas.height);
this.drawBackground();
this.drawTime(new Date);
};
/**
* Draw the background art to the canvas.
*/
Clock.prototype.drawBackground = function() {
var vec = this.vec;
var ctx = vec.ctx;
var radius = 43;
var gradient_background = vec.exec("createLinearGradient", new VecCtx.Rel(0, 0), new VecCtx.Rel(0, 90));
gradient_background.addColorStop(0, "#0077bb");
gradient_background.addColorStop(1, "#12a1e9");
var gradient_border = vec.exec("createLinearGradient", new VecCtx.Rel(0, 0), new VecCtx.Rel(0, 100));
gradient_border.addColorStop(0, "#8a8a8a");
gradient_border.addColorStop(1, "#d9d9d9");
var gradient_border_outer = vec.exec("createLinearGradient", new VecCtx.Rel(0, 0), new VecCtx.Rel(0, 100));
gradient_border_outer.addColorStop(0, "#d9d9d9");
gradient_border_outer.addColorStop(1, "#8a8a8a");
// outer border
ctx.beginPath();
vec.exec("arc", new VecCtx.Rel(50, 50), new VecCtx.Rel(radius + 3, "x"), 0, 2 * Math.PI, false);
ctx.strokeStyle = gradient_border_outer;
ctx.lineWidth = vec.rel2abs(6, "x");
ctx.stroke();
// background
ctx.beginPath();
vec.exec("arc", new VecCtx.Rel(50, 50), new VecCtx.Rel(radius, "x"), 0, 2 * Math.PI, false);
ctx.fillStyle = gradient_background;
ctx.fill();
ctx.strokeStyle = gradient_border;
ctx.lineWidth = vec.rel2abs(4, "x");
ctx.stroke();
// reflexion
var start_x = 17, start_y = 28;
var c1_x = 21, c1_y = 38;
var c2_x = 13, c2_y = 35;
var c3_x = 25, c3_y = 35;
var hor1_c1_x = 35, hor1_c1_y = 5;
var hor1_c2_x = 100 - hor1_c1_x, hor1_c2_y = hor1_c1_y;
var hor2_c1_x = hor1_c2_x, hor2_c1_y = 28;
var hor2_c2_x = hor1_c1_x, hor2_c2_y = hor2_c1_y;
ctx.beginPath();
vec.exec("moveTo", new VecCtx.Rel(start_x, start_y));
vec.exec("bezierCurveTo", new VecCtx.Rel(hor1_c1_x, hor1_c1_y), new VecCtx.Rel(hor1_c2_x, hor1_c2_y), new VecCtx.Rel(100 - start_x, start_y)); // top
vec.exec("bezierCurveTo", new VecCtx.Rel(100 - c2_x, c2_y), new VecCtx.Rel(100 - c1_x, c1_y), new VecCtx.Rel(100 - c3_x, c3_y)); // right
vec.exec("bezierCurveTo", new VecCtx.Rel(hor2_c1_x, hor2_c1_y), new VecCtx.Rel(hor2_c2_x, hor2_c2_y), new VecCtx.Rel(c3_x, c3_y)); // bottom
vec.exec("bezierCurveTo", new VecCtx.Rel(c1_x, c1_y), new VecCtx.Rel(c2_x, c2_y), new VecCtx.Rel(start_x, start_y)); // left
ctx.fillStyle = "rgba(255, 255, 255, .2)";
ctx.fill();
ctx.strokeStyle = "#fff";
ctx.lineWidth = vec.rel2abs(1, "x");
// marker
var markers = 12;
var angle = 2 * Math.PI / markers;
for (var i = 0; i < markers; ++i) {
var current_angle = i * angle;
var pos1 = this.angle2xy(current_angle, 43);
var pos2 = this.angle2xy(current_angle, 36);
ctx.beginPath();
vec.exec("moveTo", new VecCtx.Rel(pos1[0], pos1[1]));
vec.exec("lineTo", new VecCtx.Rel(pos2[0], pos2[1]));
ctx.stroke();
}
};
/**
* Draw all pointers to the canvas.
*
* @param date The current time (JavaScript Date object).
*/
Clock.prototype.drawTime = function(date) {
var vec = this.vec;
var ctx = vec.ctx;
var timestamp = (date.getTime() / 1000) % (60 * 60 * 24);
timestamp -= date.getTimezoneOffset() * 60;
var hours = timestamp / (60 * 60);
var minutes = timestamp / 60;
var seconds = timestamp;
var a_h = this.time2angle(hours, 12);
var a_m = this.time2angle(minutes, 60);
var a_s = this.time2angle(seconds, 60);
// hours
ctx.fillStyle = "#000";
this.drawPointer(a_h, -10, 34, 2);
// minutes
ctx.fillStyle = "#000";
this.drawPointer(a_m, -11, 42, 1);
// seconds
ctx.fillStyle = "#900";
this.drawPointer(a_s, -12, 43, 1);
};
/**
* Draw a single pointer to the canvas.
*
* @param angle The angle of the pointer
* @param from The position from the middle of the clock circle the pointer should start (may be negative).
* @param to The position from the middle of the clock circle the pointer should end (may be negative but makes no sense).
* @param size The size of the thick side of the pointer.
*/
Clock.prototype.drawPointer = function(angle, from, to, size) {
var vec = this.vec;
var ctx = vec.ctx;
var pos1 = this.angle2xy(angle, to);
var pos2_raw = this.angle2xy(angle, from);
var vec_x = pos2_raw[0] - pos1[0];
var vec_y = pos2_raw[1] - pos1[1];
var n = Math.sqrt(vec_x * vec_x + vec_y * vec_y);
var pos2 = [ pos2_raw[0] + vec_y / n * size, pos2_raw[1] + -vec_x / n * size, ];
var pos3 = [ pos2_raw[0] + -vec_y / n * size, pos2_raw[1] + vec_x / n * size, ];
ctx.beginPath();
vec.exec("moveTo", new VecCtx.Rel(pos2[0], pos2[1]));
vec.exec("lineTo", new VecCtx.Rel(pos1[0], pos1[1]));
vec.exec("lineTo", new VecCtx.Rel(pos3[0], pos3[1]));
ctx.fill();
};
/**
* Converts the time to an angle.
*
* @param time The time as a double value.
* @param base The base of the time format (12 = hours; 60 = minutes or seconds)
* @return The angle value (in radians!).
*/
Clock.prototype.time2angle = function(time, base) {
return (time % base) / base * 2 * Math.PI;
};
/**
* Converts the angle to coordinates.
*
* @param time The time as a double value.
* @param base The base of the time format (12 = hours; 60 = minutes or seconds)
* @return An array of coordinates (index 0 = x; index 1 = y).
*/
Clock.prototype.angle2xy = function(angle, radius) {
return [ 50 - (radius - 3) * -Math.sin(angle), 50 - (radius - 3) * Math.cos(angle), ];
};
@Kerrith

This comment has been minimized.

Copy link

Kerrith commented May 14, 2015

Newbe here.
I made a file named clock.js and uploaded it to /forums/styles/aero/theme/images/portal/clock.js

via ACP > Extensions > Portal Modules > Clock Setting I changed the name of Module Image from portal_clock.png to clock.js and where it reads: "Clock: Enter the filename of your clock. The clock needs to be located in styles/yourstyle/theme/images/portal/."

I entered clock.js. Now in Portal http://www.comcom121.org/forums/app.php/portal the digital clock doesn't show but neither does the analog clock.

I also tried putting clock.js in styles/yourstyle/theme/images/portal/clock.js but no luck
No doubt I'm not understanding something.

Kerry

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.