Neue Aktion `liegestuetze` fuer Belohnungen: jede Einloesung zaehlt um `anzahl` hoch (Vorgabe 5). Der Stand liegt in der Datenbank und uebersteht einen Neustart. Browser-Source /liegestuetze zeigt ihn, die Uebersichtsseite hat Knoepfe zum Herunterzaehlen und Setzen. Nebenbei: POST /tts landete bei der Overlay-Seite statt bei der Freigabe. Seiten werden jetzt nur noch bei GET ausgeliefert. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
187 lines
5.7 KiB
HTML
187 lines
5.7 KiB
HTML
<!doctype html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>abdulive - Liegestütze</title>
|
|
<link rel="stylesheet" href="/theme.css">
|
|
<style>
|
|
/* Offene Liegestütze aus Kanalpunkten.
|
|
*
|
|
* Eine Einlösung zählt hoch und zeigt kurz, wer es war. Wenn Abdu auf der
|
|
* Übersichtsseite herunterzählt, blinkt die Zahl grün - ohne Namen, damit
|
|
* eine Korrektur nicht wie eine Einlösung aussieht.
|
|
*
|
|
* In OBS: Browser-Quelle 420x200. Einstellungen in der Adresse:
|
|
* ?verstecken=1 bei 0 ausblenden statt "erledigt" zeigen
|
|
* ?titel=... andere Beschriftung als "Liegestütze" */
|
|
#karte {
|
|
position: absolute;
|
|
left: 12px; bottom: 12px;
|
|
display: flex; align-items: center; gap: 18px;
|
|
padding: 16px 26px 16px 18px;
|
|
overflow: hidden;
|
|
transition: opacity .5s, transform .5s, box-shadow .4s, border-color .4s;
|
|
}
|
|
#karte.weg { opacity: 0; transform: translateY(14px); }
|
|
|
|
.symbol {
|
|
width: 58px; height: 58px; flex: none;
|
|
display: grid; place-items: center;
|
|
border-radius: 14px;
|
|
background: linear-gradient(135deg, var(--lila), var(--pink));
|
|
box-shadow: 0 6px 18px rgba(169, 112, 255, .45);
|
|
}
|
|
.symbol svg { width: 36px; height: 36px; }
|
|
|
|
.text { min-width: 0; }
|
|
#titel {
|
|
font: 600 13px/1 var(--font-zahl);
|
|
letter-spacing: .16em; text-transform: uppercase;
|
|
color: var(--schrift-leise);
|
|
}
|
|
#zahl {
|
|
display: block;
|
|
font: 800 54px/1 var(--font);
|
|
font-variant-numeric: tabular-nums;
|
|
letter-spacing: -.02em;
|
|
margin-top: 6px;
|
|
}
|
|
#zahl.erledigt { font-size: 30px; color: var(--gruen); padding: 8px 0 6px; }
|
|
|
|
/* Einlösung: Karte glüht gold, Zahl springt. */
|
|
#karte.mehr { border-color: var(--gold); box-shadow: var(--schatten), 0 0 28px rgba(255, 197, 61, .55); }
|
|
#zahl.springen { animation: springen .6s cubic-bezier(.3, 1.6, .5, 1); }
|
|
@keyframes springen {
|
|
0% { transform: scale(1); }
|
|
40% { transform: scale(1.22); color: var(--gold); }
|
|
100% { transform: scale(1); }
|
|
}
|
|
|
|
/* Korrektur nach unten: kurz grün, das heißt "gemacht". */
|
|
#zahl.weniger { animation: weniger .9s ease-out; }
|
|
@keyframes weniger {
|
|
0% { color: var(--gruen); transform: translateY(-4px); }
|
|
100% { color: inherit; transform: none; }
|
|
}
|
|
|
|
/* "+5 von Name", steigt über der Karte auf. */
|
|
#plus {
|
|
position: absolute;
|
|
left: 12px; bottom: 112px;
|
|
max-width: 396px;
|
|
padding: 8px 14px;
|
|
font: 700 20px/1.1 var(--font);
|
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
opacity: 0;
|
|
}
|
|
#plus b { color: var(--gold); margin-right: 6px; }
|
|
#plus.zeigen { animation: aufsteigen 3.6s ease-out both; }
|
|
@keyframes aufsteigen {
|
|
0% { opacity: 0; transform: translateY(16px); }
|
|
12% { opacity: 1; transform: none; }
|
|
80% { opacity: 1; transform: none; }
|
|
100% { opacity: 0; transform: translateY(-10px); }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="plus" class="glas"></div>
|
|
<div id="karte" class="glas">
|
|
<div class="symbol" aria-hidden="true">
|
|
<!-- Figur im Liegestütz: Kopf, Rücken, Arme, Boden. -->
|
|
<svg viewBox="0 0 48 48" fill="none" stroke="#fff" stroke-width="3.4"
|
|
stroke-linecap="round" stroke-linejoin="round">
|
|
<circle cx="11" cy="17" r="4" fill="#fff" stroke="none"/>
|
|
<path d="M15 21 L41 29"/>
|
|
<path d="M17 22 L15 33"/>
|
|
<path d="M41 29 L42 33"/>
|
|
<path d="M6 37 H44" stroke-width="2.4" opacity=".7"/>
|
|
</svg>
|
|
</div>
|
|
<div class="text">
|
|
<div id="titel" class="kontur">Liegestütze</div>
|
|
<span id="zahl" class="kontur">0</span>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="/client.js"></script>
|
|
<script>
|
|
(function () {
|
|
'use strict';
|
|
|
|
var karte = document.getElementById('karte');
|
|
var zahl = document.getElementById('zahl');
|
|
var plus = document.getElementById('plus');
|
|
var verstecken = Overlay.param('verstecken', false);
|
|
document.getElementById('titel').textContent = Overlay.param('titel', 'Liegestütze');
|
|
|
|
var angezeigt = null; // was gerade im Bild steht
|
|
var lauf = null; // laufendes Hochzählen
|
|
var glimmen = null;
|
|
|
|
function schreibe(n) {
|
|
if (n === 0 && !verstecken) {
|
|
zahl.textContent = 'erledigt';
|
|
zahl.classList.add('erledigt');
|
|
} else {
|
|
zahl.textContent = Overlay.zahl(n);
|
|
zahl.classList.remove('erledigt');
|
|
}
|
|
karte.classList.toggle('weg', verstecken && n === 0);
|
|
}
|
|
|
|
function nochmal(el, klasse) {
|
|
el.classList.remove(klasse);
|
|
void el.offsetWidth;
|
|
el.classList.add(klasse);
|
|
}
|
|
|
|
/** Zählt sichtbar von der alten zur neuen Zahl hoch. Bei großen Sprüngen
|
|
* nicht jede einzelne Zahl, sonst dauert ein Setzen auf 500 ewig. */
|
|
function hochzaehlen(von, bis) {
|
|
clearInterval(lauf);
|
|
var schritte = Math.min(bis - von, 20);
|
|
var i = 0;
|
|
lauf = setInterval(function () {
|
|
i++;
|
|
schreibe(i >= schritte ? bis : Math.round(von + (bis - von) * i / schritte));
|
|
if (i >= schritte) clearInterval(lauf);
|
|
}, 45);
|
|
}
|
|
|
|
function stand(n, einloesung) {
|
|
if (typeof n !== 'number') return;
|
|
var alt = angezeigt;
|
|
angezeigt = n;
|
|
|
|
// Erster Stand nach dem Verbinden: einfach hinschreiben, ohne Effekt.
|
|
if (alt === null || alt === n) { clearInterval(lauf); schreibe(n); return; }
|
|
|
|
if (n > alt) {
|
|
hochzaehlen(alt, n);
|
|
nochmal(zahl, 'springen');
|
|
karte.classList.add('mehr');
|
|
clearTimeout(glimmen);
|
|
glimmen = setTimeout(function () { karte.classList.remove('mehr'); }, 2500);
|
|
} else {
|
|
clearInterval(lauf);
|
|
schreibe(n);
|
|
nochmal(zahl, 'weniger');
|
|
}
|
|
|
|
if (einloesung && einloesung.name) {
|
|
plus.innerHTML = '<b>+' + Overlay.esc(einloesung.dazu) + '</b>' +
|
|
'von ' + Overlay.esc(einloesung.name);
|
|
nochmal(plus, 'zeigen');
|
|
}
|
|
}
|
|
|
|
Overlay.on('state', function (ev) { stand(ev.liegestuetze); });
|
|
Overlay.on('liegestuetze', function (ev) {
|
|
stand(ev.offen, ev.dazu ? { dazu: ev.dazu, name: ev.name } : null);
|
|
});
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|