Rendimiento de animación del sitio web

imagen


Cuando se desarrollan sitios que van más allá del marco de un bootstrap condicional, tarde o temprano surgen preguntas sobre el rendimiento de las animaciones. Son especialmente importantes en los sitios de diseño, como los que se incluyen en los catálogos Awwwards, FWA, CSS Design Awards, etc. En este caso, a menudo la tarea de crear animaciones y la posterior optimización, si es necesario, recae sobre los hombros de desarrolladores poco experimentados que ni siquiera saben por dónde empezar. Por lo general, todo esto se traduce en sitios inhibitorios que son imposibles de usar, y la actitud negativa posterior a toda la clase de tales proyectos. En este artículo, intentaremos averiguar dónde está el límite del rendimiento de animación aceptable, qué cuellos de botella son comunes y dónde buscar en las herramientas de desarrollador en primer lugar.


Una pequeña observación: dado que este artículo está destinado más a desarrolladores principiantes y su objetivo es mostrar enfoques generales para optimizar las animaciones, muchas cosas se darán en una forma simplificada, no del todo académica.


Cómo muestra la página el navegador


En primer lugar, es útil comprender qué sucede cuando el navegador nos muestra el estado actual de la página. Hay cuatro pasos principales:


  1. Cálculo de estilos (el navegador analiza los selectores CSS, determina qué estilos se deben aplicar a qué)
  2. Creación de diseño (el diseño de página está realmente formado)
  3. Pintura (crea representaciones de elementos en píxeles para su posterior representación)
  4. Composición de la capa (el navegador recopila todo junto y lo muestra en la pantalla)

Además, el navegador siempre actúa en esta secuencia y llega al final. Cuando la página se muestra inicialmente después de cargarla, se realizan los cuatro pasos. En el futuro, nuestras acciones pueden provocar la ejecución de una de ellas, pero al mismo tiempo se ejecutarán todas las posteriores. Pero no los anteriores.


Consideraremos más a fondo los cuellos de botella de cada uno de estos pasos, y ahora haremos una pregunta tonta a primera vista, a partir de la cual, en teoría, debe comenzar ...


Si se ralentiza o no, esa es la pregunta ...


Muy a menudo puedes conocer gente que no está haciendo nada con un sitio web claramente lento y decir "pero la velocidad de mi página da 100 puntos, todo está bien". O viceversa, en un sitio que funciona bien, las personas han estado involucradas en algún tipo de optimizaciones durante mucho tiempo, porque algunos algoritmos funcionan de manera ineficiente en función de algunas métricas misteriosas. Pero entre estos extremos debería estar el medio del sentido común, entonces, ¿dónde está?


imagen


A conocer zen Para comprender si necesita optimizar sus animaciones, necesita realizar un profundo pensamiento filosófico:


Si ve que el sitio es lento, significa que es lento. Si no ve que el sitio se está desacelerando, entonces no se está desacelerando.

Por alguna razón, muchas personas encuentran esta afirmación muy estúpida, pero ¿es así? Para el usuario final, el rendimiento no es algún tipo de métrica o algoritmo ideal con una rigurosa justificación matemática. Para él, el rendimiento es una de dos cosas: se ralentiza o no se ralentiza.


¿Cómo determina esto? El ojo de una persona que pasa mucho tiempo detrás del monitor comienza a reaccionar bruscamente a una caída de fps. Esto causa una extraña sensación de incomodidad. En consecuencia, nuestra tarea, como desarrolladores, es evitar el hundimiento. ¿El usuario está acostumbrado a ver que el navegador funciona a 60 fps? Bueno, entonces estamos haciendo todo para que todo siga así. Tomamos una laptop con plancha mediana y miramos. Vemos mucho menos de 60 fps: optimizamos. Vemos unos 60, no toques nada. El usuario no notará la diferencia de todos modos, y dedicaremos mucho tiempo a la optimización en aras de las optimizaciones.


No haga optimizaciones para optimizaciones.

16.5ms


Expresarse en términos de fps no es conveniente, así que pasemos a milisegundos. Con una división simple de 1000ms / 60fps, obtenemos aproximadamente 16.5ms de tiempo por cuadro.


