Animaciones angulares explicadas con ejemplos

¿Por qué utilizar animaciones?

Los componentes web modernos utilizan animaciones con frecuencia. Las hojas de estilo en cascada (CSS) brindan a los desarrolladores las herramientas para crear animaciones impresionantes. Las transiciones de propiedades, las animaciones con nombres únicos y los fotogramas clave de varias partes son posibles con CSS. Las posibilidades animables son infinitas gracias a CSS.

En una aplicación web moderna, la animación centra la atención del usuario. Las buenas animaciones buscan guiar la atención del usuario de una manera satisfactoria y productiva. Las animaciones no deberían resultar molestas para el usuario.

Las animaciones ofrecen retroalimentación en forma de movimiento. Muestran al usuario que la aplicación está manejando activamente sus solicitudes. Algo tan simple como presionar un botón visible o un cargador cuando la aplicación debe cargar atrae la atención del usuario.

Las animaciones continúan siendo cada vez más relevantes en el caso de Angular. Google desarrolla Angular mientras promueve la filosofía de Material Design. Fomenta las interfaces de usuario (UI) concisas complementadas con comentarios animados de los usuarios. Hace que las aplicaciones web se sientan algo vivas y divertidas de usar.

La comunidad Angular desarrolla una biblioteca de widgets central llamada Material2. Este proyecto agrega una variedad de módulos de widgets a Angular. La mayoría de ellos cuentan con animaciones. Para comprender cómo funcionan, este artículo recomienda estudiar las animaciones CSS antes de seguir leyendo.

Las animaciones angulares son la versión optimizada del marco de lo que CSS proporciona de forma nativa. CSS es la tecnología central para las animaciones angulares que se producen dentro del navegador web. Sin embargo, CSS está más allá del alcance de este artículo. Es hora de abordar las animaciones angulares de frente.

Configurar animaciones

Antes de animar, BrowserAnimationsModuledebe incluirse en la matriz de importaciones del módulo raíz. Está disponible en @angular/platform-browser/animations. Este NgModule asegura que las animaciones funcionen para la plataforma dada. Este artículo asume el navegador web estándar para cada ejemplo.

Las animaciones angulares se declaran dentro de los @Componentmetadatos. @Componentdecora una clase para distinguirla como un componente de Angular. Sus metadatos contienen configuraciones de componentes, incluido el animations: []campo. Cada elemento de la matriz de este campo representa un activador de animación ( AnimationTriggerMetadata).

Las animaciones son exclusivas de su componente anfitrión a través de los metadatos del decorador. Las animaciones solo se pueden utilizar en la plantilla del componente anfitrión. Las animaciones no se heredan de los hijos del componente. Hay una solución fácil para esto.

Siempre puede crear un archivo separado que exporte una matriz. Cualquier clase de componente puede importar esa matriz desde la parte superior de su archivo de host. El token de matriz importado pasa a los metadatos de animaciones del componente. Repita este proceso para cualquier otro componente que requiera la misma matriz en sus metadatos de animaciones.

La proyección de contenido le permite aplicar animaciones al DOM (Modelo de objeto de documento) del contenido del componente A. El componente B que envuelve este contenido DOM puede proyectar el contenido en su propia plantilla. Una vez que lo hace, las animaciones del componente A no se niegan. El componente B incorpora las animaciones de A a través de la proyección de contenido.

OKAY. Sabes cómo configurar animaciones y dónde declararlas. La implementación es el siguiente paso.

Métodos de animación

Las animaciones angulares utilizan una serie de llamadas a métodos que se pueden importar @angular/animations. Cada elemento de la @Componentmatriz de animaciones comienza como un método único. Sus argumentos se desenredan como una secuencia de llamadas a métodos de orden superior. La siguiente lista muestra algunos de los métodos utilizados para crear animaciones angulares.

  • trigger(selector: string, AnimationMetadata[])

devoluciones AnimationTriggerMetadata

  • state(data: string, AnimationStyleMetadata, options?: object)

