Lightbox, Gestur Sentuh & Titik Fokus Foto
Penampil foto layar penuh yang mendukung usapan jari (swipe), pintasan papan ketik, dan koordinat pemotongan gambar khusus.
Membaca sekitar 2 menit
Penampil foto layar penuh yang mendukung usapan jari (swipe), pintasan papan ketik, dan koordinat pemotongan gambar khusus.
Menghentikan gulir otomatis galeri saat foto diklik, menampilkan kartu potret 3:4 dengan bingkai sudut estetis, serta menerjemahkan selisih koordinat sentuh (touch delta) menjadi navigasi foto sebelumnya/berikutnya.
Memotong 25 foto dengan rasio berbeda ke dalam kotak grid dan kartu 3:4 akan memenggal wajah subjek jika tidak dibekali koordinat objectPosition khusus untuk tiap foto.
Masalahnya dulu
Ketika foto-foto pre-wedding dipotong ke dalam bermacam ukuran kotak di Bento Grid maupun ditampilkan dalam bingkai kartu 3:4 di Lightbox, perataan tengah bawaan (object-position: center) sering memotong bagian kepala atau wajah pasangan. Selain itu, pengguna ponsel terbiasa menggeser foto di layar penuh dengan usapan jempol ke kiri atau kanan, sementara pengguna laptop mengandalkan tombol panah dan Escape.
Koordinat fokus per foto dan deteksi swipe ringan
Setiap objek di dalam array PHOTOS pada GallerySwiper.tsx tidak hanya menyimpan path gambar dan judul puitisnya (seperti 'Sejak Pertama Kali' atau 'Janji di Tepi Ombak'), tetapi juga properti pos berisi koordinat fokus spesifik (contohnya '50% 25%' atau '38% 30%'). Ketika salah satu foto ditekan, state active akan menyimpan indeks foto tersebut, menghentikan sejenak auto-scroll galeri di belakang layar (pausedRef.current = active !== null), dan membuka komponen Lightbox yang mendukung navigasi tombol layar, keyboard, hingga usapan sentuh (touch swipe).
Responsivitas sejati pada galeri foto bukan cuma soal ukuran layar, melainkan memastikan wajah subjek selalu berada di titik fokus pada rasio potong apa pun.
Implementasi di ngodingan ini
Berikut adalah penanganan gestur sentuh, pintasan keyboard, dan pemasangan titik fokus gambar di dalam komponen Lightbox pada src/components/GallerySwiper.tsx:
useEffect(() => {
const fn = (e: KeyboardEvent) => {
if (e.key === "Escape") onClose();
if (e.key === "ArrowRight") onNext();
if (e.key === "ArrowLeft") onPrev();
};
window.addEventListener("keydown", fn);
return () => window.removeEventListener("keydown", fn);
}, [onClose, onPrev, onNext]);
// Deteksi swipe horizontal tanpa library tambahan
onTouchStart={(e) => { tx.current = e.touches[0].clientX; }}
onTouchEnd={(e) => {
const dx = tx.current - e.changedTouches[0].clientX;
if (Math.abs(dx) > 50) {
e.stopPropagation();
dx > 0 ? onNext() : onPrev();
}
}}Jebakan dan detail yang harus dijaga
- Pada onTouchEnd, pemanggilan e.stopPropagation() saat jarak usapan melebihi ambang 50px sangat krusial agar akhir sentuhan jari tidak dianggap sebagai klik pada latar belakang gelap yang justru menutup Lightbox.
- Menambahkan prop key={index} pada komponen Next.js <Image> di dalam Lightbox memastikan gambar langsung berganti dengan bersih ketika pengguna berpindah foto secara cepat.
- Hanya 6 foto pertama di grid yang diberi atribut loading="eager", sedangkan sisanya menggunakan "lazy" agar pembukaan halaman galeri tidak membanjiri jaringan seluler tamu undangan.