¿Qué significa esto? Durante 16.5 ms, el navegador debe mostrarnos el estado actual de la página con la animación, siguiendo los pasos que vimos anteriormente, y al mismo tiempo, los recursos deben permanecer para el trabajo de otros scripts, comunicación con el servidor, etc. Si se dedica más tiempo a mostrar el estado actual de la página, veremos a través de nuestros ojos el retraso. Si son unos 16 ms, no habrá hundimiento, pero es probable que la carga de hierro sea muy alta, los refrigeradores zumben y los teléfonos se calienten. Por lo tanto, debemos asegurarnos de que la representación de un cuadro no se acerque a este valor a tiempo, y aún mejor no sea más de 10 ms, de modo que haya un margen en el rendimiento. No olvide que las pruebas siempre se realizan en el hardware del medio; por ejemplo, en los siguientes ejemplos, las capturas de pantalla se tomarán en Pentium Silver con gráficos integrados.


Pruebe el hardware que es más probable que tengan sus usuarios. Si tiene un procesador de gama alta y una granja minera debajo de la mesa en su lugar de trabajo, entonces todo funcionará bien para usted, mientras que sus usuarios con computadoras portátiles económicas pueden ser muy tristes.

Para no confiar solo en su buen ojo e intuición, es útil dominar las herramientas de desarrollo, al menos a un nivel básico. No solo proporcionarán datos de rendimiento precisos, sino que también le indicarán dónde buscar el problema, si todo no funciona muy bien.


Herramientas para desarrolladores en Google Chrome


Muchas veces, las herramientas de desarrollo en el navegador sorprenden a muchos codificadores más que a la consola de Linux. Pero, de hecho, no hay nada que temer. Sí, hay muchos botones, pero son redundantes para resolver nuestros problemas. Ahora veremos dónde vale la pena prestar atención en primer lugar para comprender qué hacer con la animación y si es necesario hacer algo.


Cuando se trata de rendimiento, pasaremos la mayor parte del tiempo en la pestaña de rendimiento y presionaremos el mismo botón.


imagen


El acceso directo Ctrl-E o el botón redondo a la izquierda comienza y detiene la grabación de lo que está sucediendo. Los resultados se muestran aquí. El navegador escribe muchas cosas, pero es mejor verlo una vez que leerlo muchas veces, así que tomemos algún tipo de animación y miremos. Que sea una simple animación CSS para empezar. Si lo abre en pantalla completa, puede ver que funciona con atascos notables:



Grabaremos unos segundos en modo de pantalla completa y veremos qué sucede allí:


imagen


El navegador registra todo lo que hace. En la parte superior de la ventana vemos un gráfico fps. Se puede usar fácilmente para detectar una anomalía si, en el proceso de trabajar con la página, comienza a ralentizarse bruscamente. Si hace clic en el gráfico con el mouse y lo tira hacia un lado o gira la rueda, puede seleccionar este rango de tiempo y la información detallada para ello se mostrará a continuación. En nuestro ejemplo simple, no hay anomalías, pero se ve claramente que no todo funciona de manera muy uniforme.


Preste atención de inmediato a la línea Marcos , que contiene información sobre el tiempo dedicado a cada marco. Puede notar que esta vez salta constantemente y supera significativamente los 16 ms (a continuación, en ejemplos prácticos, mejoraremos ligeramente esta animación).


A continuación, vemos varias líneas en las que la carga se muestra en diferentes colores: puede ver cuánto tiempo pasó el navegador en diferentes tipos de actividades. Nuestra animación es uniforme y se realizan las mismas operaciones para cada cuadro, indicadas en púrpura y verde. Si mueve el mouse sobre los bloques de colores, quedará claro que estamos tratando con los elementos que se mencionaron al principio: recalcular el estilo y actualizar el árbol de capas son morados, y las capas de pintura y compuestas son verdes.


Considera otra animación. Esta vez con scripts, un simple generador de ruido. Este es un ejemplo bastante ilustrativo, aunque no es de interés desde el punto de vista del diseño:



Puede notar que se han agregado bloques amarillos que muestran la ejecución de scripts. Si habrá muchas llamadas de función, entonces para cada llamada se agregará un bloque más; por su tamaño, es fácil encontrar la función "más pesada", con la cual, probablemente, debería comenzar la optimización.


imagen


