mirror of https://github.com/espruino/BangleApps
Matrix Clock - First version
parent
72b9722666
commit
9b7bd7c156
14
apps.json
14
apps.json
|
@ -319,6 +319,20 @@
|
||||||
{"name":"sweepclock.img","url":"sweepclock-icon.js","evaluate":true}
|
{"name":"sweepclock.img","url":"sweepclock-icon.js","evaluate":true}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{ "id": "matrixclock",
|
||||||
|
"name": "Matrix Clock",
|
||||||
|
"icon": "matrixclock.png",
|
||||||
|
"version":"0.01",
|
||||||
|
"description": "inspired by The Matrix, a clock of the same style",
|
||||||
|
"tags": "clock",
|
||||||
|
"type":"clock",
|
||||||
|
"allow_emulator":true,
|
||||||
|
"readme": "README.md",
|
||||||
|
"storage": [
|
||||||
|
{"name":"matrixclock.app.js","url":"matrixclock.js"},
|
||||||
|
{"name":"matrixclock.img","url":"matrixclock-icon.js","evaluate":true}
|
||||||
|
]
|
||||||
|
},
|
||||||
{ "id": "imgclock",
|
{ "id": "imgclock",
|
||||||
"name": "Image background clock",
|
"name": "Image background clock",
|
||||||
"shortName":"Image Clock",
|
"shortName":"Image Clock",
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: Initial Release
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Matrix Clock
|
||||||
|
|
||||||
|
Inspired by the Matrix, a clock in the same style
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Requests
|
||||||
|
|
||||||
|
Please reach out to adrian@adriankirk.com if you have feature requests or notice bugs.
|
||||||
|
|
||||||
|
## Creator
|
||||||
|
|
||||||
|
Made by [Adrian Kirk](mailto:adrian@adriankirk.com)
|
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("lEowkA/4AGmYIHABHzmVCCaE0kUin4TPmUimQTQ+UzmcvJ6EjCaP/kYABCaEymYTl+Q7SMgITTmQTQPAK0RMgITm+QTS+ciPCcikQpPY4MjmYTO+czmcyHh4TCmcvJ54nCPCBjBJx4oECc8zJ6ATTn48RE4YTTHh4SDH4ImRFBwTGFBgTGFBgSGFBYmHUgITRmcyFBASFAoUjE5PzkQLBHJxiDAQP/GxAA=="))
|
|
@ -0,0 +1,272 @@
|
||||||
|
/**
|
||||||
|
* Adrian Kirk 2021-10
|
||||||
|
*
|
||||||
|
* Matrix Clock
|
||||||
|
*
|
||||||
|
* A simple clock inspired by the movie.
|
||||||
|
* Text shards move down the screen as a background to the
|
||||||
|
* time and date
|
||||||
|
**/
|
||||||
|
const Locale = require('locale');
|
||||||
|
|
||||||
|
const SHARD_COLOR =[0,1.0,0];
|
||||||
|
const SHARD_FONT_SIZE = 12;
|
||||||
|
const SHARD_Y_START = 30;
|
||||||
|
/**
|
||||||
|
* The text shard object is responsible for creating the
|
||||||
|
* shards of text that move down the screen. As the
|
||||||
|
* shard moves down the screen the latest character added
|
||||||
|
* is brightest with characters being coloured darker and darker
|
||||||
|
* going back to the eldest
|
||||||
|
*/
|
||||||
|
class TextShard {
|
||||||
|
|
||||||
|
constructor(x,y,length){
|
||||||
|
// The x and y coords of the first character of the shard
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
// The visible length of the shard. We don't make the
|
||||||
|
// whole chain visible just to save on cpu time
|
||||||
|
this.length = length;
|
||||||
|
// the list of characters making up this shard
|
||||||
|
this.txt = [];
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* The add method call adds another random character to
|
||||||
|
* the chain
|
||||||
|
*/
|
||||||
|
add(){
|
||||||
|
this.txt.push(randomChar());
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* The show method displays the latest shard image to the
|
||||||
|
* screen with the following rules:
|
||||||
|
* - latest addition is brightest, oldest is darker
|
||||||
|
* - display up to defined length of characters only
|
||||||
|
* of the shard to save cpu
|
||||||
|
*/
|
||||||
|
show(){
|
||||||
|
g.setFontAlign(-1,-1,0);
|
||||||
|
for(var i=0; i<Math.min(this.txt.length, this.length + 1) ; i++){
|
||||||
|
idx = this.txt.length - i - 1;
|
||||||
|
var color_strength=1 - i/this.length;
|
||||||
|
if(i > this.length - 2){
|
||||||
|
color_strength = 0;
|
||||||
|
}
|
||||||
|
g.setColor(color_strength*SHARD_COLOR[0],
|
||||||
|
color_strength*SHARD_COLOR[1],
|
||||||
|
color_strength*SHARD_COLOR[2]);
|
||||||
|
g.setFont("Vector",SHARD_FONT_SIZE);
|
||||||
|
g.drawString(this.txt[idx], this.x, this.y + idx*SHARD_FONT_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Method tests to see if any part of the shard chain is still
|
||||||
|
* visible on the screen
|
||||||
|
*/
|
||||||
|
isVisible(){
|
||||||
|
return (this.y + (this.txt.length - this.length - 2)*SHARD_FONT_SIZE < g.getHeight());
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* resets the shard back to the top of the screen
|
||||||
|
*/
|
||||||
|
reset(){
|
||||||
|
this.y = SHARD_Y_START;
|
||||||
|
this.txt = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* random character chooser to be called by the shard when adding characters
|
||||||
|
*/
|
||||||
|
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~`!@#$%^&*()_+-={}[]:";\'<>?,./|\\';
|
||||||
|
function randomChar(){
|
||||||
|
return chars[Math.floor(Math.random() * chars.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now set up the shards
|
||||||
|
// we are going to have a limited no of shards (to save cpu)
|
||||||
|
// but randomize the x value and length every reset to make it look as if there
|
||||||
|
// are more
|
||||||
|
var shards = [];
|
||||||
|
const NO_SHARDS = 4;
|
||||||
|
const channel_width = g.getWidth()/NO_SHARDS;
|
||||||
|
|
||||||
|
function shard_x(i){
|
||||||
|
return i*channel_width + Math.random() * channel_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
function shard_length(){
|
||||||
|
return Math.floor(Math.random()*5) + 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var i=0; i<NO_SHARDS; i++){
|
||||||
|
shards.push(new TextShard(shard_x(i),50 + Math.random()*100,shard_length()) );
|
||||||
|
}
|
||||||
|
|
||||||
|
var timeStr = "";
|
||||||
|
var dateStr = "";
|
||||||
|
var last_draw_time = null;
|
||||||
|
|
||||||
|
const TIME_X_COORD = 20;
|
||||||
|
const TIME_Y_COORD = 100;
|
||||||
|
const DATE_X_COORD = 170;
|
||||||
|
const DATE_Y_COORD = 30;
|
||||||
|
/**
|
||||||
|
* main loop to draw the clock face
|
||||||
|
*/
|
||||||
|
function draw_clock(){
|
||||||
|
// first move all the shards down the screen
|
||||||
|
for(var i=0; i<this.shards.length; i++){
|
||||||
|
if(!shards[i].isVisible() && Math.random() > 0.7){
|
||||||
|
shards[i].reset();
|
||||||
|
shards[i].length = shard_length();
|
||||||
|
shards[i].x = shard_x(i);
|
||||||
|
if(shards[i].x > DATE_X_COORD - 20){
|
||||||
|
shards[i].y = 50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shards[i].add();
|
||||||
|
shards[i].show();
|
||||||
|
}
|
||||||
|
var now = new Date();
|
||||||
|
// draw time. Have to draw time on every loop
|
||||||
|
g.setFont("Vector",45);
|
||||||
|
g.setFontAlign(-1,-1,0);
|
||||||
|
if(last_draw_time == null || now.getMinutes() != last_draw_time.getMinutes()){
|
||||||
|
g.setColor(0,0,0);
|
||||||
|
g.drawString(timeStr, TIME_X_COORD, TIME_Y_COORD);
|
||||||
|
timeStr = format_time(now);
|
||||||
|
}
|
||||||
|
g.setColor(SHARD_COLOR[0],
|
||||||
|
SHARD_COLOR[1],
|
||||||
|
SHARD_COLOR[2]);
|
||||||
|
g.drawString(timeStr, TIME_X_COORD, TIME_Y_COORD);
|
||||||
|
//
|
||||||
|
// draw date when it changes
|
||||||
|
g.setFont("Vector",15);
|
||||||
|
g.setFontAlign(-1,-1,0);
|
||||||
|
if(last_draw_time == null || now.getDate() != last_draw_time.getDate()){
|
||||||
|
g.setColor(0,0,0);
|
||||||
|
g.drawString(dateStr, DATE_X_COORD, DATE_Y_COORD);
|
||||||
|
dateStr = format_date(now);
|
||||||
|
g.setColor(SHARD_COLOR[0],
|
||||||
|
SHARD_COLOR[1],
|
||||||
|
SHARD_COLOR[2]);
|
||||||
|
g.drawString(dateStr, DATE_X_COORD, DATE_Y_COORD);
|
||||||
|
}
|
||||||
|
last_draw_time = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
function format_date(now){
|
||||||
|
return Locale.dow(now,1) + " " + format00(now.getDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function format_time(now){
|
||||||
|
var time = new Date(now.getTime());
|
||||||
|
var hours = time.getHours() % 12;
|
||||||
|
if(hours < 1){
|
||||||
|
hours = 12;
|
||||||
|
}
|
||||||
|
var am_pm;
|
||||||
|
if(time.getHours() < 12){
|
||||||
|
am_pm = "AM";
|
||||||
|
} else {
|
||||||
|
am_pm = "PM";
|
||||||
|
}
|
||||||
|
return format00(hours) + ":" + format00(time.getMinutes()) + " "+ am_pm;
|
||||||
|
}
|
||||||
|
|
||||||
|
function format00(num){
|
||||||
|
var value = (num | 0);
|
||||||
|
if(value > 99 || value < 0)
|
||||||
|
throw "must be between in range 0-99";
|
||||||
|
if(value < 10)
|
||||||
|
return "0" + value.toString();
|
||||||
|
else
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function button1pressed(){
|
||||||
|
console.log("button 1 pressed");
|
||||||
|
}
|
||||||
|
|
||||||
|
function button3pressed(){
|
||||||
|
console.log("button 3 pressed");
|
||||||
|
}
|
||||||
|
|
||||||
|
function load_settings(){
|
||||||
|
console.log("load_settings called");
|
||||||
|
}
|
||||||
|
|
||||||
|
// The interval reference for updating the clock
|
||||||
|
let intervalRef = null;
|
||||||
|
|
||||||
|
function clearTimers(){
|
||||||
|
if(intervalRef != null) {
|
||||||
|
clearInterval(intervalRef);
|
||||||
|
intervalRef = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldRedraw(){
|
||||||
|
return Bangle.isLCDOn();
|
||||||
|
}
|
||||||
|
|
||||||
|
function startTimers(){
|
||||||
|
clearTimers();
|
||||||
|
if (Bangle.isLCDOn()) {
|
||||||
|
intervalRef = setInterval(() => {
|
||||||
|
if (!shouldRedraw()) {
|
||||||
|
//console.log("draw clock callback - skipped redraw");
|
||||||
|
} else {
|
||||||
|
draw_clock();
|
||||||
|
}
|
||||||
|
}, 100
|
||||||
|
);
|
||||||
|
|
||||||
|
if (shouldRedraw()) {
|
||||||
|
draw_clock();
|
||||||
|
} else {
|
||||||
|
console.log("scheduleDrawClock - skipped redraw");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log("scheduleDrawClock - skipped not visible");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Bangle.on('lcdPower', (on) => {
|
||||||
|
if (on) {
|
||||||
|
console.log("lcdPower: on");
|
||||||
|
startTimers();
|
||||||
|
} else {
|
||||||
|
console.log("lcdPower: off");
|
||||||
|
clearTimers();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Bangle.on('faceUp',function(up){
|
||||||
|
//console.log("faceUp: " + up + " LCD: " + Bangle.isLCDOn());
|
||||||
|
if (up && !Bangle.isLCDOn()) {
|
||||||
|
//console.log("faceUp and LCD off");
|
||||||
|
clearTimers();
|
||||||
|
Bangle.setLCDPower(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
g.clear();
|
||||||
|
load_settings();
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
|
startTimers();
|
||||||
|
// Show launcher when middle button pressed
|
||||||
|
setWatch(Bangle.showLauncher, BTN2,{repeat:false,edge:"falling"});
|
||||||
|
|
||||||
|
// Handle button 1 being pressed
|
||||||
|
setWatch(button1pressed, BTN1,{repeat:true,edge:"falling"});
|
||||||
|
|
||||||
|
// Handle button 3 being pressed
|
||||||
|
setWatch(button3pressed, BTN3,{repeat:true,edge:"falling"});
|
Binary file not shown.
After Width: | Height: | Size: 2.6 KiB |
Loading…
Reference in New Issue