L'audio et la vidéo

Une des grandes nouveautés du HTML5 est l'apparition des éléments<audio>et<video>, qui permettent de jouer des sons et d'exécuter des vidéos, le tout nativement, c'est-à-dire sans plugins tels que Flash, QuickTime ou même Windows Media Player. Nous allons donc voir ici comment interagir, via le JavaScript, avec ces deux éléments !

L'audio

Les éléments<audio>et<video>se ressemblent fortement. D'ailleurs, ils sont représentés par le même objet, à savoirHTMLMediaElement. Comme ils dérivent du même objet, ils en possèdent les propriétés et méthodes.

L'insertion d'un élément<audio>est très simple.

<audio id="audioPlayer" src="hype_home.mp3"></audio>

Ce bout de code suffit à insérer un lecteur audio qui lira le sonhype_home.mp3. Mais, nous, nous n'allons pas utiliser l'attributsrc, mais plutôt deux éléments<source>, comme ceci :

<audio id="audioPlayer">
<source src="hype_home.ogg">
<source src="hype_home.mp3">
</audio>

De cette manière, si le navigateur est capable de lire le format .ogg, il le fera. Sans quoi, il lira le format .mp3. Ça permet une plus grande interopérabilité (compatibilité entre les navigateurs et les plates-formes).

Pour afficher un contrôleur de lecteur, il faut utiliser l'attribut booléencontrols, comme ceci :<audio controls="controls"></audio>. Mais ici, c'est un cours de JavaScript, donc nous allons créer notre propre contrôleur de lecture !

Contrôles simples

Voyons pour commencer comment recréer les boutons « Play », « Pause » et « Stop ». On commence par accéder à l'élément :

var player = document.querySelector('#audioPlayer');

Si on veut lancer la lecture, on utilise la méthodeplay():

player.play();

Si on veut faire une pause, c'est la méthodepause():

player.pause();

Par contre, il n'y a pas de méthodestop(). Si on appuie sur un bouton « Stop », la lecture s'arrête et se remet au début. Pour ce faire, il suffit de faire « Pause » et d'indiquer que la lecture doit se remettre au début, avec la propriétécurrentTime, exprimée en secondes :

player.pause();
player.currentTime = 0;

On va créer un petit lecteur, dont voici le code HTML de base :

<audio id="audioPlayer">
<source src="hype_home.ogg">
<source src="hype_home.mp3">
</audio>
<button class="control" onclick="play('audioPlayer', this)">Play</button>
<button class="control" onclick="resume('audioPlayer')">Stop</button>

Deux boutons ont été placés : le premier est un bouton « Play » et « Pause » en même temps (comme sur la plupart des lecteurs modernes), et le second permet de stopper et de rembobiner la lecture. Voici les fonctionsplayetresume:

function play(idPlayer, control) {
var player = document.querySelector('#' + idPlayer);
if (player.paused) {
player.play();
control.textContent = 'Pause';
} else {
player.pause();
control.textContent = 'Play';
}
}
function resume(idPlayer) {
var player = document.querySelector('#' + idPlayer);
player.currentTime = 0;
player.pause();
}

Le fonctionnement du bouton « Play » est simple : avec la méthodepaused, on vérifie si la lecture est en pause. En fonction de ça, on faitplay()oupause(), et on change le libellé du bouton.

Essayer le code

Contrôle du volume

L'intensité sonore se règle avec la propriétévolumesur une échelle allant de 0 à 1. Si le volume est à 0, il est muet, et s'il est à 1, il est à fond. Pour le diminuer de moitié, on mettra 0,5. On va faire un système très simple : cinq barres verticales cliquables qui permettent de choisir un niveau sonore prédéfini :

<span class="volume">
<a class="stick1" onclick="volume('audioPlayer', 0)"></a>
<a class="stick2" onclick="volume('audioPlayer', 0.3)"></a>
<a class="stick3" onclick="volume('audioPlayer', 0.5)"></a>
<a class="stick4" onclick="volume('audioPlayer', 0.7)"></a>
<a class="stick5" onclick="volume('audioPlayer', 1)"></a>
</span>

Et la fonction associée :

function volume(idPlayer, vol) {
var player = document.querySelector('#' + idPlayer);
player.volume = vol;
}

Essayer le code

Barre de progression et timer

