Grégory Bourguin
SysReIC - LISIC - ULCO
Web2 : TP02 - Diaporama (?? séance(s))

Préliminaires

Avant de commencer ce TP

Il est nécessaire que vous ayez compris le cours jusqu'à la partie Timers -> Callback.

Pour commencer ce TP, je vous invite donc à (re)faire par vous-mêmes (et pas avec de bêtes des copier/coller) tous les exemples du cours concernés.


Structure de Page Web

Template

La structure de ce diaporama sera constituée d'une zone d'affichage principale d'images (#display), et d'une sorte de menu (#thumbs-menu) présentant les vignettes de toutes les images qui seront diffusées dans le diaporama.

index.html

<div id="diaporama">

    <div id="display">
      <img id="display-image">
    </div>
    
    <div id="thumbs-menu" style="position: relative">
      <div id="thumbs-list"></div>
    </div>
    
</div>

Mise en forme

Nous allons ensuite ajouter du CSS pour structurer la page telle que nous le souhaitons.

Template CSS :

Exercice 01

Créez et intégrez un fichier css/main.css, pour faire en sorte que votre page index.html soit affichée comme suit.

Structure attendue :

NB: vous pouvez temporairement ajouter des bords à vos conteneurs pour voir s'ils s'affichent correctement.

Création des Vignettes (thumbs)

Nous allons maintenant remplir le #thumbs_menu (en réalité la #thumbs-list).

Liste des Images

Pour ce faire, nous allons créer une liste de src d'images nommé images.
Ces images sont dans le fichier images.zip (à ajouter au projet).

La liste sera construite comme suit :

<script>
    let images = []
    // NB: dans un vrai site, cette liste serait générée dynamiquement...
    images.push("images/610747.jpg")
    images.push("images/888509.jpg")
    images.push("images/695524.jpg")
    images.push("images/600918.jpg")
    images.push("images/216856.jpg")
</script>

NB: dans un vrai site, cette liste serait générée dynamiquement, par exemple à partir du contenu du dossier images en php.

Création des .thumbs

L'étape suivante va consister à utiliser la liste images pour générer les éléments HTML représentant les vignettes : des div portant la classe .thumb.

Les vignettes générées auront la structure suivante :

<div class="thumb">
    <img src="...">
</div>

Cette génération sera réalisée au sein d'un fonction nommée createThumbs() qui devra être appelée une fois le contenu de la page HTML chargé.

La structure résultante dans la page sera du type :

<div id="thumbs-menu">
    <div id="thumbs-list">
        <!-- Ces vignettes sont générées dynamiquement par createThumbs() -->
        <div class="thumb">
            <img src="...">
        </div>
        <!-- ...-->
        <div class="thumb">
            <img src="...">
        </div>
    </div>
</div>


Enfin, nous allons aussi ajouter les éléments HTML .thumb dans une liste Javascript que nous nommerons thumbs (i.e. une variable globale comme images), ce qui nous permettra ensuite d'y accéder facilement dans nos prochaines étapes.

Exercice 02

Créez et faites appel à la fonction createThumbs() qui :

  • Génère les éléments .thumb.
  • Les ajoute à #thumbs-list.
  • Les ajoute à la variable globale (liste) thumbs.

Résultat attendu :
(Respectez la mise en forme)

Affichage des images

Nous allons faire en sorte qu'un click sur un .thumb fasse afficher l'image correspondante dans l'élément #display-image.

La technique consistera à récupérer l'attribut src de l'élément img contenu dans le .thumb visé, ceci afin de l'affecter à l'attribut src de l'élément #display-image.

Récupération de la source : getSrc(thumb)

On va tout d'abord créer la fonction getSrc(thumb) :

/**
 * Récupération de la chaine correspondant à l'attribut 'src' de la balise 'img' du '.thumb'
 * @param thumb : un élément ".thumb"
 * @returns le contenu de l'attribut 'src'
 */
function getSrc(thumb){
    // à compléter
}

NB: vous pourrez tester en faisant afficher un des éléments de thumbs dans la console.

Affichage : displayThumb(thumb)

Grâce à getSrc(thumb), nous allons créer une fonction displayThumb(thumb) qui :

  • Affiche dans #display-image l'image contenue dans le thumb.
  • Ajoute une classe CSS .active (à définir) qui crée un bord coloré autour du thumb affiché.

NB: Il ne peut y avoir qu'un seul thumb actif à la fois. Il ne faudra donc pas oublier de "nettoyer" les thumbs pour ne pas en avoir plusieurs avec un bord "actif".

/**
 * Affiche dans #display-image l'image contenue dans thumb
 * @param thumb : l'élément contenant l'image à afficher
 */
function displayThumb(thumb){
    // à compléter
}

NB: vous pourrez tester en faisant afficher un des éléments de thumbs au chargement.

Click sur un thumb

Avec les méthodes précédentes, il ne reste plus qu'à écouter un évènement sur le click d'un .thumb pour déclencher le displayThumb correspondant.

Souci du détail: on n'oubliera pas de changer le curseur de la souris sur un .thumb.

Exercice 03

Implémentez les étapes ci-dessus.


Résultat attendu :
(Respectez la mise en forme)

Overlay

Notre but va maintenant être d'ajouter un bouton qui permettra (plus tard) de déclencher le diaporama.

Au lieu d'ajouter un bouton superflu, nous allons faire en sorte que le #display soit lui même un bouton. En réalité, ce ne sera pas l'élément display qui sera le bouton, mais un div cliquable qu'on va mettre en overlay.

Un overlay consiste à ajouter un élément (souvent) en transparence par dessus un autre. En ce qui nous concerne ici, nous voulons ajouter un div qui va totalement recouvrir le #display, et qui n'apparaîtra que lorsque la souris passe dessus.

Pour vous aider, voici la base de ce que nous devons faire :
<style>
    
    #conteneur{
        /*important pour que l'overlay fonctionne*/
        position: relative;
        
        width: 150px;
        height: 150px;
        border: 3px solid black;
        padding: 5px;
    }
    
    .overlay{
        /*pour se placer au dessus du contenu*/
        position: absolute;
        top: 0;
        left: 0;
    
        /*pour prendre tout l'espace*/
        width: 100% ;
        height: 100%;
    
        /*pour gérer le contenu de l'overlay*/
        display: flex;
        justify-content: center;
        align-items: center;
    
        /*invisible au chargement*/
        opacity: 0;
    
        color: white;
        background: black;
        
        cursor: pointer;
    }
    .overlay:hover{
        opacity: 0.6;
    }
