Audio Persisten Lintas Rute
Pemutar musik latar global yang tetap hidup saat pengguna berpindah halaman serta menangani kebijakan blokir autoplay peramban.
Membaca sekitar 2 menit
Pemutar musik latar global yang tetap hidup saat pengguna berpindah halaman serta menangani kebijakan blokir autoplay peramban.
Meletakkan elemen <audio> dan tombol piringan berputar di dalam RootLayout sehingga musik tidak terputus ketika tamu berpindah dari halaman utama (/) ke halaman galeri (/gallery).
Peramban modern memblokir pemutaran suara otomatis tanpa interaksi, sehingga dibutuhkan mekanisme percobaan berkala yang berhenti otomatis begitu izin pemutaran didapat.
Masalahnya dulu
Musik latar membangun suasana emosional saat membaca undangan pernikahan. Namun ada dua kendala klasik di web modern: pertama, musik sering terputus dan mengulang dari awal ketika tamu berpindah dari halaman utama (/) ke halaman galeri (/gallery); kedua, peramban modern memblokir pemutaran audio otomatis (autoplay) sebelum ada interaksi pengguna di halaman.
Menempatkan player di RootLayout dan strategi retry
Di arsitektur Next.js App Router, komponen src/app/layout.tsx tidak di-unmount ketika pengguna bernavigasi antar-halaman menggunakan komponen <Link>. Dengan meletakkan elemen <audio ref={audioRef} loop> beserta tombol kontrol musik melayang (motion.button yang berputar 360 derajat saat aktif) langsung di dalam RootLayout, alunan musik dari /backsound.mp3 terus berjalan tanpa jeda sedikit pun saat tamu membuka maupun menutup halaman galeri.
Meletakkan elemen audio di RootLayout menjadikan musik sebagai lapisan atmosfer global yang kebal terhadap perpindahan halaman.
Implementasi di ngodingan ini
Kode di src/app/layout.tsx berikut mengatur pemutaran otomatis dengan volume 50% sekaligus animasi piringan tombol musik:
useEffect(() => {
const interval = setInterval(async () => {
if (audioRef.current) {
audioRef.current.volume = 0.5;
try {
await audioRef.current.play();
setIsPlaying(true);
clearInterval(interval);
} catch (err) {
console.log("Autoplay blocked by browser", err);
}
}
}, 5);
return () => clearInterval(interval);
}, []);
<motion.button
animate={{ rotate: isPlaying ? 360 : 0 }}
transition={{ repeat: isPlaying ? Infinity : 0, duration: 3, ease: "linear" }}
onClick={toggleMusic}
className="fixed bottom-5 right-5 z-50 bg-red-900 text-white p-4 rounded-full shadow-xl"
>
{isPlaying ? <FaPause /> : <FaMusic />}
</motion.button>Jebakan dan detail yang harus dijaga
- Pemanggilan audioRef.current.play() mengembalikan Promise yang akan ditolak (rejected) jika peramban menahan autoplay. Menangkap error di blok try/catch mencegah aplikasi crash dan mengizinkan interval mencoba kembali segera setelah pengguna menyentuh layar pertama kali.
- Begitu play() berhasil dijalankan, clearInterval(interval) wajib langsung dipanggil agar timer tidak terus berjalan di latar belakang.
- Volume awal diturunkan ke 0.5 (50%) agar musik latar terdengar lembut dan tidak mengagetkan tamu saat pertama kali membuka undangan.