feat: touch swipe for dating mode with live drag feedback
- swipe left/right to reject/keep, swipe up for favorite - card tilts and overlays fade in proportionally during drag - snap-back animation when swipe distance below threshold - touchcancel handled identically to touchend Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+76
-3
@@ -131,7 +131,7 @@
|
||||
.drop-zone { border: 2px dashed var(--border-mid); border-radius: 12px; padding: 2rem 1rem; text-align: center; transition: border-color 0.15s, background 0.15s; margin-bottom: 1rem; cursor: default; }
|
||||
.drop-zone.drag-over { border-color: var(--blue); background: var(--blue-dim); }
|
||||
.drop-zone.done { border-color: var(--green); background: var(--green-dim); }
|
||||
.drop-icon { font-size: 2.2rem; margin-bottom: 0.5rem; }
|
||||
.drop-icon { margin-bottom: 0.5rem; line-height: 1; }
|
||||
.drop-text { font-size: 1rem; font-weight: 500; color: var(--text); margin-bottom: 0.25rem; }
|
||||
.drop-sub { font-size: 0.8rem; color: var(--faint); }
|
||||
.upload-status { margin-bottom: 1rem; }
|
||||
@@ -163,7 +163,8 @@
|
||||
.tinder-counter { font-size: 0.92rem; color: var(--muted); font-weight: 500; }
|
||||
.tinder-done-btn { padding: 0.3rem 0.8rem; border-radius: 6px; border: 1.5px solid var(--border); background: #fff; color: var(--muted); cursor: pointer; font-size: 0.82rem; transition: border-color 0.15s, color 0.15s; }
|
||||
.tinder-done-btn:hover { border-color: var(--blue); color: var(--blue); }
|
||||
.tinder-card { position: relative; border-radius: 16px; overflow: hidden; background: #fff; box-shadow: 0 4px 24px rgba(0,0,0,0.12); transform-origin: bottom center; }
|
||||
.tinder-card { position: relative; border-radius: 16px; overflow: hidden; background: #fff; box-shadow: 0 4px 24px rgba(0,0,0,0.12); transform-origin: bottom center; touch-action: none; user-select: none; }
|
||||
.tinder-card.snapping { transition: transform 0.25s cubic-bezier(0.2,0,0.4,1); }
|
||||
.tinder-card img { width: 100%; max-height: 62vh; object-fit: contain; background: #F8F9FB; display: block; }
|
||||
.tinder-overlay { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 3rem; font-weight: 900; letter-spacing: 0.08em; opacity: 0; pointer-events: none; transition: opacity 0.12s; }
|
||||
.tinder-overlay-ok { background: rgba(34,197,94,0.25); color: var(--green); }
|
||||
@@ -277,7 +278,17 @@
|
||||
<div id="view-start" class="view active card">
|
||||
<input type="file" id="folder-picker" webkitdirectory multiple accept="image/*" style="display:none">
|
||||
<div id="drop-zone" class="drop-zone">
|
||||
<div class="drop-icon">📁</div>
|
||||
<div class="drop-icon">
|
||||
<svg width="52" height="52" viewBox="0 0 52 52" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="4" y="4" width="44" height="44" rx="10" fill="var(--blue-dim)" stroke="var(--blue)" stroke-width="2"/>
|
||||
<rect x="13" y="16" width="26" height="20" rx="4" fill="none" stroke="var(--blue)" stroke-width="2"/>
|
||||
<line x1="19" y1="16" x2="19" y2="12" stroke="var(--blue)" stroke-width="2" stroke-linecap="round"/>
|
||||
<line x1="19" y1="12" x2="28" y2="12" stroke="var(--blue)" stroke-width="2" stroke-linecap="round"/>
|
||||
<line x1="28" y1="12" x2="28" y2="16" stroke="var(--blue)" stroke-width="2" stroke-linecap="round"/>
|
||||
<line x1="26" y1="22" x2="26" y2="30" stroke="var(--blue)" stroke-width="2" stroke-linecap="round"/>
|
||||
<line x1="22" y1="26" x2="30" y2="26" stroke="var(--blue)" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="drop-text">Drag & Drop</p>
|
||||
<p style="font-size:0.8rem; color:var(--faint); margin:0.4rem 0;">oder</p>
|
||||
<button id="folder-picker-btn" style="padding:0.45rem 1.1rem; border-radius:8px; border:1.5px solid var(--border); background:#fff; color:var(--muted); font-size:0.85rem; font-weight:500; cursor:pointer; transition:border-color 0.15s, color 0.15s;">Ordner auswählen</button>
|
||||
@@ -1340,6 +1351,68 @@
|
||||
el("tinder-btn-nok").addEventListener("click", () => tinderSwipe("left"));
|
||||
el("tinder-btn-ok").addEventListener("click", () => tinderSwipe("right"));
|
||||
|
||||
// Touch swipe
|
||||
const SWIPE_H = 80; // px horizontal threshold
|
||||
const SWIPE_UP = 75; // px upward threshold (favorite)
|
||||
let _tx0 = 0, _ty0 = 0, _touching = false;
|
||||
|
||||
function _resetOverlays() {
|
||||
el("tinder-overlay-ok").style.opacity = "0";
|
||||
el("tinder-overlay-nok").style.opacity = "0";
|
||||
el("tinder-overlay-fav").style.opacity = "0";
|
||||
}
|
||||
|
||||
el("tinder-card").addEventListener("touchstart", e => {
|
||||
if (tinderSwiping) return;
|
||||
const t = e.touches[0];
|
||||
_tx0 = t.clientX; _ty0 = t.clientY;
|
||||
_touching = true;
|
||||
el("tinder-card").classList.remove("snapping");
|
||||
}, { passive: true });
|
||||
|
||||
el("tinder-card").addEventListener("touchmove", e => {
|
||||
if (!_touching || tinderSwiping) return;
|
||||
e.preventDefault();
|
||||
const t = e.touches[0];
|
||||
const dx = t.clientX - _tx0;
|
||||
const dy = t.clientY - _ty0;
|
||||
const isUp = dy < -SWIPE_UP * 0.5 && Math.abs(dx) < 55;
|
||||
const rot = isUp ? 0 : (dx / el("tinder-card").offsetWidth) * 18;
|
||||
|
||||
el("tinder-card").style.transform =
|
||||
`translateX(${isUp ? 0 : dx}px) translateY(${isUp ? dy * 0.6 : 0}px) rotate(${rot}deg)`;
|
||||
|
||||
el("tinder-overlay-ok").style.opacity = (!isUp && dx > 30) ? Math.min(1, (dx - 30) / 70) + "" : "0";
|
||||
el("tinder-overlay-nok").style.opacity = (!isUp && dx < -30) ? Math.min(1, (-dx - 30) / 70) + "" : "0";
|
||||
el("tinder-overlay-fav").style.opacity = isUp ? Math.min(1, (-dy - 37) / 50) + "" : "0";
|
||||
}, { passive: false });
|
||||
|
||||
function _onTouchEnd(e) {
|
||||
if (!_touching) return;
|
||||
_touching = false;
|
||||
const t = e.changedTouches[0];
|
||||
const dx = t.clientX - _tx0;
|
||||
const dy = t.clientY - _ty0;
|
||||
|
||||
el("tinder-card").style.transform = "";
|
||||
_resetOverlays();
|
||||
|
||||
if (dy < -SWIPE_UP && Math.abs(dx) < 55) {
|
||||
tinderFavorite();
|
||||
} else if (dx > SWIPE_H) {
|
||||
tinderSwipe("right");
|
||||
} else if (dx < -SWIPE_H) {
|
||||
tinderSwipe("left");
|
||||
} else {
|
||||
const card = el("tinder-card");
|
||||
card.classList.add("snapping");
|
||||
card.addEventListener("transitionend", () => card.classList.remove("snapping"), { once: true });
|
||||
}
|
||||
}
|
||||
|
||||
el("tinder-card").addEventListener("touchend", _onTouchEnd);
|
||||
el("tinder-card").addEventListener("touchcancel", _onTouchEnd);
|
||||
|
||||
el("tinder-fav-star").addEventListener("click", () => {
|
||||
const photo = tinderQueue[tinderIndex];
|
||||
if (!photo) return;
|
||||
|
||||
Reference in New Issue
Block a user