Animating Your Mobile UI with Famo.us and Telerik AppBuilder

The buzz that has been generated about the Famo.us JavaScript library, triggered by the amazing demo that appeared in 2012 with a 3D rendering of the periodic table, has been almost unprecedented. Developers lined up to get into the private beta, scratched, clawed, fought for seats, and mayhem ensued.

mayhem at the Famo.us launch

mayhem at the Famo.us launch

This is a slight exaggeration, but not much. More recently, we’ve seen the rollout of Famo.us University, some demos, and the development of the Famo.us/Angular project, which helps structure a codebase using Angular and declarative layout strategies.

So what is Famo.us and how can we leverage it for commercial apps?

According to the Famo.us website:

Famo.us is the only JavaScript framework that includes an open source 3D layout engine fully integrated with a 3D physics animation engine that can render to DOM, Canvas, or WebGL.

What does that mean, actually?

The Famo.us framework takes a different approach to the way elements are rendered to a screen. Because DOM manipulation is considered costly and slows app performance, writing a Famo.us app is built almost entirely in JavaScript. Instead of manipulating the DOM by writing nested HTML, “Famo.us abstracts away DOM management by maintaining a representation of it in JavaScript called the Render Tree.”

In a nutshell:

  • Writing an app in HTML and manipulating its appearance and behavior using JavaScript slows down your user’s “time to glass.” Famo.us emphasizes animation effects, promising 60 frames per second rendering, so it needs a faster way to deliver content.
  • Famo.us’s framework uses a Render Tree to avoid costly manipulation of nested DOM elements – this tree is a representation of the DOM, but written in JavaScript.
  • Read more about the Render Tree and how to leverage it here.

Why would I want to use this?

Smooth, fast animations can be important to ensure a good user experience. More and more, we see websites that leverage movement to lead a user to perform certain tasks, whether it be done using a library such as GreenSock, Paper.js, or hand-coded CSS transformations. If your application has a complex UI and/or requires animation that you need to work smoothly across devices, then Famo.us could be a useful option to explore. In addition, Famo.us works for desktop apps, so you could use the code you develop for a mobile app on a responsive website.

Let’s build something!

We’re going to build a quiz flashcard app with a Famo.us front-end that is connected to the Quizlet API and deployed on the Telerik Platform. It’s going to look like this:

