diff --git a/popup/script.js b/popup/script.js index 56261ad..8c4d944 100644 --- a/popup/script.js +++ b/popup/script.js @@ -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) { @@ -102,17 +102,16 @@ playBtn.onclick = function() { case "stopwatch": startStopwatch(startOffset); break; - } + } // 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); } - diff --git a/popup/style.css b/popup/style.css index 34ebc2f..cc1cb9d 100644 --- a/popup/style.css +++ b/popup/style.css @@ -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; } -