Animaciones y transiciones con AngularJS

danii . martes 14 de mayo de 2013. a las 08:59

Animaciones y transciones con Angularjs

A partir de la versión 1.1.4 del framework (descargar aquí del Google CDN), AngularJS viene con soporte nativo para animaciones y transiciones gracias a la nueva directiva ng-animate. Os recomendamos encarecidamente echarle un vistazo al fantástico artículo en el blog yearofmoo que nos explica cómo utilizar esta nueva y genial feature de AngularJS y nos proporciona un buen número de ejemplos.

Puesto que esta directiva es una novedad de esta versión, debemos consultar la documentación de ngAnimate en los docs de la versión 1.1.4 ya que en versiones anteriores no vamos a encontrar referencias a las animaciones.

Demo: Creación y edición de Slides con animaciones y transiciones

Para demostrar estas animaciones hemos preparado una pequeña aplicación de creación y edición de Slides en formato markdown en AngularJS, con transiciones de entrada y salida que además son dinámicas y dependientes del estado de la aplicación. Es decir, las animaciones de entrada y salida de las slides serán con dirección izquierda-derecha cuando nos movamos a una slide posterior, o derecha->izquierda si nos movemos a una slide posterior 😉


Demo app: AngularJS con animaciones

ver demo

ng directives

Las animaciones en AngularJS se declaran mediante la directiva ngAnimate y pueden asignarse a uno o varios eventos de animación. AngularJs actualmente soporta animaciones asignadas a los siguientes eventos:

  • hide: cuando un elemento del DOM pasa de visible a hidden,
  • show: cuando un elemento del DOM pasa de hidden a visible,
  • enter: cuando un elemento nuevo se añade al DOM,
  • leave: cuando un elemento es eliminado o deja de estar en el DOMm
  • move: cuando un elemento cambia de posición en el DOM.

Cada una de las directivas que permite animciones va a poder asignar distintas animaciones a cada uno de estos eventos:

  • La directiva ngRepeat soporta los eventos enter, leave y move;
  • ngInclude, ngSwitch y ngView soportan los eventos enter y leave;
  • y por último las direcivas ngShow y ngHide soportan los enventos show y hide, lógicamente.

También podemos usar el servicio $animator para que nuestras directivas custom soporten animaciones asignadas a cualquiera de estos eventos.

Transiciones CSS3 vs Animaciones JavaScript

AngularJS nos ofrece la potencia de utilizar animaciones y transiciones definidas en CSS3, pero también podemos programarlas mediante JQuery o cualquier otro framework de animaciones en JavaScript que queramos.

Animaciones y transciones con Angularjs + CSS3

Las animaciones basadas en transiciones CSS3 son mucho más sencillas de utilizar ya que simplemente hemos de declararlas en el CSS de nuestra app siguiendo la siguiente nomenclatura: nombreAnimacion-evento-setup para definir la transition (no olvidarse de los malditos vendor prefixes!) y el estado inicial, y nombreAnimacion-evento-start para definir el estado final. Por ejemplo en nuestra demo hemos definido la siguiente animación para la transición entre el estado de edición del slide activo, desde alpha opacidad 0 a opacidad 1 en 2/3 de segundo:

/* ANIMATIONS */
.fade-show-setup {
  -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.66s;
  -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.66s;
  -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.66s;
  transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.66s;
  
  opacity:0;
}
.fade-show-setup.fade-show-start {
  opacity:1;
}

Mientras que para animaciones mediante JavaScript utilizaremos una sintaxis más complicada, pero que obviamente nos ofrece mucha más potencia que CSS, ya que podemos recibir parámetros, operar con ellos y también acceder a servicios de Angular. Por ejemplo en nuestra demo hemos definido la siguiente animación de entrada, que tomará dirección izquierda-derecha o derecha->izquierda según un valor precalculado (que dependerá de la slide actual y a la que nos queremos mover):

