Fotos mit dem Finger wischen (Swipe) — auf jeder Karte, überall auf der Seite

Das Foto-Karussell auf den Produktkarten (Shop-Grid, Startseite, Sale,
Charakter-Kacheln, "Weitere Modelle" — überall wo ProductCard.astro
verwendet wird) lässt sich jetzt zusätzlich zu den ‹/›-Pfeilen auch per
Wisch-Geste bedienen: Finger auf dem Handy oder Maus ziehen am PC.
Technisch über die Pointer-Events-API (deckt Touch UND Maus mit
derselben Logik ab). Ein Wisch zählt erst ab einer Mindeststrecke und
nur bei überwiegend horizontaler Bewegung, damit normales Scrollen der
Seite nicht versehentlich als Wisch missverstanden wird. Da die ganze
Karte ein Link ist, wird der nachfolgende Klick nach einem echten Wisch
unterdrückt — sonst würde man ungewollt zur Produktseite springen, nur
weil man durch die Fotos blättern wollte.

Gleiches Wischen jetzt auch auf der Produktdetailseite selbst, für das
große Hauptbild (springt zum nächsten/vorherigen Thumbnail).

Verifiziert: Wisch ändert das Bild UND verhindert nachweislich die
Navigation; normale Klicks navigieren weiterhin korrekt; die Pfeil-
Buttons funktionieren unverändert (kein Regressionsfehler).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
qcigano
2026-08-03 01:12:46 +02:00
co-authored by Claude Sonnet 5
parent c7c500e23b
commit ba3ab44ef3
2 changed files with 60 additions and 2 deletions
+42 -1
View File
@@ -96,11 +96,52 @@ const desc = product.beschreibung[lang];
karussellWechseln(media, current + Number(control.dataset.dir));
}
});
// Wischen/Swipen (Finger auf dem Handy, Maus ziehen am PC) — überall auf der Seite, wo eine
// Produktkarte mit mehreren Fotos vorkommt. Pointer Events statt separater Touch-/Maus-Logik,
// damit EIN Code sowohl Finger als auch Maus abdeckt. Ein "Swipe" zählt erst ab einer gewissen
// Mindeststrecke (SWIPE_SCHWELLE) und nur, wenn die Bewegung überwiegend horizontal war — sonst
// würde normales Scrollen der Seite (vertikal) versehentlich als Wisch-Geste missverstanden.
const SWIPE_SCHWELLE = 40;
let wischStart: { media: Element; x: number; y: number } | null = null;
let letzterWischZeitpunkt = 0;
document.addEventListener("pointerdown", (e) => {
const media = (e.target as HTMLElement).closest("[data-carousel]");
if (!media) return;
if (media.querySelectorAll(".carousel-img").length <= 1) return;
wischStart = { media, x: e.clientX, y: e.clientY };
});
document.addEventListener("pointerup", (e) => {
if (!wischStart) return;
const { media, x, y } = wischStart;
wischStart = null;
const dx = e.clientX - x;
const dy = e.clientY - y;
if (Math.abs(dx) < SWIPE_SCHWELLE || Math.abs(dx) < Math.abs(dy)) return;
letzterWischZeitpunkt = Date.now();
const imgs = [...media.querySelectorAll<HTMLElement>(".carousel-img")];
const current = Math.max(0, imgs.findIndex((img) => img.style.display !== "none"));
karussellWechseln(media, current + (dx < 0 ? 1 : -1));
});
document.addEventListener("pointercancel", () => { wischStart = null; });
// Direkt nach einem echten Wisch darf der Klick NICHT mehr zur Produktseite weiterspringen
// (sonst landet man ungewollt auf der Produktseite, nur weil man das Foto durchblättern
// wollte). Capture-Phase, damit das VOR dem normalen Link-Klick des <a class="product-card">
// greift. Ein kurzes Zeitfenster (300ms) reicht, da Klick direkt nach dem Loslassen kommt.
document.addEventListener("click", (e) => {
if (Date.now() - letzterWischZeitpunkt > 300) return;
const link = (e.target as HTMLElement).closest("a.product-card");
if (link) { e.preventDefault(); e.stopPropagation(); }
}, true);
</script>
<style>
.product-card { display: flex; flex-direction: column; gap: 0.6rem; padding: 1rem; }
.product-media { position: relative; isolation: isolate; }
.product-media { position: relative; isolation: isolate; touch-action: pan-y; }
.product-media .img-placeholder { aspect-ratio: 1 / 1; min-height: 0; }
.product-badges { position: absolute; top: 0.6rem; left: 0.6rem; display: flex; gap: 0.4rem; flex-wrap: wrap; z-index: 2; }
+18 -1
View File
@@ -209,9 +209,26 @@ const felder: [string, string | undefined][] = [
// Klick auf ein Miniaturbild zeigt es groß im Hauptbild an (mehrere Produktfotos).
const heroImg = document.getElementById("product-hero-img") as HTMLImageElement | null;
document.querySelectorAll<HTMLImageElement>(".thumb.product-photo").forEach((thumb) => {
const thumbs = [...document.querySelectorAll<HTMLImageElement>(".thumb.product-photo")];
thumbs.forEach((thumb) => {
thumb.addEventListener("click", () => {
if (heroImg && thumb.dataset.full) heroImg.src = thumb.dataset.full;
});
});
// Auch das große Hauptbild lässt sich wischen (Finger auf dem Handy, Maus ziehen am PC) — springt
// dabei einfach zum nächsten/vorherigen Miniaturbild, exakt wie ein Klick auf den Thumbnail.
if (heroImg && thumbs.length > 1) {
let startX = 0, startY = 0;
heroImg.style.touchAction = "pan-y";
heroImg.addEventListener("pointerdown", (e) => { startX = e.clientX; startY = e.clientY; });
heroImg.addEventListener("pointerup", (e) => {
const dx = e.clientX - startX;
const dy = e.clientY - startY;
if (Math.abs(dx) < 40 || Math.abs(dx) < Math.abs(dy)) return;
const aktuellerIndex = Math.max(0, thumbs.findIndex((th) => th.dataset.full && heroImg.src.endsWith(th.dataset.full)));
const naechster = ((aktuellerIndex + (dx < 0 ? 1 : -1)) % thumbs.length + thumbs.length) % thumbs.length;
heroImg.src = thumbs[naechster].dataset.full!;
});
}
</script>