Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Tempo Estimator</title> | |
<!-- Bootstrap CSS --> | |
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> | |
<!-- Google Fonts --> | |
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet"> | |
<style> | |
:root { | |
--bg-color-light: #f0f0f0; | |
--text-color-light: #000; | |
--bg-color-dark: #2c2c2c; | |
--text-color-dark: #fff; | |
} | |
body { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
height: 100vh; | |
margin: 0; | |
font-family: 'Roboto', sans-serif; | |
transition: background-color 0.3s, color 0.3s; | |
text-align: center; | |
} | |
body.light-mode { | |
background-color: var(--bg-color-light); | |
color: var(--text-color-light); | |
} | |
body.dark-mode { | |
background-color: var(--bg-color-dark); | |
color: var(--text-color-dark); | |
} | |
.btn-custom { | |
padding: 30px 60px; | |
font-size: 36px; | |
margin: 10px; | |
border-radius: 50px; | |
position: relative; | |
} | |
.btn-reset { | |
padding: 15px 30px; | |
font-size: 18px; | |
margin: 10px; | |
border-radius: 50px; | |
position: relative; | |
} | |
#tempoDisplay { | |
font-size: 36px; | |
margin-bottom: 20px; | |
} | |
.toggle-button { | |
display: flex; | |
align-items: center; | |
cursor: pointer; | |
margin-top: 20px; | |
} | |
.toggle-button input { | |
display: none; | |
} | |
.toggle-label { | |
display: flex; | |
align-items: center; | |
background-color: #ccc; | |
border-radius: 50px; | |
padding: 5px; | |
width: 40px; | |
justify-content: space-between; | |
position: relative; | |
} | |
.toggle-label .ball { | |
background-color: #fff; | |
border-radius: 50%; | |
width: 20px; | |
height: 20px; | |
position: absolute; | |
left: 0; | |
transition: transform 0.3s; | |
} | |
input:checked + .toggle-label .ball { | |
transform: translateX(20px); | |
} | |
.sparkle { | |
position: absolute; | |
width: 20px; | |
height: 20px; | |
background: orange; | |
border-radius: 50%; | |
animation: sparkle 0.5s ease-out; | |
} | |
@keyframes sparkle { | |
from { | |
transform: scale(0); | |
opacity: 1; | |
} | |
to { | |
transform: scale(1.5); | |
opacity: 0; | |
} | |
} | |
</style> | |
</head> | |
<body class="light-mode"> | |
<div id="tempoDisplay">-- BPM</div> | |
<button id="tapButton" class="btn btn-primary btn-custom">Tap</button> | |
<button id="resetButton" class="btn btn-secondary btn-reset">Reset</button> | |
<div class="toggle-button"> | |
<input type="checkbox" id="modeToggle"> | |
<label for="modeToggle" class="toggle-label"> | |
<div class="ball"></div> | |
</label> | |
</div> | |
<script> | |
let tapTimes = []; | |
document.getElementById('tapButton').addEventListener('click', (e) => { | |
const currentTime = new Date().getTime(); | |
tapTimes.push(currentTime); | |
if (tapTimes.length > 1) { | |
const timeIntervals = []; | |
for (let i = 1; i < tapTimes.length; i++) { | |
timeIntervals.push(tapTimes[i] - tapTimes[i - 1]); | |
} | |
const averageInterval = timeIntervals.reduce((a, b) => a + b) / timeIntervals.length; | |
const tempo = Math.round(60000 / averageInterval); | |
document.getElementById('tempoDisplay').innerText = `${tempo} BPM`; | |
} | |
if (tapTimes.length > 10) { | |
tapTimes.shift(); | |
} | |
// Sparkle effect | |
const sparkle = document.createElement('div'); | |
sparkle.className = 'sparkle'; | |
sparkle.style.left = `${e.clientX - e.target.offsetLeft - 10}px`; | |
sparkle.style.top = `${e.clientY - e.target.offsetTop - 10}px`; | |
e.target.appendChild(sparkle); | |
setTimeout(() => { | |
sparkle.remove(); | |
}, 500); | |
}); | |
document.getElementById('resetButton').addEventListener('click', () => { | |
tapTimes = []; | |
document.getElementById('tempoDisplay').innerText = '-- BPM'; | |
}); | |
document.getElementById('modeToggle').addEventListener('change', () => { | |
const body = document.body; | |
if (body.classList.contains('light-mode')) { | |
body.classList.remove('light-mode'); | |
body.classList.add('dark-mode'); | |
} else { | |
body.classList.remove('dark-mode'); | |
body.classList.add('light-mode'); | |
} | |
}); | |
document.addEventListener('keydown', (event) => { | |
if (event.key === ' ') { | |
document.getElementById('tapButton').click(); | |
} | |
}); | |
</script> | |
<!-- Bootstrap JS and dependencies --> | |
<script src="https://code.jquery.com/jquery-3.5.1.s | |