famo.us and angular

Creating Beautiful Animations with Famo.us-AngularJS

Click Here for TL;DR
Click here to see completed ng-famous boilerplate.

Famo.us and AngularJS

Today we will be Creating Beautiful Animations with Famo.us-AngularJS Using ng-repeat. Yeah, the title doesn’t exactly roll off the tongue. If you are a Javascript fanboy, there is a good chance that you have heard of Famo.us and Famo.us-AngularJS (can we just call this ng-famous for now on!? Yeah, I’m going to do that..). If you haven’t, prepared to be learned. Here is the Famo.us elevator pitch: 

Famo.us is a free, open source JavaScript framework that helps you create smooth, complex UIs for any screen.

What does this mean? It means Famo.us is a front-end rendering library that allows you to create animated UI’s that are both beautiful and wicked fast (like 60 fps fast) that can move on all 3 axis! And if there is latency it handles it well, and interpolates instead of lags behind. So now that we have Famo.us for rendering our front-end, we still need a modular architecture for our code and maybe a solution that’s a little less opaque to the feign of heart.

Introducing Famo.us-AngularJS

Oh man, ng-famous created by Zack Brown over at  Thomas Street is the bee’s knees. It allows you to use familiar html syntax to represent Famo.us building blocks such as `Surfaces` that can be altered in your AngularJS controllers. Let’s look at some ng-famous html below.

1
2
3
4
5
6
7
8
9
<fa-app id="myNgFamousApp">
    <fa-modifier ng-repeat="link in links">
        <fa-modifier fa-size="[100, 40]">
            <fa-surface>
                <a ng-href="{{link.href}}">{{link.title}}</a>
            </fa-surface>
        </fa-modifier>
    </fa-modifier>
</fa-app>

If you are an AngularJS developer, this definitely looks familar beside the unique ng-famous html element types. There is a good chance that you are lost with these ng-famous elements. Let me break them down. I will start from fa-surface and move outward.

fa-surfaces contain html content. Anything that you would like to be uniquely animated should be a fa-surface. fa-surfaces can not be within other fa-surfaces.

fa-modifier are elements that wrap fa-surfaces. We assign values to fa-modifiers to modify fa-surface‘s location, opacity, size, etc.

fa-app contains your famous app. fa-app can be placed inside any block HTML element such as div, body, etc.

For more info check the ng-famous website.

Creating a ng-famous Boilerplate from an AngularJS Boilerplate

We are going to create a basic ng-famous boilerplate from Jacob’s great AngularJS boilerplate seen here. This can be installed with bower using bower install jsc-angular-boilerplate (for more information on Bower, see this page). To see what we will be building, view it here, or install using bower install cv-ng-famous-boilerplate.

We will update our bower.json to include the required ng-famous dependencies.

bower.json

1
2
3
4
5
"dependencies": {
        ...
    "famous-angular": "0.5.0",
    "famous-polyfills": "~0.3.0"
}

Our module only uses ui-router currently. We will then want to update our module to now include ng-famous:
app.js

1
2
var boilerPlateApp = angular.module('boilerPlateApp', ['ui.router', 'famous.angular'])
...

Now let’s wrap our ui-view in fa-app. This will allow our ui-view to contain Famous surfaces.

index.html

1
2
3
4
5
...
<fa-app>
  <ui-view></ui-view>
</fa-app>
...

We will now make each one of our partials a Famous surface by wrapping them in fa-modifier and fa-surface.

main.html

1
2
3
4
5
6
7
<fa-modifier>
    <fa-surface>
        <h1>Main Partial Loaded</h1>
        <p>Message from controller:</p>
        <p>{{mainMessage}}</p>
    </fa-surface>
</fa-modifier>

Everything should appear the same in our browser, but when we inspect our elements we notice everything is positioned using 3d matrices. This is how you know we are at the entrance of the Famous rabbit-hole. Strap in because it is about to get exciting!

Now we will add a list of links to our second partial. These will be animated using Famous. In order to animate these, we will first need to add the $famous and $timeline dependencies to our controller.

