Animaciones: la Vía Angular | Programar Plus

AngularJS es un jugador importante en el mundo del marco JavaScript MVW. ‘Pensar en Angular’ es algo que puede eludir a los desarrolladores que provienen de jQuery u otras bibliotecas pesadas de manipulación DOM. Existe una ‘manera angular’ de hacer las cosas que se basa en datos en lugar de usar DOM transversal para impulsar los cambios de vista, y eso puede ser difícil de visualizar cuando se trata de algo como animaciones. Juntos, veremos exactamente cómo animar con las herramientas proporcionadas por el equipo de Angular.

ngAnimate y el $animate Servicio

El equipo central de Angular nos dio la ngAnimate módulo para que podamos dar a nuestras aplicaciones una forma de animar a partir de una ‘forma Angular’ basada en datos, y para que podamos conectarnos a los eventos que Angular emite a través de algunas de sus directivas integradas.

Angular, a diferencia de jQuery, se enfoca en vincular nuestra vista a un objeto JavaScript mediante el uso de controladores. Este enfoque nos permite vincular valores de vista como campos de entrada directamente a un valor correspondiente en un objeto de JavaScript y desencadenar cambios de vista a través de cambios de datos, o viceversa.

Entonces, ¿cómo conectamos las animaciones con estos eventos si pudieran ocurrir desde la vista o desde el objeto correspondiente que se está cambiando?

Primero, debemos agregar ngAnimate a nuestro proyecto.

Incluyendo Animación Angular en nuestro Proyecto

A partir de 1.2.0, las animaciones ya no forman parte del núcleo de Angular, sino que se encuentran en su propio módulo separado: ngAnimate. Para usar el $animate servicio, necesitamos incluir la biblioteca de animación después de Angular en nuestro archivo HTML, así: js/lib/angular-animate.js

Como alternativa, también puede utilizar el CDN o bower instalar angular-animate:

$ bower install --save angular-animate

Independientemente de cómo elija instalarlo, asegúrese de incluirlo en su archivo fuente, así:

<script src="https://css-tricks.com/animations-the-angular-way/js/lib/angular.js"></script>
<script src="js/lib/angular-animate.js"></script>

A continuación, tendremos que incluir el ngAnimate módulo como una dependencia a nuestra aplicación. Esto se puede hacer cuando creamos una instancia de nuestra aplicación Angular, así:

angular.module('myApp', ['ngAnimate']);

Ahora eso ngAnimate está incluido en nuestro proyecto (y siempre que no tengamos un error de inyector o algo así en nuestra consola) ¡podemos comenzar a crear animaciones con Angular!

Transiciones CSS3

La forma más fácil de incluir animaciones en cualquier aplicación es mediante transiciones CSS3. Esto se debe a que están completamente basados ​​en clases, lo que significa que la animación se define en una clase y, siempre que usemos esa clase en nuestro HTML, la animación funcionará en el navegador.

Las transiciones CSS son animaciones que permiten que un elemento HTML cambie constantemente de un estilo a otro. Para definir una transición, debemos especificar el elemento al que queremos agregar un efecto y la duración de dicho efecto.

Primero, echemos un vistazo a un ejemplo simple de una transición CSS3, y luego podemos ver cómo hacer uso de este conocimiento desde una aplicación Angular basada en datos.

Vamos a crear un sencillo div dentro de un contenedor div y aplicarle dos clases: una para el estilo básico y otra para nuestra transición.

<div class="container">
  <div class="box rotate"></div>
</div>

Ahora podemos agregar transiciones para el estado de desplazamiento o el estado estático del elemento:

.box {
  margin: 50px auto;
  background: #5FCF80;
  width: 150px;
  height: 150px;
}
.box:hover {
  transform: rotate(360deg);
  background: #9351A6;
  border-radius: 50%;
}
.rotate {
  transition: all 0.5s ease-in-out;
}
.rotate:hover {
  transition: all 1s ease-in-out;
}

Esto aplica dos estados a nuestro div: un estado normal y otro para cuando pasemos el cursor sobre el div. Las transiciones definidas en el .rotate y .rotate:hover Las clases le dicen al navegador cómo hacer la transición entre estos dos estados cuando activamos el hover y mouseleave eventos.