</style>
<div id="conteneur">
    Voici le contenu d'un div qui va se faire recouvrir !
    <div id="ov-button" class="overlay">
        OVERLAY
    </div>
</div>    
<script>
document.addEventListener("DOMContentLoaded", function (){
    let ovButton = document.getElementById("ov-button")
    ovButton.addEventListener('click', function (event){
        alert("On a cliqué sur l'overlay !")
    })
})
</script>
Passez la souris pour voir l'overlay :
Voici le contenu d'un div qui va se faire recouvrir !
OVERLAY

Exercice 04

Utilisez la technique ci-avant pour ajouter un "bouton" div avec id='play-button' en overlay sur le #display.

Pour le contenu de #play-button, j'ai utilisée un icône Bootstrap Play fill.

Résultat attendu :
(Respectez la mise en forme)

Au suivant : displayNextThumb()

L'étape suivante consiste à lier notre nouveau bouton à une fonction qui va faire passer l'affichage automatiquement à l'élément suivant dans la liste des thumbs.

Nous allons donc créer une fonction displayNextThumb() telle que :

/**
 * Fais passer l'affichage dans #diplay-image au thumb suivant dans thumbs
 */
function displayNextThumb(){
    // à compléter
}

NB: pour savoir quel est l'élément en cours d'affichage, il va falloir introduire une nouvelle variable globale currentThumb destinée à contenir l'index (dans thumbs) de l'élément en cours d'affichage.

Exercice 05

Implémentez la fonction displayNextThumb() (en utilisant la nouvelle variable globale currentThumb), et utilisez-là dans le handler de click de #play-button.

Pour le contenu, j'ai utilisée un un icône Bootstrap Play fill.


  • Vous ferez attention à ce que que, quand on clique sur une image dans le menu, le prochain appel à displayNextThumb() passe bien à l'image qui suit celle qui vient d'être sélectionnée "manuellement".
  • Vous pourrez remarquer que le passage au thumb suivant fait scroller la liste pour amener le thumb activé dans la zone d'affichage.
    Ceci est réalisé grâce à un appel à :
    thumb.scrollIntoView({behavior: "smooth", block: "nearest", inline: "center"})
    (où thumb est l'élément qui vient d'être activé)
Résultat attendu :
(Respectez la mise en forme)

Diaporama

La suite du programme est de faire en sorte qu'un click sur #play-button n'affiche pas simplement le thumb suivant, mais lance un diaporama qui passe automatiquement d'un thumb à l'autre au bout d'un certain temps.

Play / Stop

Lancer un diaporama revient "simplement" à exécuter displayNextThumb() en boucle, en attendant un certain délai entre chaque exécution.

La technique à utiliser est donc exactement celle qui est mise en avant dans le cours à propos de la méthode setInterval.

Nous voulons qu'un click sur #play-button lance le diaporama s'il n'est pas déjà en train de tourner, et l'arrête dans le cas contraire.

Pour ce faire, nous allons implémenter 3 méthodes :

  • Une fonction play(delay) qui crée un timer avec setInterval, et fais s'exécuter displayNextThumb() toutes les delay ms.
    (On fixera delay à 3000 par défaut).
  • Une fonction stop() qui arrête (et nettoie) le timer.
  • Une fonction togglePlay() qui est la fonction qui sera déclenchée par un click sur #play-button, et qui appelle play(...) ou stop() en fonction de l'état du diaporama.

Exercice 06

Implémentez le diaporama avec la technique développée ci-avant.

Quand votre diaporama fonctionnera correctement, vous pourrez remarquer dans ma version ci-dessous que le #play-button change d'icône en fonction de l'état du diaporama...
(... à mettre en place dans une seconde phase).

Résultat attendu : (il y a 3s entre chaque changement)
(Respectez la mise en forme)

Exercice 07

En utilisant les timers, il est possible de générer des effets de transition.

NB: Il y a des méthodes plus simples, mais ici, on veut "jouer" avec les timers ;).

But

Dans cet exercice, à chaque changement d'image, on enchaine 2 setInterval pour que se suivent les actions suivantes :

  • Une "lente" baisse d'opacité de l'image de 1 à 0 (fade-out) -> l'image se cache :
    on définit un petit interval, et à chaque "pas", on baisse un peu l'opacité, ceci jusqu'à ce qu'elle atteigne 0.
  • Un changement d'image -> appel à displayNextThumb().
  • Une "lente" augmentation de l'opacité de l'image (fade-in) -> l'image (ré)apparait :
    on définit un petit interval, et à chaque "pas", on augmente un peu l'opacité, ceci jusqu'à ce qu'elle atteigne 1.

Pour les "fades", j'ai utilisé la technique du callback.

Résultat attendu : (chaque fade dure 1s)
(Respectez la mise en forme)

Correction

La correction est --> ici <--