start of tap tempo

This commit is contained in:
alex
2025-10-14 08:56:11 +01:00
parent 69a82ddc5b
commit fe117e23fd
2 changed files with 73 additions and 0 deletions
+71
View File
@@ -0,0 +1,71 @@
import React, { useState, useRef, useEffect } from "react";
export default function Tap({ initialCps = 0.4, maxSamples = 3}) {
const [timestamps, setTimestamps] = useState([]);
const [cps, setCps] = useState(initialCps);
function addTap(ts = Date.now()) {
setTimestamps(prev => {
const next = [...prev, ts].slice(-(maxSamples + 1));
calcCps(next);
return next;
});
}
function calcCps(times) {
if (!times || times.length < 2) return;
const intervals = [];
for (let i = 1; i < times.length; i++) {
intervals.push(times[i] - times[i - 1]);
}
const avgMs = intervals.reduce((a, b) => a + b, 0) / intervals.length;
const newCps = 1000 / avgMs;
if (Number.isFinite(newCps) && newCps > 0 && newCps < 1000) {
setCps(newCps);
}
}
function handleTap(e) {
e && e.preventDefault();
addTap();
}
function handleReset(e) {
e && e.preventDefault();
reset();
}
useEffect(() => {
function onKey(e) {
if (e.code === "Space") {
e.preventDefault();
addTap();
} else if (e.code === "Backspace") {
e.preventDefault();
reset();
}
}
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, []);
function reset() {
setTimestamps([]);
// setCps(initialCps);
}
return (
<div>
<div class="flex flex-col items-center p-7 font-medium text-white">
<div>{cps.toFixed(2)} cps</div>
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onClick={handleTap}
>
Tap
</button>
</div>
</div>
);
}
+2
View File
@@ -1,6 +1,7 @@
---
import HeadCommon from '../components/HeadCommon.astro';
import { Vinyl } from '../components/Vinyl.jsx';
import Tap from '../components/Tap.jsx';
---
<html lang="en" class="m-0 dark">
@@ -11,5 +12,6 @@ import { Vinyl } from '../components/Vinyl.jsx';
<body class="h-app-height bg-background m-0">
<div>Hello there.</div>
<Vinyl client:only="react" />
<Tap client:only="react" />
</body>
</html>