Skip to content

Commit 32309b8

Browse files
authored
Merge pull request #3959 from sossinayDev/master
Created Flux Watchface Clock
2 parents 0c394ed + 346dcd1 commit 32309b8

File tree

9 files changed

+478
-0
lines changed

9 files changed

+478
-0
lines changed

apps/flux/ChangeLog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0.01: New App!

apps/flux/README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Flux Watchface
2+
3+
This app slightly resembles Apples "Flux" watchface.
4+
5+
## Usage
6+
7+
Install it and set it as your default clock.
8+
It currently has no widget support. In the future, widgets will be a toggle in the settings.
9+
You can change the colors in the settings.
10+
11+
## Features
12+
13+
- Customizeable themes (4 color inputs)
14+
- Random number alignment (every minute or on reload)
15+
- 24h clock (12h not yet supported)
16+
- Power-saving mode
17+
- Color filling effect in 2 directions (`Left > Right` and `Right > Left` are currently unsupported.)
18+
- 12 different colors
19+
20+
## Controls
21+
22+
BTN to go to launcher. No other mappings.
23+
24+
## Requests
25+
26+
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").
27+
28+
## Creator
29+
30+
Yanis Ocaña, sossinay
31+
[pixelnet.ocaña.ch](https://pixelnet.ocaña.ch)
32+
33+
## Known issues
34+
- When loading the app in the first 2 seconds of a minute, the numbers fly in from a seemingly random position
35+
- `Left > Right` and `Right > Left` are currently unsupported

apps/flux/app-icon.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/flux/app.js

Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
const font = {
2+
"0": [
3+
[[0,0],[1,0]],
4+
[[1,0],[1,1]],
5+
[[1,1],[0,1]],
6+
[[0,1],[0,0]]
7+
],
8+
9+
"1": [
10+
[[1,0],[1,1]]
11+
],
12+
13+
"2": [
14+
[[0,0],[1,0]],
15+
[[1,0],[1,0.5]],
16+
[[1,0.5],[0,0.5]],
17+
[[0,0.5],[0,1]],
18+
[[0,1],[1,1]]
19+
],
20+
21+
"3": [
22+
[[0,0],[1,0]],
23+
[[1,0],[1,1]],
24+
[[0,0.5],[1,0.5]],
25+
[[0,1],[1,1]]
26+
],
27+
28+
"4": [
29+
[[0,0],[0,0.5]],
30+
[[1,0],[1,1]],
31+
[[0,0.5],[1,0.5]]
32+
],
33+
34+
"5": [
35+
[[1,0],[0,0]],
36+
[[0,0],[0,0.5]],
37+
[[0,0.5],[1,0.5]],
38+
[[1,0.5],[1,1]],
39+
[[1,1],[0,1]]
40+
],
41+
42+
"6": [
43+
[[1,0],[0,0]],
44+
[[0,0],[0,1]],
45+
[[0,1],[1,1]],
46+
[[1,1],[1,0.5]],
47+
[[1,0.5],[0,0.5]]
48+
],
49+
50+
"7": [
51+
[[0,0],[1,0]],
52+
[[1,0],[1,1]]
53+
],
54+
55+
"8": [
56+
[[0,0],[1,0]],
57+
[[1,0],[1,1]],
58+
[[1,1],[0,1]],
59+
[[0,1],[0,0]],
60+
[[0,0.5],[1,0.5]]
61+
],
62+
63+
"9": [
64+
[[0,0],[1,0]],
65+
[[0,0],[0,0.5]],
66+
[[0,0.5],[1,0.5]],
67+
[[1,0],[1,1]]
68+
],
69+
70+
"NaN": [
71+
[[],[]]
72+
]
73+
};
74+
75+
const corner_base_positions = [
76+
[[10, 10], [78, 78]], // top-left (unchanged)
77+
[[93, 10], [165, 78]], // top-right (x + 5)
78+
[[10, 93], [78, 165]], // bottom-left (y + 5)
79+
[[93, 93], [165, 165]] // bottom-right (x + 5, y + 5)
80+
];
81+
82+
let corner_positions = [];
83+
let previous_corner_positions = [];
84+
85+
const padding = 10;
86+
const width = 175;
87+
const height = 175;
88+
89+
const colors = {
90+
"Black": [0,0,0],
91+
"White": [1,1,1],
92+
"Red": [1,0,0],
93+
"Blue": [0,0,1],
94+
"Green": [0,1,0],
95+
"Yellow": [1,1,0],
96+
"Orange": [1,0.6,0],
97+
"Purple": [0.7,0,1],
98+
"Lime": [0,1,0.5],
99+
"Cyan": [0, 1, 1],
100+
"Light Blue": [0,0.5,1],
101+
"Pink": [1,0.5,1]
102+
};
103+
104+
let bg_color = [0,0,0];
105+
let fg_color = [1,1,1];
106+
let bg_color_topo = [1,0,0.5];
107+
let fg_color_topo = [0,1,0.5];
108+
109+
function randomize_numbers(){
110+
previous_corner_positions = JSON.parse(JSON.stringify(corner_positions));
111+
corner_positions = JSON.parse(JSON.stringify(corner_base_positions));
112+
113+
if (!previous_corner_positions || previous_corner_positions.length === 0) {
114+
previous_corner_positions = JSON.parse(JSON.stringify(corner_positions));
115+
}
116+
117+
// Your offsets and modifications follow here as before
118+
let x_offset = Math.floor(Math.random() * 61) - 30;
119+
let left_y_offset = Math.floor(Math.random() * 61) - 30;
120+
let right_y_offset = Math.floor(Math.random() * 61) - 30;
121+
122+
[0,1,2,3].forEach(function(i) {
123+
let min_x = corner_positions[i][0][0];
124+
let min_y = corner_positions[i][0][1];
125+
let max_x = corner_positions[i][1][0];
126+
let max_y = corner_positions[i][1][1];
127+
128+
if (min_x !== padding) {
129+
min_x += x_offset;
130+
}
131+
if (max_x !== width - padding) {
132+
max_x += x_offset;
133+
}
134+
135+
let yoff = (i % 2) > 0 ? right_y_offset : left_y_offset;
136+
137+
if (min_y !== padding) {
138+
min_y += yoff;
139+
}
140+
if (max_y !== height - padding) {
141+
max_y += yoff;
142+
}
143+
144+
// Update corner_positions with new offsets
145+
corner_positions[i][0][0] = min_x;
146+
corner_positions[i][0][1] = max_x;
147+
corner_positions[i][1][0] = min_y;
148+
corner_positions[i][1][1] = max_y;
149+
});
150+
}
151+
152+
function drawThickLine(x1, y1, x2, y2, width, topoY) {
153+
let half = Math.floor(width / 2);
154+
155+
// Expand to thickness by offsetting both ends
156+
if (x1 === x2) {
157+
// Vertical line
158+
x1 -= half;
159+
x2 += half;
160+
} else if (y1 === y2) {
161+
// Horizontal line
162+
y1 -= half;
163+
y2 += half;
164+
} else {
165+
// Diagonal fallback
166+
for (let i = -half; i <= half; i++) {
167+
g.drawLine(x1 + i, y1, x2 + i, y2);
168+
}
169+
return;
170+
}
171+
172+
// Order the coordinates for fillRect
173+
let left = Math.min(x1, x2);
174+
let right = Math.max(x1, x2);
175+
let top = Math.min(y1, y2);
176+
let bottom = Math.max(y1, y2);
177+
178+
// Split based on topoY (topographic height)
179+
if (top < topoY && bottom > topoY) {
180+
// Split into two regions
181+
g.setColor(fg_color_topo[0], fg_color_topo[1], fg_color_topo[2]);
182+
g.fillRect(left, top, right, topoY);
183+
184+
g.setColor(fg_color[0], fg_color[1], fg_color[2]);
185+
g.fillRect(left, topoY + 1, right, bottom);
186+
} else if (bottom <= topoY) {
187+
// Entire line is above topo
188+
g.setColor(fg_color_topo[0], fg_color_topo[1], fg_color_topo[2]);
189+
g.fillRect(left, top, right, bottom);
190+
} else {
191+
// Entire line is below topo
192+
g.setColor(fg_color[0], fg_color[1], fg_color[2]);
193+
g.fillRect(left, top, right, bottom);
194+
}
195+
}
196+
197+
198+
199+
200+
function draw_numbers(transition_value, topo_position){
201+
//console.log(previous_corner_positions);
202+
topo_position *= height;
203+
let d = new Date();
204+
205+
let hour = d.getHours();
206+
let padded = ("0" + hour).slice(-2);
207+
208+
let tl = padded[0];
209+
let tr = padded[1];
210+
211+
let min = d.getMinutes();
212+
padded = ("0" + min).slice(-2);
213+
214+
215+
let bl = padded[0];
216+
let br = padded[1];
217+
218+
g.clear();
219+
g.setColor(bg_color_topo[0],bg_color_topo[1],bg_color_topo[2]);
220+
g.fillRect(0,0,width,topo_position);
221+
g.setColor(bg_color[0],bg_color[1],bg_color[2]);
222+
g.fillRect(0,topo_position,width,height);
223+
224+
[tl,tr,bl,br].forEach(function(corner, i) {
225+
let path = font[corner];
226+
path.forEach(function(line){
227+
let pcp = previous_corner_positions[i].slice();
228+
let ccp = corner_positions[i].slice();
229+
230+
let x1 = (ccp[0][0] - pcp[0][0]) * transition_value + pcp[0][0];
231+
let x2 = (ccp[0][1] - pcp[0][1]) * transition_value + pcp[0][1];
232+
let y1 = (ccp[1][0] - pcp[1][0]) * transition_value + pcp[1][0];
233+
let y2 = (ccp[1][1] - pcp[1][1]) * transition_value + pcp[1][1];
234+
235+
236+
237+
let point_1 = [line[0][0]*(x2-x1) + x1, line[0][1]*(y2-y1) + y1];
238+
let point_2 = [line[1][0]*(x2-x1) + x1, line[1][1]*(y2-y1) + y1];
239+
240+
drawThickLine(point_1[0], point_1[1], point_2[0], point_2[1], 10, topo_position);
241+
});
242+
});
243+
}
244+
245+
let powersaver = false;
246+
let update_timeout = null;
247+
248+
let prev_min = -1;
249+
function update_clock() {
250+
let d = new Date();
251+
let min = d.getMinutes();
252+
253+
if (min != prev_min) {
254+
randomize_numbers();
255+
prev_min = min;
256+
}
257+
258+
if (powersaver) {
259+
if (direction == 1) {
260+
draw_numbers(1, 2);
261+
}
262+
else if (direction == 0) {
263+
draw_numbers(1, 0);
264+
}
265+
update_timeout = setTimeout(update_clock, 5000);
266+
}
267+
else {
268+
let seconds = d.getSeconds() + d.getMilliseconds() / 1000;
269+
if (direction == 1) {
270+
draw_numbers(Math.min(seconds, 1) / 1, 1.00-(seconds/60));
271+
}
272+
else if (direction == 0) {
273+
draw_numbers(Math.min(seconds, 1) / 1, seconds / 60);
274+
}
275+
if (seconds < 1.5) {
276+
update_timeout = setTimeout(update_clock, 50);
277+
} else {
278+
update_timeout = setTimeout(update_clock, 1000);
279+
}
280+
}
281+
}
282+
283+
let settings = require("Storage").readJSON("flux.settings.json", 1) || {};
284+
console.log(settings);
285+
let direction = settings.direction || 0;
286+
bg_color = colors[settings.bg || "Black"];
287+
fg_color = colors[settings.fg || "White"];
288+
bg_color_topo = colors[settings.bg2 || "Green"];
289+
fg_color_topo = colors[settings.fg2 || "Black"];
290+
if (direction == 1) {
291+
let temp = bg_color;
292+
bg_color = bg_color_topo;
293+
bg_color_topo = temp;
294+
295+
temp = fg_color;
296+
fg_color = fg_color_topo;
297+
fg_color_topo = temp;
298+
}
299+
300+
301+
update_clock();
302+
303+
Bangle.on('backlight', function(isOn) {
304+
if (isOn) {
305+
powersaver = false;
306+
clearTimeout(update_timeout);
307+
update_clock();
308+
} else {
309+
powersaver = true;
310+
let d = new Date();
311+
let seconds = d.getSeconds() + d.getMilliseconds() / 1000;
312+
draw_numbers(Math.min(seconds, 1) / 1, 0);
313+
}
314+
});
315+
316+
Bangle.setUI("clock");

apps/flux/app.png

2.45 KB
Loading

0 commit comments

Comments
 (0)