swipe things
This commit is contained in:
98
client/src/components/SwipeNavigation.astro
Normal file
98
client/src/components/SwipeNavigation.astro
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
---
|
||||||
|
// SwipeNavigation component for mobile swipe-to-go-back functionality
|
||||||
|
---
|
||||||
|
|
||||||
|
<div id="swipe-container" class="fixed inset-0 pointer-events-none z-50">
|
||||||
|
<div id="swipe-area" class="absolute left-0 top-0 w-8 h-full pointer-events-auto"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
class SwipeNavigation {
|
||||||
|
private startX: number = 0;
|
||||||
|
private startY: number = 0;
|
||||||
|
private isTracking: boolean = false;
|
||||||
|
private readonly minSwipeDistance: number = 100;
|
||||||
|
private readonly maxVerticalDistance: number = 50;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private init() {
|
||||||
|
const swipeArea = document.getElementById('swipe-area');
|
||||||
|
if (!swipeArea) return;
|
||||||
|
|
||||||
|
// Only enable on mobile devices
|
||||||
|
if (!this.isMobile()) return;
|
||||||
|
|
||||||
|
swipeArea.addEventListener('touchstart', this.handleTouchStart.bind(this), { passive: true });
|
||||||
|
swipeArea.addEventListener('touchmove', this.handleTouchMove.bind(this), { passive: true });
|
||||||
|
swipeArea.addEventListener('touchend', this.handleTouchEnd.bind(this), { passive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
private isMobile(): boolean {
|
||||||
|
return 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleTouchStart(e: TouchEvent) {
|
||||||
|
const touch = e.touches[0];
|
||||||
|
this.startX = touch.clientX;
|
||||||
|
this.startY = touch.clientY;
|
||||||
|
this.isTracking = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleTouchMove(e: TouchEvent) {
|
||||||
|
if (!this.isTracking) return;
|
||||||
|
|
||||||
|
const touch = e.touches[0];
|
||||||
|
const deltaX = touch.clientX - this.startX;
|
||||||
|
const deltaY = Math.abs(touch.clientY - this.startY);
|
||||||
|
|
||||||
|
// If vertical movement is too large, cancel the swipe
|
||||||
|
if (deltaY > this.maxVerticalDistance) {
|
||||||
|
this.isTracking = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent default only if we're swiping left (negative deltaX)
|
||||||
|
if (deltaX < 0) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleTouchEnd(e: TouchEvent) {
|
||||||
|
if (!this.isTracking) return;
|
||||||
|
|
||||||
|
const touch = e.changedTouches[0];
|
||||||
|
const deltaX = touch.clientX - this.startX;
|
||||||
|
const deltaY = Math.abs(touch.clientY - this.startY);
|
||||||
|
|
||||||
|
// Check if it's a valid left swipe
|
||||||
|
if (deltaX < -this.minSwipeDistance && deltaY < this.maxVerticalDistance) {
|
||||||
|
this.goBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isTracking = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private goBack() {
|
||||||
|
// Check if there's history to go back to
|
||||||
|
if (window.history.length > 1) {
|
||||||
|
window.history.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize swipe navigation when the DOM is ready
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
new SwipeNavigation();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#swipe-area {
|
||||||
|
background: transparent;
|
||||||
|
/* Optional: Add a subtle visual indicator for debugging */
|
||||||
|
/* background: rgba(255, 0, 0, 0.1); */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
import "../styles/global.css";
|
import "../styles/global.css";
|
||||||
import Head from "./Head.astro";
|
import Head from "./Head.astro";
|
||||||
import Navigation from "../components/Navigation.astro";
|
import Navigation from "../components/Navigation.astro";
|
||||||
|
import SwipeNavigation from "../components/SwipeNavigation.astro";
|
||||||
import { getLanguageFromPath } from "../lib/i18n";
|
import { getLanguageFromPath } from "../lib/i18n";
|
||||||
import ReloadPrompt from '../components/ReloadPrompt.astro';
|
import ReloadPrompt from '../components/ReloadPrompt.astro';
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ const currentLang = getLanguageFromPath(Astro.url.pathname);
|
|||||||
<ReloadPrompt />
|
<ReloadPrompt />
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
<SwipeNavigation />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user