Animaciones expandibles de alto rendimiento: creación de fotogramas clave sobre la marcha

 

 

 


Índice
  1. Generando fotogramas clave con JavaScript
  2. Construyendo una sección expandible
  3. control de rendimiento
  4. Consideraciones finales

Las animaciones han recorrido un largo camino y brindan continuamente a los desarrolladores mejores herramientas. Las animaciones CSS , en particular, han definido la base para resolver la mayoría de los casos de uso. Sin embargo, hay algunas animaciones que requieren un poco más de trabajo.

 

Probablemente sepas que las animaciones deben ejecutarse en la capa compuesta. (No me extenderé más aquí, pero si quieres saber más, consulta este artículo). Eso significa animación transformo opacitypropiedades que no activan el diseño o las capas de pintura. Animar propiedades como heighty widthes un gran no-no, ya que activan esas capas, lo que obliga al navegador a recalcular los estilos.

Además de eso, incluso al animar transformpropiedades, si realmente quieres alcanzar animaciones de 60 FPS, probablemente deberías obtener un poco de ayuda de JavaScript, utilizando la técnica FLIP para obtener animaciones más fluidas.

Sin embargo, el problema de usar transformanimaciones expandibles es que la scalefunción no es exactamente la misma que animar width/ heightpropiedades. Crea un efecto sesgado en el contenido, ya que todos los elementos se estiran (al ampliar) o se comprimen (al reducir).

Por eso, mi solución preferida ha sido (y probablemente sigue siendo, por razones que detallaré más adelante) la técnica n.° 3 del artículo de Brandon Smith. Esto todavía tiene una transición activada height, pero usa Javascript para calcular el tamaño del contenido y forzar una transición usando requestAnimationFrame. En OutSystems, utilizamos esto para crear la animación del patrón de acordeón de la interfaz de usuario de OutSystems.

Generando fotogramas clave con JavaScript

Recientemente, me topé con otro gran artículo de Paul Lewis, que detalla una nueva solución para expandir y contraer animaciones, lo que me motivó a escribir este artículo y difundir esta técnica.

 

Según sus palabras, la idea principal consiste en generar fotogramas claves dinámicas, stepping…

[…] de 0 a 100 y calcule qué valores de escala serán necesarios para el elemento y su contenido. Luego, estos se pueden reducir a una cadena, que se puede inyectar en la página como un elemento de estilo.

Para lograrlo, hay tres pasos principales.

Paso 1: Calcular los estados inicial y final

Necesitamos calcular el valor de escala correcto para ambos estados. Eso significa que usamos getBoundingClientRect()el elemento que servirá como proxy para el estado inicial y lo dividimos con el valor del estado final. Debería ser algo como esto:

function calculateStartScale () { const start= startElement.getBoundingClientRect(); const end= endElement.getBoundingClientRect(); return { x: start.width / end.width, y: start.height / end.height };}

Paso 2: generar los fotogramas clave

Ahora, necesitamos ejecutar un forbucle, usando la cantidad de fotogramas necesarios como longitud. (En realidad, no debería ser inferior a 60 para garantizar una animación fluida). Luego, en cada iteración, calculamos el valor de aceleración correcto, usando una easefunción:

function ease (v, pow=4) { return 1 - Math.pow(1 - v, pow);}let easedStep = ease(i / frame);

Con ese valor, obtendremos la escala del elemento en el paso actual, usando las siguientes matemáticas:

const xScale = x + (1 - x) * easedStep;const yScale = y + (1 - y) * easedStep;

Y luego agregamos el paso a la cadena de animación:

animation += `${step}% { transform: scale(${xScale}, ${yScale});}`;

Para evitar que el contenido se estire o sesgue, debemos realizar una animación de contador, usando los valores invertidos:

const invXScale = 1 / xScale;const invYScale = 1 / yScale;inverseAnimation += `${step}% { transform: scale(${invXScale}, ${invYScale});}`;

Finalmente, podemos devolver las animaciones completadas o inyectarlas directamente en una etiqueta de estilo recién creada.

Paso 3: habilita las animaciones CSS

En el lado de CSS, necesitamos habilitar las animaciones en los elementos correctos:

.element--expanded { animation-name: animation; animation-duration: 300ms; animation-timing-function: step-end;}.element-contents--expanded { animation-name: inverseAnimation ; animation-duration: 300ms; animation-timing-function: step-end;}

