Todo lo que necesitas saber sobre las animaciones FLIP en React
Con una actualización muy reciente de Safari, la API de animaciones web (WAAPI) ahora es compatible sin bandera en todos los navegadores modernos (excepto IE). Aquí tienes un práctico lápiz donde puedes comprobar qué funciones admite tu navegador. WAAPI es una buena manera de hacer animación (que debe hacerse en JavaScript) porque es nativo, lo que significa que no requiere bibliotecas adicionales para funcionar. Si es completamente nuevo en WAAPI, aquí tiene una muy buena introducción de Dan Wilson.
Uno de los enfoques más eficientes para la animación es FLIP. FLIP requiere un poco de JavaScript para funcionar.
Echemos un vistazo a la intersección del uso de WAAPI, FLIP y la integración de todo eso en React. Pero primero comenzaremos sin React y luego llegaremos a eso.
FLIP y WAAPI
¡Las animaciones FLIP se hacen mucho más fáciles gracias a WAAPI!
Repaso rápido sobre FLIP: la gran idea es que coloques el elemento donde quieres que termine primero. A continuación, aplique transformaciones para moverlo a la posición inicial. Luego anule la aplicación de esas transformaciones.
Animar transformaciones es súper eficiente, por lo que FLIP es súper eficiente. Antes de WAAPI, teníamos que manipular directamente los estilos de los elementos para establecer transformaciones y esperar a que el siguiente cuadro lo desarme/invirtiera:
// FLIP Before the WAAPIel.style.transform = `translateY(200px)`;
requestAnimationFrame(() = { el.style.transform = '';});
Muchas bibliotecas se basan en este enfoque. Sin embargo, existen varios problemas con esto:
- Todo parece un gran truco.
- Es extremadamente difícil invertir la animación FLIP. Si bien las transformaciones CSS se revierten “gratis” una vez que se elimina una clase, este no es el caso aquí. Iniciar un nuevo FLIP mientras se ejecuta uno anterior puede causar fallas. Revertir requiere analizar una matriz de transformación
getComputedStyles
y usarla para calcular las dimensiones actuales antes de configurar una nueva animación. - Las animaciones avanzadas son casi imposibles. Por ejemplo, para evitar distorsionar los hijos de un padre escalado, necesitamos tener acceso al valor de escalada actual de cada cuadro. Esto sólo se puede hacer analizando la matriz de transformación.
- Hay muchos errores en el navegador. Por ejemplo, a veces para que una animación FLIP funcione perfectamente en Firefox es necesario realizar
requestAnimationFrame
dos llamadas:
requestAnimationFrame(() = { requestAnimationFrame(() = { el.style.transform = ''; });});
No tenemos ninguno de estos problemas cuando se utiliza WAAPI. Con la reverse
función se puede invertir la inversión sin problemas. También es posible contraescalar a los niños. Y cuando hay un error, es fácil identificar al culpable exacto, ya que solo trabajamos con funciones simples, como animate
y reverse
, en lugar de analizar cosas como el requestAnimationFrame
enfoque.
Aquí está el resumen de la versión WAAPI:
el.classList.toggle('someclass');const keyframes = /* Calculate the size/position diff */;el.animate(keyframes, 2000);
Voltear y reaccionar
Para comprender cómo funcionan las animaciones FLIP en React, es importante saber cómo y, lo más importante, por qué funcionan en JavaScript simple. Recuerde la anatomía de una animación FLIP:
Todo lo que tenga un fondo morado debe ocurrir antes del paso de “pintar” del renderizado. De lo contrario, veríamos por un momento un destello de nuevos estilos lo cual no es bueno. Las cosas se vuelven un poco más complicadas en React ya que todas las actualizaciones del DOM las realizamos por nosotros.
La magia de las animaciones FLIP es que un elemento se transforma antes de que el navegador tenga la oportunidad de pintar. Entonces, ¿cómo sabemos el momento “antes de pintar” en React?
Conoce el useLayoutEffect
anzuelo. Si siquiera te preguntaste para qué sirve… ¡aquí está! Todo lo que pasamos en esta devolución de llamada ocurre sincrónicamente después de las actualizaciones de DOM pero antes de pintar. En otras palabras, ¡este es un gran lugar para configurar un FLIP!
Hagamos algo para que la técnica FLIP sea muy buena: animar la posición DOM. No hay nada que CSS pueda hacer si queremos animar cómo un elemento se mueve de una posición DOM a otra. (Imagínese completar una tarea en una lista de tareas pendientes y moverla a la lista de tareas “completas”, como cuando hace clic en los elementos del lápiz a continuación).
Veamos el ejemplo más simple. Al hacer clic en cualquiera de los dos cuadrados del siguiente lápiz, estos intercambian posiciones. Sin FLIP, esto sucedería instantáneamente.
Están sucediendo muchas cosas allí. Observe cómo ocurre todo el trabajo dentro de las devoluciones de llamadas de gancho del ciclo de vida: useEffect
y useLayoutEffect
. Lo que lo hace un poco confuso es que la línea de tiempo de nuestra animación FLIP no es obvia solo por el código, ya que ocurre en dos renderizados de React. Aquí está la anatomía de una animación de React FLIP para mostrar el orden diferente de las operaciones:
Aunque useEffect
siempre se ejecuta useLayoutEffect
después de pintar el navegador, es importante que almacenemos en caché la posición y el tamaño del elemento después del primer renderizado. No tendremos la oportunidad de hacerlo en el segundo render porque useLayoutEffect
se ejecuta después de todas las actualizaciones de DOM. Pero el procedimiento es esencialmente el mismo que con las animaciones FLIP básicas.
Advertencias
Como la mayoría de las cosas, hay algunas advertencias a considerar al trabajar con FLIP en React.
Manténgalo por debajo de 100 ms.
Se calcula una animación FLIP. El cálculo lleva tiempo y antes de que puedas mostrar esa transformación suave de 60 fps necesitas hacer bastante trabajo. La gente no notará un retraso si es inferior a 100 ms, así que asegúrese de que todo esté por debajo de esa cantidad. La pestaña Rendimiento en DevTools es un buen lugar para comprobarlo.
renders innecesarios
No podemos usar useState para almacenar en caché el tamaño, las posiciones y los objetos de animación porque todo setState
provocará un renderizado innecesario y ralentizará la aplicación. Incluso puede causar errores en el peor de los casos. Intente usarlo useRef
en su lugar y considérelo como un objeto que puede mutarse sin representar nada.
Diseño de paliza
Evite activar repetidamente el diseño del navegador. En el contexto de las animaciones FLIP, eso significa evitar recorrer elementos y leer su posición con getBoundingClientRect
, y luego animarlos inmediatamente con animate. "Lee" y "escribe" por lotes siempre que sea posible. Esto permitirá animaciones extremadamente fluidas.
Cancelación de animación
Intente hacer clic aleatoriamente en los cuadrados de la demostración anterior mientras se mueven y luego nuevamente después de que se detengan. Verás fallos. En la vida real, los usuarios interactuarán con los elementos mientras se mueven, por lo que vale la pena asegurarse de cancelarlos, pausarlos y actualizarlos sin problemas.
Sin embargo, no todas las animaciones se pueden revertir con reverse
. A veces queremos que se detengan y luego se muevan a una nueva posición (como cuando se mezcla aleatoriamente una lista de elementos). En este caso, necesitamos:
- obtener un tamaño/posición de un elemento en movimiento
- terminar la animación actual
- calcular las nuevas diferencias de tamaño y posición
- iniciar una nueva animación
En React, esto puede ser más difícil de lo que parece. Perdí mucho tiempo luchando con eso. El objeto de animación actual debe estar almacenado en caché. Una buena forma de hacerlo es crear un Map
archivo para obtener la animación mediante un ID. Luego, necesitamos obtener el tamaño y la posición del elemento móvil. Hay dos maneras de hacerlo:
- Utilice un componente de función: simplemente recorra cada elemento animado justo en el cuerpo de la función y almacene en caché las posiciones actuales.
- Utilice un componente de clase: utilice el
getSnapshotBeforeUpdate
método del ciclo de vida.
De hecho, los documentos oficiales de React recomiendan usar getSnapshotBeforeUpdate
"porque puede haber retrasos entre los ciclos de vida de la fase de" renderizado "(como render
) y los ciclos de vida de la fase de" confirmación "(como getSnapshotBeforeUpdate
y componentDidUpdate
)". Sin embargo, todavía no existe una contraparte de gancho para este método. Descubrí que usar el cuerpo del componente de función está bien.
No luches contra el navegador
Lo he dicho antes, pero evita luchar contra el navegador e intenta que las cosas sucedan de la forma en que lo haría el navegador. Si necesitamos animar un cambio de tamaño simple, entonces considere si CSS sería suficiente (por ejemplo transform: scale()
). Descubrí que las animaciones FLIP se usan mejor cuando los navegadores realmente no pueden ayudar:
- Animar el cambio de posición del DOM (como hicimos arriba)
- Compartir animaciones de diseño
La segunda es una versión más complicada de la primera. Hay dos elementos DOM que actúan y parecen como si uno cambiara de posición (mientras el otro está desmontado/oculto). Estos trucos permiten algunas animaciones geniales. Por ejemplo, esta animación está hecha con una biblioteca que construí llamada react-easy-flip
y que utiliza este enfoque:
Bibliotecas
Hay bastantes bibliotecas que facilitan las animaciones FLIP en React y abstraen el texto estándar. Los que actualmente se mantienen activamente incluyen: react-flip-toolkit
y el mío, react-easy-flip
.
Si no te importa algo más pesado pero capaz de realizar animaciones más generales, consulta framer-motion
. ¡También crea interesantes animaciones de diseño compartido! Hay un vídeo que profundiza en esa biblioteca.
Recursos y referencias
- Animando lo inanimable por Josh W. Comeau
- Cree animaciones de expansión y colapso de alto rendimiento realizadas por Paul Lewis y Stephen McGruer
- La magia dentro del movimiento mágico de Matt Perry
- Usando variables CSS animadas desde JavaScript, tuiteadas por @keyframers
- Una mirada al interior del navegador web moderno (parte 3) por Mariko Kosaka
- Construyendo una animación de interfaz de usuario compleja en React, simplemente por Alex Holachek
- Animación de diseños con la técnica FLIP de David Khourshid
- Animaciones fluidas con React Hooks, nuevamente por Kirill Vasiltsov
- Transición de elementos compartidos con React Hooks por Jayant Bhawal
Deja un comentario