Skip to content

Instantly share code, notes, and snippets.

@magcks
Last active August 29, 2015 14:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save magcks/11153513 to your computer and use it in GitHub Desktop.
Save magcks/11153513 to your computer and use it in GitHub Desktop.
"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
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