En el ejemplo, el tiempo dedicado a un cuadro fluctúa alrededor de 80 ms. Pero lo que hay allí, incluso a simple vista, puedes ver claramente cómo se bloquea todo. Mirando la sección de resumen a continuación, vemos que las secuencias de comandos ocupan más tiempo. En comparación con ellos, el renderizado y la pintura parecen errores que se pueden descuidar. No siempre, por supuesto, sucede, pero con bastante frecuencia.


Si hace clic en el bloque marcado como llamada de función , a continuación hay un enlace a la función en el código del script. Si lo revisa, puede ver que en este ejemplo hay un ciclo a través de todos los píxeles en la pantalla. Sería más lógico hacer esa tarea en los sombreadores, luego el rendimiento sería mucho mejor. Pero lo veremos en ejemplos prácticos.


Qué hacer si ...


Aprendimos qué pasos hay cuando se muestra el estado actual de una página en un navegador, y dónde ver cuál toma más tiempo. Es hora de familiarizarse con las razones más comunes por las cuales un paso en particular comienza a requerir demasiados recursos y da un par de consejos sobre qué hacer en un caso particular.


Cálculo de estilo


Si ve que ya en este paso comienzan los problemas, lo más probable es que el punto no esté ni siquiera en la animación, sino en el hecho de que hay demasiados elementos en la página. En los sitios de diseño esto es bastante raro, por lo general, tal problema es un satélite de tablas grandes con miles de elementos, pero si aún se encuentra con esto:


Reduzca el número de elementos en la página, simplifique el diseño. Preste especial atención a la repetición de piezas de código con envoltorios, es probable que puedan eliminarse.

La segunda razón asociada con la primera son los selectores CSS complejos. Si en páginas pequeñas es bastante posible usar anidamiento profundo, trucos complicados con elementos vecinos, etc., en una página realmente grande, todo esto puede conducir a un bajo rendimiento.


Simplifique los selectores CSS, use BEM.

Creación de diseño


Este elemento ya está más cerca del diseño y las animaciones, aquí comienzan cosas interesantes. Lo primero que es importante entender es que se forma todo el diseño. Si cambiamos algo, se forma de nuevo. Por esta razón, incluso pequeños cambios en la página grande pueden causar retrasos notables en este paso.


La regla principal que nos guía al crear animaciones es no permitir la reestructuración del diseño a cualquier costo. Por lo tanto, generalmente no intentamos optimizarlo (y no hay oportunidades particulares), es decir, tratamos de evitarlo.


Hay muchas propiedades que pueden causar la reconstrucción del diseño, puede encontrar listas en Internet, por ejemplo, csstriggers.com no es malo. Más a menudo que otros en las animaciones puedes encontrar propiedades:


display position / top / left / right / bottom width / height padding / margin border font-size / font-weight / line-height ... 

Puede notar que todas estas propiedades están unidas por una cosa: describen las características geométricas de los elementos: parámetros de visualización, tamaño y ubicación física. Entonces, en lugar de memorizarlos a todos, recuerde a qué se refieren.


No cambie las propiedades geométricas de los elementos, es mejor usar transformación y opacidad.

Por separado, vale la pena señalar que cambiar el fondo del elemento también nos devolverá a este paso. Se olvidan constantemente de esto, por lo que destacamos en una recomendación por separado:


No cambie los elementos de fondo.

En algunos navegadores ( No voy a meter un dedo en Firefox ) puede aparecer un retraso típico de animaciones CSS con transformaciones, especialmente si se realiza más de una animación por unidad de tiempo. Exteriormente, esto puede parecer no solo como una pausa en su trabajo, sino también como "averías" de la animación desde el principio. Parece que el navegador está constantemente recalculando algo. Este comportamiento casi siempre se corrige utilizando la propiedad de visibilidad posterior .


Si es posible, agregue visibilidad posterior: oculta a los elementos animados.

Además, la reconstrucción del diseño es causada por nuestras llamadas a elementos desde scripts. Además, esto no tiene que ser un cambio directo a CSS, también puede ser atractivo para algunas propiedades y métodos de elementos. Los más comunes son:


 offset*** client*** inner*** scroll*** 

En las animaciones, debes tener cuidado con ellas, porque Si comenzamos a referirnos a estas propiedades y métodos para una gran cantidad de elementos, cada vez esto causará una reestructuración del diseño.