Un lecteur sans une barre de progression n'est pas un lecteur ! Le HTML5 introduit un nouvel élément destiné à afficher une progression : l'élément<progress>. Il n'est toutefois utilisable qu'avec Firefox et Chrome. Mais nous n'allons pas l'utiliser ici, car cet élément n'est pas facilement personnalisable avec du CSS. On l'utilisera plus tard dans le chapitre sur l'API File.

Nous allons donc créer une barre de progression « à la main », avec des<div>et quelques calculs de pourcentages !

Ajoutons ce code HTML après l'élément<audio>:

<div>
<div id="progressBarControl">
<div id="progressBar">Pas de lecture</div>
</div>
</div>
Analyser la lecture

Un élémentHTMLMediaElementpossède toute une série d'événements pour analyser et agir sur le lecteur. L'événementontimeupdateva nous être utile pour détecter quand le média est en train d'être joué par le lecteur. Cet événement est déclenché continuellement pendant la lecture.

Ajoutons donc cet événement sur notre élément<audio>:

<audio id="audioPlayer" ontimeupdate="update(this)">

Et commençons à coder la fonctionupdate():

function update(player) {
var duration = player.duration; // Durée totale
var time = player.currentTime; // Temps écoulé
var fraction = time / duration;
var percent = Math.ceil(fraction * 100);
var progress = document.querySelector('#progressBar');
progress.style.width = percent + '%';
progress.textContent = percent + '%';
}

L'idée est de récupérer le temps écoulé et de calculer un pourcentage de manière à afficher la barre de progression (qui fait 100 % de large). Donc, si la chanson dure dix minutes et qu'on en est à une minute de lecture, on a lu 10 %.

La propriétédurationsert à récupérer la durée totale du média. Le calcul est simple : on divise le temps écoulé par la durée totale et on multiplie par 100. Comme ça ne tombera certainement pas juste, on arrondit avecMath.ceil(). Une fois le pourcentage récupéré, on définit la largeur de la barre de progression, et on affiche le pourcentage à l'intérieur.

Le petit lecteur est désormais terminé !

Essayer le code

Améliorations

L'interface réalisée précédemment est fonctionnelle, mais rudimentaire. Deux améliorations principales sont possibles :

  • Afficher le temps écoulé ;

  • Rendre la barre de progression cliquable.

Afficher le temps écoulé

Afficher le temps écoulé ? Ce n'est pas compliqué, il suffit d'utiliser la propriétécurrentTime. Le souci est quecurrentTimeretourne le temps écoulé en secondes avec ses décimales. Il est donc possible de voir s'afficher un temps de lecture de 4,133968 secondes. Il convient donc de faire quelques opérations pour rendre ce nombre compréhensible. Voici la fonctionformatTime():

function formatTime(time) {
var hours = Math.floor(time / 3600);
var mins = Math.floor((time % 3600) / 60);
var secs = Math.floor(time % 60);
if (secs < 10) {
secs = "0" + secs;
}
if (hours) {
if (mins < 10) {
mins = "0" + mins;
}
return hours + ":" + mins + ":" + secs; // hh:mm:ss
} else {
return mins + ":" + secs; // mm:ss
}
}

On opère quelques divisions et arrondissements afin d'extraire le nombre d'heures, de minutes et de secondes. Puis on complète avec des 0 pour un affichage plus joli.
On peut donc ajouter ceci à notre fonctionupdate():

document.querySelector('#progressTime').textContent = formatTime(time);

Et modifier la barre de progression pour y ajouter un<span>dans lequel s'affichera le temps écoulé :

<div id="progressBarControl">
<div id="progressBar">Pas de lecture</div>
</div>
<span id="progressTime">00:00</span>

Rendre la barre de progression cliquable

Ici, ça se corse un peu. Si on clique sur la barre de progression, le comportement attendu est la lecture du fichier audio à partir de cet endroit. Il va donc falloir calculer l'endroit où on a cliqué et positionner la lecture en conséquence, avec la propriétécurrentTime.

Pour savoir où l'on a cliqué au sein d'un élément, il faut connaître deux choses : les coordonnées de la souris et les coordonnées de l'élément. Ces coordonnées sont calculées à partir du coin supérieur gauche de la page. Voici une explication plus imagée :

Les coordonnées sont calculées à partir du coin supérieur gauche de la page
Les coordonnées sont calculées à partir du coin supérieur gauche de la page

