Skip to content

Commit 896a8bd

Browse files
author
GitHub Actions
committed
Deploy derdilla/personal-website to derdilla/personal-website:gh-pages
0 parents  commit 896a8bd

30 files changed

+1828
-0
lines changed

.nojekyll

Whitespace-only changes.

404.html

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>Not found</title>
6+
</head>
7+
<body style="background:#000;color:#FFF">
8+
<h1>Nothing to see here, <a style="color:#3dbeff" href="https://derdilla.com">move along</a>.</h1>
9+
</body>
10+
</html>

alarm/index.html

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Non-Linear Timer</title>
7+
<!-- Load Tailwind CSS -->
8+
<script src="https://cdn.tailwindcss.com"></script>
9+
<style>
10+
/* Custom styles for the slider to be more visible and touch-friendly */
11+
input[type=range] {
12+
-webkit-appearance: none;
13+
width: 100%;
14+
height: 12px;
15+
background: #cbd5e1; /* slate-300 */
16+
border-radius: 6px;
17+
cursor: pointer;
18+
outline: none;
19+
}
20+
21+
/* Thumb styling for Webkit/Blink */
22+
input[type=range]::-webkit-slider-thumb {
23+
-webkit-appearance: none;
24+
appearance: none;
25+
width: 24px;
26+
height: 24px;
27+
border-radius: 50%;
28+
background: #1e3a8a; /* blue-900 */
29+
cursor: pointer;
30+
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
31+
margin-top: -6px; /* Center the thumb vertically */
32+
}
33+
</style>
34+
</head>
35+
<body class="bg-gray-50 flex items-center justify-center min-h-screen p-4 font-sans">
36+
37+
<div id="timer-card" class="bg-white p-8 md:p-12 rounded-xl shadow-2xl w-full max-w-xl transition-all duration-300">
38+
<h1 class="text-3xl font-extrabold text-gray-900 mb-6 text-center">Set Your Timer</h1>
39+
40+
<!-- Time Display -->
41+
<div id="time-display" class="text-6xl md:text-8xl font-mono font-bold text-center text-blue-800 mb-10 select-none">
42+
00:00:05
43+
</div>
44+
45+
<!-- Timer Status Message -->
46+
<p id="status-message" class="text-center text-sm mb-6 h-5 text-gray-500">
47+
Slide and release to start
48+
</p>
49+
50+
<!-- Slider Input -->
51+
<input type="range" id="timer-slider" min="0" max="100" value="0" class="w-full">
52+
53+
<!-- Custom Modal for Alarm/Message -->
54+
<div id="alarm-modal" class="hidden fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center z-50 p-4">
55+
<div class="bg-white p-8 rounded-xl shadow-2xl max-w-md w-full text-center">
56+
<h2 id="modal-title" class="text-3xl font-bold text-red-600 mb-4">Timer Complete!</h2>
57+
<p class="text-lg text-gray-700 mb-6">Your countdown has finished.</p>
58+
<button onclick="closeAlarmModal()" class="px-6 py-3 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-700 transition duration-150">
59+
Close
60+
</button>
61+
</div>
62+
</div>
63+
</div>
64+
65+
<script>
66+
// --- CONSTANTS AND STATE ---
67+
const MIN_TIME_SECONDS = 5; // 5 seconds
68+
const MAX_TIME_SECONDS = 36000; // 10 hours (10 * 60 * 60)
69+
const EXP_FACTOR = MAX_TIME_SECONDS / MIN_TIME_SECONDS; // 7200
70+
71+
let totalTimeSeconds = MIN_TIME_SECONDS;
72+
let remainingSeconds = MIN_TIME_SECONDS;
73+
let timerInterval = null;
74+
let isRunning = false;
75+
76+
// --- DOM ELEMENTS ---
77+
const timeDisplay = document.getElementById('time-display');
78+
const slider = document.getElementById('timer-slider');
79+
const statusMessage = document.getElementById('status-message');
80+
const timerCard = document.getElementById('timer-card');
81+
const alarmModal = document.getElementById('alarm-modal');
82+
83+
// --- MATH/MAPPING LOGIC ---
84+
85+
/**
86+
* Maps a linear slider value (0-100) to a non-linear time in seconds.
87+
* The exponential formula makes it more sensitive at the low (5s) end.
88+
* T = T_min * (T_max / T_min) ^ (x / 100)
89+
* @param {number} sliderValue - The value from the range input (0 to 100).
90+
* @returns {number} The calculated time in seconds.
91+
*/
92+
function mapSliderToTime(sliderValue) {
93+
// Formula: 5 * (7200)^(sliderValue / 100)
94+
const time = MIN_TIME_SECONDS * Math.pow(EXP_FACTOR, sliderValue / 100);
95+
return Math.round(time); // Use Math.round to get clean integer seconds
96+
}
97+
98+
/**
99+
* Formats total seconds into HH:mm:ss string.
100+
* @param {number} seconds - The total number of seconds.
101+
* @returns {string} The formatted time string.
102+
*/
103+
function formatTime(seconds) {
104+
const h = String(Math.floor(seconds / 3600)).padStart(2, '0');
105+
const m = String(Math.floor((seconds % 3600) / 60)).padStart(2, '0');
106+
const s = String(seconds % 60).padStart(2, '0');
107+
return `${h}:${m}:${s}`;
108+
}
109+
110+
// --- TIMER CONTROL ---
111+
112+
/**
113+
* Updates the display every second and manages the countdown.
114+
*/
115+
function updateTimer() {
116+
if (remainingSeconds <= 0) {
117+
stopTimer();
118+
triggerAlarm();
119+
return;
120+
}
121+
122+
remainingSeconds--;
123+
timeDisplay.textContent = formatTime(remainingSeconds);
124+
let minI = 0;
125+
let minVal = MAX_TIME_SECONDS;
126+
for (let i = 0; i <= 100; i++) {
127+
const delta = Math.abs(mapSliderToTime(i) - remainingSeconds);
128+
if (delta < minVal) {
129+
minVal = delta;
130+
minI = i;
131+
}
132+
}
133+
slider.value = minI;
134+
}
135+
136+
/**
137+
* Stops the current timer interval.
138+
*/
139+
function stopTimer() {
140+
if (timerInterval) {
141+
clearInterval(timerInterval);
142+
timerInterval = null;
143+
}
144+
isRunning = false;
145+
}
146+
147+
/**
148+
* Starts the countdown based on the currently set remainingSeconds.
149+
*/
150+
function startTimer() {
151+
if (isRunning) {
152+
stopTimer();
153+
}
154+
155+
// Set the interval to run every 1000 milliseconds (1 second)
156+
timerInterval = setInterval(updateTimer, 1000);
157+
isRunning = true;
158+
statusMessage.textContent = 'Timer running...';
159+
timerCard.classList.add('ring-4', 'ring-blue-400/50');
160+
}
161+
162+
/**
163+
* Handles the alarm UI state when the timer reaches zero.
164+
*/
165+
function triggerAlarm() {
166+
statusMessage.textContent = 'TIME IS UP!';
167+
timerCard.classList.remove('ring-4', 'ring-blue-400/50');
168+
timerCard.classList.add('ring-4', 'ring-red-500/80');
169+
alarmModal.classList.remove('hidden');
170+
}
171+
172+
/**
173+
* Closes the alarm modal and resets the UI state.
174+
*/
175+
function closeAlarmModal() {
176+
alarmModal.classList.add('hidden');
177+
timerCard.classList.remove('ring-4', 'ring-red-500/80');
178+
slider.disabled = false;
179+
resetDisplay(totalTimeSeconds); // Reset display to the last set value
180+
statusMessage.textContent = 'Slide and release to start';
181+
}
182+
window.closeAlarmModal = closeAlarmModal; // Expose to global scope for HTML onclick
183+
184+
/**
185+
* Updates the time display to a specific value and updates the internal state.
186+
*/
187+
function resetDisplay(seconds) {
188+
remainingSeconds = seconds;
189+
timeDisplay.textContent = formatTime(seconds);
190+
}
191+
192+
// --- EVENT LISTENERS ---
193+
194+
// 1. Update time display continuously while the user drags the slider (input event)
195+
slider.addEventListener('input', (event) => {
196+
if (isRunning) {
197+
stopTimer();
198+
statusMessage.textContent = 'Timer paused. Release slider to restart.';
199+
timerCard.classList.remove('ring-4', 'ring-blue-400/50');
200+
}
201+
202+
const value = parseInt(event.target.value);
203+
totalTimeSeconds = mapSliderToTime(value);
204+
resetDisplay(totalTimeSeconds);
205+
206+
// Hide alarm if it was showing while interacting with slider
207+
if (!alarmModal.classList.contains('hidden')) {
208+
alarmModal.classList.add('hidden');
209+
}
210+
});
211+
212+
// 2. Start the timer when the user releases the slider (change event)
213+
slider.addEventListener('change', () => {
214+
if (totalTimeSeconds > 0) {
215+
startTimer();
216+
}
217+
});
218+
219+
// --- INITIALIZATION ---
220+
221+
// Set initial state based on slider value 0
222+
document.addEventListener('DOMContentLoaded', () => {
223+
// Update time once on load for initial value (0 -> 5s)
224+
const initialValue = parseInt(slider.value);
225+
totalTimeSeconds = mapSliderToTime(initialValue);
226+
resetDisplay(totalTimeSeconds);
227+
});
228+
</script>
229+
</body>
230+
</html>
231+

assets/android-levels.svg

Lines changed: 3 additions & 0 deletions
Loading

assets/btn-progress.svg

Lines changed: 121 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)