Créer un lecteur vidéo personnalisé avec CSS3, HTML5 et JavaScript

Johnny Simpson

Traducteur

Didier Mouronval

Le Web d'aujourd'hui est devenu largement multimédia et une large part de la conception de sites consiste maintenant à inclure des vidéos. Personnaliser un lecteur vidéo devient une des préoccupations principale des créateurs Web lorsqu'ils intègrent ces éléments, car le plus souvent le lecteur par défaut ne correspond pas à la charte graphique du site, mais aussi pour des questions de support des navigateurs et des différents appareils.
Dans cet article, nous allons voir comment réaliser un lecteur vidéo personnalisé étape par étape.

Cet article est la traduction de Making Custom CSS3 Video Players With HTML5 and Javascript publié sur InsertHTML.

Commentez

- Titre : Créer un lecteur vidéo personnalisé avec CSS3, HTML5 et JavaScript

- Auteur : Johnny Simpson

- Traducteur : Didier Mouronval

- Parution : 25 mars 2013

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2013 Johnny Simpson. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. L'API Media Element

- II. Fonctionnalités

- III. Créons notre lecteur

- III-A. Le CSS

- III-B. jQuery

- IV. Utiliser le plugin

- V. Conclusion et remerciements

I - L'API Media Element

Voir une démo en ligne

Vous pouvez déjà voir un exemple en ligne ou télécharger l'archive de l'exemple.

L'API Media Element vous permet de contrôler des vidéos à l'aide de simples commandes JavaScript. Par exemple, la balise <video> s'implémente habituellement comme ceci en HTML5 :

<video id="our-video" width="600" height="400" controls>
<source src="movie.mp4" type="video/mp4">
<source src="movie-hd.mp4" type="video/mp4">
</video>

Nous avons utilisé l'attribut controls dans la balise <video> de façon à faire apparaître différents boutons pour cette vidéo (lecture, pause, etc.). C'est suffisant la plupart du temps, mais il peut arriver que nous ayons envie d'utiliser pour cela des éléments personnalisés, ou de limiter les actions possibles. Pour cela, nous allons utiliser l'API Media Element. Par exemple, imaginons un lien tel que :

<a href="#" class="play">Click me to play the video!</a>

Nous pourrons utiliser l'API Media Element avec jQuery en écrivant un code aussi simple que :

$('#our-video')[0].play();

Nous utilisons la notation [0] afin de cibler l'élément DOM (comme nous l'aurions fait avec getElementById()) et non l'objet jQuery.

Facile, non ? Eh bien nous allons pouvoir contrôler un bon nombre de paramètres de la vidéo de la même manière avec cette API et créer ainsi des choses particulièrement intéressantes.

II - Fonctionnalités

L'API Media Element comprend un certain nombre de fonctionnalités vous permettant de contrôler la vidéo ou la manière dont elle est affichée. Voici une sélection des plus utiles pour créer un lecteur vidéo.

$('#our-video')[0].play(); // Lance la vidéo
$('#our-video')[0].pause(); // Met la vidéo en pause
$('#our-video')[0].volume = 1; // Définit le volume de la vidéo (valeur entre 0 et 1)
$('#our-video')[0].currentTime; // Temps actuel de la vidéo
$('#our-video')[0].duration; // Durée de la vidéo
$('#our-video')[0].buffered; // Quantité (en secondes) de la vidéo en mémoire tampon
if($('#our-video')[0].canPlayType('video/mp4')) {...} // Si la balise <video> peut lire ce format
$('#our-video')[0].requestFullscreen; // Expérimental : affiche la vidéo en plein écran

En combinant ces fonctions avec d'autres astuces que nous allons voir, nous créerons un lecteur que vous pourrez personnaliser uniquement en CSS.

III - Créons notre lecteur

Tout d'abord, habituons-nous à utiliser la balise HTML5 <video>.
Pour ajouter une nouvelle vidéo, il suffit d'utiliser la balise <video>. Il est recommandé de prévoir plusieurs formats d'encodage, car tous les navigateurs n'acceptent pas les mêmes. Les formats .webm et .mp4 devraient couvrir l'ensemble des navigateurs. Vous pourrez convertir vos vidéos au format .webm facilement avec le convertisseur de Miro.

