Skip to main content

Build the smoothest UIs you've ever experienced

Branches and leaves

Say goodbye to the DOM and hello to the render tree.

Say goodbye to the DOM and hello to the render tree.

To understand the effects of Modifiers we must first understand Famo.us' render tree. Luckily, the core ideas are simple: a Famo.us context is the root of the render tree. The render tree has a Surface for each leaf. A leaf is affected by exactly the Modifiers between it and the root.

var surface1 = new Surface();
var surface2 = new Surface();
var modifier1 = new Modifier();
var modifier2 = new Modifier();
var modifier3 = new Modifier();
context.add( modifier1 ).add( surface1 );
context.add( modifier2 ).add( modifier3 ).add( surface2 );

Adding a Modifier or Surface directly to our context creates a branch. Any Surfaces added to a branch (by chaining the add method) will only be affected by the Modifiers further up the branch. modifier3 being added to modifier2 demonstrates Modifier chaining, which is a pattern we can use to separate different parts of a modification.

For example, if we wanted to rotate and move surface2 , modifier2 could handle the rotation whilst modifier3 could handle the positioning.

Views

The key to building maintainable, scalable large applications is to create smaller composable pieces that can be brought together to form our application. Famo.us views allow us to group Surfaces, encapsulate functionality and also provide an event input and output, allowing communication with other views in a decoupled manner.

Unlike other frameworks, views in Famo.us are not renderable, so they will not change our application's appearance unless we have added Renderables to the view. Views have no default appearance; they are simply a collection of Modifiers and Surfaces, which themselves affect what is rendered.

Views are treated as a single leaf on the render tree's branch, causing any Surfaces that are added to it to be affected by Modifiers higher up the branch.

var surface = new Surface({
  content: 'My first Surface'
});
var surface2 = new Surface({
  content: 'Another surface'
});
var view = new View();
view.add( surface );
view.add( surface2 );
context.add( view );

Famo.us comes with many in-built views – such as FlexibleLayout, a view for providing ratio-based responsive layouts – that provide us with a starting point for our applications. By composing several of these views, we can quickly build common application layouts with minimal effort.

Can touch this

Our application isn't going to be very interesting if we can't interact with it. Famo.us events are captured at three different levels. The first level is on the Surface, the second is the Surface's parent context, and the third and final level is the Engine.

surface.on( 'click', function( e ) {
  console.log( 'Surface clicked', e );
});

context.on( 'resize', function() {
    console.log( 'Context resized' );
});

Engine.on( 'keyup', function( e ) {
  console.log( 'Keyup event', e );
});

Animation

We've covered many of Famo.us' basics, but we still seem to be focusing on displaying content, something that modern browser engines are pretty good at. Be it the animation of a scrollview or the transitions between screens and menus, our animations have to be smooth and feel natural to the user's input.

Famo.us achieves this by doing away with CSS animations, instead applying matrix transformations directly to the Surfaces it is animating. We will use a specific type of Modifier called a StateModifier , which is simply a Modifier which contains its own state via setTransform.

var context = Engine.createContext();
 
var surface = new Surface({
  content: 'My first surface'
}); 

var modifier = new StateModifier();
context.add( modifier ).add( surface );

modifier.setTransform(
  Transform.translate( 100, 300, 0 ),
  { duration : 1000, curve: Easing.inOutBack }
);

StateModifier's setTransform method allows animations to be triggered on Surfaces. The first parameter determines what we want to animate (such as rotations or opacity), and the second describes how it should perform the animation, including the duration and the type of animation curve. setTransform has a third parameter – a callback that runs once our animation is complete.

We're using Famo.us' Easing object to dictate the curve of our animation. The Easing object provides us with over 30 easing curves to use in our animations. Should we wish to sequence several animations, we can chain them by calling setTransform multiple times. When we chain animations, the previous animation will finish before the next one begins. Complex sequences of animations are simple to create using this pattern.

Physics

Where Famo.us really excels is the combination of its physics engine with animations. Our previous animation occurred over a fixed amount of time. Famo.us gives us the ability to create animations that occur for varying durations, depending on the way an element is interacted with, and with a choice of transition styles.

Transitionable.registerMethod( 'wall', WallTransition );

var surface = new Surface({
  size: [ 150, 150 ],
  properties: {
    backgroundColor: '#67b2e8'
  }
});

var modifier = new StateModifier();

context.add( modifier ).add( surface );

var wall = {
  method: 'wall',
  period: 500,
  dampingRatio: 0.1
};

modifier.setTransform(
  Transform.translate( 0, 400, 0 ),  wall
);

This example shows how to register a Transitionable method called 'wall', which we use as the second argument to our StateModifier's setTransform method.

This causes our Surface to animate 400 pixels from the top of our context over a duration of 500 milliseconds, and when it arrives at its destination it bounces as if it has hit a wall, causing it to come to a gradual stop.

What's below the surface

Famo.us is an exiting framework that is simple to use whilst being incredibly powerful. This article couldn't cover everything it does, so check out the resources in the sidebars to find out more.

Words: Mark Jones

Mark Jones is lead front end developer at Kaldor Group. Follow him on twitter at @mark_jones. This article first appeared in net magazine issue 260.

Like this? Read this!