Animez au niveau du compositeur avec transform et opacity, structurez vos @keyframes et respectez les préférences de mouvement des utilisateurs.
Ouvrir cette leçon dans KodokonAvant de choisir quoi animer, rappelez-vous le pipeline de rendu : layout (géométrie), paint (pixels), composite (assemblage des calques sur le GPU). Animer width ou top déclenche un layout à chaque frame, souvent suivi d'un paint : c'est la recette du jank sous 60 fps. transform et opacity sont traitées directement par le compositeur, sans repasser par les deux premières étapes. La règle du métier : n'animez que ces deux propriétés dès que possible, et simulez le reste (translate au lieu de top, scale au lieu de width).
.button {
transform: translateY(0);
transition:
transform 0.2s cubic-bezier(0.2, 0.8, 0.2, 1),
opacity 0.15s ease-out;
}
.button:hover {
transform: translateY(-2px);
}
.button:active {
transform: translateY(0) scale(0.97);
}Quand une transition ne suffit plus (étapes intermédiaires, boucle, départ automatique), passez à @keyframes. Deux réglages font la différence entre une animation propre et un bug visuel : animation-fill-mode contrôle l'état avant le départ (backwards applique la première keyframe pendant le délai) et après la fin (forwards fige la dernière) ; both combine les deux. Par ailleurs, la timing function s'applique entre chaque paire de keyframes, pas sur la durée totale : vous pouvez la redéfinir à l'intérieur d'une keyframe.
@keyframes slide-in {
from {
transform: translateX(-100%);
opacity: 0;
}
70% {
transform: translateX(8px);
opacity: 1;
}
to {
transform: translateX(0);
}
}
.toast {
animation: slide-in 0.35s ease-out both;
}Deux outils complémentaires à connaître. will-change: transform promeut l'élément sur son propre calque avant l'animation, évitant un raster tardif ; mais chaque calque coûte de la mémoire GPU, donc appliquez-le juste avant l'animation et retirez-le après, jamais en style permanent sur des dizaines d'éléments. steps(n) remplace l'interpolation continue par des sauts discrets : indispensable pour les sprites, les curseurs de type machine à écrire ou les horloges.
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}transform: translateX(100px) à left: 100px pour un déplacement animé ?transform est mieux supporté par les anciens navigateurstransform est traité par le compositeur sans recalcul de layout à chaque frameleft ne fonctionne que sur les éléments en position absolue, transform partouttransform accepte des valeurs négatives, contrairement à leftanimation-fill-mode: both ?will-change ?* pour accélérer toute la pagetransform: translateZ(0)transition pour les micro-interactions