<video width="600" height="340">
<source src="big-video.mp4" type="video/mp4">
<source src="movie.webm" type="video/webm">
</video>

III-A - Le CSS

Nous allons utiliser CSS pour donner des styles à notre lecteur. Cela ouvre de très nombreuses possibilités de personnalisation, car CSS étant très facile à utiliser, modifier l'apparence du lecteur sera particulièrement simple.
Pour l'exemple de cet article, voici le code CSS que j'ai utilisé, mais à vous ensuite d'écrire le vôtre.

body {
font-size: 62.5%;
}

.player {
background: #2a2a2a;
box-sizing: border-box;
border-radius: 5px;
height: 70px;
-moz-box-sizing: border-box;
float: left;
font-family: Arial, sans-serif;
position: absolute;
padding: 0;
bottom: 20px;
z-index: 2;
opacity: 1;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
-webkit-transition: opacity 0.3s ease-in;
transition: opacity 0.3s ease-in;
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
}

.video {
position: relative;
}

.video:hover .player {
opacity: 1;
}

.player .progress {
width: 60%;
height: 20px;
border-radius: 5px;
background: #676767;
box-shadow: inset 0 -5px 10px rgba(0,0,0,0.1);
float: left;
cursor: pointer;
margin: 24px 0 0 0;
padding: 0;
position: relative;
}

.player .progress-bar {
background: #33b5d5;
box-shadow: inset -30px 0px 69px -20px #89f6f5;
border-radius: 5px;
height: 100%;
position: relative;
z-index: 999;
width: 0;
}

.player .button-holder {
position: relative;
left: 10px;
}

.player .progress-button {
background: #fff;
box-shadow: 0 0 20px rgba(0,0,0,0.3);
border-radius: 30px;
width: 20px;
height: 20px;
position: absolute;
left: -20px;
text-decoration: overline;
}

.player [class^="buffered"] {
background: rgba(255,255,255,0.1);
position: absolute;
top: 0;
left: 30px;
height: 100%;
border-radius: 5px;
z-index: 1;
}

.player .play-pause {
display: inline-block;
font-size: 3em;
float: left;
text-shadow: 0 0 0 #fff;
color: rgba(255,255,255,0.8);
width: 10%;
padding: 17px 0 0 3%;
cursor: pointer;
font-variant: small-caps;
}

.player .play, .player .pause-button {
-webkit-transition: all 0.2s ease-out;
}

.player .play .pause-button, .player .pause .play-button {
display: none;
}

.player .pause-button {
padding: 5px 2px;
box-sizing: border-box;
-moz-box-sizing: border-box;
height: 34px;
}

.player .pause-button span {
background: #fff;
width: 8px;
height: 24px;
float: left;
display: block;
}

.player .pause-button span:first-of-type {
margin: 0 4px 0 0;
}

.player .time {
color: #fff;
font-weight: bold;
font-size: 1.2em;
position: absolute;
right: 0;
top: 24px;
}

.player .stime, .ttime {
color: #444;
}
.player .play:hover {
text-shadow: 0 0 5px #fff;
}

.player .play:active, .pause-button:active span {
text-shadow: 0 0 7px #fff;
}

.player .pause-button:hover span {
box-shadow: 0 0 5px #fff;
} .player .pause-button:active span {
box-shadow: 0 0 7px #fff;
}

.player .volume {
position: relative;
float: left;
width: 8%;
margin: 0 0 0 4%;
height: 100%;
}

.player .volume-icon {
padding: 1.5%;
height: 100%;
cursor: pointer;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-transition: all 0.15s linear;
}

.player .volume-icon-hover {
background-color: #4f4f4f;
}

.player .volume-holder {
height: 100px;
width: 100%;
background: black;
position: absolute;
display: none;
background: #4f4f4f;
left: 0;
border-radius: 5px 5px 0 0;
top: -100px;
}