1
2
3
...
.controller('secondController', function($scope, $famous, $timeline) {
...

We will now create a Transitionable and Easing object. The Transitionable object will allow us to transition any modifiable surface attribute (such as translation, opacity, and scale). The Easing object will let us define how to ease these transitions.

1
2
3
4
5
...
.controller('secondController', function($scope, $famous, $timeline) {
   var Transitionable = $famous['famous/transitions/Transitionable'];
   var Easing = $famous['famous/transitions/Easing'];
...

Now we will make an array containing text and Transitionable objects for animating the location and opacity of our fa-surfaces.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var Easing = $famous['famous/transitions/Easing'];
$scope.links = [
  {
    text : 'First',
    transform : new Transitionable(0), // each modifier attribute we modify requires its own Transitionable
    opacity : new Transitionable(0)
  },
  {
    text : 'Second',
    transform : new Transitionable(0),
    opacity : new Transitionable(0)
  },
  ...
];

Now we will update our second partial and ng-repeat our links.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<fa-modifier>
    <fa-surface>
        <h1>Second Partial Loaded</h1>
        <p>Message from controller:</p>
        <p>{{secondMessage}}</p>
        <!-- Requires fa-app to have fa-surfaces, held within other surfaces -->
        <fa-app>
            <fa-modifier ng-repeat="link in links"
                fa-translate="link.transform"
                fa-opacity="link.opacity"
                fa-size="[400, 40]"
                fa-index="$index">
                <fa-surface>
                    {{link.text}}
                </fa-surface>
            </fa-modifier>
        </fa-app>
    </fa-surface>
</fa-modifier>

fa-app is added inside our fa-surface so that we can have nested fa-surfaces.
We can see that we will be using our link.transform and link.opacity. If you run this, the links will be transparent because link.opacity is set to 0. If you want to test to see that it is working, change fa-opacity="link.opacity" to fa-opacity="1".

We are going to make a function that rolls our Transitionables over a timeline.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
$scope.links = [...];

$scope.linkTimelines = {
    translate: function($transition){
      return $timeline([
        [0, [500, 0, 0], Easing.outQuad],
        [.2, [200, 250, 0], Easing.outBounce],
        [1, [0, 0, 0]]
      ])($transition.get()); // we need to apply the transition and return it
   
    }, // translate
    opacity: function($transition){
      return $timeline([
        [0, 0, Easing.outQuad],
        [1, 1]
      ])($transition.get());
   
    } // opacity
 
} // linkTimeLines
...

We can see the $scope.linkTimelines has two function that take a Transitionable and transitions it over a $timeline. $timeline takes a multi-dimensional array as its argument. This array can be tricky, so let me break it down for you:

1
2
3
4
5
6
$timeline_array1 = [
    0, // first offset is a number from 0 to 1, representing where on the timeline the current modification will be
    [0,200,0], // for the second offset, we set the transitionable's value at this point on the timeline. For example, Transform takes [x,y,z], and opacity takes a number from 0 to 1
    Easing.outQuad // This is how we will ease to the next spot on the timeline. The last array will not need this
]
$array = [$timeline_array1, $timeline_array2];

Now let’s connect this to our partial:

1
2
3
4
5
6
7
8
9
10
11
...
<fa-modifier ng-repeat="link in links"
    fa-translate="linkTimelines.translate(link.transform)"
    fa-opacity="linkTimelines.opacity(link.opacity)"
    fa-size="[400, 40]"
    fa-index="$index">
    <fa-surface>
        {{link.text}}
    </fa-surface>
</fa-modifier>
...

Okay, now we need a way to fire the animations of $scope.linkTimelines. Let’s create a function that does just this.

1
2
3
4
5
6
7
8
9
10
11
...
$scope.linkTimelines = {...}
...
$scope.linkEnter = function(transform, opacity, $index, $done){
    transform.delay((($index*150)), function(){ // we will delay each animation based on index
        transform.set(1, {duration: 1400}) // we will set each Transitionable to 1 == which is the index of the $timeline to transition to
        opacity.set(1, {duration: 1600}, $done)

    });

} // linkEnter

$scope.linkEnter takes a Transitionable $timeline transform object, a Transitionable $timeline opacity object, the current $index in the loop, and a $done object to let us know that the function is complete.

The Transitionable $timeline transform object is delayed so that not all links animate at the same time. After this delay, we then set the Transitionable $timeline object to 1 on the timeline over 1400 milliseconds. During this, we also set the Transitionable $timeline opacity object to 1 on the timeline over 1600 milliseconds. Finally, we pass the $done object to the completion callback.

Now we add this function to the fa-animate-enter attribute of our fa-modifiers.

1
2
3
4
5
6
7
8
9
10
11
12
...
<fa-modifier ng-repeat="link in links"
    fa-translate="linkTimelines.translate(link.transform)"
    fa-opacity="linkTimelines.opacity(link.opacity)"
    fa-animate-enter="linkEnter(link.transform, link.opacity, $index, $done)"
    fa-size="[400, 40]"
    fa-index="$index">
    <fa-surface>
        {{link.text}}
    </fa-surface>
</fa-modifier>
...

Now we have the skills to make some killer animations ng-famous!

In The End

Making beautiful, performant, and modular animations in AngularJS-Famous can be tough at first, but the pay-off in the long run is huge. To see the completed ng-famous boilerplate, See here.

To see the completed ng-famous boilerplate, view it here.

Pro Tip: Make Your ng-repeat Scrollable

You can make your list of links scrollable by wrapping your fa-modifier ng-repeat="link in links" in a fa-scroll-view.

Usage:

1
2
3
4
5
6
7
8
9
10
11
12
<fa-scroll-view fa-pipe-from="scrollHandler">
    <fa-modifier ng-repeat="link in links"
        fa-translate="linkTimelines.translate(link.transform)"
        fa-opacity="linkTimelines.opacity(link.opacity)"
        fa-animate-enter="linkEnter(link.transform, link.opacity, $index, $done)"
        fa-size="[400, 40]"
        fa-index="$index">
        <fa-surface fa-pipe-to="scrollHandler">
            {{link.text}}
        </fa-surface>
    </fa-modifier>
</fa-scroll-view>

And update our Javascript to:

1
2
3
4
5
6
.controller('secondController', function($scope, $famous, $timeline) {
  var Transitionable = $famous['famous/transitions/Transitionable'];
  var Easing = $famous['famous/transitions/Easing'];
  var EventHandler = $famous['famous/core/EventHandler'];
 
  $scope.scrollHandler = new EventHandler();

And now we have a scroll-view!

3 thoughts on “Creating Beautiful Animations with Famo.us-AngularJS

  1. Reza

    Your sample would be better if you had provided some screen shots or step by step out put in plunker or jsfiddle

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *