Bagaimana cara membuat pengalaman media seluler terbaik di Web? Mudah! Semuanya bergantung pada engagement pengguna dan tingkat kepentingan yang Anda berikan pada media di halaman web. Saya rasa kita semua setuju bahwa jika video adalah ALASAN kunjungan pengguna, pengalaman pengguna harus imersif dan menarik.

Dalam artikel ini, saya akan menunjukkan cara meningkatkan pengalaman media Anda secara progresif dan membuatnya lebih imersif berkat banyak Web API. Itulah sebabnya kami akan membuat pengalaman pemutar seluler sederhana dengan kontrol kustom, layar penuh, dan pemutaran latar belakang. Anda dapat mencoba contoh sekarang dan menemukan kode di repositori GitHub kami.
Kontrol kustom

Seperti yang dapat Anda lihat, tata letak HTML yang akan kita gunakan untuk pemutar media
cukup sederhana: elemen root <div>
berisi elemen media <video>
dan
elemen turunan <div>
yang didedikasikan untuk kontrol video.
Kontrol video yang akan kita bahas nanti, meliputi: tombol putar/jeda, tombol layar penuh, tombol cari mundur dan maju, serta beberapa elemen untuk waktu saat ini, durasi, dan pelacakan waktu.
<div id="videoContainer">
<video id="video" src="file.mp4"></video>
<div id="videoControls"></div>
</div>
Membaca metadata video
Pertama, mari kita tunggu metadata video dimuat untuk menetapkan durasi
video, waktu saat ini, dan menginisialisasi status progres. Perhatikan bahwa
fungsi secondsToTimeCode()
adalah fungsi utilitas kustom yang telah saya tulis yang
mengonversi sejumlah detik menjadi string dalam format "hh:mm:ss" yang lebih cocok
dalam kasus kita.
<div id="videoContainer">
<video id="video" src="file.mp4"></video>
<div id="videoControls">
<strong>
<div id="videoCurrentTime"></div>
<div id="videoDuration"></div>
<div id="videoProgressBar"></div>
</strong>
</div>
</div>
video.addEventListener('loadedmetadata', function () {
videoDuration.textContent = secondsToTimeCode(video.duration);
videoCurrentTime.textContent = secondsToTimeCode(video.currentTime);
videoProgressBar.style.transform = `scaleX(${
video.currentTime / video.duration
})`;
});