devoluciones AnimationStateMetadata

  • style(CSSKeyValues: object)

devoluciones AnimationStyleMetadata

  • animate(timing: string|number, AnimationStyleMetadata|KeyframesMetadata)

devoluciones AnimationAnimateMetadata

  • transition(stateChange: string, AnimationMetadata|AnimationMetadata[], options?: object)

devoluciones AnimationTransitionMetadata

Si bien ciertamente hay más métodos para elegir, estos cinco métodos manejan los conceptos básicos. Tratar de comprender estos métodos básicos como una lista no ayuda mucho. Las explicaciones paso a paso seguidas de un ejemplo le darán más sentido.

activador (selector: cadena, AnimationMetadata [])

El trigger(...)método encapsula un solo elemento de animación dentro de la matriz de animaciones.

El primer argumento del método selector: stringcoincide con el [@selector]atributo de miembro. Actúa como una directiva de atributo en la plantilla del componente. Básicamente, conecta el elemento de animación a la plantilla a través de un selector de atributos.

El segundo argumento es una matriz que contiene una lista de métodos de animación aplicables. La trigger(...)mantiene en conjunto en una sola matriz.

estado (datos: cadena, AnimationStyleMetadata, opciones ?: objeto)

El state(...)método define el estado final de la animación. Aplica una lista de propiedades CSS al elemento de destino después de que concluye una animación. Esto es para que el CSS del elemento animado coincida con la resolución de la animación.

El primer argumento coincide con el valor de los datos vinculados al enlace de animación. Es decir, el valor vinculado [@selector]en la plantilla coincide con el primer argumento de a state(...). El valor de los datos determina el estado final. El cambio del valor determina los medios de animación (ver transition(...)).

El segundo argumento aloja los estilos CSS que se aplican a un elemento después de la animación. Los estilos se pasan invocando style(...)y pasando a su argumento los estilos deseados como un objeto.

Una lista de opciones ocupa opcionalmente el tercer argumento. Las state(...)opciones predeterminadas deben permanecer sin cambios a menos que se indique lo contrario.

estilo (CSSKeyValues: objeto)

Es posible que lo hayas notado AnimationStyleMetadatavarias veces en la lista anterior. El style(...)componente devuelve este tipo exacto de metadatos. Dondequiera que se apliquen los estilos CSS, el style(...)método debe invocar. Un objeto que contiene estilos CSS sustituye a su argumento.

Por supuesto, los estilos animables en CSS se transfieren al style(...)método Angular . Por supuesto, nada imposible para CSS se vuelve posible de repente con las animaciones angulares.

animar (tiempo: cadena | número, AnimationStyleMetadata | AnimationKeyframesMetadata)

La animate(...)función acepta una expresión de tiempo como primer argumento. Este argumento cronometra, acelera y / o retrasa la animación del método. Este argumento acepta una expresión numérica o de cadena. El formato se explica aquí.

El segundo argumento de animate(...)es la propiedad CSS que garantiza la animación. Esto toma la forma del style(...)método que regresa AnimationStyleMetadata. Piense animate(...)en el método que inicia la animación.

También se puede aplicar una serie de fotogramas clave al segundo argumento. Los fotogramas clave son una opción más avanzada que este artículo explica más adelante. Los fotogramas clave distinguen varias secciones de la animación.

animate(...)puede que no reciba un segundo argumento. En ese caso, el tiempo de animación del método solo se aplica al CSS reflejado en los state(...)métodos. Se animarán los cambios de propiedad en los state(...)métodos del disparador .

transición (changExpr: cadena, AnimationMetadata | AnimationMetadata [], opciones ?: objeto)

animate(...)inicia una animación mientras transition(...)determina qué animación se inicia.

The first argument consists of a unique form of micro-syntax. It denotes a change in state (or change in data) taking place. The data bound to the template animation binding ([selector]="value") determines this expression. The upcoming section titled “Animation State” explains this concept a bit further.