Evite referirse a las propiedades y métodos mencionados para elementos individuales en bucles.

Pintura y composición de capas.


Consideraremos estos dos pasos juntos, como están algo relacionados y, por lo general, si hay problemas con uno, lo estarán con el otro. Omitir estos pasos, evitarlos no funcionará, por lo que estamos tratando de optimizarlos de alguna manera.


El navegador no prepara la imagen en píxeles de la página, sino en partes: las capas. Puede haber muchos. Cada capa existe como si fuera en sí misma y no afecta al resto, lo que crea la base para algunos hacks CSS. Pero hablaremos de ellos en otro momento. Luego, la imagen final se recoge de estas capas. En el contexto de las animaciones, es muy útil colocar elementos animados en una capa separada para que sus cambios no afecten a todo. Es deseable que el contenido de los elementos sea pequeño. Podemos hacer esto usando la propiedad will-change o, como lo hicimos antes, transform: translateZ (0) . Lo único que debe recordar es que no puede aumentar el número de capas indefinidamente. En algún momento, esto jugará un truco y el rendimiento opuesto caerá. Entonces habrá dos consejos:


Utilice will-change o transform: translateZ (0) para mover los elementos animados a una capa separada.

pero al mismo tiempo


No te excedas con este negocio. Compruebe en las herramientas de desarrollador que no es peor.

Muy a menudo, los filtros causan problemas serios que de alguna manera transforman la imagen de los elementos. Pueden ser simples filtros CSS con opciones borrosas u confusas con SVG, pero el efecto será el mismo: una disminución notable en el rendimiento.


No use filtros complejos. Si aún necesita el efecto deseado, considere implementarlo en WebGL.

¿Cómo funcionan estos consejos?


Funcionan, pero no necesita esperar un milagro de ellos. En la red, los novatos a veces dicen: "Agregué un cambio de voluntad, pero nada ha cambiado". Por lo general, esto significa que el problema principal estaba en otro lugar, y esta técnica produjo un aumento tan pequeño en la productividad que pasó desapercibido. Por eso es importante usar las herramientas del desarrollador para comprender claramente dónde está el cuello de botella y no gastar tiempo y esfuerzo tratando de optimizar lo que funciona bien de todos modos.


De todo esto podemos concluir que no hay muchas formas de influir en la representación de una página, y el efecto de ellas no siempre será significativo. Estos trucos no son balas de plata, son más bien necesarios para pulir la animación. Si observamos sitios con un rendimiento realmente bajo, notaremos que en la gran mayoría de los casos nuestros propios guiones son los culpables, y no problemas misteriosos con el análisis de CSS en algún lugar de las entrañas del navegador.


Guiones ...


¿Sabes dónde crecen los problemas con las animaciones inhibitorias con mayor frecuencia (según mis observaciones)? Aquí de este enfoque de desarrollo:


imagen


Suena tonto, pero lo es. Constantemente hay soluciones, obviamente copiadas de algún lugar completamente sin entender lo que estaba sucediendo. Incluso sucede que puede eliminar la mitad del código y todo continuará funcionando. A menudo, el código en las respuestas a SO o Toaster no está destinado a su producción. Eso debería ser obvio. Muestra la idea, responde la pregunta, pero no es la opción final óptima para su tarea específica.


Si ya está copiando, al menos mire el código en busca de acciones innecesarias.

RequestAnimationFrame


A menudo hablan sobre este método y recomiendan usarlo en lugar de setTimeout / setInterval en las animaciones. Esto tiene sentido, ya que esos métodos tienden a estar fuera de sincronización con los marcos que el navegador vuelve a dibujar, lo que resulta en pequeños retrasos. Pero hay dos puntos.


En primer lugar, si se anima más de un elemento en la página y llamamos a requestAnimationFrame muchas veces, esto conducirá a un fuerte hundimiento de fps. En teoría, esto no debería ser, pero en la práctica, todo sucede así. Puede familiarizarse con las pruebas aquí .


Combine todas las devoluciones de llamada de animación en una sola solicitudAnimationFrame.

