Famo.us y Angular

 

Gonzalo Ruiz de Villa

@gruizdevilla


http://slides.com/gruizdevilla/angular-famo-us

Meetup AngularJS Madrid

about me

Gonzalo Ruiz de Villa

@gruizdevilla

@adesis

siguientes meetups

¿Qué es Famo.us?

  • Un motor OSS de renderizado web.
  • Permite  realizar animaciones con gran rendimiento de forma imperativa
  • Tiene como objetivo conseguir que las aplicaciones web den la sensación de aplicaciones nativas

 

https://famo.us/demos/

Pero en AngularJS las animaciones son por CSS3

Lo cual puede estar muy bien para cosas sencillas.

.reveal-animation.ng-enter {
  -webkit-animation: enter_sequence 1s linear; /* Safari/Chrome */
  animation: enter_sequence 1s linear; /* IE10+ and Future Browsers */
}
@-webkit-keyframes enter_sequence {
  from { opacity:0; }
  to { opacity:1; }
}
@keyframes enter_sequence {
  from { opacity:0; }
  to { opacity:1; }
}

Pero cuando queremos algo SOLO UN POCO

más sofisticado...


.element-animation{
  animation: animationFrames linear 4s;
  animation-iteration-count: 1;
  transform-origin: ;
  -webkit-animation: animationFrames linear 4s;
  -webkit-animation-iteration-count: 1;
  -webkit-transform-origin: ;
  -moz-animation: animationFrames linear 4s;
  -moz-animation-iteration-count: 1;
  -moz-transform-origin: ;
  -o-animation: animationFrames linear 4s;
  -o-animation-iteration-count: 1;
  -o-transform-origin: ;
  -ms-animation: animationFrames linear 4s;
  -ms-animation-iteration-count: 1;
  -ms-transform-origin: ;
}

@keyframes animationFrames{
  0% {
    left:-299px;
    top:-196px;
    opacity:1;
    transform:  rotate(-1deg) scaleX(1) scaleY(1) ;
  }
  50% {
    left:-137px;
    top:25px;
    transform:  rotate(976deg) scaleX(0.77) scaleY(3.179999999999998) ;
  }
  100% {
    left:200px;
    top:0px;
    opacity:1;
    transform:  rotate(24deg) scaleX(3.52) scaleY(3.179999999999998) ;
  }
}

@-moz-keyframes animationFrames{
  0% {
    left:-299px;
    top:-196px;
    opacity:1;
    -moz-transform:  rotate(-1deg) scaleX(1) scaleY(1) ;
  }
  50% {
    left:-137px;
    top:25px;
    -moz-transform:  rotate(976deg) scaleX(0.77) scaleY(3.179999999999998) ;
  }
  100% {
    left:200px;
    top:0px;
    opacity:1;
    -moz-transform:  rotate(24deg) scaleX(3.52) scaleY(3.179999999999998) ;
  }
}

@-webkit-keyframes animationFrames {
  0% {
    left:-299px;
    top:-196px;
    opacity:1;
    -webkit-transform:  rotate(-1deg) scaleX(1) scaleY(1) ;
  }
  50% {
    left:-137px;
    top:25px;
    -webkit-transform:  rotate(976deg) scaleX(0.77) scaleY(3.179999999999998) ;
  }
  100% {
    left:200px;
    top:0px;
    opacity:1;
    -webkit-transform:  rotate(24deg) scaleX(3.52) scaleY(3.179999999999998) ;
  }
}

@-o-keyframes animationFrames {
  0% {
    left:-299px;
    top:-196px;
    opacity:1;
    -o-transform:  rotate(-1deg) scaleX(1) scaleY(1) ;
  }
  50% {
    left:-137px;
    top:25px;
    -o-transform:  rotate(976deg) scaleX(0.77) scaleY(3.179999999999998) ;
  }
  100% {
    left:200px;
    top:0px;
    opacity:1;
    -o-transform:  rotate(24deg) scaleX(3.52) scaleY(3.179999999999998) ;
  }
}