The second argument of transition(...) comprises AnimationMetadata (returned by animate(...)). The argument accepts either an array of AnimationMetadata or a single instance.

The first argument’s value matches against the value of the data bound in the template ([selector]="value") . If a perfect match occurs, the argument evaluates successfully. The second argument then initiates an animation in response to the success of the first.

A list of options optionally occupies the third argument. The default transition(...) options should remain unchanged unless reasoned otherwise.

Animation Example

import { Component, OnInit } from '@angular/core'; import { trigger, state, style, animate, transition } from '@angular/animations'; @Component({ selector: 'app-example', template: ` 

Click the button to change its color!

Toggle Me! // animation binding `, animations: [ // metadata array trigger('toggleClick', [ // trigger block state('true', style({ // final CSS following animation backgroundColor: 'green' })), state('false', style({ backgroundColor: 'red' })), transition('true => false', animate('1000ms linear')), // animation timing transition('false => true', animate('1000ms linear')) ]) ] // end of trigger block }) export class ExampleComponent { isGreen: string = 'true'; toggleIsCorrect() { this.isGreen = this.isGreen === 'true' ? 'false' : 'true'; // change in data-bound value } }

The above example performs a very simple color swap with each button click. Of course, the color transitions quickly in a linear fade as per animate('1000ms linear'). The animation binds to the button by matching the first argument of trigger(...) to the [@toggleClick] animation binding.

The binding binds to the value of isGreen from the component class. This value determines the resulting color as set by the two style(...) methods inside the trigger(...) block. The animation binding is one-way so that changes to isGreen in the component class notify the template binding. That is, the animation binding [@toggleClick].

The button element in the template also has a click event bound to it. Clicking the button causes isGreen to toggle values. This changes the component class data. The animation binding picks up on this and invokes its matching trigger(...) method. The trigger(...) lies within the animations array of the component’s metadata. Two things occur upon the trigger’s invocation.

The first occurrence concerns the two state(...) methods. The new value of isGreen matches against a state(...) method’s first argument. Once it matches, the CSS styles of style(...) apply to the final state of the animation binding’s host element. `The final state takes effect following all animation.

Now for the second occurrence. The data change that invoked the animation binding compares across the two transition(...) methods. One of them matches the change in data to their first argument. The first button click caused isGreen to go from ‘true’ to ‘false’ (‘true => false’). That means the first transition(...) method activates its second argument.

The animate(...) function corresponding the successfully evaluated transition(...) method initiates. This method sets the duration of the animated color fade along with the fade’s pacing. The animation executes and the button fades to red.

This process can happen any number of times following a button click. The backgroundColor of the button will cycle between green and red in a linear fade.

Animation State

The transition(...) micro-syntax is worth addressing in detail. Angular determines animations and their timing by evaluating this syntax. There exists the following state transitions. They model a changes in data bound to an animation binding.

  • ‘someValue’ => ‘anotherValue’

An animation trigger where the bound data changes from ‘someValue’ to ‘anotherValue’.

  • ‘anotherValue’ => ‘someValue’

An animation trigger where the bound data changes from ‘anotherValue’ to ‘someValue’.

  • ‘someValue’ ‘anotherValue’

Data changes from ‘someValue` to ‘anotherValue’ or vice versa.

There also exists void and * states. void indicates that the component is either entering or leaving the DOM. This is perfect for entry and exit animations.

  • ‘someValue’ => void : host component of bound data is leaving the DOM
  • void => ‘someValue’ : host component of bound data is entering the DOM

* denotes a wildcard state. Wildcard states can interpret to “any state”. This includes void plus any other change to the bound data.

Keyframes

This article touched on the basics for animating Angular applications. Advanced animation techniques exist alongside these basics. Grouping together keyframes is one such technique. Its inspired from the @keyframes CSS rule. If you have worked with CSS @keyframes, you already understand how keyframes in Angular work. It becomes just a matter of syntax

The keyframes(...) method imports from @angular/animations. It passes into the second argument of animate(...) instead of the typical AnimationStyleMetadata. The keyframes(...) method accepts one argument as an array of AnimationStyleMetadata. This can also be referred to as an array of style(...) methods.

Each keyframe of the animation goes inside the keyframes(...) array. These keyframe elements are style(...) methods supporting the offset property. offset indicates a point in the animation’s duration where its accompanying style properties should apply. Its value spans from 0 (animation start) to 1 (animation end).

import { Component } from '@angular/core'; import { trigger, state, style, animate, transition, keyframes } from '@angular/animations'; @Component({ selector: 'app-example', styles: [ `.ball { position: relative; background-color: black; border-radius: 50%; top: 200px; height: 25px; width: 25px; }` ], template: ` 

Arcing Ball Animation

Arc the Ball! `, animations: [ trigger('animateArc', [ state('true', style({ left: '400px', top: '200px' })), state('false', style({ left: '0', top: '200px' })), transition('false => true', animate('1000ms linear', keyframes([ style({ left: '0', top: '200px', offset: 0 }), style({ left: '200px', top: '100px', offset: 0.50 }), style({ left: '400px', top: '200px', offset: 1 }) ]))), transition('true => false', animate('1000ms linear', keyframes([ style({ left: '400px', top: '200px', offset: 0 }), style({ left: '200px', top: '100px', offset: 0.50 }), style({ left: '0', top: '200px', offset: 1 }) ]))) ]) ] }) export class ExampleComponent { arc: string = 'false'; toggleBounce(){ this.arc = this.arc === 'false' ? 'true' : 'false'; } }

The main difference of the above example compared to the other example is the second argument of animate(...). It now contains a keyframes(...) method hosting an array of animation keyframes. While the animation itself is also different, the technique to animate is similar.

Clicking the button causes the button to arc across the screen. The arc moves as per the keyframes(...) method’s array elements (keyframes). At the animation’s mid-point (offset: 0.50), the ball changes trajectory. It descends to its original height as it continues across the screen. Clicking the button again reverses the animation.

left and top are animatable properties after setting position: relative; for the element. The transform property can perform similar movement-based animations. transform is an expansive yet fully animatable property.

Any number of keyframes can existing between offset 0 and 1. Intricate animation sequences take the form of keyframes. They are one of many advanced techniques in Angular animations.

Animations With Host Binding

You will undoubtedly come across the situation where you want to attach an animation to the HTML element of a component itself, instead of an element in the component’s template. This requires a little more effort since you can’t just go into the template HTML and attach the animation there. Instead, you’ll have to import HostBinding and utilize that.

The minimal code for this scenario is shown below. I’ll re-use the same animation condition for the code above for consistency and I don’t show any of the actual animation code since you can easily find that above.

import { Component, HostBinding } from '@angular/core'; @Component({ ... }) export class ExampleComponent { @HostBinding('@animateArc') get arcAnimation() { return this.arc; } }

The idea behind animating the host component is pretty much the same as animating a element from the template with the only difference being your lack of access to the element you are animating. You still have to pass the name of the animation (@animateArc) when declaring the HostBinding and you still have to return the current state of the animation (this.arc). The name of the function doesn’t actual matter, so arcAnimation could have been changed to anything, as long as it doesn’t clash with existing property names on the component, and it would work perfectly fine.

Conclusion

This covers the basics of animating with Angular. Angular makes setting up animations very easy using the Angular CLI. Getting started with your first animation only requires a single component class. Remember, animations scope to the component’s template. Export your transitions array from a separate file if you plan to use it across multiple components.

Every animation utility/method exports from @angular/animations. They all work together to provide a robust system of animation inspired from CSS. There are more methods beyond what this article could cover.

Now that you know the basics, feel free to explore the links below for more on Angular animations.

More info on Angular Animations:

  • Angular Documentation
  • How to use animation with Angular 6