feat: add volume slider

This commit is contained in:
2024-07-21 21:39:29 +01:00
parent 7dd3ffdf1d
commit a25501b9ce
3 changed files with 88 additions and 16 deletions

View File

@@ -12,6 +12,10 @@
<div class="button-container"> <div class="button-container">
<button id="play-btn" class="button">play</button> <button id="play-btn" class="button">play</button>
</div> </div>
<div class="volume-slider-container">
<output id="current-volume-label" for="volume-slider">100%</output>
<input id="volume-slider" type="range" min="0" max="100" step="1" />
</div>
</main> </main>
<img class="cat" src="/images/cat-0.png"></img> <img class="cat" src="/images/cat-0.png"></img>
<img class="eeping-cat" src="/images/eeping-cat.png"></img> <img class="eeping-cat" src="/images/eeping-cat.png"></img>

View File

@@ -1,5 +1,7 @@
const playBtn = document.getElementById("play-btn"); const playBtn = document.getElementById("play-btn");
const catImg = document.getElementsByClassName("cat")[0]; const catImg = document.getElementsByClassName("cat")[0];
const volumeSlider = document.getElementById("volume-slider");
const currentVolumeLabel = document.getElementById("current-volume-label");
const clickAudio = new Audio("/audio/click.wav"); const clickAudio = new Audio("/audio/click.wav");
const clickReleaseAudio = new Audio("/audio/click-release.wav"); const clickReleaseAudio = new Audio("/audio/click-release.wav");
@@ -8,8 +10,10 @@ const CROSSFADE_INTERVAL_MS = 20;
const AUDIO_DURATION_MS = 60000; const AUDIO_DURATION_MS = 60000;
let isPlaying = false; let isPlaying = false;
let isFading = false;
let currentAudio; let currentAudio;
let volume = 0; let maxVolume = 100;
let currentVolume = 0;
function playAudio() { function playAudio() {
// add a random query parameter at the end to prevent browser caching // add a random query parameter at the end to prevent browser caching
@@ -20,11 +24,11 @@ function playAudio() {
}; };
currentAudio.onpause = () => { currentAudio.onpause = () => {
isPlaying = false; isPlaying = false;
volume = 0; currentVolume = 0;
playBtn.innerText = "play"; playBtn.innerText = "play";
}; };
currentAudio.onended = () => { currentAudio.onended = () => {
volume = 0; currentVolume = 0;
playAudio(); playAudio();
}; };
currentAudio.volume = 0; currentAudio.volume = 0;
@@ -40,33 +44,43 @@ function playAudio() {
function pauseAudio() { function pauseAudio() {
currentAudio.pause(); currentAudio.pause();
currentAudio.volume = 0; currentAudio.volume = 0;
volume = 0; currentVolume = 0;
} }
function fadeIn() { function fadeIn() {
isFading = true;
// volume ranges from 0 to 100, this determines by how much the volume number // volume ranges from 0 to 100, this determines by how much the volume number
// should be incremented at every step of the fade in // should be incremented at every step of the fade in
const volumeStep = 100 / (CROSSFADE_DURATION_MS / CROSSFADE_INTERVAL_MS); const volumeStep =
maxVolume / (CROSSFADE_DURATION_MS / CROSSFADE_INTERVAL_MS);
const handle = setInterval(() => { const handle = setInterval(() => {
volume += volumeStep; currentVolume += volumeStep;
if (volume >= 100) { if (currentVolume >= maxVolume) {
clearInterval(handle); clearInterval(handle);
currentVolume = maxVolume;
isFading = false;
} else { } else {
currentAudio.volume = volume / 100; currentAudio.volume = currentVolume / 100;
} }
}, CROSSFADE_INTERVAL_MS); }, CROSSFADE_INTERVAL_MS);
} }
function fadeOut() { function fadeOut() {
isFading = true;
// volume ranges from 0 to 100, this determines by how much the volume number // volume ranges from 0 to 100, this determines by how much the volume number
// should be decremented at every step of the fade out // should be decremented at every step of the fade out
const volumeStep = 100 / (CROSSFADE_DURATION_MS / CROSSFADE_INTERVAL_MS); const volumeStep =
maxVolume / (CROSSFADE_DURATION_MS / CROSSFADE_INTERVAL_MS);
const handle = setInterval(() => { const handle = setInterval(() => {
volume -= volumeStep; currentVolume -= volumeStep;
if (volume <= 0) { if (currentVolume <= 0) {
clearInterval(handle); clearInterval(handle);
currentVolume = 0;
isFading = false;
} else { } else {
currentAudio.volume = volume / 100; currentAudio.volume = currentVolume / 100;
} }
}, CROSSFADE_INTERVAL_MS); }, CROSSFADE_INTERVAL_MS);
} }
@@ -102,4 +116,14 @@ playBtn.onclick = () => {
} }
}; };
volumeSlider.oninput = () => {
maxVolume = volumeSlider.value;
currentVolumeLabel.textContent = `${maxVolume}%`;
if (!isFading) {
currentAudio.volume = maxVolume / 100;
currentVolume = maxVolume;
}
};
volumeSlider.value = 100;
animateCat(); animateCat();

View File

@@ -1,9 +1,11 @@
:root { :root {
font-family: monospace; font-family: monospace;
--text: #4c4f69; --text: #4c4f69;
--surface0: #ccd0da;
--surface1: #bcc0cc; --surface1: #bcc0cc;
--base: #eff1f5; --base: #eff1f5;
--lavender: #7287fd;
--range-height: 12px;
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
@@ -11,6 +13,7 @@
--text: #cdd6f4; --text: #cdd6f4;
--surface1: #45475a; --surface1: #45475a;
--base: #1e1e2e; --base: #1e1e2e;
--lavender: #b4befe;
} }
} }
@@ -20,8 +23,8 @@ body {
} }
body { body {
width: calc(100% - 4rem - 1px); width: calc(100% - 4rem - 2px);
height: calc(100vh - 4rem - 1px); height: calc(100vh - 4rem - 2px);
padding: 2rem; padding: 2rem;
margin: 0; margin: 0;
background-color: var(--base); background-color: var(--base);
@@ -35,6 +38,7 @@ h5,
h6 { h6 {
margin: 0; margin: 0;
font-weight: 400; font-weight: 400;
color: var(--text);
} }
main { main {
@@ -65,7 +69,7 @@ main {
display: flex; display: flex;
align-items: end; align-items: end;
justify-content: start; justify-content: start;
border: 1px solid var(--text); border: 2px solid var(--text);
border-radius: 2px; border-radius: 2px;
} }
@@ -112,6 +116,46 @@ main {
display: flex; display: flex;
} }
.volume-slider-container {
position: absolute;
top: 0;
right: 0;
padding: 0.5rem 1rem;
margin: 1rem;
display: flex;
justify-content: start;
align-items: center;
}
.volume-slider-container > output {
color: var(--text);
margin-right: 1rem;
}
@media screen and (-webkit-min-device-pixel-ratio: 0) {
input[type="range"] {
overflow: hidden;
-webkit-appearance: none;
border: 2px solid var(--text);
background-color: var(--base);
}
input[type="range"]::-webkit-slider-runnable-track {
height: var(--range-height);
-webkit-appearance: none;
color: #13bba4;
margin-top: -1px;
}
input[type="range"]::-webkit-slider-thumb {
width: var(--range-height);
height: var(--range-height);
-webkit-appearance: none;
background: var(--text);
box-shadow: -80px 0 0 80px var(--lavender);
}
}
.button { .button {
border: 2px solid var(--text); border: 2px solid var(--text);
background: var(--base); background: var(--base);