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
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!
Angular + Famo.us
By Gonzalo Ruiz de Villa
Angular + Famo.us
Angular y Famo.us: combinando lo mejor de dos mundos distintos
- 3,616