vCarousel/src/vCarousel.ts

141 lines
6.5 KiB
TypeScript

// La classe vCarousel est instanciée en recevant un tableau d'ids des conteneurs HTML des vidéos à afficher successivement.
// Le tableau n'est pas dédoublonné, pour rester libre d'afficher plusieurs fois la même vidéo durant un tour du carrousel.
// Plusieurs options sont possibles :
// - firstVideoId est une chaîne permettant de fournir l'id du conteneur de la première vidéo à afficher lors de l'appel. Par défaut la première vidéo de la liste sera affichée. Si on passe une chaîne vide, aucune ne sera affichée.
// - playFirstVideo et playNextVideos demandent de lancer automatiquement la lecture respectivement de la vidéo demandée et de celles automatiquement chargée par la fonction. Ce n'est pas le cas par défaut et peut être bloqué par le navigateur.
// - noStop permet de demander à ce que les vidéos continuent à tourner même quand toutes ont été affichées. Par défaut, le carrousel s'arrête.
interface videoDOM
{
id: string;
containerElt: HTMLElement;
videoElt: HTMLMediaElement;
}
export class vCarousel
{
private _vContainers: videoDOM[] = [];
private _firstVideoId: string;
private _playFirstVideo: boolean = false;
private _playNextVideos: boolean = false;
private _noStop: boolean = false;
private nbVContainers: number = 0;
private currentVideo: HTMLMediaElement;
// Les ids reçus doivent correspondre à des éléments HTML contenant une vidéo.
set vContainers(vContainersIds: string[])
{
for(let containerId of vContainersIds)
{
let checkContainerExist=document.getElementById(containerId);
if(checkContainerExist === null)
throw new Error("Aucun élément HTML trouvé dans la page pour l'id "+containerId+".");
else
{
let checkVideoExist=<HTMLMediaElement>document.querySelector("#"+containerId+" video");
if(checkVideoExist === null)
throw new Error("Aucune balise vidéo trouvée dans l'élément HTML ayant comme id "+containerId+".");
else
this._vContainers.push({id:containerId, containerElt:checkContainerExist, videoElt:checkVideoExist});
}
}
// Il faut au moins 2 vidéos à faire tourner.
this.nbVContainers=this._vContainers.length;
if(this.nbVContainers < 2)
throw new Error("Il vous faut fournir au moins deux conteneurs de vidéo valides pour pouvoir lancer le carrousel.");
}
// Si this._firstVideoId a été fourni, on vérifie qu'il est présent dans la liste des conteneurs de vidéos.
set firstVideoId(firstVideo: string)
{
if(firstVideo!=="" && this._vContainers.findIndex(video => video.id === firstVideo) === -1)
console.error("Vous avez fourni l'id de la vidéo à afficher en premier ("+firstVideo+"), mais il n'est pas valide.");
else
this._firstVideoId=firstVideo;
}
get firstVideoId() : string
{
return this._firstVideoId;
}
set playFirstVideo(playFirstVideo: boolean)
{
this._playFirstVideo=playFirstVideo;
}
set playNextVideos(playNextVideos: boolean)
{
this._playNextVideos=playNextVideos;
}
set noStop(noStop: boolean)
{
this._noStop=noStop;
}
// transformer l'attribut pour accepter getters et setters ?
public getCurrentVideo() : HTMLMediaElement
{
return this.currentVideo;
}
public run(): void
{
const vCarousel=this;// évite les confusions avec le "this" des événements.
if(vCarousel.nbVContainers < 2) // dans le cas où on lancerait run() sans passer par le setter.
throw new Error("vCarousel ne peut être lancé sans au moins 2 vidéos.");
for (let i = 0; i < vCarousel.nbVContainers; i++)
{
let vContainer=vCarousel._vContainers[i].containerElt;
let video=vCarousel._vContainers[i].videoElt;
if((vCarousel._firstVideoId!==undefined && vCarousel._vContainers[i].id!==vCarousel._firstVideoId) || (vCarousel._firstVideoId===undefined && i!==0) || vCarousel._firstVideoId==="")
vContainer.style.display = "none";
else
{
// Si une vidéo est déjà visible et en cours de lecture, je la stoppe et la remets à 0
// Sinon elle risque de continuer à être lue tout en étant cachée.
if(vCarousel.currentVideo!==undefined && !vCarousel.currentVideo.paused)
{
vCarousel.currentVideo.pause();
vCarousel.currentVideo.currentTime = 0;
}
// J'affiche la première vidéo et j'essaye de la lancer, si cela est demandé
vContainer.style.display = "block";
vCarousel.currentVideo=video;
if(vCarousel._playFirstVideo===true)
video.play();
}
var nbTurn=0;
video.addEventListener("ended", function()
{
// Sauf si demandé, le carrousel s'arrête lorsque toutes les vidéos ont été affichées une fois.
if(nbTurn < (vCarousel.nbVContainers-1) || vCarousel._noStop===true)
{
vContainer.style.display = "none";
let nextVContainer: HTMLElement, nextVideo: HTMLMediaElement, nextHash: string;
if(i < (vCarousel.nbVContainers-1))
{
nextVContainer=vCarousel._vContainers[i+1].containerElt;
nextVideo=vCarousel._vContainers[i+1].videoElt;
nextHash=vCarousel._vContainers[i+1].id;
}
else
{
nextVContainer=vCarousel._vContainers[0].containerElt;
nextVideo=vCarousel._vContainers[0].videoElt;
nextHash=vCarousel._vContainers[0].id;
}
nextVContainer.style.display = "block";
// On adapte l'ancre de l'url de manière à ce qu'elle soit cohérente avec la vidéo affichée.
// Attention car cela peut provoquer un déplacement dans la page pour atteindre l'ancre.
window.location.assign("#"+nextHash);
if(vCarousel._playNextVideos===true)
nextVideo.play();
vCarousel.currentVideo=nextVideo;
nbTurn++;
}
});
}
}
}