Skip to content

Commit 103f4ec

Browse files
committed
dd
1 parent d8d2aa3 commit 103f4ec

1 file changed

Lines changed: 94 additions & 0 deletions

File tree

pages/children/cps-tester.html

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
66
<title>CPS Tester — Both Buttons</title>
7+
8+
<link rel="icon" type="image/svg+xml" id="favicon-link">
79
<link
810
href="https://fonts.googleapis.com/css2?family=Syne:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap"
911
rel="stylesheet"
@@ -836,5 +838,97 @@ <h1>CPS Tester</h1>
836838
}
837839
}
838840
</script>
841+
842+
843+
<script>
844+
// FAVICON ANIMATION
845+
const SVG_W = 32;
846+
const SVG_H = 32;
847+
848+
/* Mouse geometry (all coords in 32×32 space) */
849+
const BODY_PATH = "M7,14 Q7,4 16,4 Q25,4 25,14 L25,24 Q25,28 16,28 Q7,28 7,24 Z";
850+
const DIV_LINE = "M16,4 L16,14"; /* centre divider between buttons */
851+
const SCROLL_RX = 1.5, SCROLL_RY = 3; /* scroll-wheel ellipse */
852+
853+
/* Colours */
854+
const BG = "transparent";
855+
const BODY_F = "#1a1a1a";
856+
const BODY_S = "#888";
857+
const BTN_IDLE = "#1a1a1a";
858+
const BTN_FLASH= "#4af"; /* bright blue-white flash */
859+
const LINE_C = "#666";
860+
const WHEEL_F = "#555";
861+
862+
function makeSVG(leftOn, rightOn) {
863+
/* clip-paths for left / right button regions */
864+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32">
865+
<defs>
866+
<clipPath id="cl"><rect x="0" y="0" width="16" height="14"/></clipPath>
867+
<clipPath id="cr"><rect x="16" y="0" width="16" height="14"/></clipPath>
868+
</defs>
869+
870+
<!-- body outline -->
871+
<path d="${BODY_PATH}" fill="${BODY_F}" stroke="${BODY_S}" stroke-width="1.2"/>
872+
873+
<!-- left button flash -->
874+
<path d="${BODY_PATH}" fill="${leftOn ? BTN_FLASH : BTN_IDLE}" clip-path="url(#cl)"/>
875+
876+
<!-- right button flash -->
877+
<path d="${BODY_PATH}" fill="${rightOn ? BTN_FLASH : BTN_IDLE}" clip-path="url(#cr)"/>
878+
879+
<!-- body outline on top so the stroke is always visible -->
880+
<path d="${BODY_PATH}" fill="none" stroke="${BODY_S}" stroke-width="1.2"/>
881+
882+
<!-- centre divider -->
883+
<line x1="16" y1="4" x2="16" y2="14" stroke="${LINE_C}" stroke-width="0.8"/>
884+
885+
<!-- scroll wheel -->
886+
<ellipse cx="16" cy="13" rx="${SCROLL_RX}" ry="${SCROLL_RY}" fill="${WHEEL_F}" stroke="${LINE_C}" stroke-width="0.6"/>
887+
</svg>`;
888+
}
889+
890+
function setFavicon(leftOn, rightOn) {
891+
const svg = makeSVG(leftOn, rightOn);
892+
const blob = new Blob([svg], {type: "image/svg+xml"});
893+
const url = URL.createObjectURL(blob);
894+
const link = document.getElementById("favicon-link");
895+
const old = link.href;
896+
link.href = url;
897+
if (old) URL.revokeObjectURL(old);
898+
}
899+
900+
/*
901+
Animation schedule over 2 s (2000 ms), looping:
902+
0 ms — LEFT on
903+
200 ms — LEFT off
904+
450 ms — LEFT on (second flash)
905+
650 ms — LEFT off
906+
1000 ms — RIGHT on
907+
1200 ms — RIGHT off
908+
2000 ms — loop
909+
*/
910+
const schedule = [
911+
{ t: 0, L: true, R: false },
912+
{ t: 200, L: false, R: false },
913+
{ t: 450, L: true, R: false },
914+
{ t: 650, L: false, R: false },
915+
{ t: 1000, L: false, R: true },
916+
{ t: 1200, L: false, R: false },
917+
];
918+
const CYCLE = 2000;
919+
920+
function tick() {
921+
const now = Date.now() % CYCLE;
922+
let frame = schedule[0];
923+
for (const s of schedule) {
924+
if (now >= s.t) frame = s;
925+
}
926+
setFavicon(frame.L, frame.R);
927+
}
928+
929+
/* initial frame immediately, then drive at ~30 fps */
930+
setFavicon(false, false);
931+
setInterval(tick, 33);
932+
</script>
839933
</body>
840934
</html>

0 commit comments

Comments
 (0)