Putar/jeda video
Setelah metadata video dimuat, mari kita tambahkan tombol pertama yang memungkinkan pengguna
memutar dan menjeda video dengan video.play()
dan video.pause()
, bergantung pada
status pemutarannya.
<div id="videoContainer">
<video id="video" src="file.mp4"></video>
<div id="videoControls">
<strong><button id="playPauseButton"></button></strong>
<div id="videoCurrentTime"></div>
<div id="videoDuration"></div>
<div id="videoProgressBar"></div>
</div>
</div>
playPauseButton.addEventListener('click', function (event) {
event.stopPropagation();
if (video.paused) {
video.play();
} else {
video.pause();
}
});
Daripada menyesuaikan kontrol video di pemroses peristiwa click
, kita menggunakan
peristiwa video play
dan pause
. Membuat kontrol berbasis peristiwa akan membantu
fleksibilitas (seperti yang akan kita lihat nanti dengan Media Session API) dan akan memungkinkan
kita untuk tetap menyinkronkan kontrol jika browser melakukan intervensi dalam pemutaran.
Saat video mulai diputar, kita mengubah
status tombol menjadi "jeda" dan menyembunyikan kontrol video. Saat video dijeda, kita
cukup mengubah status tombol ke "putar" dan menampilkan kontrol video.
video.addEventListener('play', function () {
playPauseButton.classList.add('playing');
});
video.addEventListener('pause', function () {
playPauseButton.classList.remove('playing');
});
Saat waktu yang ditunjukkan oleh atribut currentTime
video berubah melalui
peristiwa video timeupdate
, kami juga memperbarui kontrol kustom jika
terlihat.
video.addEventListener('timeupdate', function () {
if (videoControls.classList.contains('visible')) {
videoCurrentTime.textContent = secondsToTimeCode(video.currentTime);
videoProgressBar.style.transform = `scaleX(${
video.currentTime / video.duration
})`;
}
});
Saat video berakhir, kita cukup mengubah status tombol menjadi "putar", menetapkan currentTime
video kembali ke 0, dan menampilkan kontrol video untuk saat ini. Perhatikan bahwa kita juga dapat
memilih untuk memuat video lain secara otomatis jika pengguna telah mengaktifkan beberapa
jenis fitur "Putar Otomatis".
video.addEventListener('ended', function () {
playPauseButton.classList.remove('playing');
video.currentTime = 0;
});
Memutar mundur dan maju
Mari kita lanjutkan dan tambahkan tombol "cari mundur" dan "cari maju" agar pengguna dapat dengan mudah melewati beberapa konten.
<div id="videoContainer">
<video id="video" src="file.mp4"></video>
<div id="videoControls">
<button id="playPauseButton"></button>
<strong
><button id="seekForwardButton"></button>
<button id="seekBackwardButton"></button
></strong>
<div id="videoCurrentTime"></div>
<div id="videoDuration"></div>
<div id="videoProgressBar"></div>
</div>
</div>
var skipTime = 10; // Time to skip in seconds
seekForwardButton.addEventListener('click', function (event) {
event.stopPropagation();
video.currentTime = Math.min(video.currentTime + skipTime, video.duration);
});
seekBackwardButton.addEventListener('click', function (event) {
event.stopPropagation();
video.currentTime = Math.max(video.currentTime - skipTime, 0);
});
Seperti sebelumnya, daripada menyesuaikan gaya visual video di pemroses peristiwa click
tombol ini, kita akan menggunakan peristiwa video seeking
dan seeked
yang diaktifkan untuk
menyesuaikan kecerahan video. Class CSS seeking
kustom saya sesederhana filter: brightness(0);
.
video.addEventListener('seeking', function () {
video.classList.add('seeking');
});
video.addEventListener('seeked', function () {
video.classList.remove('seeking');
});
Berikut adalah hal yang telah kita buat sejauh ini. Di bagian berikutnya, kita akan menerapkan tombol layar penuh.
Layar penuh
Di sini, kita akan memanfaatkan beberapa Web API untuk menciptakan pengalaman layar penuh yang sempurna dan lancar. Untuk melihat cara kerjanya, lihat contoh.
Tentu saja, Anda tidak harus menggunakan semuanya. Cukup pilih yang sesuai dengan Anda dan gabungkan untuk membuat alur kustom.
Mencegah layar penuh otomatis
Di iOS, elemen video
secara otomatis masuk ke mode layar penuh saat pemutaran
media dimulai. Karena kami mencoba menyesuaikan dan mengontrol pengalaman
media kami di seluruh browser seluler sebanyak mungkin, sebaiknya tetapkan atribut playsinline
elemen video
untuk memaksanya diputar secara inline di iPhone dan tidak
memasuki mode layar penuh saat pemutaran dimulai. Perhatikan bahwa tindakan ini tidak memiliki efek samping
di browser lain.
<div id="videoContainer"></div>
<video id="video" src="file.mp4"></video><strong>playsinline</strong></video>
<div id="videoControls">...</div>
</div>
Beralih ke layar penuh saat tombol diklik
Setelah mencegah layar penuh otomatis, kita harus menangani
mode layar penuh untuk video dengan Fullscreen API. Saat pengguna
mengklik "tombol layar penuh", mari kita keluar dari mode layar penuh dengan
document.exitFullscreen()
jika mode layar penuh saat ini digunakan oleh
dokumen. Jika tidak, minta layar penuh di penampung video dengan metode
requestFullscreen()
jika tersedia atau kembali ke webkitEnterFullscreen()
di
elemen video hanya di iOS.
<div id="videoContainer">
<video id="video" src="file.mp4"></video>
<div id="videoControls">
<button id="playPauseButton"></button>
<button id="seekForwardButton"></button>
<button id="seekBackwardButton"></button>
<strong><button id="fullscreenButton"></button></strong>
<div id="videoCurrentTime"></div>
<div id="videoDuration"></div>
<div id="videoProgressBar"></div>
</div>
</div>
fullscreenButton.addEventListener('click', function (event) {
event.stopPropagation();
if (document.fullscreenElement) {
document.exitFullscreen();
} else {
requestFullscreenVideo();
}
});
function requestFullscreenVideo() {
if (videoContainer.requestFullscreen) {
videoContainer.requestFullscreen();
} else {
video.webkitEnterFullscreen();
}
}
document.addEventListener('fullscreenchange', function () {
fullscreenButton.classList.toggle('active', document.fullscreenElement);
});
Beralih ke layar penuh saat orientasi layar berubah
Saat pengguna memutar perangkat dalam mode lanskap, mari kita lakukan dengan cerdas dan meminta layar penuh secara otomatis untuk menciptakan pengalaman yang imersif. Untuk ini, kita memerlukan Screen Orientation API yang belum didukung di mana-mana dan masih diawali dengan awalan di beberapa browser pada saat itu. Jadi, ini akan menjadi progressive enhancement pertama kita.
Bagaimana cara kerjanya? Segera setelah kita mendeteksi perubahan orientasi layar, mari kita minta layar penuh jika jendela browser dalam mode lanskap (yaitu, lebarnya lebih besar dari tingginya). Jika tidak, mari kita keluar dari layar penuh. Itu saja.
if ('orientation' in screen) {
screen.orientation.addEventListener('change', function () {
// Let's request fullscreen if user switches device in landscape mode.
if (screen.orientation.type.startsWith('landscape')) {
requestFullscreenVideo();
} else if (document.fullscreenElement) {
document.exitFullscreen();
}
});
}
Layar kunci dalam mode lanskap saat tombol diklik
Karena video mungkin lebih baik dilihat dalam mode lanskap, sebaiknya kita mengunci layar dalam lanskap saat pengguna mengklik "tombol layar penuh". Kita akan menggabungkan Screen Orientation API yang digunakan sebelumnya dan beberapa kueri media untuk memastikan pengalaman ini adalah yang terbaik.
Mengunci layar dalam mode lanskap semudah memanggil
screen.orientation.lock('landscape')
. Namun, kita hanya boleh melakukannya saat
perangkat dalam mode potret dengan matchMedia('(orientation: portrait)')
dan dapat
dipegang dengan satu tangan dengan matchMedia('(max-device-width: 768px)')
karena hal ini
tidak akan menjadi pengalaman yang baik bagi pengguna di tablet.
fullscreenButton.addEventListener('click', function (event) {
event.stopPropagation();
if (document.fullscreenElement) {
document.exitFullscreen();
} else {
requestFullscreenVideo();
<strong>lockScreenInLandscape();</strong>;
}
});
function lockScreenInLandscape() {
if (!('orientation' in screen)) {
return;
}
// Let's force landscape mode only if device is in portrait mode and can be held in one hand.
if (
matchMedia('(orientation: portrait) and (max-device-width: 768px)').matches
) {
screen.orientation.lock('landscape');
}
}
Membuka kunci layar saat orientasi perangkat berubah
Anda mungkin telah melihat bahwa pengalaman layar kunci yang baru saja kita buat tidak sempurna karena kita tidak menerima perubahan orientasi layar saat layar terkunci.
Untuk memperbaikinya, mari kita gunakan Device Orientation API jika
tersedia. API ini memberikan informasi dari hardware yang mengukur posisi
dan gerakan perangkat dalam ruang: giroskop dan kompas digital untuk
orientasinya, dan akselerometer untuk kecepatannya. Saat mendeteksi perubahan orientasi
perangkat, mari kita buka kunci layar dengan screen.orientation.unlock()
jika
pengguna memegang perangkat dalam mode potret dan layar terkunci dalam mode lanskap.
function lockScreenInLandscape() {
if (!('orientation' in screen)) {
return;
}
// Let's force landscape mode only if device is in portrait mode and can be held in one hand.
if (matchMedia('(orientation: portrait) and (max-device-width: 768px)').matches) {
screen.orientation.lock('landscape')
<strong>.then(function() {
listenToDeviceOrientationChanges();
})</strong>;
}
}
function listenToDeviceOrientationChanges() {
if (!('DeviceOrientationEvent' in window)) {
return;
}
var previousDeviceOrientation, currentDeviceOrientation;
window.addEventListener(
'deviceorientation',
function onDeviceOrientationChange(event) {
// event.beta represents a front to back motion of the device and
// event.gamma a left to right motion.
if (Math.abs(event.gamma) > 10 || Math.abs(event.beta) < 10) {
previousDeviceOrientation = currentDeviceOrientation;
currentDeviceOrientation = 'landscape';
return;
}
if (Math.abs(event.gamma) < 10 || Math.abs(event.beta) > 10) {
previousDeviceOrientation = currentDeviceOrientation;
// When device is rotated back to portrait, let's unlock screen orientation.
if (previousDeviceOrientation == 'landscape') {
screen.orientation.unlock();
window.removeEventListener(
'deviceorientation',
onDeviceOrientationChange,
);
}
}
},
);
}
Seperti yang dapat Anda lihat, ini adalah pengalaman layar penuh yang lancar yang kita cari. Untuk melihat cara kerjanya, lihat contoh.
Putar di latar belakang
Jika Anda mendeteksi bahwa halaman web atau video di halaman web tidak terlihat lagi, Anda dapat memperbarui analisis untuk mencerminkan hal ini. Hal ini juga dapat memengaruhi pemutaran saat ini seperti memilih trek yang berbeda, menjeda, atau bahkan menampilkan tombol kustom kepada pengguna.
Menjeda video saat visibilitas halaman berubah
Dengan Page Visibility API, kita dapat menentukan visibilitas halaman saat ini dan menerima notifikasi tentang perubahan visibilitas. Kode di bawah menjeda video saat halaman disembunyikan. Hal ini terjadi saat kunci layar aktif atau saat Anda beralih tab misalnya.
Karena sebagian besar browser seluler kini menawarkan kontrol di luar browser yang memungkinkan melanjutkan video yang dijeda, sebaiknya tetapkan perilaku ini hanya jika pengguna diizinkan untuk memutar di latar belakang.
document.addEventListener('visibilitychange', function () {
// Pause video when page is hidden.
if (document.hidden) {
video.pause();
}
});
Menampilkan/menyembunyikan tombol bisukan saat visibilitas video berubah
Jika menggunakan Intersection Observer API baru, Anda dapat lebih terperinci tanpa biaya. API ini memungkinkan Anda mengetahui kapan elemen yang diamati memasuki atau keluar dari area pandang browser.
Mari kita tampilkan/sembunyikan tombol bisukan berdasarkan visibilitas video di halaman. Jika
video diputar tetapi saat ini tidak terlihat, tombol bisukan mini akan ditampilkan di
sudut kanan bawah halaman untuk memberi pengguna kontrol atas suara video. Peristiwa
video volumechange
digunakan untuk memperbarui gaya tombol bisukan.
<button id="muteButton"></button>
if ('IntersectionObserver' in window) {
// Show/hide mute button based on video visibility in the page.
function onIntersection(entries) {
entries.forEach(function (entry) {
muteButton.hidden = video.paused || entry.isIntersecting;
});
}
var observer = new IntersectionObserver(onIntersection);
observer.observe(video);
}
muteButton.addEventListener('click', function () {
// Mute/unmute video on button click.
video.muted = !video.muted;
});
video.addEventListener('volumechange', function () {
muteButton.classList.toggle('active', video.muted);
});
Hanya memutar satu video dalam satu waktu
Jika ada lebih dari satu video di halaman, sebaiknya Anda hanya memutar satu dan menjeda video lainnya secara otomatis sehingga pengguna tidak perlu mendengar beberapa trek audio yang diputar secara bersamaan.
// This array should be initialized once all videos have been added.
var videos = Array.from(document.querySelectorAll('video'));
videos.forEach(function (video) {
video.addEventListener('play', pauseOtherVideosPlaying);
});
function pauseOtherVideosPlaying(event) {
var videosToPause = videos.filter(function (video) {
return !video.paused && video != event.target;
});
// Pause all other videos currently playing.
videosToPause.forEach(function (video) {
video.pause();
});
}
Menyesuaikan Notifikasi Media
Dengan Media Session API, Anda juga dapat menyesuaikan notifikasi media dengan memberikan metadata untuk video yang sedang diputar. Hal ini juga memungkinkan Anda menangani peristiwa terkait media seperti mencari atau mengubah trek yang mungkin berasal dari notifikasi atau tombol media. Untuk melihat cara kerjanya, lihat contoh.
Saat aplikasi web memutar audio atau video, Anda sudah dapat melihat notifikasi media di baki notifikasi. Di Android, Chrome akan melakukan yang terbaik untuk menampilkan informasi yang sesuai dengan menggunakan judul dokumen dan gambar ikon terbesar yang dapat ditemukan.
Mari kita lihat cara menyesuaikan notifikasi media ini dengan menetapkan beberapa metadata sesi media seperti judul, artis, nama album, dan karya seni dengan Media Session API.
playPauseButton.addEventListener('click', function(event) {
event.stopPropagation();
if (video.paused) {
video.play()
<strong>.then(function() {
setMediaSession();
});</strong>
} else {
video.pause();
}
});
function setMediaSession() {
if (!('mediaSession' in navigator)) {
return;
}
navigator.mediaSession.metadata = new MediaMetadata({
title: 'Never Gonna Give You Up',
artist: 'Rick Astley',
album: 'Whenever You Need Somebody',
artwork: [
{src: 'https://dummyimage.com/96x96', sizes: '96x96', type: 'image/png'},
{
src: 'https://dummyimage.com/128x128',
sizes: '128x128',
type: 'image/png',
},
{
src: 'https://dummyimage.com/192x192',
sizes: '192x192',
type: 'image/png',
},
{
src: 'https://dummyimage.com/256x256',
sizes: '256x256',
type: 'image/png',
},
{
src: 'https://dummyimage.com/384x384',
sizes: '384x384',
type: 'image/png',
},
{
src: 'https://dummyimage.com/512x512',
sizes: '512x512',
type: 'image/png',
},
],
});
}
Setelah pemutaran selesai, Anda tidak perlu "melepaskan" sesi media karena
notifikasi akan otomatis hilang. Perlu diingat bahwa
navigator.mediaSession.metadata
saat ini akan digunakan saat pemutaran dimulai. Itulah
alasan Anda perlu memperbaruinya untuk memastikan Anda selalu menampilkan informasi
yang relevan dalam notifikasi media.
Jika aplikasi web Anda menyediakan playlist, sebaiknya izinkan pengguna menavigasi playlist langsung dari notifikasi media dengan beberapa ikon "Lagu Sebelumnya" dan "Lagu Berikutnya".
if ('mediaSession' in navigator) {
navigator.mediaSession.setActionHandler('previoustrack', function () {
// User clicked "Previous Track" media notification icon.
playPreviousVideo(); // load and play previous video
});
navigator.mediaSession.setActionHandler('nexttrack', function () {
// User clicked "Next Track" media notification icon.
playNextVideo(); // load and play next video
});
}
Perhatikan bahwa pengendali tindakan media akan tetap ada. Hal ini sangat mirip dengan pola pemroses peristiwa, kecuali bahwa menangani peristiwa berarti browser berhenti melakukan perilaku default dan menggunakan ini sebagai sinyal bahwa aplikasi web Anda mendukung tindakan media. Oleh karena itu, kontrol tindakan media tidak akan ditampilkan kecuali jika Anda menetapkan pengendali tindakan yang sesuai.
Selain itu, membatalkan penetapan pengendali tindakan media semudah menetapkannya ke null
.
Media Session API memungkinkan Anda menampilkan ikon notifikasi media "Seek Backward" dan "Seek Forward" jika ingin mengontrol jumlah waktu yang dilewati.
if ('mediaSession' in navigator) {
let skipTime = 10; // Time to skip in seconds
navigator.mediaSession.setActionHandler('seekbackward', function () {
// User clicked "Seek Backward" media notification icon.
video.currentTime = Math.max(video.currentTime - skipTime, 0);
});
navigator.mediaSession.setActionHandler('seekforward', function () {
// User clicked "Seek Forward" media notification icon.
video.currentTime = Math.min(video.currentTime + skipTime, video.duration);
});
}
Ikon "Putar/Jeda" selalu ditampilkan di notifikasi media dan peristiwa terkait ditangani secara otomatis oleh browser. Jika karena alasan tertentu perilaku default tidak berfungsi, Anda masih dapat menangani peristiwa media "Putar" dan "Jeda".
Hal yang menarik dari Media Session API adalah bahwa baki notifikasi bukan satu-satunya tempat metadata dan kontrol media terlihat. Notifikasi media disinkronkan secara otomatis ke perangkat wearable yang disambungkan. Selain itu, pesan juga akan muncul di layar kunci.