add ClockFace module
parent
ec1bed62b5
commit
12f84357ca
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
Most of the boilerplate needed to run a clock.
|
||||||
|
See ClockFace.md for documentation
|
||||||
|
*/
|
||||||
|
function ClockFace(options) {
|
||||||
|
if ("function"=== typeof options) options = {draw: options}; // simple usage
|
||||||
|
// some validation, in the hopes of at least catching typos/basic mistakes
|
||||||
|
Object.keys(options).forEach(k => {
|
||||||
|
if (![
|
||||||
|
"precision",
|
||||||
|
"init", "draw", "update",
|
||||||
|
"pause", "resume",
|
||||||
|
"up", "down", "upDown"
|
||||||
|
].includes(k)) throw `Invalid ClockFace option: ${k}`;
|
||||||
|
});
|
||||||
|
if (!options.draw && !options.update) throw "ClockFace needs at least one of draw() or update() functions";
|
||||||
|
this.draw = options.draw || (t=> {
|
||||||
|
options.update.apply(this, [t, {d: true, h: true, m: true, s: true}]);
|
||||||
|
});
|
||||||
|
this.update = options.update || (t => {
|
||||||
|
g.clear();
|
||||||
|
options.draw.apply(this, [t, {d: true, h: true, m: true, s: true}]);
|
||||||
|
});
|
||||||
|
if (options.precision===1000||options.precision===60000) throw "ClockFace precision is in seconds, not ms";
|
||||||
|
this.precision = (options.precision || 60);
|
||||||
|
if (options.init) this.init = options.init;
|
||||||
|
if (options.pause) this._pause = options.pause;
|
||||||
|
if (options.resume) this._resume = options.resume;
|
||||||
|
if ((options.up || options.down) && options.upDown) throw "ClockFace up/down and upDown cannot be used together";
|
||||||
|
if (options.up || options.down) this._upDown = (dir) => {
|
||||||
|
if (dir<0 && options.up) options.up.apply(this);
|
||||||
|
if (dir>0 && options.down) options.down.apply(this);
|
||||||
|
};
|
||||||
|
if (options.upDown) this._upDown = options.upDown;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClockFace.prototype.tick = function() {
|
||||||
|
const time = new Date();
|
||||||
|
const now = {
|
||||||
|
d: `${time.getFullYear()}-${time.getMonth()}-${time.getDate()}`,
|
||||||
|
h: time.getHours(),
|
||||||
|
m: time.getMinutes(),
|
||||||
|
s: time.getSeconds(),
|
||||||
|
};
|
||||||
|
if (!this._last) {
|
||||||
|
g.clear(true);
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
g.reset();
|
||||||
|
this.draw.apply(this, [time, {d: true, h: true, m: true, s: true}]);
|
||||||
|
} else {
|
||||||
|
let c = {d: false, h: false, m: false, s: false}; // changed
|
||||||
|
if (now.d!==this._last.d) c.d = c.h = c.m = c.s = true;
|
||||||
|
else if (now.h!==this._last.h) c.h = c.m = c.s = true;
|
||||||
|
else if (now.m!==this._last.m) c.m = c.s = true;
|
||||||
|
else if (now.s!==this._last.s) c.s = true;
|
||||||
|
g.reset();
|
||||||
|
this.update.apply(this, [time, c]);
|
||||||
|
}
|
||||||
|
this._last = now;
|
||||||
|
if (this.paused) return; // called redraw() while still paused
|
||||||
|
// figure out timeout: if e.g. precision=60s, update at the start of a new minute
|
||||||
|
const interval = this.precision*1000;
|
||||||
|
this._timeout = setTimeout(() => this.tick(), interval-(Date.now()%interval));
|
||||||
|
};
|
||||||
|
|
||||||
|
ClockFace.prototype.start = function() {
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
if (this.init) this.init.apply(this);
|
||||||
|
if (this._upDown) Bangle.setUI("clockupdown", d=>this._upDown.apply(this,[d]));
|
||||||
|
else Bangle.setUI("clock");
|
||||||
|
delete this._last;
|
||||||
|
this.tick();
|
||||||
|
|
||||||
|
Bangle.on("lcdPower", on => {
|
||||||
|
if (on) this.resume();
|
||||||
|
else this.pause();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
ClockFace.prototype.pause = function() {
|
||||||
|
if (!this._timeout) return; // already paused
|
||||||
|
clearTimeout(this._timeout);
|
||||||
|
delete this._timeout;
|
||||||
|
this.paused = true; // apps might want to check this
|
||||||
|
if (this._pause) this._pause.apply(this);
|
||||||
|
};
|
||||||
|
ClockFace.prototype.resume = function() {
|
||||||
|
if (this._timeout) return; // not paused
|
||||||
|
delete this._last;
|
||||||
|
delete this.paused;
|
||||||
|
if (this._resume) this._resume.apply(this);
|
||||||
|
this.tick(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force a complete redraw
|
||||||
|
*/
|
||||||
|
ClockFace.prototype.redraw = function() {
|
||||||
|
delete this._last;
|
||||||
|
this.tick();
|
||||||
|
};
|
||||||
|
|
||||||
|
exports = ClockFace;
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
ClockFace
|
||||||
|
=========
|
||||||
|
|
||||||
|
This module handles most of the tasks needed to set up a clock, so you can
|
||||||
|
concentrate on drawing the time.
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
Tthe [tutorial clock](https://www.espruino.com/Bangle.js+Clock) converted to use
|
||||||
|
this module:
|
||||||
|
|
||||||
|
```js
|
||||||
|
|
||||||
|
// Load fonts
|
||||||
|
require("Font7x11Numeric7Seg").add(Graphics);
|
||||||
|
// position on screen
|
||||||
|
const X = 160, Y = 140;
|
||||||
|
|
||||||
|
var ClockFace = require("ClockFace");
|
||||||
|
var clock = new ClockFace({
|
||||||
|
precision: 1, // update every second
|
||||||
|
draw: function(d) {
|
||||||
|
// work out how to display the current time
|
||||||
|
var h = d.getHours(), m = d.getMinutes();
|
||||||
|
var time = (" "+h).substr(-2)+":"+("0"+m).substr(-2);
|
||||||
|
// draw the current time (4x size 7 segment)
|
||||||
|
g.setFont("7x11Numeric7Seg", 4);
|
||||||
|
g.setFontAlign(1, 1); // align right bottom
|
||||||
|
g.drawString(time, X, Y, true /*clear background*/);
|
||||||
|
// draw the seconds (2x size 7 segment)
|
||||||
|
g.setFont("7x11Numeric7Seg", 2);
|
||||||
|
g.drawString(("0"+d.getSeconds()).substr(-2), X+30, Y, true /*clear background*/);
|
||||||
|
// draw the date, in a normal font
|
||||||
|
g.setFont("6x8");
|
||||||
|
g.setFontAlign(0, 1); // align center bottom
|
||||||
|
// pad the date - this clears the background if the date were to change length
|
||||||
|
var dateStr = " "+require("locale").date(d)+" ";
|
||||||
|
g.drawString(dateStr, g.getWidth()/2, Y+15, true /*clear background*/);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
clock.start();
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Complete Usage
|
||||||
|
--------------
|
||||||
|
|
||||||
|
```js
|
||||||
|
|
||||||
|
var ClockFace = require("ClockFace");
|
||||||
|
var clock = new ClockFace({
|
||||||
|
precision: 1, // optional, defaults to 60: how often to call update(), in seconds
|
||||||
|
init: function() { // optional
|
||||||
|
// called only once before starting the clock, but after setting up the
|
||||||
|
// screen/widgets, so you can use Bangle.appRect
|
||||||
|
},
|
||||||
|
draw: function(time, changed) { // at least draw or update is required
|
||||||
|
// (re)draw entire clockface, time is a Date object
|
||||||
|
// `changed` is the same format as for update() below, but always all true
|
||||||
|
},
|
||||||
|
// The difference between draw() and update() is that the screen is cleared
|
||||||
|
// before draw() is called, so it needs to always redraw the entire clock
|
||||||
|
update: function(time, changed) { // at least draw or update is required
|
||||||
|
// redraw date/time, time is a Date object
|
||||||
|
// if you want, you can only redraw the changed parts:
|
||||||
|
if (changed.d) // redraw date (changed.h/m/s will also all be true)
|
||||||
|
if (changed.h) // redraw hours
|
||||||
|
if (changed.m) // redraw minutes
|
||||||
|
if (changed.s) // redraw seconds
|
||||||
|
},
|
||||||
|
pause: function() { // optional, called when the screen turns off
|
||||||
|
// for example: turn off GPS/compass if the watch used it
|
||||||
|
},
|
||||||
|
resume: function() { // optional, called when the screen turns on
|
||||||
|
// for example: turn GPS/compass back on
|
||||||
|
},
|
||||||
|
up: function() { // optional, up handler
|
||||||
|
},
|
||||||
|
down: function() { // optional, down handler
|
||||||
|
},
|
||||||
|
upDown: function(dir) { // optional, combined up/down handler
|
||||||
|
if (dir === -1) // Up
|
||||||
|
else // (dir === 1): Down
|
||||||
|
},
|
||||||
|
});
|
||||||
|
clock.start();
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Simple Usage
|
||||||
|
------------
|
||||||
|
Basic clocks can pass just a function to redraw the entire screen every minute:
|
||||||
|
|
||||||
|
```js
|
||||||
|
|
||||||
|
var ClockFace = require("ClockFace");
|
||||||
|
var clock = new ClockFace(function(time) {
|
||||||
|
// draw the current time at the center of the screen
|
||||||
|
g.setFont("Vector:50").setFontAlign(0, 0)
|
||||||
|
.drawString(
|
||||||
|
require("locale").time(time, true),
|
||||||
|
Bangle.appRect.w/2, Bangle.appRect.h/2
|
||||||
|
);
|
||||||
|
});
|
||||||
|
clock.start();
|
||||||
|
|
||||||
|
```
|
||||||
Loading…
Reference in New Issue