Add mouse-wheel scrolling to the package tile row

Client feedback: after removing mouse-drag on the coverflow (it fought
the 3D transform), the thin scrollbar underneath was the only desktop
way to browse tiles - too fiddly.

Hovering the tile row and scrolling the wheel now moves the row
horizontally, fluid/1:1 with the wheel delta rather than snapping one
card per tick, with scroll-snap briefly suspended during the burst and
re-enabled ~150ms after the last tick so cards settle into place
afterward - the same pattern already used for the old drag interaction.

At the first card, scrolling further "up" is left alone (no
preventDefault): the event falls through to native behavior, which is
already exactly how the page scrolls today when hovering the row, so
the page scrolls up instead. Same at the last card scrolling "down" -
the page takes over. Horizontal wheel/trackpad sideswipe (deltaX) is
untouched, since the container already handles that natively. Applies
uniformly to both strip and coverflow (same scrollerRef container); the
scrollbar stays as-is.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Maaxxs 2026-07-20 21:24:28 +02:00
parent 79fc5f2f76
commit eb59e9c1c9
3 changed files with 63 additions and 22 deletions

View file

@ -317,8 +317,9 @@ export default function ModelPage() {
// Kein Karten-Drag per Maus mehr (Kundenfeedback: fuehlte sich in Kombination mit der
// 3D-Transformation im Coverflow nicht gut an). Auf dem Desktop navigiert man
// stattdessen ueber die eigene Scroll-Leiste unten; Touch-Geraete behalten ohnehin
// das native, fluessige Wisch-Scrolling der scroll-snap-Kartenreihe.
// stattdessen per Mausrad (siehe Effect weiter unten) oder ueber die eigene
// Scroll-Leiste; Touch-Geraete behalten ohnehin das native, fluessige
// Wisch-Scrolling der scroll-snap-Kartenreihe.
//
// Stattdessen: einmaliger kurzer Wisch-Hinweis beim ersten Laden auf Touch-Geraeten
// (Kundenwunsch) - die Reihe faehrt kurz ein Stueck nach rechts und wieder zurueck,
@ -340,6 +341,46 @@ export default function ModelPage() {
};
}, [layout, data]);
// Mausrad ueber der Kartenreihe scrollt horizontal (Kundenfeedback: die Scroll-
// Leiste allein war als einzige Desktop-Bedienung zu fummelig). Steht man an der
// ersten/letzten Karte und "scrollt" in Richtung des Randes hinaus, wird NICHT
// abgefangen (kein preventDefault) - der Browser scrollt dann ganz normal die
// Seite weiter, genau wie es heute schon passiert, wenn die Kartenreihe keinen
// Overflow hat. Reines horizontales Wheel/Trackpad-Sideswipe (deltaX) faellt
// schon nativ auf den Container und wird hier nicht angefasst.
useEffect(() => {
const el = scrollerRef.current;
if (layout === 'grid' || !el) return undefined;
let snapTimer = 0;
const onWheel = (e) => {
if (Math.abs(e.deltaX) > Math.abs(e.deltaY)) return;
const maxScroll = el.scrollWidth - el.clientWidth;
if (maxScroll <= 1) return;
const atStart = el.scrollLeft <= 1;
const atEnd = el.scrollLeft >= maxScroll - 1;
if ((e.deltaY < 0 && atStart) || (e.deltaY > 0 && atEnd)) return;
e.preventDefault();
// Snap waehrend der Wheel-Boe aussetzen (wie beim frueheren Ziehen), sonst
// kaempft scroll-snap gegen schnell aufeinanderfolgende Wheel-Ticks. Der
// Nachlauf-Timer wird bei jedem Tick verlaengert, rastet also erst kurz
// nach dem letzten Tick der Boe wieder ein.
el.style.scrollSnapType = 'none';
el.scrollLeft += e.deltaY;
clearTimeout(snapTimer);
snapTimer = setTimeout(() => {
el.style.scrollSnapType = '';
}, 150);
};
el.addEventListener('wheel', onWheel, { passive: false });
return () => {
el.removeEventListener('wheel', onWheel);
clearTimeout(snapTimer);
};
}, [layout, data]);
// Eigene, zum Design passende Scroll-Leiste unter der Kartenreihe (Strip + Coverflow):
// die native Browser-Leiste ist ausgeblendet, stattdessen ein schmaler zentrierter
// Track mit markengruenem, leuchtendem Daumen. Daumen ist ziehbar, Klick auf den

File diff suppressed because one or more lines are too long

View file

@ -43,7 +43,7 @@
"sameAs": ["https://www.youtube.com/@hifiplanet2812"]
}
</script>
<script type="module" crossorigin src="/assets/index-D7QmGTZX.js"></script>
<script type="module" crossorigin src="/assets/index-3Dk2mE1-.js"></script>
<link rel="modulepreload" crossorigin href="/assets/lucide-icons-CnpfS6R8.js">
<link rel="stylesheet" crossorigin href="/assets/index-9666bnLb.css">
</head>