Le scrollytelling est une technique populaire utilisée pour créer des expériences Web engageantes et immersives. Il s'agit d'animer des éléments sur une page à mesure que l'utilisateur fait défiler l'écran, ce qui permet de raconter une histoire ou de guider l'utilisateur à travers un récit. Avec les animations CSS liées au défilement, vous pouvez créer ces effets directement en CSS, sans avoir à utiliser JavaScript. Les animations sont contrôlées par la position de défilement, et non par une horloge temporelle, ce qui garantit qu'elles sont toujours synchronisées avec le défilement de l'utilisateur.
Mise en œuvre
Pour créer une expérience de scrollytelling, vous avez besoin de deux ensembles d'éléments : l'un pour suivre la position de défilement et l'autre pour être animé.
Tout d'après, définissez un view-timeline nommé sur les éléments que vous souhaitez suivre. Ils serviront de moteurs pour vos animations.
#tracked {
section:nth-child(1){ view-timeline: --tl-1 block; }
section:nth-child(2){ view-timeline: --tl-2 block; }
section:nth-child(3){ view-timeline: --tl-3 block; }
section:nth-child(4){ view-timeline: --tl-4 block; }
section:nth-child(5){ view-timeline: --tl-5 block; }
}
Ensuite, appliquez des animations aux éléments que vous souhaitez animer et associez-les aux chronologies que vous venez de créer à l'aide de la propriété animation-timeline.
#animated {
section {
animation: animate-in auto linear both, animate-out auto linear forwards;
animation-range: entry 25% cover 50%, exit 50% exit 75%;
}
section:nth-child(1){ animation-timeline: --tl-1; }
section:nth-child(2){ animation-timeline: --tl-2; }
section:nth-child(3){ animation-timeline: --tl-3; }
section:nth-child(4){ animation-timeline: --tl-4; }
section:nth-child(5){ animation-timeline: --tl-5; }
}
Pour que le animation-timeline puisse référencer les chronologies nommées, elles doivent se trouver dans le même champ d'application. Vous pouvez utiliser la propriété timeline-scope sur un ancêtre commun pour rendre les chronologies disponibles pour tous les éléments qui en ont besoin. L'élément :root est souvent un bon choix pour cela.
html {
timeline-scope: --tl-1, --tl-2, --tl-3, --tl-4, --tl-5;
}
Enfin, vous pouvez utiliser la propriété animation-range pour spécifier la plage exacte de la chronologie pendant laquelle l'animation doit s'exécuter. Vous pouvez ainsi contrôler précisément le moment où les animations sont déclenchées et leur progression.
#animated section {
animation-range: entry 25% cover 50%, exit 50% exit 75%;
}
Exemple de code
html {
timeline-scope: --tl-1, --tl-2, --tl-3, --tl-4, --tl-5;
}
#tracked {
section:nth-child(1){ view-timeline: --tl-1 block; }
section:nth-child(2){ view-timeline: --tl-2 block; }
section:nth-child(3){ view-timeline: --tl-3 block; }
section:nth-child(4){ view-timeline: --tl-4 block; }
section:nth-child(5){ view-timeline: --tl-5 block; }
}
@keyframes animate-in {
from { scale: 0.5; opacity: 0; transform: rotateY(-180deg); }
to { transform: rotateY(0deg); }
}
@keyframes animate-out {
to { translate: 100% 0; opacity: 0; }
}
#animated {
section {
animation: animate-in auto linear both, animate-out auto linear forwards;
animation-range: entry 25% cover 50%, exit 50% exit 75%;
backface-visibility: hidden;
}
section:nth-child(1){ animation-timeline: --tl-1; }
section:nth-child(2){ animation-timeline: --tl-2; }
section:nth-child(3){ animation-timeline: --tl-3; }
section:nth-child(4){ animation-timeline: --tl-4; }
section:nth-child(5){ animation-timeline: --tl-5; }
}
/* MANDATORY Copy-Paste Safety: Disable continuous storytelling motion for sensitive users */
@media (prefers-reduced-motion: reduce) {
#animated section {
animation: none !important;
opacity: 1 !important;
transform: none !important;
}
}
Bonnes pratiques
Lorsque vous utilisez des animations basées sur le défilement, il est important de suivre quelques bonnes pratiques pour garantir une expérience fluide et accessible :
- À FAIRE : incluez la détection de fonctionnalités. Tous les navigateurs ne sont pas compatibles avec les animations déclenchées par le défilement. Utilisez
@supports ((animation-timeline: scroll()) and (animation-range: 0% 100%))pour vérifier la compatibilité et fournir une solution de remplacement pour les navigateurs qui ne sont pas compatibles.- La vérification
(animation-range: 0% 100%)DOIT être incluse ici pour filtrer les navigateurs qui ne sont que partiellement compatibles. - N'UTILISEZ PAS le package
scroll-timeline-polyfillpour la stratégie de remplacement, car il n'est pas complet et présente de nombreux problèmes connus. - Si l'animation n'est considérée que comme décorative, optez pour l'amélioration progressive et NE FOURNISSEZ PAS de solution de remplacement.
- La vérification
- À FAIRE : Respectez les préférences des utilisateurs. Certains préfèrent avoir moins de mouvements sur le Web. Utilisez la requête média
prefers-reduced-motionpour désactiver ou réduire vos animations pour ces utilisateurs. - À FAIRE : Essayez de n'animer que les propriétés CSS performantes. Pour des animations plus fluides, n'animez que les propriétés pouvant être gérées par le thread du compositeur du navigateur, telles que
transformetopacity. L'animation d'autres propriétés telles quewidthouheightpeut entraîner des problèmes de performances. - À FAIRE : Utilisez l'ordre de déclaration correct. Lorsque vous utilisez la propriété abrégée
animation, déclarezanimation-timelineetanimation-rangeaprès pour éviter que la propriété abrégée ne réinitialise la chronologie.
Lorsque vous utilisez la propriété view-timeline pour créer une animation basée sur le défilement :
- À FAIRE : utilisez une
<dashed-ident>CSS pour le nom. - FACULTATIF : Soyez explicite sur l'axe à suivre. Lorsque vous ne ciblez pas l'axe
blockpar défaut (par exemple, dans un défilement horizontal), indiquez explicitement l'axe à suivre avecview-timeline-axis. - À FAIRE : Assurez-vous que le champ d'application de la recherche fonctionne. Lorsque l'élément qui déclare
view-timelinen'est pas un ancêtre de l'arborescence plate de l'élément animé, augmentez la visibilité du nom deview-timelineen utilisanttimeline-scopesur un ancêtre partagé.
Stratégies de remplacement
Pour les navigateurs qui ne sont pas compatibles avec les animations déclenchées par le défilement, vous pouvez utiliser une solution de remplacement pour recréer les effets visuels. Les solutions de remplacement sont généralement créées avec un écouteur de défilement (pour les effets ScrollTimeline) ou l'API IntersectionObserver (pour les effets ViewTimeline).
Dans les navigateurs compatibles de manière native avec les animations liées au défilement, utilisez TOUJOURS l'implémentation CSS native, car elle est plus performante.
Notez que tous les effets ne peuvent pas être recréés à l'aide de l'approche de remplacement.
Pour ce cas d'utilisation spécifique, le script suivant applique la solution de remplacement pour les navigateurs qui ne sont pas compatibles avec les animations déclenchées par le défilement. Il utilise un IntersectionObserver pour suivre la visibilité de chaque élément #tracked section et met à jour la propriété transform du #animated section correspondant en conséquence.
const animatedSections = document.querySelectorAll('#animated section');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
const sectionIndex = Array.from(document.querySelectorAll('#tracked section')).indexOf(entry.target);
if (sectionIndex !== -1) {
const animatedSection = animatedSections[sectionIndex];
const ratio = entry.intersectionRatio;
// Animate-in
animatedSection.style.opacity = ratio;
animatedSection.style.transform = `scale(${0.5 + ratio * 0.5}) rotateY(${-180 + ratio * 180}deg)`;
// Animate-out
if (ratio < 0.5) {
animatedSection.style.translate = `${(0.5 - ratio) * 2 * 100}% 0`;
} else {
animatedSection.style.translate = '0 0';
}
}
});
}, { threshold: Array.from({length: 101}, (_, i) => i / 100) });
document.querySelectorAll('#tracked section').forEach(section => {
observer.observe(section);
});
Et le CSS associé :
#animated section {
opacity: 0;
transform: scale(0.5) rotateY(-180deg);
backface-visibility: hidden;
}
/* MANDATORY Copy-Paste Safety: Ensure content remains fully visible and legible for assistive technologies or users with motion sensitivities */
@media (prefers-reduced-motion: reduce) {
#animated section {
opacity: 1 !important;
transform: none !important;
translate: 0 0 !important;
}
}
Cette solution de remplacement fournit une animation plus précise basée sur le défilement pour les navigateurs qui ne sont pas compatibles avec la fonctionnalité CSS native, ce qui garantit une expérience plus cohérente pour tous les utilisateurs. En utilisant une série de seuils pour IntersectionObserver, nous pouvons suivre la position de défilement avec plus de précision et créer une animation plus fluide.