-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclock.js
More file actions
518 lines (436 loc) · 17 KB
/
clock.js
File metadata and controls
518 lines (436 loc) · 17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
// Default coordinates (Mecca)
let lat = 21.4241;
let lng = 39.8262;
// Try to get saved coordinates from localStorage
const savedLat = localStorage.getItem('latitude');
const savedLng = localStorage.getItem('longitude');
if (savedLat && savedLng) {
lat = parseFloat(savedLat);
lng = parseFloat(savedLng);
}
// Initialize sun times
let sunTimes = SunCalc.getTimes(new Date(), lat, lng);
let lastUpdate = new Date();
// Update location inputs with current values
function updateLocationInputs() {
const latInput = document.getElementById('latitude');
const lngInput = document.getElementById('longitude');
if (latInput && lngInput) {
latInput.value = lat;
lngInput.value = lng;
}
}
// Get browser location
async function getBrowserLocation() {
try {
const position = await new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(
(position) => resolve(position),
(error) => reject(error),
{
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
}
);
});
lat = position.coords.latitude;
lng = position.coords.longitude;
// Save to localStorage
localStorage.setItem('latitude', lat.toString());
localStorage.setItem('longitude', lng.toString());
// Update input fields with new location
updateLocationInputs();
// Update sun times
updateSunTimes();
// Update status
// document.getElementById('locationStatus').textContent = `Location: ${lat.toFixed(4)}, ${lng.toFixed(4)}`;
} catch (error) {
console.error('Error getting location:', error);
document.getElementById('locationStatus').textContent = 'Could not get location. Please try again or enter coordinates manually.';
// Show custom location inputs
document.getElementById('customLocation').style.display = 'block';
}
}
// Solar hands visibility state
let solarHandsVisible = true;
// Wait for DOM to be loaded before setting up event listeners
document.addEventListener('DOMContentLoaded', () => {
// Request location immediately
getBrowserLocation();
// Initialize browser location button
document.getElementById('browserLocationButton').addEventListener('click', getBrowserLocation);
// Set up toggle button
const toggleButton = document.getElementById('toggleSolarButton');
if (toggleButton) {
toggleButton.addEventListener('click', () => {
solarHandsVisible = !solarHandsVisible;
toggleButton.textContent = solarHandsVisible ? 'Hide Solar Hands' : 'Show Solar Hands';
sunriseHand.visible = solarHandsVisible;
sunsetHand.visible = solarHandsVisible;
});
}
// Initialize location input fields
const latInput = document.getElementById('latitude');
const lngInput = document.getElementById('longitude');
if (latInput && lngInput) {
latInput.value = lat;
lngInput.value = lng;
}
// Add event listener to custom location update button
const customLocationBtn = document.getElementById('customLocationButton');
if (customLocationBtn) {
customLocationBtn.addEventListener('click', updateCustomLocation);
}
});
// Function to handle custom location updates
function updateCustomLocation() {
const latInput = document.getElementById('latitude');
const lngInput = document.getElementById('longitude');
const newLat = parseFloat(latInput.value);
const newLng = parseFloat(lngInput.value);
if (isNaN(newLat) || isNaN(newLng)) {
alert('Please enter valid coordinates');
return;
}
if (newLat < -90 || newLat > 90) {
alert('Latitude must be between -90 and 90');
return;
}
if (newLng < -180 || newLng > 180) {
alert('Longitude must be between -180 and 180');
return;
}
lat = newLat;
lng = newLng;
// Save to localStorage
localStorage.setItem('latitude', lat.toString());
localStorage.setItem('longitude', lng.toString());
// Update the sun times
updateSunTimes();
}
// Store sun times and update them periodically
function updateSunTimes() {
sunTimes = SunCalc.getTimes(new Date(), lat, lng);
lastUpdate = new Date();
updateHourNumbers();
}
// Constants for clock dimensions
const CANVAS_SIZE = 300; // Increased from 300
const CENTER = CANVAS_SIZE / 2;
const CLOCK_RADIUS = CANVAS_SIZE * 0.35; // Keeping the same proportion
const OUTER_RADIUS = CLOCK_RADIUS * 1.15; // Smaller outer circle
const NUMBER_RADIUS = CLOCK_RADIUS * 1.15; // Keep numbers at their original position
const DOT_RADIUS = CLOCK_RADIUS * 1.075; // Position for dots and arc between circles
const HOUR_HAND_LENGTH = CLOCK_RADIUS * 0.6;
const MINUTE_HAND_LENGTH = CLOCK_RADIUS * 0.8;
const SECOND_HAND_LENGTH = CLOCK_RADIUS * 0.9;
const app = new PIXI.Application({
width: CANVAS_SIZE,
height: CANVAS_SIZE,
backgroundColor: 0xffffff,
forceCanvas: true, // Force Canvas renderer instead of WebGL
antialias: true // Enable antialiasing for smoother graphics
});
document.body.insertBefore(app.view, document.getElementById('locationStatus'));
// Create graphics objects for clock elements
const clockFace = new PIXI.Graphics();
const hourHand = new PIXI.Graphics();
const minuteHand = new PIXI.Graphics();
const secondHand = new PIXI.Graphics();
const sunriseHand = new PIXI.Graphics();
const sunsetHand = new PIXI.Graphics();
const centerPoint = new PIXI.Graphics();
const trueHoursArc = new PIXI.Graphics();
const hourNumbers = new PIXI.Container();
// Create text style for numbers
const numberStyle = {
fontFamily: 'Arial',
fontSize: 16, // Increased from 14
fill: 0x000000,
align: 'center'
};
// Update time displays
function updateTimeDisplays(hours, minutes, seconds, stretchedHours, stretchedMinutes) {
const realTime = String(hours).padStart(2, '0') + ':' +
String(minutes).padStart(2, '0') + ':' +
String(seconds).padStart(2, '0');
const naturalTime = String(stretchedHours).padStart(2, '0') + ':' +
String(stretchedMinutes).padStart(2, '0') + ':' +
String(seconds).padStart(2, '0'); // Using real seconds
document.getElementById('realTimeDisplay').textContent = realTime;
document.getElementById('naturalTimeDisplay').textContent = naturalTime;
}
// Add all elements to stage in correct order
app.stage.addChild(clockFace);
app.stage.addChild(trueHoursArc);
app.stage.addChild(hourNumbers); // Add numbers before the hands
app.stage.addChild(hourHand);
app.stage.addChild(minuteHand);
app.stage.addChild(secondHand);
app.stage.addChild(sunriseHand);
app.stage.addChild(sunsetHand);
app.stage.addChild(centerPoint);
// Initialize clock face
clockFace.beginFill(0xffffff);
clockFace.drawCircle(CENTER, CENTER, CLOCK_RADIUS);
clockFace.endFill();
clockFace.lineStyle(4, 0x000000);
clockFace.drawCircle(CENTER, CENTER, CLOCK_RADIUS);
// Draw outer circle for numbers
clockFace.lineStyle(2, 0x000000, 0.3);
clockFace.drawCircle(CENTER, CENTER, OUTER_RADIUS);
// Clock markings
for (let i = 0; i < 24; i++) {
const angle = (i * Math.PI / 12) - Math.PI / 2;
const markLength = i % 6 === 0 ? 20 : (i % 3 === 0 ? 15 : 10);
const startX = CENTER + Math.cos(angle) * (CLOCK_RADIUS - 20);
const startY = CENTER + Math.sin(angle) * (CLOCK_RADIUS - 20);
const endX = CENTER + Math.cos(angle) * (CLOCK_RADIUS - 20 - markLength);
const endY = CENTER + Math.sin(angle) * (CLOCK_RADIUS - 20 - markLength);
clockFace.lineStyle(i % 6 === 0 ? 6 : (i % 3 === 0 ? 4 : 2), 0x000000);
clockFace.moveTo(startX, startY);
clockFace.lineTo(endX, endY);
}
hourHand.lineStyle(8, 0x333333);
minuteHand.lineStyle(4, 0x666666);
secondHand.lineStyle(2, 0xff0000);
sunriseHand.lineStyle(3, 0xFFA500);
sunsetHand.lineStyle(3, 0x800080);
// Center point
centerPoint.beginFill(0x000000);
centerPoint.drawCircle(CENTER, CENTER, 10);
centerPoint.endFill();
function updateHourNumbers() {
// Clear existing numbers
while (hourNumbers.children[0]) {
hourNumbers.children[0].destroy();
}
const sunrise = sunTimes.sunrise;
const sunset = sunTimes.sunset;
// Convert times to hours and minutes
const sunriseHours = sunrise.getHours();
const sunriseMinutes = sunrise.getMinutes();
const sunsetHours = sunset.getHours();
const sunsetMinutes = sunset.getMinutes();
const sunriseTime = sunriseHours + sunriseMinutes / 60;
const sunsetTime = sunsetHours + sunsetMinutes / 60;
// Calculate day and night lengths
let dayLength = sunsetTime - sunriseTime;
if (dayLength < 0) dayLength += 24;
const nightLength = 24 - dayLength;
// Create hour numbers
for (let hour = 0; hour < 24; hour++) {
let angle;
const realHour = (hour + sunriseHours) % 24;
if (hour < dayLength) {
// Day time - map between 0 and PI
angle = (hour / dayLength) * Math.PI - Math.PI/2;
} else {
// Night time - map between PI and 2PI
const nightHour = hour - dayLength;
angle = Math.PI * (nightHour / nightLength + 1) - Math.PI/2;
}
// Position the number
const x = CENTER + NUMBER_RADIUS * Math.cos(angle);
const y = CENTER + NUMBER_RADIUS * Math.sin(angle);
// Add a small circle at the hour position
const hourDot = new PIXI.Graphics();
hourDot.beginFill(hour < dayLength ? 0x000000 : 0x666666);
const dotX = CENTER + DOT_RADIUS * Math.cos(angle);
const dotY = CENTER + DOT_RADIUS * Math.sin(angle);
hourDot.drawCircle(dotX, dotY, 2);
hourDot.endFill();
hourNumbers.addChild(hourDot);
// Create the text
const text = new PIXI.Text(realHour.toString(), numberStyle);
// Center the text on its position
text.anchor.set(0.5);
text.position.set(
CENTER + (NUMBER_RADIUS + 15) * Math.cos(angle),
CENTER + (NUMBER_RADIUS + 15) * Math.sin(angle)
);
// Add to container
hourNumbers.addChild(text);
}
}
function updateClock(hours, minutes, seconds) {
// Update sun times if needed (every minute)
const now = new Date();
if (!sunTimes || now.getMinutes() !== lastUpdate.getMinutes()) {
sunTimes = SunCalc.getTimes(now, lat, lng);
lastUpdate = now;
updateHourNumbers();
}
const sunrise = sunTimes.sunrise;
const sunset = sunTimes.sunset;
// Convert times to hours and minutes
const sunriseHours = sunrise.getHours();
const sunriseMinutes = sunrise.getMinutes();
const sunsetHours = sunset.getHours();
const sunsetMinutes = sunset.getMinutes();
const sunriseTime = sunriseHours + sunriseMinutes / 60;
const sunsetTime = sunsetHours + sunsetMinutes / 60;
// Calculate day and night lengths
let dayLength = sunsetTime - sunriseTime;
if (dayLength < 0) dayLength += 24;
const nightLength = 24 - dayLength;
// Calculate current and stretched time
const currentTime = hours + minutes / 60 + seconds / 3600;
let stretchedHours, stretchedMinutes;
if (currentTime >= sunriseTime && currentTime <= sunsetTime) {
// Daytime - map between 0 and 12
const dayProgress = (currentTime - sunriseTime) / dayLength;
stretchedHours = Math.floor(dayProgress * 12);
stretchedMinutes = Math.floor((dayProgress * 12 % 1) * 60);
} else {
// Nighttime - map between 12 and 24
let nightProgress;
if (currentTime > sunsetTime) {
nightProgress = (currentTime - sunsetTime) / nightLength;
} else {
nightProgress = (currentTime + 24 - sunsetTime) / nightLength;
}
stretchedHours = Math.floor(12 + nightProgress * 12);
stretchedMinutes = Math.floor((nightProgress * 12 % 1) * 60);
}
// Make sure to call updateTimeDisplays with both stretched hours and minutes
updateTimeDisplays(hours, minutes, seconds, stretchedHours, stretchedMinutes);
// Calculate true hours arc
const nightTimeRatio = 8 / nightLength; // What portion of night is 8 hours
const nightArcLength = Math.PI; // Night is mapped to π radians
const trueHoursArcLength = nightTimeRatio * nightArcLength; // Scale arc by ratio
// Draw true hours arc
trueHoursArc.clear();
trueHoursArc.lineStyle(10, 0xff0000, 0.3);
trueHoursArc.arc(CENTER, CENTER, DOT_RADIUS,
-Math.PI/2 - trueHoursArcLength, // Start proportional to night length
-Math.PI/2 // End at top (projected sunrise)
);
// Clear all hands
hourHand.clear();
minuteHand.clear();
secondHand.clear();
if (solarHandsVisible) {
sunriseHand.clear();
sunsetHand.clear();
}
// Hour hand position based on sunrise/sunset
let hourAngle;
if (currentTime >= sunriseTime && currentTime <= sunsetTime) {
// Daytime - map between 0 and PI
const dayProgress = (currentTime - sunriseTime) / dayLength;
hourAngle = dayProgress * Math.PI - Math.PI/2;
} else {
// Nighttime - map between PI and 2PI
let nightProgress;
if (currentTime > sunsetTime) {
nightProgress = (currentTime - sunsetTime) / nightLength;
} else {
nightProgress = (currentTime + 24 - sunsetTime) / nightLength;
}
hourAngle = Math.PI * (nightProgress + 1) - Math.PI/2;
}
// Draw hour hand
hourHand.lineStyle(8, 0x333333);
hourHand.moveTo(CENTER, CENTER);
hourHand.lineTo(
CENTER + Math.cos(hourAngle) * HOUR_HAND_LENGTH,
CENTER + Math.sin(hourAngle) * HOUR_HAND_LENGTH
);
// Draw minute hand (using real time)
const minuteAngle = (minutes * 6 + seconds * 0.1) * Math.PI / 180 - Math.PI/2; // Convert degrees to radians
minuteHand.lineStyle(4, 0x666666);
minuteHand.moveTo(CENTER, CENTER);
minuteHand.lineTo(
CENTER + Math.cos(minuteAngle) * MINUTE_HAND_LENGTH,
CENTER + Math.sin(minuteAngle) * MINUTE_HAND_LENGTH
);
// Draw second hand
const secondAngle = seconds * (Math.PI / 30) - Math.PI/2;
secondHand.lineStyle(2, 0xff0000);
secondHand.moveTo(CENTER, CENTER);
secondHand.lineTo(
CENTER + Math.cos(secondAngle) * SECOND_HAND_LENGTH,
CENTER + Math.sin(secondAngle) * SECOND_HAND_LENGTH
);
// Draw solar hands if visible
if (solarHandsVisible) {
drawSunriseHand();
drawSunsetHand();
}
}
function drawSunriseHand() {
sunriseHand.clear();
// Position at center
sunriseHand.position.set(CENTER, CENTER);
// Draw the stem line with 50% transparency
sunriseHand.lineStyle(2, 0xFFAA00, 0.5);
sunriseHand.moveTo(0, 0);
sunriseHand.lineTo(0, -CLOCK_RADIUS + 20);
// Draw the half-sun (solid)
sunriseHand.lineStyle(2, 0xFFAA00, 1);
sunriseHand.beginFill(0xFFFF00);
sunriseHand.arc(0, -CLOCK_RADIUS + 20, 8, Math.PI, 0);
sunriseHand.lineTo(0, -CLOCK_RADIUS + 20);
sunriseHand.endFill();
// Draw rays (solid)
sunriseHand.lineStyle(2, 0xFFAA00, 1);
for (let i = 0; i < 8; i++) {
const angle = (i * Math.PI) / 7;
const x1 = Math.cos(angle) * 8;
const y1 = -Math.sin(angle) * 8 - CLOCK_RADIUS + 20;
const x2 = Math.cos(angle) * 12;
const y2 = -Math.sin(angle) * 12 - CLOCK_RADIUS + 20;
sunriseHand.moveTo(x1, y1);
sunriseHand.lineTo(x2, y2);
}
}
function drawSunsetHand() {
sunsetHand.clear();
// Position at center
sunsetHand.position.set(CENTER, CENTER);
// Draw the stem line with 50% transparency
sunsetHand.lineStyle(2, 0x800080, 0.5);
sunsetHand.moveTo(0, 0);
sunsetHand.lineTo(0, CLOCK_RADIUS - 20);
// Draw moon symbol (solid)
sunsetHand.lineStyle(0);
sunsetHand.beginFill(0x800080);
sunsetHand.drawCircle(0, CLOCK_RADIUS - 20, 8);
sunsetHand.endFill();
// Draw the overlapping circle to create crescent shape
sunsetHand.beginFill(0xFFFFFF);
sunsetHand.drawCircle(4, CLOCK_RADIUS - 20, 6);
sunsetHand.endFill();
// Add stars around the moon
sunsetHand.beginFill(0x800080);
const starPositions = [
{ dx: -10, dy: -8 },
{ dx: -6, dy: 10 },
{ dx: -12, dy: 2 }
];
starPositions.forEach(pos => {
for (let i = 0; i < 4; i++) {
const starAngle = i * Math.PI / 2;
sunsetHand.drawCircle(
pos.dx + Math.cos(starAngle) * 1.5,
CLOCK_RADIUS - 20 + pos.dy + Math.sin(starAngle) * 1.5,
1
);
}
});
sunsetHand.endFill();
}
// Start the animation loop
function animate() {
const now = new Date();
const hours = now.getHours();
const minutes = now.getMinutes();
const seconds = now.getSeconds();
updateClock(hours, minutes, seconds);
requestAnimationFrame(animate);
}
// Initial setup
updateSunTimes(); // This will also call updateHourNumbers
animate();