Terminamos con un efecto como este:

Transición básica de CSS3

Animación CSS3 basada en datos angulares

Ahora veamos cómo podríamos hacer algo así en una aplicación Angular y vincular esta misma funcionalidad a algunos datos dentro de nuestra aplicación.

En lugar de hacer esta transición en :hover, podemos crear una animación simple vinculando transiciones a una clase, .rotatey cree una clase para los estados de “caja” y “círculo” del div. Esto nos permite cambiar entre clases usando el ng-class Directiva integrada en Angular.

.box {
  margin: 20px auto;
  background: #5FCF80;
  width: 150px;
  height: 150px;
}
.circle {
  transform: rotate(360deg);
  background: #9351A6;
  border-radius: 50%;
  margin: 20px auto;
  width: 150px;
  height: 150px;
}
.rotate {
  transition: all 1s ease-in-out;
}

Para hacer esto, necesitaremos configurar nuestra aplicación Angular y crear una declaración condicional en el ng-class directiva para cambiar la clase en función del valor de un booleano en el $scope.

<div ng-app="myApp" ng-controller="MainCtrl">
  <div class="container">
    <input type="checkbox" ng-model="boxClass" />
    <div class="box rotate" ng-class="{'box': boxClass, 'circle': !boxClass} "></div>
  </div>
</div>

Ahora configuremos nuestro JavaScript:

angular.module('myApp', [])
.controller('MainCtrl', function($scope) {
  $scope.boxClass = true;
});

Aquí, vinculamos el valor booleano que se adjunta a $scope.boxClass a si el elemento debe o no tener la .box o .circleclase. Si el valor booleano es verdadero, entonces el elemento tendrá el .boxclase. Si es falsa, tendrá la .circleclase. Esto nos permite activar una transición CSS3 cambiando el valor de nuestros datos, sin manipulación alguna del DOM.

Esto no utiliza el $animate servicio, pero quería proporcionar un ejemplo de una instancia en la que podría usar CSS3 solo y no tener que depender de $animate y ngAnimate.

El resultado de esto es una animación que se desencadena estrictamente por un cambio de datos cuando cambiamos el booleano subyacente al hacer clic en la casilla de verificación.

Transición de CSS3 basada en datos angulares

Transiciones con $animate

Si queremos aprovechar las transiciones de CSS3 y el $animate servicio, entonces necesitamos saber un par de cosas sobre cómo $animate trabaja detrás de escena.

El $animate El servicio admite varias directivas integradas en Angular. Esto está disponible sin ninguna otra configuración y nos permite crear animaciones para nuestras directivas en CSS simple. Para usar animaciones de esta manera, ni siquiera necesita incluir $animate en su controlador; solo incluye ngAnimatecomo una dependencia de su módulo Angular.

Una vez que incluya ngAnimate en su módulo, hay un cambio en la forma en que Angular maneja ciertas directivas integradas. Angular comenzará a conectarse y monitorear estas directivas, y agregará clases especiales al elemento en la activación de ciertos eventos. Por ejemplo, cuando agrega, mueve o elimina un elemento de una matriz que está siendo utilizada por el ngRepeatdirectiva, Angular ahora detectará ese evento y agregará una serie de clases a ese elemento en el ngRepeat.

Aquí puedes ver las clases que ngAnimate agrega en el evento enter de un ngRepeat:

ngRepeat Clases de eventos

Las clases CSS adjuntas toman la forma de ng-{EVENT} y ng-{EVENT}-active para eventos estructurales como entrar, moverse o salir. Pero, para animaciones basadas en clases, toma la forma de {CLASS}-add, {CLASS}-add-active, {CLASS}-remove, y {CLASS}-remove-active. Las excepciones a estas reglas son ng-hidey ng-show. Ambas directivas tienen eventos de agregar y quitar que se activan, al igual que ng-class, pero ambos comparten la .ng-hide class, que se agrega o elimina cuando corresponde. también verás ngAnimate Agrega un .ng-animate clase a algunas de estas directivas sobre animación.