Es probable que el segundo punto esté relacionado con la situación en la que ya tenemos animación pesada, tal vez con el uso del lienzo, del cual no podemos deshacernos o no tenemos tiempo para rehacer, y sucede lo siguiente: digamos que la animación debe completarse en N segundos y ya usamos requestAnimationFrame . Pero se necesitan muchos recursos para calcular el estado actual y vemos esta imagen: la animación funciona sin problemas, bellamente, pero en 2N, o incluso 3N segundos. Como resultado, todo se percibe taaaaaan. Para corregir de alguna manera este comportamiento, puede ir en contra de todas las recomendaciones, usar setInterval / setTimeout y vincular los estados de los elementos animados al tiempo físico y no a cuadros abstractos. Como resultado, obtenemos una disminución formal en fps, pero con el efecto psicológico de las ganancias de productividad.


En el caso de animaciones extremadamente lentas, podría tener sentido rechazar requestAnimationFrame a favor de setInterval / setTimeout.

Lonas y sombreadores


Una parte importante de las animaciones en sitios no estándar está relacionada con el lienzo. Esto es comprensible, CSS es algo limitado, pero aquí podemos realizar las fantasías de cualquier diseñador. Pero debe tener en cuenta que el 2d-canvas habitual está lejos de ser la tecnología más productiva. Si comienzas a dibujar muchos elementos en él o trabajas con píxeles directamente, entonces te darás cuenta rápidamente de que fps se está hundiendo, o que de repente la pintura y la composición de capas comienzan a tomar indecentemente mucho tiempo. Este problema se puede ver claramente en el ejemplo:



Veamos qué hace el navegador (la última versión de Google Chrome en Linux):


imagen


Observe cuánto se ha expandido el paso de composición de capa . Parece un poco ilógico, porque solo hay un elemento, ¿qué se puede ensamblar allí durante tanto tiempo? Pero cuando se usa el lienzo 2D, este comportamiento no es infrecuente, y algo que tiene que ver con esto es muy problemático. Esta es una de las razones por las que solemos utilizar WebGL, no existen tales preguntas.


Si hay una opción entre el lienzo 2d y WebGL, elija el segundo. Esto dará una bonificación de rendimiento inicial en las mismas tareas.

¿Qué se suele asociar con WebGL? Con sombreadores. Y depurar sombreadores es un dolor de cabeza para cualquiera que trabaje con ellos. Y las herramientas de desarrollo aquí son prácticamente impotentes. Por lo general, si hay demasiados cálculos en los sombreadores, vemos en el resumen a continuación que la mayor parte del tiempo es "simple", que de hecho es la ejecución de nuestros sombreadores independientemente del navegador, y no podemos obtener ningún detalle útil.


Hay diferentes recomendaciones sobre qué funciones preferir sobre los sombreadores, porque supuestamente están mejor optimizadas. O que se deben evitar las operaciones de bloqueo. Todo esto es cierto, pero según mis observaciones, en la mayoría de los casos, los sombreadores que ralentizan demasiado el sitio son simplemente sombreadores muy grandes. Si escribió 100 líneas GLSL en un solo lugar, es casi seguro que funcionará mal. Y si también hay diferentes construcciones anidadas, bucles, entonces todo: la escritura se ha ido. Es difícil dar alguna recomendación aquí, a menos que:


Si durante el trabajo se dio cuenta de que todo es más complicado de lo que parecía inicialmente, y que habrá mucho código y se ralentizará, es mejor discutir esto con el diseñador y el cliente lo antes posible y pensar en lo que se puede cambiar.

A menudo, puede llegar a la conclusión de que un video preparado previamente funcionará mucho mejor que tratar de generar algún tipo de confusión en tiempo real. Recuerda esto Sí, todos quieren mostrarse, quieren presumir "pero puedo hacerlo así", pero no se olviden de los usuarios finales.


En relación con este pensamiento, recuerdo la "enfermedad" a la que la antigua Olimpiada es especialmente susceptible. Por alguna razón, se manifiesta fuertemente cuando se trabaja con lienzo. Por esta razón, siempre debe copiar cuidadosamente el código de esas personas. Intentan utilizar los algoritmos matemáticos "correctos", fórmulas físicas complejas, para calcular todos los movimientos de los elementos con gran precisión, incluso cuando es completamente inútil. Esto lleva a un aumento en la carga del procesador y al hecho de que para nuestros 10 ms condicionales no tiene tiempo para contar nada. En la práctica, a menudo puedes sobrevivir con fórmulas aproximadas y conocimientos escolares de física. No es necesario complicar las cosas, creamos sitios web, no software para misiles balísticos.