Pour connaître la distance représentée par la flèche turquoise, il suffit de soustraire la distance représentée par la flèche rouge (la position de la barre sur l'axe des X) à la distance représentée par la flèche bleue (la position X du curseur de la souris). C'est tout simple, mais la récupération des coordonnées n'est pas évidente.

Récupérer les coordonnées du curseur de la souris

Nous allons créer la fonctiongetMousePosition()qui recevra comme paramètre un événement et qui retournera les positions X et Y du curseur :

function getMousePosition(event) {
return {
x: event.pageX,
y: event.pageY
};
}

Les propriétéspageXetpageYde l'objeteventpermettent respectivement de récupérer les positions sur l'axe des X et sur l'axe des Y.

Essayer le code

Récupérer les coordonnées d'un élément

Comme l'élément n'est pas positionné de façon absolue, il n'est pas possible de connaître les coordonnées de son coin supérieur gauche via le CSS. Il va donc falloir calculer le décalage entre lui et son élément parent, puis le décalage entre cet élément parent et son parent… et ainsi de suite, jusqu'à arriver à l'élément racine, c'est-à-dire<html>:

function getPosition(element){
var top = 0, left = 0;
do {
top += element.offsetTop;
left += element.offsetLeft;
} while (element = element.offsetParent);
return { x: left, y: top };
}

Si vous ne connaissez plus le rôle des propriétés offsetLeft,offsetTopetoffsetParent, nous vous conseillez de revenir sur la partie qui aborde le sujet dans le chapitre sur la manipulation du CSS.

On clique !

Maintenant que nous avons nos deux fonctionsgetMousePosition()etgetPosition(), nous pouvons écrire la fonctionclickProgress(), qui sera exécutée dès que l'internaute cliquera sur la barre de progression :

function clickProgress(idPlayer, control, event) {
var parent = getPosition(control); // La position absolue de la progressBar
var target = getMousePosition(event); // L'endroit de la progressBar où on a cliqué
var player = document.querySelector('#' + idPlayer);
var x = target.x - parent.x;
var wrapperWidth = document.querySelector('#progressBarControl').offsetWidth;
var percent = Math.ceil((x / wrapperWidth) * 100);
var duration = player.duration;
player.currentTime = (duration * percent) / 100;
}

On récupère la distancex, qui est la distance entre le bord gauche de la barre et l'endroit où on a cliqué. On divisexpar la largeur totale du conteneur de la barre de progression (avecoffsetWidth) et on multiplie par 100 pour obtenir un pourcentage. Ensuite, on calcule lecurrentTimeen multipliant le temps total de la chanson par le pourcentage, le tout divisé par 100.

Et n'oublions pas de modifier le code HTML en conséquence :

<div id="progressBar" onclick="clickProgress('audioPlayer', this, event)">Pas de lecture</div>

Et ça marche !

Essayer le code

La vidéo

Il n'y a pas grand-chose à ajouter en ce qui concerne les vidéos. Le principe de fonctionnement est exactement le même que pour les lectures audio. L'élément<video>possède toutefois quelques propriétés en plus :

Propriété

Description

height

Hauteur de la zone de lecture

width

Largeur de la zone de lecture

poster

Récupère l'attributposter

videoHeight

La hauteur de la vidéo

videoWidth

La largeur de la vidéo

En dehors de ça, la création et la personnalisation de la lecture d'une vidéo est rigoureusement identique à celle d'une piste audio.

Il peut être utile d'utiliser un framework JavaScript destiné à la lecture d'éléments<audio>et<video> afin se faciliter la vie. De plus, ce genre de framework propose généralement une solution en Flash si le navigateur n'est pas à la hauteur du HTML5.

Voici quelques frameworks qu'il peut être intéressant de considérer :

En résumé
  • Les éléments<audio>et<video>possèdent tous les deux de nombreux attributs et méthodes afin que chacun puisse créer un lecteur entièrement personnalisé.

  • La différence entre les deux éléments est minime. Ils sont tous les deux basés sur le même objet, l'élément<video>n'apporte que quelques attributs supplémentaires permettant de gérer l'affichage.

  • Contrairement au Flash, la protection du contenu n'existe pas (encore) avec ces deux éléments. Réfléchissez donc bien avant d'écarter définitivement toute solution avec Flash !