Los efectos de diapositiva de carrusel son una excelente manera de agregar interés visual a un carrusel. A medida que el usuario se desplaza por las diapositivas, cada una de ellas puede animarse cuando entra, se centra y sale del puerto de desplazamiento. Por ejemplo, las diapositivas pueden aparecer y desaparecer gradualmente, rotar o cambiar de tamaño. Esto crea una experiencia del usuario dinámica y atractiva. A diferencia de las animaciones de entrada y salida simples, este efecto usa una sola animación continua para controlar la apariencia de la diapositiva en todo el puerto de desplazamiento.
Cómo implementarlo
Sigue estos pasos para crear efectos de diapositiva de carrusel:
Crea un elemento de desplazamiento: Este elemento actuará como contenedor de las diapositivas del carrusel. En este ejemplo, se usa
overflow-x: scrollpara permitir el desplazamiento horizontal.<ul class="scroller"> <li class="entry">1</li> <li class="entry">2</li> <li class="entry">3</li> … </ul>.scroller { overflow-x: scroll; }Define la animación: Crea una animación de CSS que defina los diferentes estados de tus diapositivas a medida que atraviesan el puerto de desplazamiento. Puedes definir fotogramas clave para cualquier parte de la animación. Por ejemplo, puedes definir un estado para cuando las diapositivas estén en el centro del puerto de desplazamiento incluyendo un fotograma clave
50%. En este ejemplo, la propiedadscalehace que las diapositivas crezcan a medida que se acercan al centro y se reduzcan a medida que se alejan.@keyframes animate { 0% { scale: 0.5; } 50% { scale: 1; } 100% { scale: 0.5; } }Aplica la animación y el
view-timeline: Adjunta la animación a las diapositivas del carrusel y vincúlala a unview-timelineque haga un seguimiento del elemento a medida que se desplaza por el contenedor..scroller > * { animation: animate auto linear both; animation-timeline: view(inline); }De forma predeterminada,
view()hace un seguimiento del elemento en el ejeblock. Si necesitas hacer un seguimiento en el ejeinline, puedes usarview(inline).
Ejemplo de código
Este código anima los elementos del carrusel de un desplazador horizontal durante el desplazamiento con un cronograma de vista anónimo:
@keyframes animate {
0% {
scale: 0.5;
}
50% {
scale: 1;
}
100% {
scale: 0.5;
}
}
.scroller > * {
/* Applies the animation using an `auto` duration */
animation: animate auto linear both;
/* Sets the animation timeline to use an anonymous view progress timeline, tracking the element's progress through the scroller on the inline axis */
animation-timeline: view(inline);
}
Este código anima los elementos del carrusel de un desplazador horizontal cuando se desplaza con un cronograma de vista con nombre:
@keyframes animate {
0% {
scale: 0.5;
}
50% {
scale: 1;
}
100% {
scale: 0.5;
}
}
/* This creates a named view-timeline on each carousel item. The timeline is used to drive the animation that is applied on the same element. */
.scroller > * {
/* Applies the animation using an `auto` duration */
animation: animate auto linear both;
/* Defines a named view progress timeline, tracking the element's progress through the scroller on the inline axis */
view-timeline: --item inline;
/* Sets the animation timeline to use the named view progress timeline defined above */
animation-timeline: --item;
}
Prácticas recomendadas
Cuando uses animaciones controladas por el desplazamiento, es importante que sigas algunas prácticas recomendadas para garantizar una experiencia fluida y accesible:
- SÍ incluye la detección de funciones: No todos los navegadores admiten animaciones controladas por el desplazamiento. Usa
@supports ((animation-timeline: view()) and (animation-range: entry))para verificar la compatibilidad y proporcionar un resguardo para los navegadores que no lo admiten.- La verificación de
(animation-range: entry)DEBE incluirse aquí para filtrar los navegadores que solo tienen compatibilidad parcial. - NO uses el paquete
scroll-timeline-polyfillpara la estrategia de resguardo, ya que no tiene todas las funciones y tiene muchos problemas conocidos. - Si la animación solo se considera decorativa, opta por la mejora progresiva y NO proporciones un resguardo.
- La verificación de
- SÍ respeta las preferencias del usuario: Algunos usuarios prefieren tener menos movimiento en la Web. Usa la consulta de medios
prefers-reduced-motionpara inhabilitar o reducir las animaciones para estos usuarios. - SÍ intenta animar solo las propiedades de CSS que tengan un buen rendimiento: Para obtener las animaciones más fluidas, limítate a animar las propiedades que pueda controlar el subproceso del compositor del navegador, como
transformyopacity. Animar otras propiedades, comowidthoheight, puede provocar problemas de rendimiento. - SÍ uses el orden de declaración correcto: Cuando uses la propiedad abreviada
animation, declaraanimation-timelineyanimation-rangedespués de ella para evitar que la abreviatura restablezca la línea de tiempo.
Prefiere un view-timeline con nombre cuando varios elementos del DOM deban animarse según la misma línea de tiempo o cuando necesites animar elementos secundarios del elemento que tiene definido el view-timeline. Si el elemento que animas también es el que define el view-timeline, puedes usar un cronograma de vista anónimo con view().
Cuando uses la función view() para crear una animación controlada por el desplazamiento, ten en cuenta lo siguiente:
- OPCIONAL Sé explícito sobre el eje que se debe seguir: Cuando no se oriente al eje
blockpredeterminado (como en un desplazador horizontal), sé explícito sobre qué eje se debe seguir conview(block)oview(inline).
Cuando uses la propiedad view-timeline para crear una animación controlada por el desplazamiento, ten en cuenta lo siguiente:
- SÍ uses un
<dashed-ident>de CSS para el nombre (p. ej.,view-timeline: --my-custom-name). - OPCIONAL Sé explícito sobre el eje que se debe seguir: Cuando no se oriente al eje
blockpredeterminado (como en un desplazador horizontal), sé explícito sobre qué eje se debe seguir conview-timeline-axis. - SÍ asegúrate de que el alcance de la búsqueda funcione: Cuando el elemento que declara el
view-timelineno es un ancestro de árbol plano del elemento animado, eleva la visibilidad del nombre delview-timelinecontimeline-scopeen un ancestro compartido.
Compatibilidad con navegadores y estrategias de resguardo
Por lo tanto, suele ser necesaria una estrategia de resguardo.
En el caso de los navegadores que no admiten animaciones basadas en el desplazamiento, puedes usar un resguardo para recrear los efectos visuales. Por lo general, las alternativas se crean con un objeto de escucha de desplazamiento (para los efectos de ScrollTimeline) o la API de IntersectionObserver (para los efectos de ViewTimeline).
En los navegadores con compatibilidad integrada para animaciones basadas en el desplazamiento, usa SIEMPRE la implementación nativa de CSS, ya que tiene un mejor rendimiento.
Ten en cuenta que no todos los efectos se pueden recrear con el enfoque de resguardos.
Específicamente para este caso de uso, la siguiente secuencia de comandos aplica el resguardo para los navegadores que no admiten animaciones controladas por el desplazamiento. Usa la API de Web Animations (Element.animate()) para crear una animación en pausa para cada elemento del carrusel. Luego, escucha el evento scroll en el elemento de desplazamiento y actualiza el currentTime de cada animación en función del progreso de desplazamiento del elemento dentro del elemento de desplazamiento.
// Fallback for browsers that don't support scroll-driven animations
if (!CSS.supports('(animation-timeline: view()) and (animation-range: entry)')) {
const scroller = document.querySelector('.scroller');
const entries = document.querySelectorAll('.entry');
// Create a map to store animations
const animations = new Map();
entries.forEach(entry => {
const animation = entry.animate(
{
scale: ['0.5', '1', '0.5']
},
{
duration: 1, // We'll control the time ourselves
fill: 'both'
}
);
animation.pause();
animations.set(entry, animation);
});
// Update animations on scroll
const tick = () => {
const scrollerRect = scroller.getBoundingClientRect();
entries.forEach(entry => {
const animation = animations.get(entry);
if (!animation) return;
const entryRect = entry.getBoundingClientRect();
const progress = (entryRect.left + entryRect.width / 2 - scrollerRect.left) / scrollerRect.width;
animation.currentTime = progress;
});
};
scroller.addEventListener('scroll', tick);
tick();
}