This app has three requirements:

  • Include a form where the user can input a Quiz ID (find the quiz ID in the URL of any Quizlet quiz set. To find the ID, go to Quizlet.com, search for a quiz set, and grab its ID from the URL. For example, the ID from the URL http://quizlet.com/2154706/200-first-french-verbs-flash-cards/ would be 2154706)
  • The app will have a cards UI interface. Touch a ‘happy face’ icon to acknowledge comprehension and slide the card to the bottom left. Touch a ‘star’ icon to archive the card for future review.
  • Each card will flip to include questions and answers as delivered by Quizlet.

Port the code to AppBuilder

The completed code can be found here.

I have chosen to use the Famo.us/Angular project to structure the code for this app, as I appreciate the ease of the two-way binding for delivering content to the cards and the way this project offers a tag-based declarative format for the view.

Clone it to your desktop and then create a blank project in Telerik Platform following the steps below.

Don’t have a Platform license? Don’t worry, you can try it free for 30 days.

  1. Create a workspace in the Platform called Sweet Quizzes:
    Workspace creation
  2. Create a blank AppBuilder hybrid project within this workspace by cloning the SweetQuizzes Github repository (https://github.com/jlooper/SweetQuizzes):

    10

  3. Sign up for a Quizlet account and then register for an API Key. In your Quizlet dashboard, you will see your Client ID. Open the scripts>config.js file and add your client_id:
    var CLIENT_ID = "your-client-id"

At this point, you can now run your app by clicking the ‘Run’ button at the top of the screen and select the simulator of your choice to view the app:

run

In the textbox, type 2154706 as a sample quiz ID and click the ‘Get Quiz’ button. You should see cards appear; click them to flip the questions and answers of the quiz, and click either the ‘smiley’ or ‘star’ icons to slide them left or right. If you click the ‘smiley’ the question/answer pair is removed from the collection on which the quiz is built; if you click the ‘star’ you have added it to a ‘review’ collection to revisit it later:

sim2

Exploring Famo.us

Now that we have the app set up and running in AppBuilder, let’s take a look at the code. The code structure is based on the Famo.us/Angular seed project which you can download here.

After downloading the starter project and expanding it, from the command line, cd to the folder you just downloaded and navigate to starter/app. Assuming you have npm installed with a local server module like serve, you can simply type serve and the project will run at http://localhost:3000. The starter project is simply a demo of the Famo.us logo, spinning around – not terribly interesting, although you can use a slider to control the speed of the spin.

If you cd to /examples/app, however, you can dig in a bit more. Within your current terminal window hit control+c to stop serving localhost:3000. Change directory into the /examples/app folder and type ‘serve’ in the terminal window to restart localhost:3000. Now you should see an image of an iphone with a baboon (I don’t know why there is a baboon, but there you are).

monkey

Open the /examples folder in Sublime text or your editor of choice. You can view several different animation and layout effects by navigating to the endpoints specified in /scripts/controllers/app.js. Try changing your local URL to http://localhost:3000/#/physics-particles and mouse over the image for an interesting physics-oriented demo. Navigating through these code demos gives you a good idea of what might be possible in terms of crafting an interesting user interface.

The code structure

For our purposes, we are going to use Famo.us for its animations, and Famo.us/Angular for its layout and declarative syntax. Note how the project you imported into AppBuilder mimics the folder structure of the starter app:

app
– bower_components
– css
– img
– partials
– scripts
index.html

In the bower_components folder, we have several dependencies that Famo.us/Angular uses to create and manage the screens. These dependencies are included in index.html along with several .css files.

<script src="bower_components/famous/famous-global.min.js"></script><script src="bower_components/famous-angular/dist/famous-angular.js"></script>

The CSS contains some special styles to make the app display the nice colors I chose on ColourLovers.com. The img folder contains the happy face and star SVG graphics that act as buttons.

Layout

The meat of the app, however, resides in the partials and scripts folders.

In partials, we use the Famo.us/Angular method of markup to create the app’s layout. The entire code for the user interface is below:

<fa-app class="full-screen" id="app">


 <fa-header-footer-layout fa-options="{headerSize: 75, footerSize: 75}">


     <fa-surface fa-background-color="'#FBC5C5'"><p class="title">Sweet Quizzes</p></fa-surface>


      <fa-surface fa-background-color="'#9ECEBE'">
          <div class="getnew">

              <form>
                 <input type="text" name="quizId" placeholder="Quiz Id" ng-model="quiz.quizId">
                <button class="blue" ng-click="getNewQuiz(quiz)">Get Quiz</button>
                <button class="pink" ng-click="getReviewQuiz()"><img src="img/star.svg" class="star-icon"/>Review</button>              
              </form>

           </div>

               <div align="center">
                  {{quizTitle}}
              </div>

            <img style="float:left" src="img/happy.svg" ng-click="nextQuestion()"/>
            <img style="float:right" src="img/star.svg" ng-click="storeQuestion()"/> 

      </fa-surface>

  </fa-header-footer-layout>

</fa-app>

Notice the use of ‘fa-header-footer-layout’. This markup tells famo.us/Angular that we are creating a certain type of layout, and I have included two “fa-surfaces” which are essentially divs, to contain the top header and an area containing the control buttons and form within which the cards will fall. If you wanted a header, inner div, and footer, you would include three fa-surfaces, but I found that having an absolutely-positioned footer created issues with my cards on device due to some CSS layout conflicts, so I opted for a simpler layout.

The simple form allows the user to input a quiz ID from Quizlet, which loads content to display in the cards that fall from the top. But where are the cards? They don’t appear in this layout file; they are entirely created and controlled in the Famo.us-flavored JavaScript files in the /scripts folder. Let’s take a look at those.

The Famo.us code

Now we get to the hot stuff: the code that is driving the cards. This is the /scripts/main/main-ctrl.js file. This file gives us a good window on how to structure a Famo.us app.

First, include only the elements of Famo.us that you need to use for your app. The card interface that I built includes an animated ‘flipper’, a card that consists of a front and back surface that contain content brought in via the Quizlet factory that is referenced in the controller. To accomplish this, we are going to include several parts of the Famo.us library:

  • First we need an Engine and Modifier, which are the most common elements of a Famo.us app and act as the base structure of the aforementioned Rendering tree.
  • We include a Surface which is the child of the Modifier
  • Finally, we include the Transform library that allows us to move the Modifier around the screen:
    var Engine = $famous['famous/core/Engine'];
    var Modifier = $famous['famous/core/Modifier'];
    var Surface = $famous['famous/core/Surface'];
    var Transform = $famous['famous/core/Transform'];

Add a Flipper, which is the type of view governing our cards interface:

var Flipper = $famous['famous/views/Flipper'];

Add the Timer utility, which allows us control over the length of our animations:

var Timer = $famous['famous/utilities/Timer'];

And finally delineate the types of transitions we need:

var Easing = $famous['famous/transitions/Easing'];
var Transitionable = $famous['famous/transitions/Transitionable'];
var SpringTransition = $famous['famous/transitions/SpringTransition'];
Transitionable.registerMethod('spring', SpringTransition);

Next, we create some variables in $scope that we will use later:

$scope.index = 0;
//sample quiz
$scope.quizId = 2273151;
$scope.reviewQuizData=[];

Let’s create the main context of our Famo.us app:

var mainContext = Engine.createContext();

Get the quiz data from Quizlet after the user inputs a valid quiz id:

$scope.getNewQuiz = function (quiz) {
  if(angular.isDefined(quiz)){
    $scope.quizId = quiz.quizId;
    if($scope.quizData){
      $scope.clear();
    }
      $scope.loadQuiz()
    }
  else{
    alert("Please enter a quiz id from Quizlet. Try 2154706")
  }
};

We also create a function where a user’s stored quiz questions can be recalled and reviewed:

$scope.getReviewQuiz = function (quiz) {
  $scope.clear();
  $scope.quizData = $scope.reviewQuizData;
  $scope.buildInterface();
};

Once we have the data from Quizlet, we grab the quiz title and associated data that we need for the interface:

$scope.loadQuiz = function () {            
  var promise = quizlet.getQuiz($scope.quizId);
  promise.success(function (data) {
    $scope.quizTitle = data.title;
    $scope.quizData = data.terms;
    $scope.buildInterface();
  });
  promise.error(function () {
    console.log("API ERROR!", arguments);
  })
};

At this point, we’re ready build the cards interface. Here, we create a new Flipper with a center modifier positioned a little below the center of the screen (to allow space for the top elements) and specify the dimensions of the front and back surfaces. We add the quiz data to the front and back surfaces of the flipper and tell it how we want it to behave when clicked. Finally, we add the pieces all together in the Famo.us-style building of the Render Tree, adding the flipper to the modifier to the maincontext.

$scope.buildInterface = function() {
    if(!angular.isDefined($scope.quizData)){
        alert("Sorry, that quiz id seems to be invalid")
     } 
     else {
      if($scope.quizData.length>0){
        $scope.flipper = new Flipper();
        $scope.centerModifier = new Modifier({
           origin: [0.5, 0.25],
           align: [0.5, 0.25]
        });

        var frontSurface = new Surface({
             size: [200, 200],
             content: $scope.quizData[$scope.index].term,
             properties: {
                  background: '#F09BA2',
                  textAlign: 'center',
                   border: '5px solid #FBC5C5',
                   borderRadius: '10px',
                   padding:'10px'
              }
        });

        var backSurface = new Surface({
             size: [200, 200],
             content: $scope.quizData[$scope.index].definition,
             properties: {
                 background: '#FBC5C5',
                 color: 'black',
                 textAlign: 'center',
                 border: '5px solid #FBC5C5',
                 borderRadius: '10px',
                 padding:'10px'
             }
       });

       $scope.flipper.setFront(frontSurface);
       $scope.flipper.setBack(backSurface);

       var toggle = false;
        frontSurface.on('click', function () {
            var angle = toggle ? 0 : Math.PI;
              $scope.flipper.setAngle(angle, {
                 curve: 'easeOutBounce',
                 duration: 800
              });
            toggle = !toggle;
         });
         backSurface.on('click', function () {
            var angle = toggle ? 0 : Math.PI;
               $scope.flipper.setAngle(angle, {
                  curve: 'easeOutBounce',
                  duration: 800
               });
             toggle = !toggle;
         });

         var spring = {
            method: 'spring',
            period: 1000,
            dampingRatio: 0.3
         };

         $scope.centerModifier.setTransform(
             Transform.translate(0,200,0),spring
         );
                                     mainContext.add($scope.centerModifier).add($scope.flipper);

     }
     else{
      alert("all done!")
     }
   }
};

At the end of this file, we have three additional functions. The first is a clearing function to clear out any old quiz data and start fresh:

$scope.clear = function(){
    $scope.quizData=[];
    $scope.centerModifier.setTransform(
       Transform.translate(-300,800,0),
          { duration : 200, curve: 'easeInOut' }
       );
};

We also have the ability to get the next question if the user clicks the smile icon (demonstrating that s/he has learned the question and won’t need to return to review it) by pushing the card to the left of the screen and building a new card:

$scope.nextQuestion = function() {
    //remove learned question
    $scope.quizData.splice($scope.index, 1);
    $scope.centerModifier.setTransform(
       Transform.translate(-300,800,0),
         { duration : 200, curve: 'easeInOut' }
    );

    Timer.setTimeout(function() {
        $scope.buildInterface()
     }, 1000);
};

Similarly, we include a storeQuestion function so that the user can return to review questions:

$scope.storeQuestion = function() {
    //capture the data for future review
    $scope.quizData.splice($scope.index, 1);            
    $scope.reviewData = $scope.quizData[$scope.index]
    $scope.reviewQuizData.push($scope.reviewData);
    $scope.centerModifier.setTransform(
       Transform.translate(300,800,0),
          { duration : 200, curve: 'easeInOut' }
    );
       Timer.setTimeout(function() {
           $scope.buildInterface()
        }, 1000);         
};

Finishing Up

While the interface for this app is very simple, I think Famo.us’s animations help to make it pretty and engaging. I can envision making a Tinder or Jelly-style card-sorting interface with user-controlled animations – they could control the easing and bounce via some sort of settings to custom-tailor their interface. Famo.us offers a lot of snazzy firepower to give the developer almost unlimited movement around screen sizes of all types. In addition, using the Famo.us/Angular model makes creating a layout to house your animations a little easier to create. Smooth, seamless animation seems to me to be one of the important coming trends in the near future for mobile and desktop apps, so learning Famo.us now seems like a good investment of time. I’m eager to see new apps created using Famo.us! If you’ve got one, feel free to post the link below.

Comments

  • burkeholland

    I’m kind of surprised that you still have to manually create surfaces, even with Famo.us Angular. It seems like the whole point of those directives is so you don’t have to do that. Nice article though. I’m interested to see if Famo.us can live up to it’s promise – especially cross-platform.

    • Jen Looper

      I found famo.us/Angular an intuitive way to structure the basic layout of your app, but once you get down to brass tacks and need to start manipulating surfaces, I felt that you needed to write that in plain famo.us JS format. I think there are many ways to slice this particular pie, and all we can do is try these sort of solutions to see if they can work for our given use case. I’m looking for production apps that use famo.us, and some are starting to appear.

  • zoomclub

    Packing famo.us in react views would be more elegant yet. Add in some of the advanced event management via JS-CSP coming to react and things get sweeter than apple pie. See the Taming User Interfaces section in this link:

    http://jlongster.com/Taming-the-Asynchronous-Beast-with-CSP-in-JavaScript

    • Jen Looper

      Thanks for sharing this link. Yes, mixing React and famo.us would be really sweet! And I like pie 🙂

  • Pingback: Famo.us Angular and Telerik AppBuilder for Smooth Animations | LadeezFirstMedia.com - mobile apps, web development, and more!()

  • Jerry Gagliano

    Famo.us is an interesting concept but still not impressed with the 60fps. Tested the demo apps on firefox on Android and it was very choppy. Again it all depends on the browser and hardware.

    If you want to target chrome & safari webkit browsers then your in the right path.

    • Jen Looper

      I agree….I had a rough time making my demo work properly on device. I think Famo.us has a long ways to go, time will tell!