Build the smoothest UIs you've ever experienced

Mark Jones explains how the Famo.us framework can be used to bring native application performance to the web.

60 frames per second. It's the target we should be aiming for when building digital interfaces. Despite improvements to our development tools, it is difficult – and at times seemingly impossible – to achieve animations that run smoothly and feel natural.

Why is this? Browser layout engines like WebKit and Gecko were built for rendering text and links with a few images in-between, rather than the complex applications we see in browsers today.

With the success of initiatives such as Jank Free, it's clear developers trying to create experiences matching their native counterparts are required to understand browser rendering intimately. Whilst this isn't something to discourage, Famo.us aims to provide a different option. Famo.us is “a free, open source JavaScript framework that helps you create smooth, complex UIs for any screen”.

At its core is a 3D layout engine alongside a 3D physics-based animation engine. Famo.us renders to the DOM, with Canvas and WebGL rendering contexts coming in the near future.

To follow along with this tutorial, download the repository at github.com and use main.js as your starting point. main.js requires the modules needed for all of the code samples, but when developing properly you will only want to require the specific modules your code uses.

Each code sample has a corresponding branch in the netmag-famous repository that you can switch to in order to see the code in full, or if you have trouble getting an example to work.

Creating Context

The first aspect of Famo.us to understand is the concept of a ‘context'. A context is not a visible element, but instead provides the frame in which a Famo.us application will run.

var context = Engine.createContext();

To create something that our users will be able to see, we need to create some Renderables, then add them to our context. A Renderable is anything that can be seen on the screen in our Famo.us context, the most primitive of these being a Surface.

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

Surfaces map to elements in the DOM tree, in this case a <div> containing a text node. Our content property contains a string that will be inserted into the Surface. This can be an arbitrarily long string of HTML, following the normal rules of HTML. Also available are ImageSurfaces, CanvasSurfaces, InputSurfaces and VideoSurfaces, with all of these mapping to their corresponding HTML element.

Size

Passing in the size property when we create our Surface allows us to specify width and height in a variety of ways. We can specify our Surface's dimensions as a number (in pixels), as undefined (100 per cent of the Surface's parent context width/height) and also as true (size the Surface to the width/height of its contents).

var surface = new Surface({
  content: 'My first surface',
  size: [ 150, 150 ]
});

Styling

Right now our Surface is looking kind of boring. We can make it more visually appealing by passing in a properties object during the Surface's instantiation. You'll notice these are standard CSS properties, following the rule that the HTML inside a Surface operates untouched by Famo.us.

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

This method of styling Surfaces may seem unusual at first, but the reasoning is linked to Famo.us' rendering being abstracted from the DOM. When the Canvas and WebGL contexts are finished, we will be able to render our application to either one due to not relying on DOM-based concepts such as classes and IDs.

Transform

Our Surface will render in the top left of our context by default. To move our Surface away from the top left corner, we need to create a Modifier.

var modifier = new Modifier({
  transform: Transform.translate( 100, 100, 0 )
});

var surface = new Surface({
  content: 'My first surface'
}); 

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

Modifiers let us define a collection of visual changes to apply to our Surface. The Modifier we have created applies a transform matrix, causing our Surface to be rendered 100 pixels from the top and left-hand edges of our context. Modifiers also allow us to change our Surface's opacity, size, origin and alignment.

Origin and alignment

A Modifier that changes our Surface's origin and alignment is another way that we can lay out our interface.

var modifier = new Modifier({
  align: [ 0.5, 0.5 ],
  origin: [ 0.5, 0.5 ]
});

var surface = new Surface({
  content: ‘My first surface',
  size: [ 150, 150 ],
  properties: {
    color: ‘white',
    backgroundColor: ‘#67b2e8'
  }
});

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

The align property defines the Modifier's anchor point for the parent context, and the origin property defines the anchor point for the child Surface. These particular values will cause our Surface to be centred within our context. Notice how the Surface adjusts its position when we change our browser window size.

Try setting the align array to [0, 0]. This causes the centre of our Surface to anchor to the top-left of the parent context, as this is now the parent context's anchor point.

Next page: the tutorial continues...