fix progress bar

This commit is contained in:
Theis Pieter Hollebeek 2026-04-03 16:04:34 +02:00
parent 382992661e
commit 60e34a4d67
2 changed files with 86 additions and 79 deletions

View File

@ -1,7 +1,9 @@
var params = parseQueryParams();
// Set window title
if (params.name) document.title = decodeURIComponent(params.name) + " - Popup Timer";
if (params.name) {
document.title = decodeURIComponent(params.name) + " - Popup Timer";
}
var hours, minutes, seconds;
var secondsElem = document.getElementById("seconds");
@ -12,18 +14,20 @@ var progressBar = document.getElementById("progress-fg");
var timeout;
var timesUpStr = "[ TIME'S UP! ] ";
var lastPaused = -1;
var requiredProgress = -1;
var latestUpdate = Date.now();
var currentAnimationId = 0;
// Init audio
var oscillator, audioContext;
if (params.beep === "true") {
try {
audioContext = new (window.AudioContext || window.webkitAudioContext);
} catch(e) {}
audioContext = new (window.AudioContext || window.webkitAudioContext)();
} catch (e) {}
}
// Initialize clock and start
switch (params.type) {
case "timer":
initTimer();
if (params.autostart === "true") startTimer(1000);
@ -39,15 +43,13 @@ switch (params.type) {
}
// Reset timer/stopwatch
document.getElementById("restart").onclick = function() {
document.getElementById("restart").onclick = function () {
if (timeout) clearTimeout(timeout);
var paused = playBtn.classList.contains("fa-play");
switch (params.type) {
case "timer":
// Reset progress bar
progressBar.style.animationName = "none";
progressBar.offsetHeight; // Trigger reflow
@ -58,7 +60,6 @@ document.getElementById("restart").onclick = function() {
break;
case "stopwatch":
// Reset displayed time
secondsElem.innerText = "00s";
secondsElem.classList.remove("highlighted");
@ -72,25 +73,24 @@ document.getElementById("restart").onclick = function() {
break;
}
}
};
// Pause / Resume
playBtn.onclick = function() {
playBtn.onclick = function () {
// Pause
if (playBtn.classList.contains("fa-pause")) {
lastPaused = Date.now();
if (params.type === "timer") progressBar.style.animationPlayState = "paused";
if (params.type === "timer") {
progressBar.style.animationPlayState = "paused";
}
clearTimeout(timeout);
// Change icon
playBtn.classList.remove("fa-pause");
playBtn.classList.add("fa-play");
// Resume
// Resume
} else {
var startOffset = lastPaused % 1000;
switch (params.type) {
@ -107,12 +107,11 @@ playBtn.onclick = function() {
// Change icon
playBtn.classList.remove("fa-play");
playBtn.classList.add("fa-pause");
}
}
};
function initTimer() {
document.getElementById("progress-bg").style.width = "100%";
hours = parseInt(params.h);
minutes = parseInt(params.m);
@ -138,35 +137,51 @@ function initTimer() {
secondsElem.innerText = pad(seconds) + "s";
// Animate progress bar
var totalSeconds = (hours * 60 * 60) + (minutes * 60) + seconds;
progressBar.style.animationDuration = totalSeconds + "s";
progressBar.style.animationName = "progress";
requiredProgress = (hours * 60 * 60) + (minutes * 60) + seconds;
++currentAnimationId;
var runId = currentAnimationId;
var animate;
animate = function () {
var paused = playBtn.classList.contains("fa-play");
if (currentAnimationId !== runId || paused) {
return;
}
var milliProgress = (Date.now() - latestUpdate) / 1000;
var progress = (hours * 60 * 60) + (minutes * 60) + seconds -
milliProgress;
var percentage = 1 - (progress /
requiredProgress);
document.getElementById("progress-fg").style.transform =
`scaleX(${percentage})`;
requestAnimationFrame(animate);
};
requestAnimationFrame(animate);
}
function startTimer(startOffset) {
if (hours === 0 && minutes == 0 && seconds === 0) return;
latestUpdate = Date.now();
playBtn.classList.remove("fa-play");
playBtn.classList.add("fa-pause");
// Wait the starting offset
timeout = setTimeout(updateTimer, startOffset);
document.getElementById("progress-fg").style.animationPlayState = "running";
}
function updateTimer() {
seconds--;
if (seconds === 0 && minutes === 0 && hours === 0) secondsElem.classList.remove("highlighted");
if (seconds === 0 && minutes === 0 && hours === 0) {
secondsElem.classList.remove("highlighted");
}
if (seconds <= -1) {
seconds = 59;
minutes--;
if (minutes === 0 && hours === 0) minutesElem.classList.remove("highlighted");
if (minutes === 0 && hours === 0) {
minutesElem.classList.remove("highlighted");
}
if (minutes <= -1) {
minutes = 59;
@ -179,24 +194,26 @@ function updateTimer() {
}
secondsElem.innerText = pad(seconds) + "s";
latestUpdate = Date.now();
if (hours === 0 && minutes === 0 && seconds == 0) timeUp();
else timeout = setTimeout(updateTimer, 1000);
}
// Makes the numbers blink red etc.
function timeUp() {
var timeUpInterval = setInterval(function() {
var timeUpInterval = setInterval(function () {
// Blink red
[].slice.call(document.querySelectorAll("main > span")).forEach(function(elem) {
elem.classList.toggle("red");
});
[].slice.call(document.querySelectorAll("main > span")).forEach(
function (elem) {
elem.classList.toggle("red");
},
);
// Blink title
if (document.title.indexOf(timesUpStr) === -1) document.title = timesUpStr + document.title;
else document.title = document.title.substr(timesUpStr.length);
if (document.title.indexOf(timesUpStr) === -1) {
document.title = timesUpStr + document.title;
} else document.title = document.title.substr(timesUpStr.length);
// Play beep
if (params.beep === "true" && audioContext) {
@ -207,30 +224,30 @@ function timeUp() {
oscillator.connect(audioContext.destination);
oscillator.start();
oscillator.stop(audioContext.currentTime + 0.25);
} catch(e) {}
} catch (e) {}
}
}, 500);
// Stop when user clicks
document.onclick = function() {
document.onclick = function () {
if (oscillator) oscillator.stop();
// Reset color
[].slice.call(document.querySelectorAll("main > span")).forEach(function(elem) {
elem.classList.remove("red");
});
[].slice.call(document.querySelectorAll("main > span")).forEach(
function (elem) {
elem.classList.remove("red");
},
);
// Remove "TIME UP" from title
if (document.title.indexOf(timesUpStr) !== -1) document.title = document.title.substr(timesUpStr.length);
if (document.title.indexOf(timesUpStr) !== -1) {
document.title = document.title.substr(timesUpStr.length);
}
// Clean up
clearInterval(timeUpInterval);
document.onclick = null;
}
};
}
function initStopwatch() {
@ -240,25 +257,26 @@ function initStopwatch() {
}
function startStopwatch(startOffset) {
playBtn.classList.remove("fa-play");
playBtn.classList.add("fa-pause");
timeout = setTimeout(updateStopwatch, startOffset);
}
function updateStopwatch() {
// Update seconds
seconds++;
if (hours === 0 && minutes === 0 && seconds === 1) secondsElem.classList.add("highlighted");
if (hours === 0 && minutes === 0 && seconds === 1) {
secondsElem.classList.add("highlighted");
}
if (seconds >= 60) {
seconds = 0;
// Update minutes
minutes++;
if (hours === 0 && minutes === 1) minutesElem.classList.add("highlighted");
if (hours === 0 && minutes === 1) {
minutesElem.classList.add("highlighted");
}
if (minutes >= 60) {
minutes = 0;
@ -266,23 +284,20 @@ function updateStopwatch() {
hours++;
if (hours === 1) hoursElem.classList.add("highlighted");
hoursElem.innerText = pad(hours) + "h";
}
// Minutes
minutesElem.innerText = pad(minutes) + "m";
}
// Seconds
secondsElem.innerText = pad(seconds) + "s";
timeout = setTimeout(updateStopwatch, 1000);
}
function parseQueryParams() {
if (location.search === "" || location.search.indexOf("?") !== 0) return {};
var result = {};
location.search.substr(1).split("&").forEach(function(str) {
location.search.substr(1).split("&").forEach(function (str) {
var key = str.split("=")[0];
var value;
if (str.split("=").length > 1) value = str.split("=")[1];
@ -296,4 +311,3 @@ function parseQueryParams() {
function pad(num) {
return ("00" + num).substr(-2);
}

View File

@ -1,13 +1,14 @@
@font-face {
font-family: "Jetbrains Mono";
src: url("/assets/JetBrainsMono-Regular.woff2") format("woff2"),
url("/assets/JetBrainsMono-Regular.ttf") format("truetype");
src:
url("/assets/JetBrainsMono-Regular.woff2") format("woff2"),
url("/assets/JetBrainsMono-Regular.ttf") format("truetype");
}
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #FAFAFA;
background-color: #fafafa;
color: #424242;
font-family: "Jetbrains Mono", helvetica, arial, sans-serif;
}
@ -21,13 +22,13 @@ main {
main > span {
font-size: 15vw; /* Browsers that don't support min() */
font-size: min(15vw, 50vh);
color: #9E9E9E;
color: #9e9e9e;
}
main > span.highlighted {
color: #424242;
}
main > span.red {
color: #F4511E;
color: #f4511e;
}
.progress {
position: absolute;
@ -37,28 +38,20 @@ main > span.red {
width: 0;
}
#progress-bg {
background-color: #9E9E9E;
background-color: #9e9e9e;
}
#progress-fg {
background-color: #43A047;
animation-timing-function: linear;
animation-fill-mode: forwards;
}
@keyframes progress {
from {
width: 0;
}
to {
width: 100vw;
}
background-color: #43a047;
width: 100%;
transform: scaleX(0);
transform-origin: center left;
}
/* FontAwesome */
i {
font-size: 12px;
color: #9E9E9E;
color: #9e9e9e;
}
i:hover {
cursor: pointer;
color: #424242;
}