Usa algoritmos simples.

Hay otro truco llamado RayMarching . Algunas personas consideran que la creación de diferentes efectos es algo así como un desafío, un calentamiento para la mente y, a veces, los resultados son muy impresionantes. Por ejemplo, aquí se genera un mundo submarino completo (inserté un video, porque de los cálculos de esto en tiempo real el teléfono / computadora portátil puede colgarse):



El sombreador en sí se puede encontrar aquí .


En la práctica, todo esto requiere recursos increíbles para trabajar. En modo de pantalla completa, tenemos 400-800ms por cuadro (en general, en este ejemplo, puede llegar hasta 1500ms):


imagen


Entonces, si te sorprendiste pensando en hacer algo como esto en un sitio de combate, date un teclado en la cabeza, bebe té y piensa en opciones alternativas para implementar efectos.


No use RayMarching, esta es una forma segura de matar el rendimiento.

Ejemplo práctico


A menudo no hay suficientes ejemplos en artículos sobre productividad, pero puede ser difícil expresar una palabra. Así que considera una pareja. ¿Recuerdas el primer ejemplo de túnel giratorio CSS? El navegador hizo muchas cosas:


imagen


Queremos acelerar un poco las cosas. Por donde empezar Vemos bloques morados, lo que significa que el navegador está reconstruyendo constantemente el diseño. No hay scripts allí, pero hay animaciones CSS en las que algo cambia. Veamos su código:


 @keyframes rotate { from { transform: rotate(0); } to { transform: rotate(360deg); } } @keyframes move-block { from { transform: translateX(0); background: @color1; } to { transform: translateX(-@block-size * 6); background: @color2; } } 

Las transformaciones no nos asustan, pero vemos un cambio en el fondo de los elementos. Recordamos que esto puede causar una reestructuración del diseño, y creemos que se puede hacer en esta situación ...


El cambio del fondo debe eliminarse a toda costa, por lo que, según la idea general de animación, decidimos que puede colocar un degradado radial en la parte superior, lo que creará casi el mismo efecto de volumen. Alguien dirá que los gradientes tienen un efecto negativo en el rendimiento, pero no vamos a cambiarlo. Que sea mejor una vez que afecte gravemente que tendremos una montaña entera de elementos que afectan constantemente. El resultado es:



Veamos qué hace el navegador:


imagen


Wow ... En lugar de un montón de acciones, vemos llamadas raras a la GPU y nada más, mientras que la animación en sí comenzó a funcionar notablemente más suave.


Otro ejemplo


Recuerde cómo se veía el navegador en el generador de ruido:


imagen


El problema definitivamente está en los guiones. Se puede ver que el bloque "render" es el más grande. Esta es nuestra función principal para renderizar la imagen. Miremosla:


 function render() { let imageData = CTX.createImageData(CTX.canvas.width, CTX.canvas.height); for (let i = 0; i < imageData.data.length; i += 4) { const color = getRandom(); imageData.data[i] = color; imageData.data[i + 1] = color; imageData.data[i + 2] = color; imageData.data[i + 3] = 255; } CTX.putImageData(imageData, 0, 0); requestAnimationFrame(render); } 

Definitivamente hay trabajo en curso con píxeles individuales. Esto no es muy saludable. Dijimos que, si es posible, es mejor usar no 2d-canvas, sino WebGL, y esta tarea solo quiere ser paralela usando un sombreador. Hagámoslo:



¿Cuál será el resultado? Véalo usted mismo:


imagen


El tiempo para un cuadro disminuyó a casi 16 ms. Por supuesto, esto no es ideal, pero aún mejor que 80ms. En animaciones hermosas y complejas, tal aumento de rendimiento puede ser muy notable. Aprovecho esta oportunidad para recomendar que los principiantes se familiaricen con la introducción de sombreadores en la programación y con la continuación con ejemplos .


Conclusión


En este artículo, descubrimos cuándo se trata de optimizar el rendimiento de las animaciones, cómo usar las herramientas de desarrollador en Chrome en este contexto y qué buscar primero. Espero que esta información sea útil para los desarrolladores que se enfrentan a tales tareas por primera vez y no saben por dónde empezar.

Source: https://habr.com/ru/post/450484/


All Articles