@-ms-keyframes animationFrames {
  0% {
    left:-299px;
    top:-196px;
    opacity:1;
    -ms-transform:  rotate(-1deg) scaleX(1) scaleY(1) ;
  }
  50% {
    left:-137px;
    top:25px;
    -ms-transform:  rotate(976deg) scaleX(0.77) scaleY(3.179999999999998) ;
  }
  100% {
    left:200px;
    top:0px;
    opacity:1;
    -ms-transform:  rotate(24deg) scaleX(3.52) scaleY(3.179999999999998) ;
  }
}

¿Te gusta?

No gusta porque

  • Es muy pesado
  • Esos malditos prefijos y tanta repetición (¿gulp-autoprefixer al rescate?)
  • ¿Pero qué es lo que está haciendo exactamente?
  • El código declarativo tiene limitaciones para explicar ciertas cosas (grunt vs gulp, ya que estamos...).

Famo.us tiene otra filosofía

  • Las animaciones se describen mejor de forma imperativa
  • Tiene sus propios artefactos que nos permiten abstraernos y el motor se ejecuta permanentemente actualizando la pantalla
  • Los usuarios (de móviles) esperan de las aplicaciones muchas animaciones, unas más vistosas y otras más sutiles.
var sync = new GenericSync({
    "mouse"  : {},
    "touch"  : {},
    "scroll" : {scale : .5}
});

sync.on('update', function(data){
    var currentPosition = position.get();
    position.set([
        currentPosition[0] + data.delta[0],
        currentPosition[1] + data.delta[1]
    ]);
});	

¿Cómo funciona Famo.us?

  • Usa transformaciones CSS3, que permiten pasar por la GPU para mejorar el rendimiento
  • Usa requestAnimationFrame para ir a 60fps
  • En cada tick, calcula la posición mediante álgebra lineal, multiplicando matrices y acaba informando de sus posiciones a cada elemento. 

Pero Famo.us es 100% JavaScript

Crea un contexto y debajo cuelga sus piezas

context             var context = Engine.createContext();
   │
surface                    context.add(surface);'

El árbol de Famo.us es JS y no DOM

context                    var context = Engine.createContext();
    │
 modifier                   var chain = context.add(modifier);
    │
 surface                    chain.add(surface);
      context
         │
      modifier
         │
     scrollview
 ┌───┬───┼───────┐
S1  S2  S3  ⋯  S10
           ┌─────┴─────┐
       modifier1    modifier2
           │           │
       surface1     surface2
      div.app
         │
 ┌───┬───┼───────┐
div div div  ⋯  div
           

Aunque tengamos un árbol complicado, en el DOM se ve una estructura plana donde se modifica la propiedad style con transformaciones CSS3 de cada capa

Recapitulando sobre Famo.us

  • No hay HTML
  • Si queremos usar un MVC, ¿donde están las Views?
  • DOM con HTML funciona muy bien para representar estructurar jerárquicas, como los UI
  • Pero en Famo.us no tenemos DOM, utilizamos otra semántica

Famo.us y AngularJS v0

  • Sin disponer de HTML, no hay un sitio común donde se puedan encontrar AngularJS y Famo.us
  • AngularJS se construye a partir del estándar HTML, enriqueciéndolo (compilando los nodos y bindeándolos)
  • La aproximación de Famo.us es radicalmente opuesta
  • Los dos frameworks tienen puntos muy fuertes para algunas cosas y débiles en otras. Están especializados.

Ignorando los problemas anteriores...

 

¿Qué es lo que queremos?

¡Mantener el rendimiento de Famo.us!

¿Y que más queremos?

¡Usar el databinding de AngularJS!

¿Y que más queremos?

¡Usar componentes de AngularJS! (incluso de terceros)

¿Y que más queremos?

