Cela faisait un certain moment que je voulais tester ce module CSS ainsi d’autres choses, l’occasion m’en a été donnée avec mes slides de Sud Web 2018. Voici un petit retour d’expérience.
Attention, je le dis de suite : certaines parties de ce délire ne sont clairement PAS utilisables en production à grande échelle. Je me le suis permis car j’étais dans un contexte totalement maitrisé… et vous allez voir que je ne m’en suis vraiment pas privé. Grosso modo, c’est un RevealJS en version très light, codé à la rache en moins de deux heures.
CSS Scroll Snap Points
L’idée de CSS Scroll Snap Points est de permettre de définir une force d’adhérence aux points d’accroche en cas de défilement d’un conteneur. Le viewport du conteneur doit s’arrêter à un point d’accroche s’il n’est pas en train de défiler. Ce n’est pas clair ? Rassurez-vous, c’est normal !
Un exemple vaut mille mots, allez voir (idéalement avec Firefox) un exemple.
Comme vous pouvez le voir, l’idée des slides en HTML est assez simple. En clair, chaque élément aura une taille de 100%
et une hauteur de 100vh
. Et dès qu’on se déplacera, CSS Scroll Snap Points permettra d’aller de slide en slide, nativement si j’ose dire.
La petite surprise, c’est que le touch est très très bien géré ainsi (essayez sur une Surface sur Firefox ou sur Edge). Et une fois que le focus est sur la page… les flèches gauche et droite permettent aussi de gérer le déplacement. Vous voyez l’intérêt pour un carrousel par exemple ? :)
En clair, ce module CSS a beaucoup d’avantages. Actuellement, le touch est très bien géré par Edge et Firefox (les points d’adhérence sont effectifs). Chrome et autres ne semblent pas (encore) les gérer.
Dans mon cas, cela s’est limité à définir un conteneur scroll-snap-type: mandatory
et scroll-snap-points-x: repeat(100%)
. Et à donner à chaque slide la largeur/hauteur complète du viewport. Oui, c’est aussi simple que cela.
La doc : CSS Scroll Snap Points
Intersection Observer
Grosso modo, l’idée de cette API est de gérer quand un élément « observé » rentre ou sort du viewport, d’appeler une fonction quand c’est le cas, cela évite de devoir le faire soi-même avec des fonctions plus ou hasardeuses et/ou coûteuses en performance.
Pour être très franc, au début, j’ai voulu utiliser cela juste pour avoir l’occasion de m’en servir (je n’en ai eu l’utilité que plus tard), voir la documentation d’Intersection Observer.
L’idée était de pouvoir ajouter une classe pour savoir quelle était la slide active. En clair, je surveille toutes les slides.
if ('IntersectionObserver' in window) {
// IntersectionObserver Supported
let config = {
root: null,
rootMargin: '0px',
threshold: 0.5
};
let observer = new IntersectionObserver(onChange, config);
slides.forEach(slide => observer.observe(slide));
} else {
// IntersectionObserver NOT Supported
console.log('Whomp Whomp Whomp');
}
Et quand une passe dans le viewport, je lui ajoute la classe is-active
et au passage j’ajoute le fragment correspondant dans l’URL.
function onChange(changes) {
changes.forEach(change => {
if (change.intersectionRatio > 0.5) {
change.target.classList.add('is-active');
history.pushState(null, null, location.pathname + location.search + '#' + change.target.getAttribute('id'));
} else {
change.target.classList.remove('is-active');
}
});
}
C’était un caprice au début, mais cela va se révéler utile ensuite.
Ajouter des contenus qui apparaissent/bougent
Dans la présentation, à plusieurs moments je me suis dit que ce serait bien d’avoir des contenus qui apparaissent au fur et à mesure dans la même slide. Du coup, j’ai mis quelques boutons ainsi.
<button class="js-next" data-next="next_2">Ce qui change…</button>
En clair, quand ce bouton est activé, il va chopper l’élément qui a l’id
next_2
et il le fait apparaître.
Oui c’est pas génial point de vue accessibilité, on peut faire mieux, je sais ! :)
Gérer la télécommande
Durant les répétitions, je me suis rendu compte que devoir me rapprocher de ma Surface pour activer les boutons/passer les slides était gênant pour le rythme et ma liberté sur scène (et bien m’en a pris vu la configuration du lieu le jour même). C’est là que je me suis amusé à coder un peu de raffinement… qui va s’avérer vital durant la présentation.
Grosso modo, la télécommande envoie un événement clavier (voir keycode.info), comme si on appuyait sur Page Down (34) ou Page Up (33). J’avais déjà codé que si cet événement arrivait, j’appelais une fonction qui passait à la slide suivante/précédente. Pour l’animation, je ne me suis pas cassé la tête, j’ai utilisé scrollIntoView
, et comme je n’avais pas de souci de compatibilité, j’ai utilisé l’option behavior: "smooth"
(attention, le support est très variable selon les navigateurs, la version de base ne marche pas trop mal, mais scrollIntoViewOptions
est plus rare).
Comme je voulais pouvoir activer les boutons, l’idée est assez simple : si j’ai un bouton non encore activé dans la slide active, alors j’envoie un clic dessus.
if (eventName === 'keydown' && 34 === e.keyCode) {
let activeSlide = document.querySelector('.slide.is-active');
let buttonNext = activeSlide.querySelector('.js-next:not(.has-been-pressed)');
if (buttonNext) {
buttonNext.click();
} else {
let buttonAnim = activeSlide.querySelector('.js-anim:not(.has-been-played)');
if (buttonAnim) {
buttonAnim.click();
} else {
let slides = [].slice.call(document.querySelectorAll('.slide'));
selectSlideInList(slides, 'next');
}
}
}
Dans la fonction qui permet de revenir en arrière (sait-on jamais, des fois que j’en aie besoin durant la présentation), j’ai réinitialisé les boutons d’animation et suivants. Ce n’est pas optimal, mais ça me permettra de les rejouer si besoin est.
Et hop, le job était fait, je contrôlais l’intégralité de la présentation à la télécommande. Bien m’en a pris, car ce sera obligatoire sur place.
En conclusion
Certes c’est un exercice de style fait à la rache, mais grosso modo, avec quelques APIs, de la CSS, très peu de JavaScript et un peu d’huile de coude, j’ai pu tester quelques trucs et recréer un mini-système façon RevealJS en très peu de temps.
Il serait assez facile d’ajouter une version imprimable, de perfectionner le système, j’avais même fait quelques essais pour avoir des slides horizontales et verticales, à creuser pour plus tard, car là je n’ai clairement pas eu le temps.
Là où je me dis que cela est intéressant, c’est pour un hypothétique carrousel : on n'aurait plus à gérer le touch à coups de JavaScript, mais tout serait fait automatiquement grâce à CSS Scroll Snap Points… quand il fonctionnera correctement chez Webkit.
Voilà, si vous avez des remarques ou des commentaires, à votre disposition !