.player .volume-bar-holder {
background: #333;
width: 20px;
box-shadow: inset 0px 0px 5px rgba(0,0,0,0.3);
margin: 15px auto;
height: 80px;
border-radius: 5px;
position: relative;
cursor: pointer;
}

.player .volume-button {
background: #fff;
box-shadow: 0 0 20px rgba(0,0,0,0.3);
border-radius: 30px;
width: 20px;
height: 20px;
}

.player .volume-button-holder {
position: relative;
top: -10px;
}

.player .volume-bar {
background: #33b5d5;
box-shadow: inset -30px 0px 69px -20px #89f6f5;
border-radius: 5px;
width: 100%;
height: 100%;
position: absolute;
bottom: 0;
}

.player .fullscreen {
width: 12%;
cursor: pointer;
float: left;
height: 100%;
}

.player .fullscreen a {
width: 25px;
height: 20px;
border-radius: 3px;
background: #fff;
display: block;
position: relative;
top: 23px;
margin: 0px auto;
}

.player .volume-icon span {
width: 20%;
height: 13%;
background-color: #fff;
display: block;
position: relative;
z-index: 1;
font-weight: bold;
top: 40%;
color: #fff;
left: 22%;
}

.player .volume-icon span:before,
.player .volume-icon span:after {
content: '';
position: absolute;
}
.player .volume-icon span:before {
width: 0;
height: 0;
border: 1em solid transparent;
border-left: none;
border-right-color: #fff;
z-index: 2;
top: -2px;
left: 10%;
margin-top: -40%;
}
.player .volume-icon span:after {
width: 2%;
height: 2%;
border: 1px solid #fff;
left: 190%;
border-width: 0px 0px 0 0;
top: 5px;
border-radius: 0 50px 0 0;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
font-variant: small-caps;
}

.player .v-change-11 span:after { border-width: 10px 10px 0 0; top: 0; }
.player .v-change-10 span:after { border-width: 9px 9px 0 0; top: 1px; }
.player .v-change-9 span:after { border-width: 8px 8px 0 0; top: 1px; }
.player .v-change-8 span:after { border-width: 7px 7px 0 0; top: 2px; }
.player .v-change-7 span:after { border-width: 6px 6px 0 0; top: 2px; }
.player .v-change-6 span:after { border-width: 5px 5px 0 0; top: 3px; }
.player .v-change-5 span:after { border-width: 4px 4px 0 0; top: 3px; }
.player .v-change-4 span:after { border-width: 3px 3px 0 0; top: 4px; }
.player .v-change-3 span:after { border-width: 2px 2px 0 0; top: 4px; }
.player .v-change-2 span:after { border-width: 1px 1px 0 0; top: 5px; }
.player .v-change-1 span:after { border-width: 0px 0px 0 0; top: 5px; }

.player .v-change-1 span:after {
content: '+';
-webkit-transform: rotate(45deg);
font-size: 20px;
top: -6px;
left: 25px;
}

III-B - jQuery

La façon la plus simple et la plus légère pour gérer le lecteur est d'utiliser jQuery et de créer un plugin. N'oubliez donc pas d'inclure jQuery dans votre page ! Comme pour tout plugin jQuery, créez un nouveau fichier JavaScript contenant le code suivant :

(function($) {

$.fn.videoPlayer = function(options) { // videoPlayer est le nom de notre plugin

var settings = {
playerWidth : '0.95', // Par défaut 95 %
videoClass : 'video'  // Classe CSS de la vidéo
}

// Fusionne les options pour qu'elles soient prises en compte par le plugin
if(options) {
$.extend(settings, options);
}

// Nous utilisons .each() pour permettre le chainage
return this.each(function() {

// Le code vient ici

});

}

})(jQuery);

J'ai ajouté quelques options personnalisables (lorsque vous lancez le plugin, vous pouvez modifier ces options comme bon vous semble). Ces options concernent la largeur du lecteur pour des questions de mise en forme (par défaut 0.95 soit 95 %) ainsi que la classe CSS de la vidéo (au cas où vous auriez besoin de la changer pour éviter des conflits de nommage).
L'ensemble du code se trouvera à l'intérieur de l'instruction return this.each(function() { });.

