Jak tworzyć najlepsze wrażenia z korzystania z multimediów mobilnych w internecie? To proste. Wszystko zależy od zaangażowania użytkowników i znaczenia mediów w witrynie. Zgadzamy się chyba, że jeśli film jest powodem wizyty użytkownika, to musi być wciągający i zachęcać do ponownego korzystania z usługi.

Z tego artykułu dowiesz się, jak stopniowo ulepszać wrażenia z multimediów i czynić je bardziej wciągającymi dzięki licznym interfejsom API dla sieci Web. Dlatego opracujemy prosty odtwarzacz mobilny z niestandardowymi ustawieniami, trybem pełnoekranowym i odtwarzaniem w tle. Możesz już teraz wypróbować przykład i znaleźć kod w naszym repozytorium GitHub.
Elementy sterujące niestandardowe

Jak widać, układ HTML, którego użyjemy do odtwarzacza multimediów, jest dość prosty: element główny <div>
zawiera element multimedialny <video>
i element podrzędny <div>
przeznaczony do obsługi elementów sterujących filmem.
Elementy sterujące filmem, które omówimy później, to m.in. przycisk odtwarzania/wstrzymywania, przycisk pełnoekranu, przyciski przewijania do tyłu i do przodu oraz elementy dotyczące bieżącego czasu, czasu trwania i śledzenia czasu.
<div id="videoContainer">
<video id="video" src="file.mp4"></video>
<div id="videoControls"></div>
</div>
Odczytywanie metadanych
Najpierw zaczekajmy, aż wczytają się metadane filmu, aby ustawić jego czas trwania, bieżący czas i inicjializować pasek postępu. Funkcja secondsToTimeCode()
to niestandardowa funkcja pomocnicza, którą napisałem, aby zamieniać liczbę sekund na ciąg znaków w formacie „godzina:minuty:sekundy”, który jest w naszym przypadku bardziej odpowiedni.
<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
})`;
});

Odtwórz/wstrzymaj film
Gdy metadane filmu zostaną załadowane, dodamy pierwszy przycisk, który pozwoli użytkownikowi odtwarzać i wstrzymywać film za pomocą przycisków video.play()
i video.pause()
w zależności od stanu odtwarzania.
<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();
}
});
Zamiast dostosowywać elementy sterujące filmem w detektorze zdarzeń click
używamy zdarzeń wideo play
i pause
. Używanie zdarzeń do sterowania pozwala zwiększyć elastyczność (co zobaczysz później w przypadku interfejsu Media Session API) i utrzymanie synchronizacji elementów sterujących, jeśli przeglądarka ingeruje w odtwarzanie.
Gdy film zaczyna się odtwarzać, zmieniamy stan przycisku na „Wstrzymaj” i ukrywamy elementy sterujące filmem. Gdy film jest wstrzymany, po prostu zmieniamy stan przycisku na „odtwórz” i wyświetlamy elementy sterujące odtwarzaniem.
video.addEventListener('play', function () {
playPauseButton.classList.add('playing');
});
video.addEventListener('pause', function () {
playPauseButton.classList.remove('playing');
});
Gdy czas wskazany przez atrybut filmu currentTime
zmieni się za pomocą zdarzenia timeupdate
wideo, aktualizujemy też nasze elementy sterujące, jeśli są widoczne.
video.addEventListener('timeupdate', function () {
if (videoControls.classList.contains('visible')) {
videoCurrentTime.textContent = secondsToTimeCode(video.currentTime);
videoProgressBar.style.transform = `scaleX(${
video.currentTime / video.duration
})`;
}
});
Gdy film się kończy, zmieniamy stan przycisku na „odtwórz”, ustawiamy wartość parametru currentTime
na 0 i tymczasowo wyświetlamy elementy sterujące odtwarzaniem. Pamiętaj, że możemy też automatycznie wczytać inny film, jeśli użytkownik włączy jakąś funkcję „Autoodtwarzania”.
video.addEventListener('ended', function () {
playPauseButton.classList.remove('playing');
video.currentTime = 0;
});
przewijanie do tyłu i do przodu,
Dodamy przyciski „przewinąć do tyłu” i „przewinąć do przodu”, aby użytkownik mógł łatwo pominąć niektóre treści.
<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);
});
Podobnie jak wcześniej, zamiast dostosowywać styl wideo w słuchaczach zdarzeń click
tych przycisków, użyjemy zdarzeń wideo seeking
i seeked
do dostosowywania jasności filmu. Moja niestandardowa klasa CSS seeking
jest tak prosta jak filter: brightness(0);
.
video.addEventListener('seeking', function () {
video.classList.add('seeking');
});
video.addEventListener('seeked', function () {
video.classList.remove('seeking');
});
Poniżej znajdziesz listę tego, co udało nam się do tej pory zrobić. W następnej sekcji zaimplementujemy przycisk pełnego ekranu.
Pełny ekran
Wykorzystamy kilka interfejsów API sieci Web, aby zapewnić użytkownikom płynne korzystanie z reklam pełnoekranowych. Aby zobaczyć, jak to działa, zapoznaj się z przykładem.
Oczywiście nie musisz używać wszystkich. Wystarczy, że wybierzesz te, które mają dla Ciebie sens, i połączysz je, aby utworzyć własny proces.
Zapobieganie automatycznemu wyświetlaniu na pełnym ekranie
Na iOS elementy video
automatycznie przechodzą w tryb pełnoekranowy, gdy rozpoczyna się odtwarzanie multimediów. Staramy się jak najlepiej dostosować i kontrolować sposób wyświetlania multimediów w przeglądarkach mobilnych, dlatego zalecamy ustawienie atrybutu playsinline
elementu video
tak, aby wymuszał on odtwarzanie w ramce na iPhonie i nie przechodził w tryb pełnoekranowy po rozpoczęciu odtwarzania. Pamiętaj, że nie ma to wpływu na inne przeglądarki.
<div id="videoContainer"></div>
<video id="video" src="file.mp4"></video><strong>playsinline</strong></video>
<div id="videoControls">...</div>
</div>
Włączanie i wyłączanie trybu pełnoekranowego po kliknięciu przycisku
Ponieważ nie chcemy, aby filmy automatycznie wyświetlały się na pełnym ekranie, musimy sami zająć się obsługą trybu pełnoekranowego za pomocą interfejsu Fullscreen API. Gdy użytkownik kliknie „przycisk pełnoekranowy”, dokument powinien wyjść z trybu pełnoekranowego za pomocą document.exitFullscreen()
, jeśli jest on obecnie używany. W przeciwnym razie poproś o pełny ekran w kontenerze wideo za pomocą metody requestFullscreen()
, jeśli jest dostępna, lub użyj opcji webkitEnterFullscreen()
w elemencie wideo tylko na 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);
});
Przełączanie trybu pełnoekranowego po zmianie orientacji ekranu
Gdy użytkownik obróci urządzenie w poziomej, automatycznie poproś o tryb pełnoekranowy, aby zapewnić lepsze wrażenia. W tym celu potrzebujemy interfejsu Screen Orientation API, który nie jest jeszcze obsługiwany wszędzie i w tym czasie nadal wymaga prefiksu w niektórych przeglądarkach. Będzie to nasze pierwsze ulepszenie progresywne.
Jak to działa? Gdy tylko wykryjemy zmianę orientacji ekranu, poprośmy o pełny ekran, jeśli okno przeglądarki jest w trybie poziomym (czyli jego szerokość jest większa niż wysokość). Jeśli nie, wyjdź z trybu pełnoekranowego. To wszystko.
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();
}
});
}
Blokowanie ekranu w poziomie po kliknięciu przycisku
Ponieważ filmy lepiej ogląda się w orientacji poziomej, warto zablokować ekran w orientacji poziomej, gdy użytkownik kliknie „Przycisk pełnoekranowy”. Aby zapewnić Ci jak najlepsze wrażenia, połączymy używany wcześniej interfejs Screen Orientation API z niektórymi zapytaniami media.
Zablokowanie ekranu w orientacji poziomej jest tak proste jak nawiązanie połączeniascreen.orientation.lock('landscape')
. Należy jednak robić to tylko wtedy, gdy urządzenie jest w orientacji pionowej (matchMedia('(orientation: portrait)')
) i można je trzymać w jednej ręce (matchMedia('(max-device-width: 768px)')
), ponieważ nie jest to wygodne dla użytkowników tabletów.
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');
}
}
Odblokowywanie ekranu po zmianie orientacji urządzenia
Być może zauważysz, że nasza właśnie utworzona wersja ekranu blokady nie jest idealna, ponieważ nie otrzymujemy powiadomień o zmianach orientacji ekranu, gdy jest on zablokowany.
Aby to naprawić, użyj interfejsu Device Orientation API, jeśli jest dostępny. Ten interfejs API udostępnia informacje z urządzenia dotyczące jego położenia i ruchu w przestrzeni: żyroskop i kompas cyfrowy służą do określania orientacji, a przyspieszeniomierz do określania prędkości. Gdy wykryjemy zmianę orientacji urządzenia, zezwól na odblokowanie ekranu za pomocą screen.orientation.unlock()
, jeśli użytkownik trzyma urządzenie w orientacji pionowej, a ekran jest zablokowany w orientacji poziomej.
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,
);
}
}
},
);
}
Jak widać, jest to płynne wyświetlanie pełnoekranowe, którego szukaliśmy. Aby zobaczyć, jak to działa, zapoznaj się z przykładem.
Odtwarzanie w tle
Jeśli zauważysz, że strona internetowa lub film na niej nie są już widoczne, możesz zaktualizować dane analityczne, aby odzwierciedlić tę zmianę. Może to mieć wpływ na bieżące odtwarzanie, np. wybór innego utworu, wstrzymanie odtwarzania czy wyświetlenie użytkownikowi niestandardowych przycisków.
Wstrzymywanie filmu po zmianie widoczności strony
Dzięki interfejsowi Page Visibility API możemy określić bieżącą widoczność strony i otrzymywać powiadomienia o zmianach widoczności. Kod poniżej wstrzymuje film, gdy strona jest ukryta. Dzieje się tak, gdy aktywna jest blokada ekranu lub gdy przełączasz karty.
Większość przeglądarek mobilnych oferuje obecnie elementy sterujące poza przeglądarką, które umożliwiają wznowienie odtwarzania wstrzymanego filmu. Zalecamy stosowanie tej opcji tylko wtedy, gdy użytkownik może odtwarzać film w tle.
document.addEventListener('visibilitychange', function () {
// Pause video when page is hidden.
if (document.hidden) {
video.pause();
}
});
Pokaż/ukryj przycisk wyciszenia po zmianie widoczności filmu
Jeśli korzystasz z nowego interfejsu Intersection Observer API, możesz uzyskać jeszcze bardziej szczegółowe informacje bez ponoszenia dodatkowych kosztów. Ten interfejs API informuje, kiedy obserwowany element wchodzi do widoku lub z niego wychodzi.
Pokaż/ukryj przycisk wyciszenia w zależności od widoczności filmu na stronie. Jeśli film jest odtwarzany, ale nie jest widoczny, w prawym dolnym rogu strony pojawi się przycisk wyciszenia, dzięki któremu użytkownik będzie mógł kontrolować dźwięk. Zdarzenie volumechange
dotyczące filmu służy do aktualizowania stylu przycisku wyciszenia.
<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);
});
odtwarzać tylko 1 film w danym momencie,
Jeśli na stronie jest więcej niż 1 film, zalecam odtwarzanie tylko jednego z nich, a pozostałe automatycznie wstrzymywać, aby użytkownik nie musiał słyszeć jednocześnie wielu ścieżek audio.
// 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();
});
}
Dostosowywanie powiadomień o multimediach
Za pomocą interfejsu Media Session API możesz też dostosowywać powiadomienia multimedialne, podając metadane dotyczące aktualnie odtwarzanego filmu. Pozwala też obsługiwać zdarzenia związane z multimediami, takie jak przewijanie lub zmiana ścieżki, które mogą pochodzić z powiadomień lub kluczy multimediów. Aby zobaczyć, jak to działa, zapoznaj się z przykładem.
Gdy aplikacja internetowa odtwarza dźwięk lub film, w pasku powiadomień pojawia się powiadomienie multimedialne. Na Androidzie Chrome stara się wyświetlać odpowiednie informacje, korzystając z tytułu dokumentu i największego znalezionego obrazu ikony.
Zobaczmy, jak dostosować to powiadomienie multimedialne, ustawiając niektóre metadane sesji multimedialnej, takie jak tytuł, wykonawca, nazwa albumu i grafika za pomocą interfejsu 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',
},
],
});
}
Po zakończeniu odtwarzania nie musisz „zwalniać” sesji multimediów, ponieważ powiadomienie zniknie automatycznie. Pamiętaj, że podczas odtwarzania będzie używana aktualna wersja navigator.mediaSession.metadata
. Dlatego musisz go zaktualizować, aby mieć pewność, że zawsze wyświetlasz odpowiednie informacje w powiadomieniu o mediach.
Jeśli Twoja aplikacja internetowa zawiera playlistę, możesz zezwolić użytkownikowi na poruszanie się po niej bezpośrednio z powiadomienia o multimediach za pomocą ikon „Poprzedni utwór” i „Następny utwór”.
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
});
}
Pamiętaj, że przetwarzanie działań związanych z multimediami pozostanie bez zmian. Jest to bardzo podobne do wzorca eventListener, z tym że obsługa zdarzenia oznacza, że przeglądarka przestaje wykonywać jakiekolwiek domyślne działanie i używa tego jako sygnału, że Twoja aplikacja internetowa obsługuje działanie dotyczące multimediów. Dlatego elementy sterujące działaniem multimediów nie będą widoczne, dopóki nie skonfigurujesz odpowiedniego modułu obsługi działania.
Odznaczenie przetwarzacza akcji typu Media Action jest tak samo proste jak przypisanie go do null
.
Interfejs API sesji multimediów umożliwia wyświetlanie ikon „Przewinąć do tyłu” i „Przewinąć do przodu” w powiadomieniach multimedialnych, jeśli chcesz kontrolować ilość przewiniętego czasu.
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);
});
}
Ikona „Odtwórz/Wstrzymaj” jest zawsze wyświetlana w powiadomieniu o multimediach, a powiązane zdarzenia są obsługiwane automatycznie przez przeglądarkę. Jeśli z jakiegoś powodu domyślne zachowanie nie działa, możesz obsłużyć zdarzenia „Odtwórz” i „Wstrzymaj” multimediów.
Wspaniałą rzeczą w przypadku interfejsu Media Session API jest to, że panel powiadomień nie jest jedynym miejscem, w którym są widoczne metadane i opcje sterowania multimediami. Powiadomienie o multimediach jest automatycznie synchronizowane z dowolnym sparowanym urządzeniem do noszenia. Wyświetla się też na ekranie blokady.