// Definimos animación enter
app.animation('animate-enter', ['$rootScope', function($rootScope) {
  return {
    setup : function(element) {

      // margin con respecto al tamaño de la ventana del navegador
      var margin = Math.max(100,($(window).width()-880)/2);

      // utilizamos rootScope para discernir offset a aplicar
      var px = ($rootScope.myAnimation==='fade-right')?'-'+margin+'px':margin+'px';

      jQuery(element).css({ 'opacity': 0, 'margin-left': px });

    },
    start : function(element, done) {
      // lanzar animacion
      jQuery(element).animate({
        'opacity' : 1, 'margin-left': 0
      }, function() {
        // llamar a done() cuando la animación termina
        done();
      });
    }
  };
}]);

Secuenciación de Animaciones

Uno de los pocos problemas que vemos es que actualmente, AngularJS no proporciona ninguna manera de secuenciar animaciones: si un elemento del DOM es sustituido por otro, los eventos enter y leave se dispararán simultáneamente. Esto puede presentar un problema, ya que nos parece una utilidad muy común el querer, por ejemplo, que la animación de enter del nuevo elemento se lance solo cuando el elemento reemplazado ha terminado su animación de leave, y no inmediatamente ya puede producirse un solapamiento entre ambas animaciones.

En la demo mostrada hemos solventado esto mediante un uso creativo del servicio $timeout para simular esta secuenciación. Lo que hacemos es primero ocultar el elemento saliente y lanzar la animación asignada a hide, y cuando ésta ha terminado (gracias al $timeout) se ejecutarán los cambios en el DOM que provocan que se disparen los eventos leave (que en este caso no tiene asignada ninguna animación ya que el elemento saliente ya está oculto) y enter.

animateSwitchSlide = function (id) {

    // utilizamos rootScope para discernir qué animacion haremos
    $rootScope.myAnimation = (id > $scope.slide.id)?'fade-left':'fade-right';

    $scope.hide=true;

    // transition tras timeout para dar timepo a la animación hide a finalizar
    $timeout( function(){ $location.path('/slides/'+id); }, 550 );
  };

Conclusión

Nos parece un paso adelante muy importante y un signo de madurez del framework AngularJS el empezar a soportar de forma nativa, sencilla e intuitiva una característica como las animaciones, que puede no ser una funcionalidad básica pero nos da mucho juego para vestir y enriquecer visualmente nuestra Webapp.

Además gracias al esquema de directivas ng y eventos utilizado, es realmente sencillo upgradear una aplicación ya terminada para utilizarlas, ya que simplemente hay que definir las transiciones que queremos y aplicar la directiva ng-animate donde sea necesario, sin necesidad de modificar ni una línea de código. Esto será clave también conforme se consolide el soporte para animaciones con nuevos eventos, funcionalidades y directivas soportadas.

Etiquetas: , ,

6 Comentarios
» Feed RSS de los Comentarios

  1. Iván dice:

    Que bueno!, le estáis dando mucha cañita a este framework, ya veo que os mola 😉

  2. danii dice:

    Gracias! Efectivamente, nos encanta! 🙂

  3. Sergo dice:

    Muy buenos los tutoriales estoy pensando en meterme con este y con meteor solo una pregunta , este framework se puede usar para móviles??

  4. gonzalo dice:

    Hola! me gusto mucho tu ejemplo y queria hacerte una pregunta!
    Hay otra forma de comunicarse con las animation sin usar $rootScope?

  5. danii dice:

    Buenas Gonzalo! La verdad es que el uso de $rootScope en este ejemplo es un poco un «hack» para conseguir un efecto de secuenciación de las animaciones, no es en absoluto necesario.

    Mucho ha cambiado desde que escribimos este artículo, así que para una visión algo más actualizada del estado de las animaciones en las últimas versiones de AngularJS (en concreto la rc1.2) te recomiendo que le pegues un vistazo a: http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html

  6. Queria saber si me podias ayudar con este ejemplo. no he logrado que me haga las transiciones. Saludos

Enviar comentario