La première chose à faire est de vérifier que la vidéo est prête à être lue. Dans certains cas, les métadonnées sont longues à charger, donc si nous ne faisons pas cette vérification, nous nous retrouverons avec une vidéo vide à lire (ce qui ne fonctionnera pas). Nous allons aussi définir quelques variables de base dont l'utilité apparaîtra bientôt.

$(this)[0].addEventListener('loadedmetadata', function() {

// Variables de base
var $this = $(this);
var $settings = settings;

// Entourer la balise <video> d'une balise <div> ayant la classe CSS précisée en option
$this.wrap('<div class="'+$settings.videoClass+'"></div>');

// Sélectionner la div contenant la vidéo pour faciliter son utilisation
var $that = $this.parent('.'+$settings.videoClass);

// Quelques autres variables diverses pour connaitre l'état du lecteur
var $mclicking = false,
$vclicking = false,
$vidhover = false,
$volhover = false,
$playing = false,
$drop = false,
$begin = false,
$draggingProgess = false,
$storevol,
x = 0,
y = 0,
vtime = 0,
updProgWidth = 0,
volume = 0;

Ensuite, nous allons créer notre lecteur proprement dit. Vous pouvez modifier cette partie si vous le souhaitez. Nous définissons aussi quelques variables supplémentaires et changeons la largeur du lecteur.

// Structure du lecteur
{

$( '<div class="player">'
+ '<div class="play-pause play">'
+ '<span class="play-button">&#9658;</span>'
+ '<div class="pause-button">'
+ '<span> </span>'
+ '<span> </span>'
+ '</div>'
+ '</div>'
+ '<div class="progress">'
+ '<div class="progress-bar">'
+ '<div class="button-holder">'
+ '<div class="progress-button"> </div>'
+ '</div>'
+ '</div>'
+ '<div class="time">'
+ '<span class="ctime">00:00</span>'
+ '<span class="stime"> / </span>'
+ '<span class="ttime">00:00</span>'
+ '</div>'
+ '</div>'
+ '<div class="volume">'
+ '<div class="volume-holder">'
+ '<div class="volume-bar-holder">'
+ '<div class="volume-bar">'
+ '<div class="volume-button-holder">'
+ '<div class="volume-button"> </div>'
+ '</div>'
+ '</div>'
+ '</div>'
+ '</div>'
+ '<div class="volume-icon v-change-0">'
+ '<span> </span>'
+ '</div>'
+ '</div>'
+ '<div class="fullscreen"> '
+ '<a href="#"> </a>'
+ '</div>'
+ '</div>').appendTo($that);

}

// Ajustement de la largeur de la vidéo
$videoWidth = $this.width();
$that.width($videoWidth+'px');

// Ajustement de la largeur du lecteur en fonction des options
$that.find('.player').css({'width' : ($settings.playerWidth*100)+'%', 'left' : ((100-$settings.playerWidth*100)/2)+'%'});

// Informations sur la vidéo
var $spc = $(this)[0], // Balise <video> en cours
$duration = $spc.duration, // Durée de la vidéo
$volume = $spc.volume, // Volume de la vidéo
currentTime;

// Largeur de la barre de progression
var progWidth = $that.find('.progress').width();

Ensuite, nous allons commencer à définir quelques fonctions. La première va concerner la bufferisation afin que l'utilisateur puisse visualiser quelle partie est dans le tampon. La seconde concerne la gestion du temps pour que l'affichage se mette à jour correctement. La fonction de gestion du temps sera appelée chaque fois que le temps de lecture sera mis à jour. J'ai commenté le code pour que vous puissiez comprendre comment cela fonctionne.

var bufferLength = function() {

// Les parties de la vidéo en tampon
var buffered = $spc.buffered;

// Réinitialiser les zones en tampon à chaque appel de la fonction
$that.find('[class^=buffered]').remove();

// S'il existe un tampon
if(buffered.length > 0) {

// On affecte sa taille à i
var i = buffered.length;

while(i--) {
// Début et fin du tampon
$maxBuffer = buffered.end(i);
$minBuffer = buffered.start(i);

// Le décalage et la largeur de la partie en tampon
var bufferOffset = ($minBuffer / $duration) * 100;
var bufferWidth = (($maxBuffer - $minBuffer) / $duration) * 100;

// Insérer la zone en tampon dans le lecteur
$('<div class="buffered"></div>').css({"left" : bufferOffset+'%', 'width' : bufferWidth+'%'}).appendTo($that.find('.progress'));

}
}
}

// Lancer la fonction de gestion du tampon
bufferLength();

// La fonction de gestion du temps, met à jour le compteur
var timeUpdate = function($ignore) {

// Le temps actuel de la vidéo, basé sur la barre de progression
var time = Math.round(($('.progress-bar').width() / progWidth) * $duration);

// Le temps "réel" de la vidéo
var curTime = $spc.currentTime;

// Les secondes sont initialisées à 0 par défaut, les minutes correspondent à la durée divisée par 60
// tminutes et tseconds sont les minutes et secondes totales
var seconds = 0,
minutes = Math.floor(time / 60),
tminutes = Math.round($duration / 60),
tseconds = Math.round(($duration) - (tminutes*60));

// Si le temps existe (enfin, la durée de la vidéo !)
if(time) {
// Les secondes valent la durée moins les minutes
seconds = Math.round(time) - (60*minutes);

// Si nous avons plus de 59 secondes
if(seconds > 59) {
// On augmente les minutes et on soustrait les secondes en trop
seconds = Math.round(time) - (60*minutes);
if(seconds == 60) {
minutes = Math.round(time / 60);
seconds = 0;
}
}

}

// Mise à jour de la barre de progression
updProgWidth = (curTime / $duration) * progWidth

// Ajout des zéros initiaux pour les valeurs inférieures à 10
if(seconds < 10) { seconds = '0'+seconds; }
if(tseconds < 10) { tseconds = '0'+tseconds; }

// Une variable que nous verrons plus tard
if($ignore != true) {
$that.find('.progress-bar').css({'width' : updProgWidth+'px'});
$that.find('.progress-button').css({'left' : (updProgWidth-$that.find('.progress-button').width())+'px'});
}

// Ajustement des durées
$that.find('.ctime').html(minutes+':'+seconds)
$that.find('.ttime').html(tminutes+':'+tseconds);

// En mode lecture, mise à jour des valeurs du tampon
if($spc.currentTime > 0 && $spc.paused == false && $spc.ended == false) {
bufferLength();
}

}

// Lancer la fonction de temps au démarrage puis à chaque événement lié
timeUpdate();
$spc.addEventListener('timeupdate', timeUpdate);

Ensuite vient le bouton de lecture. Nous allons ajouter et retirer des classes CSS pour qu'il alterne avec un bouton de pause lorsque l'on clique dessus. Nous allons aussi définir une variable permettant de déterminer si l'on est en lecture ou en pause, nous l'utiliserons plus tard.

// Lorsque l'utilisateur clique sur lecture, générer un événement clic
$that.find('.play-pause').bind('click', function() {

// Affectation d'une variable de lecture
if($spc.currentTime > 0 && $spc.paused == false && $spc.ended == false) {
$playing = false;
} else { $playing = true; }

// Modifier les classes CSS pour afficher le bouton lecture ou pause
if($playing == false) {
$spc.pause();
$(this).addClass('play').removeClass('pause');
bufferLength();
} else {
$begin = true;
$spc.play();
$(this).addClass('pause').removeClass('play');
}

});

L'une des parties les plus compliquées de ce code est la gestion de la barre de progression. Déjà, nous avons besoin de savoir quand l'utilisateur clique sur cette barre et dans ce cas, ajuster une variable pour l'indiquer. Ensuite, lorsque l'utilisateur déplace la souris, nous pourrons ajuster la position du curseur de façon appropriée. Nous utiliserons le même procédé pour le réglage du volume, j'ai donc inclus cette partie de code ici aussi, ainsi qu'une animation pour l'icône du volume et un moyen de gérer le survol de l'icône du volume à l'aide de la méthode .hover() de jQuery.

// Affecter un événement sur la barre de progression pour que l'utilisateur puisse choisir un point précis de la vidéo
$that.find('.progress').bind('mousedown', function(e) {

// Lorsque l'on clique sur la barre
$mclicking = true;

// Si la vidéo est en cours de lecture, on met en pause pendant le changement de timestamp
if($playing == true) {
$spc.pause();
}

// Position x de la souris lors du clic
x = e.pageX - $that.find('.progress').offset().left;

// Mise à jour du temps actuel
currentTime = (x / progWidth) * $duration;

$spc.currentTime = currentTime;

});

// Si l'on clique sur le volume, déclencher un événement de changement de volume
$that.find('.volume-bar-holder').bind('mousedown', function(e) {

// On a cliqué sur le volume
$vclicking = true;

// Position y de la souris sur le slider
y = $that.find('.volume-bar-holder').height() - (e.pageY - $that.find('.volume-bar-holder').offset().top);

// On annule (return false) si le clic se fait en dehors de la zone autorisée
if(y < 0 || y > $(this).height()) {
$vclicking = false;
return false;
}

// Ajustement des CSS pour refléter les modifications
$that.find('.volume-bar').css({'height' : y+'px'});
$that.find('.volume-button').css({'top' : (y-($that.find('.volume-button').height()/2))+'px'});

// Mise à jour de quelques variables
$spc.volume = $that.find('.volume-bar').height() / $(this).height();
$storevol = $that.find('.volume-bar').height() / $(this).height();
$volume = $that.find('.volume-bar').height() / $(this).height();

// Animation de l'icône de volume
volanim();

});

// Gestion de l'animation de l'icône de volume
var volanim = function() {

// Ajuster les classes CSS en fonction de la valeur du volume
for(var i = 0; i < 1; i += 0.1) {

var fi = parseInt(Math.floor(i*10)) / 10;
var volid = (fi * 10)+1;

if($volume == 1) {
if($volhover == true) {
$that.find('.volume-icon').removeClass().addClass('volume-icon volume-icon-hover v-change-11');
} else {
$that.find('.volume-icon').removeClass().addClass('volume-icon v-change-11');
}
}
else if($volume == 0) {
if($volhover == true) {
$that.find('.volume-icon').removeClass().addClass('volume-icon volume-icon-hover v-change-1');
} else {
$that.find('.volume-icon').removeClass().addClass('volume-icon v-change-1');
}
}
else if($volume > (fi-0.1) && volume < fi && !$that.find('.volume-icon').hasClass('v-change-'+volid)) {
if($volhover == true) {
$that.find('.volume-icon').removeClass().addClass('volume-icon volume-icon-hover v-change-'+volid);
} else {
$that.find('.volume-icon').removeClass().addClass('volume-icon v-change-'+volid);
}
}

}
}
// Lancer l'animation
volanim();

// Gestion du survol du bouton de volume
$that.find('.volume').hover(function() {
$volhover = true;
}, function() {
$volhover = false;
});

Maintenant, nous allons nous occuper du déplacement de la souris. De nombreuses choses dépendent du déplacement de la souris, donc cette fonction sera relativement longue. Par exemple, si l'utilisateur veut déplacer la barre de progression (ou la barre de volume) en cliquant dessus, celle-ci doit suivre le déplacement. En plus de cela, si l'utilisateur bouge la souris sur la vidéo pendant la lecture, il faut prévoir de masquer le lecteur pour ne pas gêner le visionnage. Le code complet est commenté ci-dessous pour vous permettre de comprendre comment tout cela fonctionne. Enfin, j'ai aussi ajouté une fonction gérant quelques problèmes avec le bouton lecture/pause, ainsi, l'icône affichée est toujours correcte.

// Pour des raisons d'ergonomie, nous affectons l'événement au document entier car nous considérons que l'utilisateur peut se déplacer en dehors de la vidéo
// lors du déplacement des curseurs de progression et de volume
$('body, html').bind('mousemove', function(e) {

// Masquer le lecteur si la lecture est en cours et que la souris quitte la vidéo
if($begin == true) {
$that.hover(function() {
$that.find('.player').stop(true, false).animate({'opacity' : '1'}, 0.5);
}, function() {
$that.find('.player').stop(true, false).animate({'opacity' : '0'}, 0.5);
});
}

// Contrôle de la barre de progression
if($mclicking == true) {

// Déplacement de la souris
$draggingProgress = true;
// La valeur à appliquer aux CSS (les changements se feront dans les conditions)
var progMove = 0;
// Largeur du bouton de progression (le bouton au bout de la barre)
var buttonWidth = $that.find('.progress-button').width();

// La position de la souris détermine la valeur de x
x = e.pageX - $that.find('.progress').offset().left;

// Si la lecture est en cours
if($playing == true) {
// et que le temps actuel est inférieur à la durée
if(currentTime < $duration) {
// alors le bouton lecture/pause doit être sur pause
$that.find('.play-pause').addClass('pause').removeClass('play');
}
}

if(x < 0) { // Si x est inférieur à zéro, la barre de progression reste à zéro
progMove = 0;
$spc.currentTime = 0;
}
else if(x > progWidth) { // Si x est supérieur à la largeur de la barre, alors progMove vaut progWidth
$spc.currentTime = $duration;
progMove = progWidth;
}
else { // Sinon, progMove vaut x
progMove = x;
currentTime = (x / progWidth) * $duration;
$spc.currentTime = currentTime;
}

// Changement de CSS en fonction des conditions précédentes
$that.find('.progress-bar').css({'width' : $progMove+'px'});
$that.find('.progress-button').css({'left' : ($progMove-buttonWidth)+'px'});

}

// Contrôle du volume
if($vclicking == true) {

// Position de la souris sur le slider
y = $that.find('.volume-bar-holder').height() - (e.pageY - $that.find('.volume-bar-holder').offset().top);

// Position à affecter au slider
var volMove = 0;

// Si la boîte contenant le contrôle du volume est masquée, on ne fait rien
if($that.find('.volume-holder').css('display') == 'none') {
$vclicking = false;
return false;
}

// Ajout de la classe correspondant au survol
if(!$that.find('.volume-icon').hasClass('volume-icon-hover')) {
$that.find('.volume-icon').addClass('volume-icon-hover');
}

// Si y est inférieur à zéro, alors volMove vaut 0
if(y < 0 || y == 0) {

$volume = 0;
volMove = 0;

$that.find('.volume-icon').removeClass().addClass('volume-icon volume-icon-hover v-change-11');

// Si y est supérieur à la hauteur, alors volMove vaut la hauteur maximale
} else if(y > $(this).find('.volume-bar-holder').height() || (y / $that.find('.volume-bar-holder').height()) == 1) {

$volume = 1;
volMove = $that.find('.volume-bar-holder').height();

$that.find('.volume-icon').removeClass().addClass('volume-icon volume-icon-hover v-change-1');

} else { // Sinon, volMove vaut y

$volume = $that.find('.volume-bar').height() / $that.find('.volume-bar-holder').height();
volMove = y;

}

// Changement de CSS en fonction des conditions précédentes
$that.find('.volume-bar').css({'height' : volMove+'px'});
$that.find('.volume-button').css({'top' : (volMove+$that.find('.volume-button').height())+'px'});

// Lancer l'animation
volanim();

// Change et stocke le volume
// La valeur stockée correspond à la valeur choisie par l'utilisateur
// s'il veut couper le son, alors il retrouvera la dernière valeur s'il le remet
$spc.volume = $volume;
$storevol = $volume;

}

// Lors du survol de la barre de volume, faire apparaitre/disparaitre et modifier la classe CSS

if($volhover == false) {

$that.find('.volume-holder').stop(true, false).fadeOut(100);
$that.find('.volume-icon').removeClass('volume-icon-hover');

}

else {
$that.find('.volume-icon').addClass('volume-icon-hover');
$that.find('.volume-holder').fadeIn(100);
}

});

// À la fin de la lecture, le bouton lecture devient un bouton pause
$spc.addEventListener('ended', function() {

$playing = false;

// S'il n'y a pas de déplacement du curseur
if($draggingProgress == false) {
$that.find('.play-pause').addClass('play').removeClass('pause');
}

});

La fonction suivante va permettre de couper ou activer le son lors du clic sur l'icône de volume. Nous utilisons la valeur du volume stockée pour rétablir le son.

// Si l'utilisateur clique sur l'icône de volume, on coupe le son et on stocke la valeur du volume
// lorsque l'on réactive le son, on lui affecte le volume stocké
$that.find('.volume-icon').bind('mousedown', function() {

$volume = $spc.volume; // Mise à jour du volume

// Si besoin, on initialise la mémorisation du volume
if(typeof $storevol == 'undefined') {
$storevol = $spc.volume;
}

// Si le volume est supérieur à zéro
if($volume > 0) {
// alors il faut couper le son, le volume passe alors à zéro
$spc.volume = 0;
$volume = 0;
$that.find('.volume-bar').css({'height' : '0'});
volanim();
}
else {
// sinon, on veut réactiver le son, on affecte au volume la valeur stockée
$spc.volume = $storevol;
$volume = $storevol;
$that.find('.volume-bar').css({'height' : ($storevol*100)+'%'});
volanim();
}

});

Ensuite, nous réinitialisons les valeurs lorsque le bouton de la souris est relâché.

// Lorsque le bouton est relâché, clicking vaut false pour le volume volume et la barre de progression
// D'autre part, on relance la vidéo si elle était en lecture avant le déplacement du curseur
// Nous lançons aussi la gestion du tampon
$('body, html').bind('mouseup', function(e) {

$mclicking = false;
$vclicking = false;
$draggingProgress = false;

if($playing == true) {
$spc.play();
}

bufferLength();

});

Enfin, nous allons ajouter un bouton pour permettre de visualiser la vidéo en plein écran. Si le navigateur ne supporte pas le mode le plein écran, le bouton ne sera pas affiché, sinon, nous détectons d'éventuels préfixes vendeurs.

Tous les navigateurs ne supportent pas le plein écran. Dans notre exemple, seuls les navigateurs compatibles afficheront le bouton.

// Vérifie le support du mode plein écran. Si ce mode n'est pas supporté, le bouton n'est pas affiché
if(!$spc.requestFullscreen && !$spc.mozRequestFullScreen && !$spc.webkitRequestFullScreen) {
$('.fullscreen').hide();
}

// On appelle le mode plein écran en prévoyant les préfixes vendeurs
$('.fullscreen').click(function() {

if ($spc.requestFullscreen) {
$spc.requestFullscreen();
}

else if ($spc.mozRequestFullScreen) {
$spc.mozRequestFullScreen();
}

else if ($spc.webkitRequestFullScreen) {
$spc.webkitRequestFullScreen();
}

});

IV - Utiliser le plugin

Nous avons terminé ! C'est tout pour le JavaScript. Vous pouvez sauvegarder le fichier et l'inclure dans le code HTML, puis lancer le plugin à l'aide du code JavaScript suivant :

$(document).ready(function() {
$('video').videoPlayer({
'playerWidth' : 0.95,
'videoClass' : 'video'
});
});

Et c'est tout ! Ceci dit, il est facile d'ajouter des fonctionnalités avec jQuery ou juste modifier l'apparence du lecteur en CSS. Les fonctionnalités utilisées, sont supportées dans tous les navigateurs récents si ce n'est le mode plein écran, mais son support va en augmentant.

V - Conclusion et remerciements

Vous pouvez voir un exemple en ligne ou télécharger l'archive de l'exemple.

Cet article est la traduction de Making Custom CSS3 Video Players With HTML5 and Javascript publié sur InsertHTML.

Nous tenons à remercier f-leb pour sa relecture attentive de cet article.