A continuación se muestra una tabla que ilustra algunas de las directivas integradas, los eventos que se activan y las clases que se agregan temporalmente cuando agrega ngAnimate a su proyecto:

Directivas incorporadas $animate Eventos

Directiva Eventos) Clases
ngRepetir ingresar ng-enter, ng-enter-activo
salir ng-permiso, ng-permiso-activo
Muevete ng-movimiento, ng-movimiento-activo
ngView, ngInclude, ngSwitch, ngIf ingresar ng-enter, ng-enter-activo
salir ng-permiso, ng-permiso-activo
Clase ng agregar ng-añadir, ng-añadir-activo
retirar ng-eliminar, ng-eliminar-activo
ngMostrar, ngOcultar agregar eliminar ng-ocultar

Angular detectará automáticamente que CSS está adjunto a una animación cuando se activa la animación y agregará el .ng-{EVENT}-active clase hasta que la animación haya seguido su curso. Luego eliminará esa clase y cualquier otra clase agregada del DOM.

A continuación se muestra un ejemplo del uso de transiciones CSS3 para animar un ngRepeat directiva. En él, adjuntamos una transición a la clase base:.fade en este caso, y luego aprovechar las clases que ngAnimate agregará a la li elementos cuando se agregan y eliminan de la matriz. Una vez más, esto nos permite tener animaciones basadas en datos, al estilo Angular.

ngRepeat $animate Transiciones CSS3 impulsadas

Como podemos ver, Angular ngAnimate nos brinda la capacidad de aprovechar fácilmente los eventos y aprovechar el poder de las transiciones CSS3 para hacer algunas animaciones naturales realmente geniales en nuestras directivas. Esta es, con mucho, la forma más fácil en que podemos hacer animaciones para nuestras aplicaciones Angular, pero ahora veremos algunas opciones más complejas.

Animaciones CSS3

Las animaciones CSS3 son más complicadas que las transiciones, pero tienen mucho de la misma implementación en el ngAnimate lado. Sin embargo, en el CSS usaremos un @keyframes regla para definir nuestra animación. Esto se hace de la misma manera que hicimos nuestra transición básica anteriormente, excepto que usamos el animation palabra clave en nuestro CSS y dar a la animación un nombre como este:

@keyframes appear {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
@keyframes disappear {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}

Aquí hemos creado un appear y disappear animación que se puede activar a través de CSS en el lugar donde estaba nuestra transición antes.

.fade.ng-enter {
  animation: 2s appear;
}

.fade.ng-leave {
  animation: 1s disappear;
}

La diferencia esta vez, como puedes ver arriba, es que ya no tenemos que usar .ng-enter-active o .ng-leave-active, sino que podemos adjuntar la animación a .ng-leave y .ng-active y la animación se disparará en los momentos apropiados debido a ngAnimate. Esta no es una forma particularmente mejor de hacerlo que nuestro método de transición anterior, pero ilustra cómo usar animaciones CSS3, que pueden ser MUCHO más poderosas que este simple efecto.

El resultado final de esta animación cuando se aplica a nuestra lista de compras anterior ngRepeat ejemplo se verá algo como esto:

ngRepeat $animate Animaciones CSS3 potenciadas

Animaciones JavaScript

Ahora para el elefante en la habitación: Animaciones JavaScript con AngularJS.

Angular está completamente basado en datos con su elegante enlace de datos bidireccional, es decir, hasta que no lo es. Esta es una de las cosas más confusas de pasar de jQuery a Angular. Se nos dice que volvamos a aprender cómo pensamos y que descartemos la manipulación del DOM en favor de los enlaces, pero luego, en algún momento, nos lo devuelven más tarde. Bueno, bienvenido a ese punto de círculo completo.

La animación de JavaScript tiene una gran ventaja: JavaScript está en todas partes y tiene una aceptación más amplia que algunas animaciones CSS3 avanzadas. Ahora, si solo está apuntando a navegadores modernos, entonces esto probablemente no será un problema para usted, pero si necesita admitir navegadores que no admiten transiciones CSS, entonces puede registrar fácilmente una animación JavaScript con Angular y usarla otra vez. y más en sus directivas. Básicamente, JavaScript es más compatible con los navegadores más antiguos y, por lo tanto, también lo son las animaciones de JavaScript.

cuando incluyes ngAnimate como una dependencia de su módulo Angular, agrega el animation método a la API del módulo. Lo que esto significa es que ahora puede usarlo para registrar sus animaciones de JavaScript y aprovechar los ganchos angulares en directivas integradas como ngRepeat. Este método toma dos argumentos: className(string) y animationFunction(function).

El className el parámetro es simplemente la clase a la que se dirige, y la función de animación puede ser una función anónima que recibirá tanto el element y done parámetros cuando se llama. El element el parámetro es solo eso, el elemento como un objeto jqLite, y el done El parámetro es una función a la que debe llamar cuando su animación termine de ejecutarse para que angular pueda continuar en su camino y sepa que desencadenar que el evento se ha completado.

Sin embargo, lo principal a comprender aquí es lo que debe devolverse de la función de animación. Angular buscará que se devuelva un objeto con claves que coincidan con los nombres de los eventos en los que desea activar animaciones para esa directiva en particular. Si no está seguro de lo que admite la directiva, simplemente consulte mi tabla anterior.

Así que para nuestro ngRepeat ejemplo, se vería algo como esto:

return {
  enter: function(element, done) {
    // Animation code goes here
    // Use done() in your animation callback
  },
  move: function(element, done) {
    // Animation code goes here
    // Use done() in your animation callback
  },
  leave: function(element, done) {
    // Animation code goes here
    // Use done() in your animation callback
  }
}

Y si unimos todo esto con el mismo viejo aburrido (lo siento) ngRepeat ejemplo de lista de compras y use jQuery para las animaciones reales:

var app = angular.module('myApp', ['ngAnimate'])
.animation('.fade', function() {
  return {
    enter: function(element, done) {
      element.css('display', 'none');
      $(element).fadeIn(1000, function() {
        done();
      });
    },
    leave: function(element, done) {
      $(element).fadeOut(1000, function() {
        done();
      });
    },
    move: function(element, done) {
      element.css('display', 'none');
      $(element).slideDown(500, function() {
        done();
      });
    }
  }
})

Ahora, permítanme desglosar lo que está pasando.

Podemos deshacernos de cualquier CSS que teníamos previamente en el .fade clase, pero aún necesitamos algún tipo de clase para registrar la animación. Entonces, por el bien de la continuidad, solo usé el viejo y bueno .fade clase.

Básicamente, lo que sucede aquí es que Angular registrará sus funciones de animación y las llamará en ese elemento específico cuando ese evento tenga lugar en esa directiva. Por ejemplo, llamará a su enter función de animación cuando un nuevo artículo entra en un ngRepeat.

Todas estas son animaciones jQuery muy básicas y no entraré en ellas aquí, pero vale la pena señalar que ngRepeat agregará automáticamente el nuevo elemento al DOM cuando se agregue a la matriz, y dicho elemento será inmediatamente visible. Por lo tanto, si está tratando de lograr un efecto de atenuación con JavaScript, debe configurar la visualización en ninguno inmediatamente antes de atenuarla. Esto es algo que podría evitar con las animaciones y transiciones CSS.

Unámoslo todo y veamos qué obtenemos:

ngRepeat $animate Animaciones de JavaScript con tecnología

Conclusión

El ngAnimate módulo es un poco de un nombre engañoso.

Por supuesto, no podría encontrar un nombre mejor si lo intentara, pero en realidad no HACE ninguna animación. Más bien, le brinda acceso al ciclo de eventos de Angular para que pueda realizar su propia manipulación DOM o animaciones CSS3 en el punto adecuado basado en datos. Esto es poderoso por derecho propio porque lo estamos haciendo ‘a la manera angular’ en lugar de tratar de forzar nuestra propia lógica y tiempo en un marco muy particular.

Otro beneficio de hacer tus animaciones con ngAnimate es que una vez que escribe sus animaciones para esa directiva, se pueden empaquetar muy bien y pasar a otros proyectos con relativa facilidad. Esto, en mi libro, siempre es algo bueno.