commit
32309b8ca7
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
# Flux Watchface
|
||||
|
||||
This app slightly resembles Apples "Flux" watchface.
|
||||
|
||||
## Usage
|
||||
|
||||
Install it and set it as your default clock.
|
||||
It currently has no widget support. In the future, widgets will be a toggle in the settings.
|
||||
You can change the colors in the settings.
|
||||
|
||||
## Features
|
||||
|
||||
- Customizeable themes (4 color inputs)
|
||||
- Random number alignment (every minute or on reload)
|
||||
- 24h clock (12h not yet supported)
|
||||
- Power-saving mode
|
||||
- Color filling effect in 2 directions (`Left > Right` and `Right > Left` are currently unsupported.)
|
||||
- 12 different colors
|
||||
|
||||
## Controls
|
||||
|
||||
BTN to go to launcher. No other mappings.
|
||||
|
||||
## Requests
|
||||
|
||||
If you have a feature request or a bug, please message me at [sossinaydev@gmail.com](mailto:sossinaydev@gmail.com "Click to send an email").
|
||||
|
||||
## Creator
|
||||
|
||||
Yanis Ocaña, sossinay
|
||||
[pixelnet.ocaña.ch](https://pixelnet.ocaña.ch)
|
||||
|
||||
## Known issues
|
||||
- When loading the app in the first 2 seconds of a minute, the numbers fly in from a seemingly random position
|
||||
- `Left > Right` and `Right > Left` are currently unsupported
|
||||
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwcBpMkyQCHzdt23bAQPTBYc0CIdN23QEwmaEA+TtMkyAREGRGSsEEJQsDpOkCJMJBAQRCEZRKBLIOgCJGTCIYICCJIjECK5ZFkwjcBwanDLMALCEbrjBCIdoCJE2XIO26dpX4W2CIRiBGpSSBEY1Nf4IAGCI9oCJBHHsARRghHGoARJMoQCDR4YICCINAgAjJhIICgECj/wIwwIBn4GDh/4CJPx/4AECJQjBCKEHCAf+BAQRIBCIjRCKBrR+O/8AGCv6PMUKIRjI6LFVCLwGELJYRabQIRPOYoACCI6SBCI+AFgwRSACA"))
|
||||
|
|
@ -0,0 +1,316 @@
|
|||
const font = {
|
||||
"0": [
|
||||
[[0,0],[1,0]],
|
||||
[[1,0],[1,1]],
|
||||
[[1,1],[0,1]],
|
||||
[[0,1],[0,0]]
|
||||
],
|
||||
|
||||
"1": [
|
||||
[[1,0],[1,1]]
|
||||
],
|
||||
|
||||
"2": [
|
||||
[[0,0],[1,0]],
|
||||
[[1,0],[1,0.5]],
|
||||
[[1,0.5],[0,0.5]],
|
||||
[[0,0.5],[0,1]],
|
||||
[[0,1],[1,1]]
|
||||
],
|
||||
|
||||
"3": [
|
||||
[[0,0],[1,0]],
|
||||
[[1,0],[1,1]],
|
||||
[[0,0.5],[1,0.5]],
|
||||
[[0,1],[1,1]]
|
||||
],
|
||||
|
||||
"4": [
|
||||
[[0,0],[0,0.5]],
|
||||
[[1,0],[1,1]],
|
||||
[[0,0.5],[1,0.5]]
|
||||
],
|
||||
|
||||
"5": [
|
||||
[[1,0],[0,0]],
|
||||
[[0,0],[0,0.5]],
|
||||
[[0,0.5],[1,0.5]],
|
||||
[[1,0.5],[1,1]],
|
||||
[[1,1],[0,1]]
|
||||
],
|
||||
|
||||
"6": [
|
||||
[[1,0],[0,0]],
|
||||
[[0,0],[0,1]],
|
||||
[[0,1],[1,1]],
|
||||
[[1,1],[1,0.5]],
|
||||
[[1,0.5],[0,0.5]]
|
||||
],
|
||||
|
||||
"7": [
|
||||
[[0,0],[1,0]],
|
||||
[[1,0],[1,1]]
|
||||
],
|
||||
|
||||
"8": [
|
||||
[[0,0],[1,0]],
|
||||
[[1,0],[1,1]],
|
||||
[[1,1],[0,1]],
|
||||
[[0,1],[0,0]],
|
||||
[[0,0.5],[1,0.5]]
|
||||
],
|
||||
|
||||
"9": [
|
||||
[[0,0],[1,0]],
|
||||
[[0,0],[0,0.5]],
|
||||
[[0,0.5],[1,0.5]],
|
||||
[[1,0],[1,1]]
|
||||
],
|
||||
|
||||
"NaN": [
|
||||
[[],[]]
|
||||
]
|
||||
};
|
||||
|
||||
const corner_base_positions = [
|
||||
[[10, 10], [78, 78]], // top-left (unchanged)
|
||||
[[93, 10], [165, 78]], // top-right (x + 5)
|
||||
[[10, 93], [78, 165]], // bottom-left (y + 5)
|
||||
[[93, 93], [165, 165]] // bottom-right (x + 5, y + 5)
|
||||
];
|
||||
|
||||
let corner_positions = [];
|
||||
let previous_corner_positions = [];
|
||||
|
||||
const padding = 10;
|
||||
const width = 175;
|
||||
const height = 175;
|
||||
|
||||
const colors = {
|
||||
"Black": [0,0,0],
|
||||
"White": [1,1,1],
|
||||
"Red": [1,0,0],
|
||||
"Blue": [0,0,1],
|
||||
"Green": [0,1,0],
|
||||
"Yellow": [1,1,0],
|
||||
"Orange": [1,0.6,0],
|
||||
"Purple": [0.7,0,1],
|
||||
"Lime": [0,1,0.5],
|
||||
"Cyan": [0, 1, 1],
|
||||
"Light Blue": [0,0.5,1],
|
||||
"Pink": [1,0.5,1]
|
||||
};
|
||||
|
||||
let bg_color = [0,0,0];
|
||||
let fg_color = [1,1,1];
|
||||
let bg_color_topo = [1,0,0.5];
|
||||
let fg_color_topo = [0,1,0.5];
|
||||
|
||||
function randomize_numbers(){
|
||||
previous_corner_positions = JSON.parse(JSON.stringify(corner_positions));
|
||||
corner_positions = JSON.parse(JSON.stringify(corner_base_positions));
|
||||
|
||||
if (!previous_corner_positions || previous_corner_positions.length === 0) {
|
||||
previous_corner_positions = JSON.parse(JSON.stringify(corner_positions));
|
||||
}
|
||||
|
||||
// Your offsets and modifications follow here as before
|
||||
let x_offset = Math.floor(Math.random() * 61) - 30;
|
||||
let left_y_offset = Math.floor(Math.random() * 61) - 30;
|
||||
let right_y_offset = Math.floor(Math.random() * 61) - 30;
|
||||
|
||||
[0,1,2,3].forEach(function(i) {
|
||||
let min_x = corner_positions[i][0][0];
|
||||
let min_y = corner_positions[i][0][1];
|
||||
let max_x = corner_positions[i][1][0];
|
||||
let max_y = corner_positions[i][1][1];
|
||||
|
||||
if (min_x !== padding) {
|
||||
min_x += x_offset;
|
||||
}
|
||||
if (max_x !== width - padding) {
|
||||
max_x += x_offset;
|
||||
}
|
||||
|
||||
let yoff = (i % 2) > 0 ? right_y_offset : left_y_offset;
|
||||
|
||||
if (min_y !== padding) {
|
||||
min_y += yoff;
|
||||
}
|
||||
if (max_y !== height - padding) {
|
||||
max_y += yoff;
|
||||
}
|
||||
|
||||
// Update corner_positions with new offsets
|
||||
corner_positions[i][0][0] = min_x;
|
||||
corner_positions[i][0][1] = max_x;
|
||||
corner_positions[i][1][0] = min_y;
|
||||
corner_positions[i][1][1] = max_y;
|
||||
});
|
||||
}
|
||||
|
||||
function drawThickLine(x1, y1, x2, y2, width, topoY) {
|
||||
let half = Math.floor(width / 2);
|
||||
|
||||
// Expand to thickness by offsetting both ends
|
||||
if (x1 === x2) {
|
||||
// Vertical line
|
||||
x1 -= half;
|
||||
x2 += half;
|
||||
} else if (y1 === y2) {
|
||||
// Horizontal line
|
||||
y1 -= half;
|
||||
y2 += half;
|
||||
} else {
|
||||
// Diagonal fallback
|
||||
for (let i = -half; i <= half; i++) {
|
||||
g.drawLine(x1 + i, y1, x2 + i, y2);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Order the coordinates for fillRect
|
||||
let left = Math.min(x1, x2);
|
||||
let right = Math.max(x1, x2);
|
||||
let top = Math.min(y1, y2);
|
||||
let bottom = Math.max(y1, y2);
|
||||
|
||||
// Split based on topoY (topographic height)
|
||||
if (top < topoY && bottom > topoY) {
|
||||
// Split into two regions
|
||||
g.setColor(fg_color_topo[0], fg_color_topo[1], fg_color_topo[2]);
|
||||
g.fillRect(left, top, right, topoY);
|
||||
|
||||
g.setColor(fg_color[0], fg_color[1], fg_color[2]);
|
||||
g.fillRect(left, topoY + 1, right, bottom);
|
||||
} else if (bottom <= topoY) {
|
||||
// Entire line is above topo
|
||||
g.setColor(fg_color_topo[0], fg_color_topo[1], fg_color_topo[2]);
|
||||
g.fillRect(left, top, right, bottom);
|
||||
} else {
|
||||
// Entire line is below topo
|
||||
g.setColor(fg_color[0], fg_color[1], fg_color[2]);
|
||||
g.fillRect(left, top, right, bottom);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function draw_numbers(transition_value, topo_position){
|
||||
//console.log(previous_corner_positions);
|
||||
topo_position *= height;
|
||||
let d = new Date();
|
||||
|
||||
let hour = d.getHours();
|
||||
let padded = ("0" + hour).slice(-2);
|
||||
|
||||
let tl = padded[0];
|
||||
let tr = padded[1];
|
||||
|
||||
let min = d.getMinutes();
|
||||
padded = ("0" + min).slice(-2);
|
||||
|
||||
|
||||
let bl = padded[0];
|
||||
let br = padded[1];
|
||||
|
||||
g.clear();
|
||||
g.setColor(bg_color_topo[0],bg_color_topo[1],bg_color_topo[2]);
|
||||
g.fillRect(0,0,width,topo_position);
|
||||
g.setColor(bg_color[0],bg_color[1],bg_color[2]);
|
||||
g.fillRect(0,topo_position,width,height);
|
||||
|
||||
[tl,tr,bl,br].forEach(function(corner, i) {
|
||||
let path = font[corner];
|
||||
path.forEach(function(line){
|
||||
let pcp = previous_corner_positions[i].slice();
|
||||
let ccp = corner_positions[i].slice();
|
||||
|
||||
let x1 = (ccp[0][0] - pcp[0][0]) * transition_value + pcp[0][0];
|
||||
let x2 = (ccp[0][1] - pcp[0][1]) * transition_value + pcp[0][1];
|
||||
let y1 = (ccp[1][0] - pcp[1][0]) * transition_value + pcp[1][0];
|
||||
let y2 = (ccp[1][1] - pcp[1][1]) * transition_value + pcp[1][1];
|
||||
|
||||
|
||||
|
||||
let point_1 = [line[0][0]*(x2-x1) + x1, line[0][1]*(y2-y1) + y1];
|
||||
let point_2 = [line[1][0]*(x2-x1) + x1, line[1][1]*(y2-y1) + y1];
|
||||
|
||||
drawThickLine(point_1[0], point_1[1], point_2[0], point_2[1], 10, topo_position);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let powersaver = false;
|
||||
let update_timeout = null;
|
||||
|
||||
let prev_min = -1;
|
||||
function update_clock() {
|
||||
let d = new Date();
|
||||
let min = d.getMinutes();
|
||||
|
||||
if (min != prev_min) {
|
||||
randomize_numbers();
|
||||
prev_min = min;
|
||||
}
|
||||
|
||||
if (powersaver) {
|
||||
if (direction == 1) {
|
||||
draw_numbers(1, 2);
|
||||
}
|
||||
else if (direction == 0) {
|
||||
draw_numbers(1, 0);
|
||||
}
|
||||
update_timeout = setTimeout(update_clock, 5000);
|
||||
}
|
||||
else {
|
||||
let seconds = d.getSeconds() + d.getMilliseconds() / 1000;
|
||||
if (direction == 1) {
|
||||
draw_numbers(Math.min(seconds, 1) / 1, 1.00-(seconds/60));
|
||||
}
|
||||
else if (direction == 0) {
|
||||
draw_numbers(Math.min(seconds, 1) / 1, seconds / 60);
|
||||
}
|
||||
if (seconds < 1.5) {
|
||||
update_timeout = setTimeout(update_clock, 50);
|
||||
} else {
|
||||
update_timeout = setTimeout(update_clock, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let settings = require("Storage").readJSON("flux.settings.json", 1) || {};
|
||||
console.log(settings);
|
||||
let direction = settings.direction || 0;
|
||||
bg_color = colors[settings.bg || "Black"];
|
||||
fg_color = colors[settings.fg || "White"];
|
||||
bg_color_topo = colors[settings.bg2 || "Green"];
|
||||
fg_color_topo = colors[settings.fg2 || "Black"];
|
||||
if (direction == 1) {
|
||||
let temp = bg_color;
|
||||
bg_color = bg_color_topo;
|
||||
bg_color_topo = temp;
|
||||
|
||||
temp = fg_color;
|
||||
fg_color = fg_color_topo;
|
||||
fg_color_topo = temp;
|
||||
}
|
||||
|
||||
|
||||
update_clock();
|
||||
|
||||
Bangle.on('backlight', function(isOn) {
|
||||
if (isOn) {
|
||||
powersaver = false;
|
||||
clearTimeout(update_timeout);
|
||||
update_clock();
|
||||
} else {
|
||||
powersaver = true;
|
||||
let d = new Date();
|
||||
let seconds = d.getSeconds() + d.getMilliseconds() / 1000;
|
||||
draw_numbers(Math.min(seconds, 1) / 1, 0);
|
||||
}
|
||||
});
|
||||
|
||||
Bangle.setUI("clock");
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"id": "flux",
|
||||
"name": "Flux Watchface",
|
||||
"shortName": "Flux",
|
||||
"version": "0.01",
|
||||
"description": "A really simple imitation of Apples \"Flux\" Watchface, with different colors.",
|
||||
"icon": "app.png",
|
||||
"tags": "clock",
|
||||
"type": "clock",
|
||||
"supports": [
|
||||
"BANGLEJS2"
|
||||
],
|
||||
"screenshots": [
|
||||
{
|
||||
"url": "screenshot_1.png"
|
||||
},
|
||||
{
|
||||
"url": "screenshot_2.png"
|
||||
}
|
||||
],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{
|
||||
"name": "flux.app.js",
|
||||
"url": "app.js"
|
||||
},
|
||||
{
|
||||
"name": "flux.settings.js",
|
||||
"url": "settings.js"
|
||||
},
|
||||
{
|
||||
"name": "flux.img",
|
||||
"url": "app-icon.js",
|
||||
"evaluate": true
|
||||
}
|
||||
],
|
||||
"data": [
|
||||
{
|
||||
"name": "flux.settings.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
|
|
@ -0,0 +1,83 @@
|
|||
(function(back) {
|
||||
const FILE = "flux.settings.json";
|
||||
|
||||
// Color list shared across fields
|
||||
const COLORS = [
|
||||
"White", "Black", "Red", "Blue", "Green",
|
||||
"Yellow", "Orange", "Purple", "Lime",
|
||||
"Cyan", "Pink", "Light Blue"
|
||||
];
|
||||
|
||||
const directions = [
|
||||
"Up > Down",
|
||||
"Down > Up"
|
||||
];
|
||||
|
||||
function colorIndex(name) {
|
||||
let i = COLORS.indexOf(name);
|
||||
return i >= 0 ? i : 0;
|
||||
}
|
||||
|
||||
// Load existing settings with defaults
|
||||
let settings = Object.assign({
|
||||
direction: 0,
|
||||
bg: "Black",
|
||||
fg: "White",
|
||||
bg2: "Lime",
|
||||
fg2: "Black"
|
||||
}, require("Storage").readJSON(FILE, true) || {});
|
||||
|
||||
function writeSettings() {
|
||||
require("Storage").writeJSON(FILE, settings);
|
||||
}
|
||||
|
||||
E.showMenu({
|
||||
"": { title: "Flux Watchface" },
|
||||
"< Back": back,
|
||||
"Direction": {
|
||||
value: settings.direction,
|
||||
min: 0, max: 1,
|
||||
format: i => directions[i],
|
||||
onchange: i => {
|
||||
settings.direction = i;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
"BG Color": {
|
||||
value: colorIndex(settings.bg),
|
||||
min: 0, max: COLORS.length - 1,
|
||||
format: i => COLORS[i],
|
||||
onchange: i => {
|
||||
settings.bg = COLORS[i];
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
"FG Color": {
|
||||
value: colorIndex(settings.fg),
|
||||
min: 0, max: COLORS.length - 1,
|
||||
format: i => COLORS[i],
|
||||
onchange: i => {
|
||||
settings.fg = COLORS[i];
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
"BG Highlight": {
|
||||
value: colorIndex(settings.bg2),
|
||||
min: 0, max: COLORS.length - 1,
|
||||
format: i => COLORS[i],
|
||||
onchange: i => {
|
||||
settings.bg2 = COLORS[i];
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
"FG Highlight": {
|
||||
value: colorIndex(settings.fg2),
|
||||
min: 0, max: COLORS.length - 1,
|
||||
format: i => COLORS[i],
|
||||
onchange: i => {
|
||||
settings.fg2 = COLORS[i];
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
});
|
||||
})
|
||||
Loading…
Reference in New Issue