Füge Unterstützung für Auswahlepochen hinzu, um die Auswahl bei Änderungen zu verwalten

This commit is contained in:
2026-02-22 11:05:23 +01:00
parent 593ac005ec
commit 532a887119

View File

@@ -4,6 +4,7 @@ document.addEventListener('DOMContentLoaded', function(){
let selecting = false; let selecting = false;
let selectionDone = false; let selectionDone = false;
let selectionTimer = null; let selectionTimer = null;
let selectionEpoch = 0;
function createFinger(t){ function createFinger(t){
const id = (t.identifier !== undefined && t.identifier !== null) ? String(t.identifier) : ('m'+Date.now()); const id = (t.identifier !== undefined && t.identifier !== null) ? String(t.identifier) : ('m'+Date.now());
@@ -57,15 +58,16 @@ document.addEventListener('DOMContentLoaded', function(){
function startSelectionIfNeeded(){ function startSelectionIfNeeded(){
if(selecting || selectionDone) return; if(selecting || selectionDone) return;
if(touches.size < 1) return; // require at least 1 finger if(touches.size < 1) return;
selecting = true; selecting = true;
const epoch = selectionEpoch;
const {items} = computeOrderedItems(); const {items} = computeOrderedItems();
const els = items.map(i=>i.el); const els = items.map(i=>i.el);
const n = els.length; const n = els.length;
// If only one finger, immediately mark it as winner (short visual)
if(n === 1){ if(n === 1){
if(epoch !== selectionEpoch) return;
const el = els[0]; const el = els[0];
el.classList.add('winner','pulse'); el.classList.add('winner','pulse');
selecting = false; selecting = false;
@@ -73,7 +75,7 @@ document.addEventListener('DOMContentLoaded', function(){
return; return;
} }
const rotations = Math.floor(Math.random()*3)+3; // 3-5 rotations const rotations = Math.floor(Math.random()*3)+3;
const winnerIndex = Math.floor(Math.random()*n); const winnerIndex = Math.floor(Math.random()*n);
const totalSteps = rotations * n + winnerIndex; const totalSteps = rotations * n + winnerIndex;
@@ -81,10 +83,11 @@ document.addEventListener('DOMContentLoaded', function(){
let current = 0; let current = 0;
for(let step=0; step<=totalSteps; step++){ for(let step=0; step<=totalSteps; step++){
const t = step/totalSteps; const t = step/totalSteps;
const delay = 40 + Math.pow(t,2)*520; // ease-out timing const delay = 40 + Math.pow(t,2)*520;
cumulative += delay; cumulative += delay;
const idx = current % n; const idx = current % n;
setTimeout(()=>{ setTimeout(()=>{
if(epoch !== selectionEpoch) return;
els.forEach((el,i)=>{ els.forEach((el,i)=>{
if(i===idx) el.classList.add('highlight'); else el.classList.remove('highlight'); if(i===idx) el.classList.add('highlight'); else el.classList.remove('highlight');
}); });
@@ -93,6 +96,7 @@ document.addEventListener('DOMContentLoaded', function(){
} }
setTimeout(()=>{ setTimeout(()=>{
if(epoch !== selectionEpoch) return;
els.forEach((el,i)=>{ els.forEach((el,i)=>{
if(i===winnerIndex){ el.classList.remove('highlight'); el.classList.add('winner','pulse'); } if(i===winnerIndex){ el.classList.remove('highlight'); el.classList.add('winner','pulse'); }
else { el.classList.remove('highlight'); el.classList.add('lost'); } else { el.classList.remove('highlight'); el.classList.add('lost'); }
@@ -103,6 +107,7 @@ document.addEventListener('DOMContentLoaded', function(){
} }
function cancelSelection(){ function cancelSelection(){
selectionEpoch++;
clearTimeout(selectionTimer); clearTimeout(selectionTimer);
selectionTimer = null; selectionTimer = null;
selecting = false; selecting = false;
@@ -113,6 +118,7 @@ document.addEventListener('DOMContentLoaded', function(){
function resetWhenAllUp(){ function resetWhenAllUp(){
if(touches.size===0){ if(touches.size===0){
setTimeout(()=>{ setTimeout(()=>{
if(touches.size > 0) return;
selecting=false; selectionDone=false; selecting=false; selectionDone=false;
const nodes = Array.from(stage.querySelectorAll('.finger')); const nodes = Array.from(stage.querySelectorAll('.finger'));
nodes.forEach(node=>node.remove()); nodes.forEach(node=>node.remove());
@@ -126,10 +132,8 @@ document.addEventListener('DOMContentLoaded', function(){
e.preventDefault(); e.preventDefault();
const changed = Array.from(e.changedTouches || []); const changed = Array.from(e.changedTouches || []);
changed.forEach(t=>createFinger(t)); changed.forEach(t=>createFinger(t));
if(!selectionDone){ cancelSelection();
clearTimeout(selectionTimer); selectionTimer = setTimeout(startSelectionIfNeeded, 700);
selectionTimer = setTimeout(startSelectionIfNeeded, 700);
}
}, {passive:false}); }, {passive:false});
stage.addEventListener('touchmove', function(e){ stage.addEventListener('touchmove', function(e){
e.preventDefault(); e.preventDefault();
@@ -147,8 +151,13 @@ document.addEventListener('DOMContentLoaded', function(){
e.preventDefault(); e.preventDefault();
const changed = Array.from(e.changedTouches || []); const changed = Array.from(e.changedTouches || []);
changed.forEach(t=>removeFingerById(t.identifier)); changed.forEach(t=>removeFingerById(t.identifier));
if(!selectionDone) cancelSelection(); cancelSelection();
if(touches.size===0) resetWhenAllUp(); if(touches.size > 0){
selectionTimer = setTimeout(startSelectionIfNeeded, 700);
}
// touches.size===0: cancelSelection hat State bereits bereinigt,
// DOM-Elemente durch removeFingerById entfernt.
// iOS feuert neue touchstart-Events für noch liegende Finger selbst.
}, {passive:false}); }, {passive:false});
// Support mouse for desktop testing // Support mouse for desktop testing