¡Usar componentes de Famo.us! (incluso de terceros)

¿Y que más queremos?

¡Minimizar el esfuerzo conceptual!

Usando las convenciones de cada librería cuando sea posible.

Pues dad al César lo que es del César, y a Dios lo que es de Dios

Veamos que propone 

famous-angular

¿Por donde empezar?

  • Famo.us tiene un RenderTree que es eso, un árbol, como el DOM.
  • ¿Y si en lugar de intentar conseguir que ambos mundos convivan en el mismo espacio y con los mismos elementos DOM, tratamos de agregar un paso de compilación?
  • AngularJS ya tiene un compilador de DOM para las directivas. ¿Y si somos capaces de comunicar esta estructura (jerarquía) definida con DOM a Famo.us?

Comunicando la jerarquía

  • Angular mantiene una jerarquía que se corresponde con scopes hijos
  • Esta jerarquía es la que se transformará en el árbol de renderizado de Famo.us
  • La forma de resolver esto es con eventos: cada hijo envía un evento a su padre. A medida que la jerarquía de scopes es recorrida por los eventos, las directivas atan entre ellos a los correspondientes componentes de Famo.us.

DOM tree -> Famo.us tree

  • AngularJS tiene varios pasos en la compilación de una directiva: compile, controller, prelink, postlink.
  • En relación al árbol, prelink se ejecuta antes de recorrer los hijos y postlink después de recorrerlos.
  • En prelink creamos los observadores de eventos, y en post link los disparamos.

¿Qué hemos conseguido?

  • Hemos creado un DSl para definir el árbol de Famo.us mediante DOM. AngularJS compila nuevas directivas que crean los componentes del árbol de renderizado.
  • Precisamente, definir un árbol es algo que se puede hacer muy bien con markup.
  • Este código de Famo.us

 

 

 

  • Se ve ahora así (con binding incluido!!!):
var myView = new View();
var mySurface = new Surface();
mySurface.setContent("<div>I'm a surface</div>");
myView.add(mySurface);
<fa-view>
  <fa-surface>
    <div>I'm {{data.bound}}</div>
  </fa-surface>
</fa-view>

Beneficios

  • Es muy fácil integrar Famo.us con aplicaciones HTML o Angular
  • Podemos usar el databinding de Angular con Famous
  • Ayuda a separar responsabilidades
  • Podemos usar nuestras directivas dentro de Famo.us
  • Mantienes los beneficios tradicionales de Angular, como organización, tests, etc.

$digest

  • Fundamental dentro de AngularJS
  • Pero es muy pesado, por eso sólo se lanza como reacción a eventos
  • Famo.us tiene un ciclo que dura 1/60 s. 
  • Si el $digest dura un poco más, tenemos un problema, si dura un poco menos apenas deja tiempo para hacer cosas.
  • Luego hay que desacoplar el ciclo de $digest con el ciclo de renderizado de AngularJS

¡Desacoplando todos los ciclos!

  • Se usa la función $parse de Angular para crear funciones que están vinculados a los Modifiers de Famo.us
  • Se fuerza un flujo unidireccional de Angular a Famo.us para que este no tenga efectos laterales (digests) durante el renderizado.

Un poco de código, por favor

Recursos

  • https://github.com/famous/famous-angular/
  • http://famo.us/integrations/angular/
  • https://github.com/thomasstreet/famous-angular-starter
  • http://thomasstreet.com/blog/famous-angular/2014/04/28/famous-angular.html
  • http://thomasstreet.com/famous-angular-google/
  • http://famo.us/university/home/#/famous-angular
  • https://famo.us/integrations/angular/docs/api/index.html

¡Gracias!

¿Preguntas?

 

Feedback: 
http://bit.ly/1Exa86F

Angular + Famo.us

By Gonzalo Ruiz de Villa

Angular + Famo.us

Angular y Famo.us: combinando lo mejor de dos mundos distintos

  • 3,395