Puede consultar el ejemplo de un Menú del artículo de Paul Lewis, en Codepen (cortesía de Chris):

Construyendo una sección expandible

Después de comprender estos conceptos básicos, quería comprobar si podía aplicar esta técnica en un caso de uso diferente, como una sección ampliable.

Sólo necesitamos animar la altura en este caso, específicamente en la función para calcular escalas. Obtenemos el Yvalor del título de la sección, para que sirva como estado contraído, y toda la sección para representar el estado expandido:

 

 _calculateScales () { var collapsed = this._sectionItemTitle.getBoundingClientRect(); var expanded = this._section.getBoundingClientRect(); // create css variable with collapsed height, to apply on the wrapper this._sectionWrapper.style.setProperty('--title-height', collapsed.height + 'px'); this._collapsed = { y: collapsed.height / expanded.height } }

Como queremos que la sección expandida tenga absoluteposicionamiento (para evitar que ocupe espacio cuando esté colapsada), estamos configurando la variable CSS con la altura colapsada, aplicada en el envoltorio. Ese será el único elemento con posicionamiento relativo.

Luego viene la función para crear los fotogramas clave: _createEaseAnimations(). Esto no difiere mucho de lo explicado anteriormente. Para este caso de uso, necesitamos crear cuatro animaciones:

  1. La animación para expandir el contenedor.
  2. La animación contraexpandida sobre el contenido.
  3. La animación para colapsar el contenedor.
  4. La animación de contra-colapso del contenido.

Seguimos el mismo enfoque que antes, ejecutando un forbucle con una longitud de 60 (para obtener una animación fluida de 60 FPS) y creamos un porcentaje de fotogramas clave, basado en el paso simplificado. Luego, lo llevamos a las cadenas de animaciones finales: Horoscopos y tarot de amor

outerAnimation.push(` ${percentage}% { transform: scaleY(${yScale}); }`); innerAnimation.push(` ${percentage}% { transform: scaleY(${invScaleY}); }`);

Comenzamos creando una etiqueta de estilo para contener las animaciones terminadas. Como está construido como un constructor, para poder agregar fácilmente múltiples patrones, queremos tener todas estas animaciones generadas en la misma hoja de estilo. Entonces, primero validamos si el elemento existe. De lo contrario, lo creamos y agregamos un nombre de clase significativa. De lo contrario, terminarías con una hoja de estilo expandible para cada sección, lo cual no es ideal.

 var sectionEase = document.querySelector('.section-animations'); if (!sectionEase) { sectionEase = document.createElement('style'); sectionEase.classList.add('section-animations'); }

Hablando de eso, es posible que ya te estés preguntando: “Hmm, si tenemos varias secciones expandibles, ¿no seguirían usando la animación con el mismo nombre, con valores posiblemente incorrectos para su contenido?”

¡Estás absolutamente en lo correcto! Entonces, para evitar eso, también estamos generando nombres de animaciones dinámicas. ¿Guay, verdad?

Hacemos uso del índice pasado al constructor desde el forbucle al realizar el querySelectorAll('.section')proceso para agregar un elemento único al nombre:

var sectionExpandAnimationName = "sectionExpandAnimation" + index;var sectionExpandContentsAnimationName = "sectionExpandContentsAnimation" + index;

Luego usamos este nombre para establecer una variable CSS en la sección expandible actual. Como esta variable solo está en este alcance, solo necesitamos configurar la animación en la nueva variable en el CSS, y cada patrón obtendrá su animation-namevalor respectivo.

 

.section.is--expanded { animation-name: var(--sectionExpandAnimation);}.is--expanded .section-item { animation-name: var(--sectionExpandContentsAnimation);}.section.is--collapsed { animation-name: var(--sectionCollapseAnimation);}.is--collapsed .section-item { animation-name: var(--sectionCollapseContentsAnimation);}

El resto del script está relacionado con la adición de detectores de eventos, funciones para alternar el estado de contraer/expandir y algunas mejoras de accesibilidad.

Acerca de HTML y CSS: se necesita un poco de trabajo adicional para que funcione la funcionalidad ampliable. Necesitamos un contenedor adicional para que sea el elemento relativo que no se anima. Los niños expandibles tienen una absoluteposición para que no ocupen espacio cuando están plegados.

Recuerde, dado que necesitamos hacer animaciones de contador, las hacemos escalar a tamaño completo para evitar un efecto sesgado en el contenido.

.section-item-wrapper { min-height: var(--title-height); position: relative;}.section { animation-duration: 300ms; animation-timing-function: step-end; contain: content; left: 0; position: absolute; top: 0; transform-origin: top left; will-change: transform;}.section-item { animation-duration: 300ms; animation-timing-function: step-end; contain: content; transform-origin: top left; will-change: transform; }

Me gustaría resaltar la importancia de la animation-timing-functionpropiedad. Debe establecerse en linearo step-endpara evitar la relajación entre cada fotograma clave.

La will-changepropiedad, como probablemente sepa, permitirá la aceleración de GPU para la animación de transformación para una experiencia aún más fluida. Y usar la containspropiedad, con un valor de contents, ayudará al navegador a tratar el elemento independientemente del resto del árbol DOM, limitando el área antes de volver a calcular las propiedades de diseño, estilo, pintura y tamaño.

Usamos visibilityy opacitypara ocultar el contenido y evitar que los lectores de pantalla accedan a él cuando esté colapsado.

.section-item-content { opacity: 1; transition: opacity 500ms ease;}.is--collapsed .section-item-content { opacity: 0; visibility: hidden;}

¡Y por fin tenemos nuestra sección ampliable! Aquí está el código completo y la demostración para que pueda verificar:

control de rendimiento

Cada vez que trabajamos con animaciones, el rendimiento debe estar en el fondo de nuestra mente. Entonces, utilizamos herramientas de desarrollo para verificar si todo este trabajo valió la pena en términos de rendimiento. Usando la pestaña Rendimiento (estoy usando Chrome DevTools), podemos analizar los FPS y el uso de la CPU durante las animaciones.

¡Y los resultados son geniales!

Usando la herramienta de medidor de FPS para comprobar los valores con mayor detalle, podemos ver que constantemente alcanza la marca de 60 FPS, incluso con un uso abusivo.

Consideraciones finales

Entonces, ¿cuál es el veredicto? ¿Esto reemplaza a todos los demás métodos? ¿Es ésta la solución del “Santo Grial”?

En mi opinión, no.

Pero… ¡está bien, de verdad! Es otra solución en la lista. Y, como ocurre con cualquier otro método, se debe analizar si es el mejor enfoque para el caso de uso.

Esta técnica definitivamente tiene sus ventajas. Como dice Paul Lewis, preparar esto requiere mucho trabajo. Pero, por otro lado, sólo necesitamos hacerlo una vez, cuando se carga la página. Durante las interacciones, simplemente alternamos clases (y atributos en algunos casos, por accesibilidad).

Sin embargo, esto trae algunas limitaciones a la interfaz de usuario de los elementos. Como puede ver en el elemento de sección expandible, la contraescala lo hace mucho más confiable para elementos absolutos y fuera del lienzo, como acciones flotantes o menús. También es difícil aplicar estilos a los bordes porque se utiliza overflow: hidden.

Sin embargo, creo que este enfoque tiene muchísimo potencial. ¡Déjame saber lo que piensas!






SUSCRÍBETE A NUESTRO BOLETÍN 

No te pierdas de nuestro contenido ni de ninguna de nuestras guías para que puedas avanzar en los juegos que más te gustan.










Al suscribirte, aceptas nuestra política de privacidad y nuestros términos de servicio.






Tal vez te puede interesar:

  1. La innovación no puede mantener la Web rápida
  2. Rendimiento web ultrarrápido
  3. Tabla de contenidos fijos con estados activos de desplazamiento
  4. “cambiar tamaño: ninguno;” en áreas de texto es una mala experiencia de usuario

Animaciones expandibles de alto rendimiento: creación de fotogramas clave sobre la marcha

Animaciones expandibles de alto rendimiento: creación de fotogramas clave sobre la marcha

Generando fotogramas clave con JavaScriptConstruyendo una sección expandiblecontrol de rendimientoConsideraciones finalesTal vez te puede interesar:Índice

programar

es

https://pseint.es/static/images/programar-animaciones-expandibles-de-alto-rendimiento-creacion-de-fotogramas-clave-sobre-la-marcha-1373-0.jpg

2024-06-13

 

Animaciones expandibles de alto rendimiento: creación de fotogramas clave sobre la marcha
Animaciones expandibles de alto rendimiento: creación de fotogramas clave sobre la marcha

Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

 

 

Update cookies preferences