Create high-performance mobile UIs with Famo.us

Level up to a native-code user experience for JavaScript apps

The JavaScript developer community eagerly greeted the spring 2014 public beta release of the Famo.us open source UI-rendering framework. Famo.us promises to eliminate some of the last bottlenecks that prevented JavaScript and web technology from dominating the mobile development scene: slow UIs and a poor user experience (UX).

Famo.us targets the hardware graphics processing unit (GPU) on the mobile device to achieve the highest possible rendering frame rate, and it adds a sophisticated physics engine for a gratifying UX. JavaScript developers are no longer at a disadvantage compared to Objective-C, Swift, or Java™ developers when they create UIs for mobile apps.

This article introduces the fundamental concepts of Famo.us and explores its design. It then goes hands-on with several working examples, including a typical mobile app UI that you can use as an application template for developing with Famo.us. (See Download to get the full sample code.)

How Famo.us works

Animation is created by the rapid display of successive pages (frames) with changed elements. Frame rate is the number of frames that are displayed per second. A high frame rate creates the optical illusion of motion because of the latency in human vision (the same principle behind motion pictures). To create animation on web pages, elements' style attributes — position, color, and opacity — are modified every frame. How quickly these attributes can be updated ultimately determines the maximum frame rate of a UI. For interaction with web applications, 60 frames per second (fps) is deemed the optimal rate for a smooth, native-application-like UX. When 60 fps can't be achieved consistently, undesirable UX effects such as jerkiness and dropouts (known collectively as Jank) occur.

rAF Modern browsers support animation libraries through the rAF mechanism. requestAnimationFrame() takes a callback function as an argument. The browser calls into the callback function before the next screen update, typically within the 16.7 ms time window that corresponds to 60 fps. Animation library implementations must call rAF again during rAF handling to set up the callback for the next frame. Cross-browser support for rAF is uneven. Chrome and Firefox now support rAF by default. Safari rAF support requires a special vendor prefix in the code. Famo.us uses a polyfill shim solution internally to accommodate the differences.

At its core, Famo.us is a cross-browser, high-performance UI layout, and optimization library with its own animation and physics engine — written in JavaScript. Famo.us is optimized to perform its work within the shortest possible time per frame. (A recent Famo.us benchmark shows that Famo.us can take as little as 1 to 2 ms per 16.7 ms frame while it handles typical animation and has no performance overhead otherwise.) The target UI can then be rendered at the best possible frame rate — typically a minimum of 60 fps. The Famo.us project is committed to maintaining or improving this fps performance in all subsequent releases.

Operationally, Famo.us replaces typical DOM-centric layout operations and 2D or 3D animations with its own high-performance alternative. Figure 1 illustrates the inner workings of Famo.us. The Famo.us engine (a singleton object) is called for every frame by the browser's requestAnimationFrame() (rAF) callback function. Sixty times per second corresponds to 60 fps or 1/60s (16.7 ms) per frame. Each callback is known as an engine tick in Famo.us.

Figure 1. Operation of the Famo.us library

The Famo.us API

The Famo.us API presents its own set of high-level composable objects — such as surfaces, views, and widgets— to the developer. Your Famo.us code creates the scene graph to be rendered (currently called a render tree in Famo.us), wires up event handling, and directs or schedules animations.

Each node in the Famo.us scene graph is a render node. The scene graph is processed into render spec and build spec intermediary representations that the Famo.us rendering component uses internally to make updating of the target more efficient.

The rendering component evaluates the scene graph at the supported frame rate (currently 60 fps) and outputs the required updates to the browser's DOM, a Canvas element, or WebGL. (The beta implementation of Famo.us targets the browser's DOM only.) Famo.us is designed to output to any of its supported targets — even simultaneously (supporting multimode operations) — on the same screen.

Surfaces and renderables

In Famo.us, you compose and animate renderables. The lowest-level common renderable that you work with is a Surface . A Surface displayed in the browser's DOM is a <div> element. (If you inspect a Famo.us Surface , you can see the <div> .) Other specialized types of surfaces in Famo.us are represented by other HTML5 elements:

Surface is a <div> .

is a . VideoSurface is a <video> .

is a . ImageSurface is a <img> .

is a . InputSurface is a <input> .

is a . CanvasSurface is a <canvas> .

The Surface class has a content field. This field is where you add the HTML to be rendered on the Surface (within the underlying <div> ).

Famo.us does not intervene in how or where you get this content , which is a string that is rendered as HTML. This string is a natural integration point for any template, data sourcing, or data binding technology. In addition, the mini-DOM that is formed by the HTML on a rendered Surface can be manipulated — except for any layout, containment, or animation work, which should be done through Famo.us.

“When you program with Famo.us, you gain in performance and rendering flexibility, in exchange for direct immediate-mode manipulation of the page's DOM.”

Immediate mode versus retained mode Famo.us is intrinsically a retained mode library. Basically, you declaratively describe the appearance and behavior of a scene graph (tree) of objects. The Famo.us engine builds an in-memory representation and then figures out how to efficiently update the DOM, once-per-frame, to display it. Current web developers, however, are typically more familiar with immediate-mode DOM manipulations — as in jQuery — whereby your code directly adds or deletes elements and modifies attributes to implement the UI. When you program with Famo.us, you gain in performance and rendering flexibility, in exchange for direct immediate-mode manipulation of the page's DOM.

Comparison with 3D rendering libraries

This architecture of Famo.us is similar to WebGL 3D-rendering JavaScript libraries, including Three.js and SceneJS. Knowing the similarities and differences can help to fast-track your mastery of Famo.us:

Frame rendering is triggered by browser rAF in both architectures.

In 3D rendering libraries, you position and place triangles and 2D objects (such as walls, or floors) and higher-level 3D geometries (such as pyramids, spheres, cubes, or meshes of polygons) into the scene graph. In Famo.us, you position and place 2D surfaces or higher-level objects such as views or widgets in your scene graph.

In 3D rendering libraries, you can transform an object by adding and chaining transformation nodes in the scene graph, where the object is the leaf. In Famo.us, you can add and chain modifier nodes into the scene graph, where a surface or other renderable is the leaf.

In SceneJS, you can specify a complex scene graph by using JSON and modify it through ID attributes on the graph nodes. Famo.us has a Scene component that you can use to build a complex scene graph via JSON; you modify it through ID attributes on render nodes.

component that you can use to build a complex scene graph via JSON; you modify it through ID attributes on render nodes. In 3D libraries, object attributes are animated and tweened on rAF callbacks. In Famo.us, the properties of surfaces (more generally, renderables) are updated and tweened on each tick of the Famo.us engine, which is driven by the browser's rAF callback.

3D libraries often include a physics engine to make animation more realistic. Famo.us includes a full physics engine to make animations more intuitive to the user.

Even though all objects in a 3D scene are composed of triangles, developers seldom need to work with individual triangles because 3D rendering libraries typically include meshes of common geometries such as spheres and pyramids. Even though the fundamental renderable in Famo.us is a surface, you seldom need to work with an individual surface because the library includes many prefabricated high-level UI views and widgets.

HTML has no role in a 3D library other than the Canvas element being the element that the 3D viewport is rendered on. In Famo.us, HTML is what you render on a surface, but HTML no longer defines the structure of the UI page; the structure is specified in JavaScript code. You no longer need to manage a raft of< <div> or <table> tags to control layout; and CSS is used only to affect element style, not layout. (HTML is still important because — when the DOM is targeted — all visual elements are HTML elements.)

or tags to control layout; and CSS is used only to affect element style, not layout. (HTML is still important because — when the DOM is targeted — all visual elements are HTML elements.) The rendering component of 3D libraries typically targets Canvas, WebGL, and SVG output — favoring WebGL for its almost direct access to the underlying 3D-rendering GPU hardware. The Famo.us rendering component targets Canvas, DOM, and WebGL. Famo.us has a DOM updater that uses known GPU optimizations offered by leading browsers to achieve its performance objective.

Working with Famo.us

Three ways to run Famo.us The easiest way to get Famo.us up and running is to download the Famo.us starter kit. Decompress the .zip file and click an example web page to see Famo.us running. The starter kit's code loads Famo.us from a content delivery network. You can easily experiment with Famo.us in the Famo.us University. In this online e-learning platform, you can modify example Famo.us code and immediately see the effect in side-by-side output windows. If you plan to host Famo.us apps on your own server, use the Famo.us Toolbelt (based on grunt and bower). With the Toolbelt, you can create a development-test-deployment environment from the latest Famo.us GitHub repository code by using the yeoman-based generator-famous package.

A look at an actual Famo.us app helps you anchor the concepts. The example in Figure 2 is the default application included with the Famo.us starter kit and also generated by default by the generator-famous package (see Three ways to run Famo.us). The application trivially rotates a rendition of the Famo.us logo around the y-axis.

Run the example app

Figure 2. Default application that is generated by generator-famous

In main.js, the solo ImageSurface is created by the code that is shown in Listing 1.

Listing 1. Creating the ImageSurface (renders to DOM <img>)

var logo = new ImageSurface({ size: [200, 200], content: '/content/images/famous_logo.png', classes: ['backfaceVisibility'] });

Setting options in Famo.us object instantiation A Famo.us object contains a set of default options. When you create an object instance, you can pass an options object, consisting of key-value pairs, as an argument. The values that you specify in the argument override the defaults — a standard pattern that recurs throughout Famo.us.

In Listing 1, the content option corresponds to the src attribute of the underlying <img> tag. The classes option adds CSS classes to the HTML element. The backfaceVisibility CSS class, which ensures that users can see the back side of the logo as it turns, is defined in app.css as:

.backfaceVisibility { -webkit-backface-visibility: visible; backface-visibility: visible; }

versus The origin attribute specifies the anchor point of an element around which a rotate transformation operates. The align attribute specifies the placement anchor point of a child's origin within the parent's display area. Both attributes can span from [0,0] to [1,1], and you use the same convention (shown in Figure 3) for specifying them. For example, the attributes origin = [0, 0] and align = [0, 0] place the element at the upper-left corner of its containing parent with the point (0,0) of the element that is aligned with the point (0,0) of the parent. Any rotation of the element is performed with respect to the element's coordinate (0,0) — the upper-left corner, not its center. If align is undefined (not specified), it defaults to the value of origin .

Context— a mini DOM manager that is associated with a DOM node — manages one scene graph, represented by all the DOM within the node. Typically you have only one context instance (unless you're working on special projects such as multiple perspectives or heads-up displays).

Modifiers are render nodes in the Famo.us scene graph that modify some attributes of (typically, apply transformations to) render nodes below them. You can chain multiple modifiers together, in which case the transformation matrixes are combined together, and the leaves of the scene graph are always renderables.

In this example, the centerSpinModifier contains an origin property and a transform property. The origin property is specified relative to the containing parent — in this case, the Famo.us context. Figure 3 shows the convention for specifying the Famo.us origin .

Figure 3. Convention for setting the origin property

In Figure 3, nine points are arranged in a 3x3 grid. [0.0] is at the upper left, and [1,1] is at the lower right. [0.5, 0.5] is the center point. The rest of the points follow this convention, such as [1,0] at the upper right and [0,1] at the lower left.

The transform attribute in centerSpinModifier returns a function that rotates around the y-axis, via Transform.rotateY() :

var initialTime = Date.now(); var centerSpinModifier = new Modifier({ origin: [0.5, 0.5], transform : function() { return Transform.rotateY(.002 * (Date.now() - initialTime)); } }); mainContext.add(centerSpinModifier).add(logo);

This code completes the simple scene graph that is shown in Figure 4.

Figure 4. Scene graph for the default application

Now the Famo.us engine evaluates the in-memory scene graph and efficiently updates the DOM on every frame, approximately 60 times per second, driven by rAF. Each time, the centerSpinModifer checks the time that is elapsed since initialTime and rotates the logo incrementally around the y-axis. You can easily adjust the 0.002 constant to vary the speed of the spin.

To summarize:

You create the logo ImageSurface . You create a Famo.us Modifier that: Sets the logo to rotate around its own center ( origin = [0.5 0.5]) at the center of the context

= [0.5 0.5]) at the center of the context Uses a transform that incrementally rotates around the y-axis You add the Modifier to the scene graph and then add the logo ImageSurface directly below the Modifier . The Famo.us engine processes the scene graph and updates the DOM at rAF rate, and the logo rotates nonstop.

Extending the example

To take the default example further, the next example rotates 100 instances of the logo, laid out in the form of a 10x10 square, alternating rotation between the x-axis and the y-axis. Figure 5 shows the final app in action.

Run the example app

Figure 5. Extended app that rotates 100 logo instances

Figure 6 shows the scene graph that must be constructed for this example.

Figure 6. Scene graph of extended app

You can see how this version extends the previous example:

The scene graph now has 100 branches instead of 1.

Each branch now has two modifiers. Another translation modifier is created and added before the rotation modifier to move the logo first to the required position.

The code to create the modifiers and add them to the context for each logo, which is shown in Listing 2, is similar to the first example. It contains the computation work to animate and update every frame.

Listing 2. Extended example of code to rotate 100 logos

var mainContext = Engine.createContext(); var initialTime = Date.now(); function rotY() { return Transform.rotateY(.002 * (Date.now() - initialTime)); } function rotX() { return Transform.rotateX(.002 * (Date.now() - initialTime)); } for (var i=0; i< 10; i ++) for (var j=0; j<10; j++) { var image = new ImageSurface({ size: [50, 50], content: '/content/images/famous_logo.png' }); var transMod = new Modifier({ size: image.getSize.bind(image), transform: Transform.translate(j * 50, i * 50, 0) } ); var rotMod = new Modifier({ origin: [0.5, 0.5], // xor transform : (((i % 2) !== (j % 2)) ? rotY : rotX) }); mainContext.add(transMod).add(rotMod).add(image);

This app has two separate transform functions, rotY to rotate around the y-axis and rotX to rotate around x-axis. The branches of the scene graph are created in a nested i - j loop. The two modifiers added to each branch are named transMod (translation of the logo image into place) and rotMod (rotation of the logo around its own origin).

To alternate between x-axis and y-axis rotation, rotMod 's transform attribute is changed alternately by:

transform : (((i % 2) !== (j % 2)) ? rotY : rotX)

As in the first example, you set up the scene graph in-memory, and the Famo.us engine takes care of processing it and updating the DOM at rAF rate.

Transitions and tweens for animation

In UI creation, you typically work with animations of finite duration. One example is the "bounce" that users observe when they reach the end of a scrolling list. Another is the flipping of a playing card from its back to reveal its face.

Famo.us supports transitions via the Transitionable class, which represents attributes that can make the transition over time. The next example shows the use of two tween transitions.

Run the example app

When you run the app, you see a list of developerWorks articles that displays within a Famo.us scroll view in the middle of the page. This list rotates around the y-axis at a constant pace for the first 10 seconds. Then, it snaps back violently and bounces around for the next 10 seconds. While this finite-duration animation is taking place, you can continually scroll through the list (with your mouse wheel if you're using a desktop browser). Figure 7 shows the app in action.

Figure 7. Scrolling article list rotates around y-axis that is animated by tween transitions

Views such as the scrolling list of articles are prefabricated higher-level Famo.us components (in this case a ScrollContainer ) that can be composed together for easy UI creation. The next example explores Famo.us views and widgets in more detail. For now, it is sufficient to understand that the scroll list consists of a sequenced set of Famo.us Surface s. Listing 3 shows the list-creation code.

Listing 3. Creating a scrolling list of articles

function createArticlesList() { artListSVC = new ScrollContainer({ scrollview: {direction: Utility.Direction.Y} }); var lines = []; artListSVC.sequenceFrom(lines); for (var i in articles) { var surf = new Surface({ content: '<div class"a-title">' + articles[i].title + '</div>', size: [undefined, 50], properties: { textAlign: 'left', color: 'black' } }); surf.addClass('article-cell'); surf.addClass('backfaceVisibility'); surf.artIdx = i; surf.pipe(eh); lines.push(surf); } artListSVC.container.addClass('backfaceVisibility'); artListSVC.container.addClass('borderbox'); }

In Listing 3, an array of Famo.us Surface s is created, named lines . Each Surface created displays the name of one developerWorks article. A Famo.us ScrollContainer named artListSVC is created, and its sequenceFrom() method is used to configure the scroll list with the lines array.

Programming tween transitions

A view such as artListSVC is also a renderable (managing its own internal scene graph with renderables as leaves). The view can be transformed via a modifier (or modifiers) and added to the context's scene graph, as in previous examples. The code that adds artListSVC to the context is:

var sm = new StateModifier({align:[0.5, 0.5], origin: [0.5, 0.5]}); mainContext.add(sm).add(artListSVC);

A StateModifier is a modifier that maintains state over time (internally, through Transitionable s). When you animate by using tween transitions, you specify only the beginning and end states (also known as key frames). The tween transition interpolates the intermediate state values and supplies them to the rendering engine on every tick. You do not need to calculate or maintain intermediate state in your own code.

Listing 4 shows the code that programs the tween transition.

Listing 4. Animating with tween transition

Transitionable.registerMethod('tween', TweenTransition); � sm.setTransform(Transform.rotateY(Math.PI), {method: 'tween', curve:'linear', duration:10000}, function() { sm.setTransform(Transform.rotateY(2 * Math.PI), {method: 'tween', duration: 10000, curve: 'spring'}); });

Easing During tweening, attribute values are interpolated over time between fixed points that you specify. This interpolation is linear by default, but a tweening engine also typically supports an easing feature, whereby you can specify a rate-of-change control curve that is used during interpolation (increasing quadratic interpolation or decreasing quartic interpolation, for example). Famo.us offers more than a dozen easing curves through famous.transitions.Easing that you can use for your tweens.

The code in Listing 4 first registers TweenTransition as a tween method with Transitionable . The setTransform() method of the StateModifier is then used to add the tweened rotateY transform. The setTransform() method takes a transform as the first argument, a Transitionable as the second, and a completion callback function as the third.

In Listing 4, the first animated transition lasts 10 seconds, and the scroll list rotates around the y-axis at a linear pace. When this first tween completes, the callback is fired, and the second tween uses a spring curve that snaps back and bounces for the next 10 seconds.

It's not necessary to register the TweenTransition explicitly, because Famo.us uses TweenTransition by default if the method attribute is not specified for a Transitionable . However, Listing 4 illustrates how you might register another transitioning method — such as a transition from the Famo.us physics engine. (Coverage of the Famo.us physics engine is out of scope for this article.)

Rendering perspective

The rendering perspective, which you specify in pixels, correlates to the distance from the viewer's "camera" to the scene that is rendered. Use Context.setPerspective() to set this value. A smaller value brings the viewer closer to the rendered objects while it maintain the same field of view and is loosely analogous to a wide-angle lens on a camera. By varying the perspective, you can enhance the look of many animations. Perspective is set to 500 in this example for a more dramatic effect.

Applying Famo.us to a typical mobile app UI

The examples so far operate Famo.us like a 3D rendering library, except with 2D surfaces — pure object animation. The next sample confirms that this style of composition and animation translates to the construction of a mobile UI.

Figure 8 shows a common mobile application layout. A navigation bar is at the top, and "back" and "more" buttons (not shown in Figure 8) are activated depending on the state of the UI. At the bottom is a tab bar that consists of a set of toggle buttons (each of which you can style by using CSS) that selects the items that are displayed in the content area. The content area is in the middle, flexibly sized depending on the device size.

Figure 8. Composing mobile app UIs in Famo.us

Data binding is not included The Famo.us library is a UI creation library and does not include any data binding or template technology. In the mobile app example, the source of data is encapsulated in a DataSource class. Through this data source, the lists of articles and videos are obtained for display within the UI. You can use any data access technology with Famo.us.

Typically, the app contains a thumb-scrollable list of items that users can further explore. In this example app, you can choose between a list of developerWorks articles and a list of open source movies.

When the user selects one of the items, the content area changes to display the selected item. The item might open only within the content area (that is, with header and footer still visible) or occupy the entire screen (obscuring the header and footer).

Trying out the app

Try the app in your mobile browser:

Run app on mobile device

When the app starts, it displays the list of articles, as in Figure 9.

Figure 9. App display of developerWorks articles

If you touch the Videos button in the tab bar, the list of open source movies is displayed, as in Figure 10.

Figure 10. App display of open source videos

Bandwidth warning On some devices or browsers, selecting a video might download the entire large file.

If you touch one of the videos in the list, it loads and plays in the content area, as in Figure 11.

Figure 11. App playing a movie

In Figure 11, notice that the navigation bar now shows a back button. If you touch that button, the list of videos is displayed again. Touch Articles again to redisplay the list of articles. Now touch one of the article names. The app loads and displays the selected article, as in Figure 12.

Figure 12. App display of a selected article

Using Famo.us views and widgets

Combining Famo.us views and widgets makes creation of the mobile app UI straightforward.

Eventing in Famo.us Famo.us components can communicate with one another in a loosely coupled manner by using events. You can source and sink events by creating instances of EventHandler . Views typically have built-in incoming and outgoing event handlers. The pipe() method pushes events to another event handler, and the subscribe() method pulls from another event handler.

Surfaces (and renderables in general) and views can be composed into widgets. Views can contain complex orchestration and interaction logic among the managed renderables. Views can receive, process, and emit events by using the eventing support in Famo.us. Widgets are themselves render nodes that can be added as leaves of the context's scene graph. Famo.us comes with a selection of ready-to-use views and widgets:

Scrollview controls a sequential list of renderables (in x or y direction) and enables scrolling through the list with touch or mouse — typically, through a sequence of surfaces.

controls a sequential list of renderables (in x or y direction) and enables scrolling through the list with touch or mouse — typically, through a sequence of surfaces. HeaderFooterLayout manages three renderables: a header and a footer of specified size, and a content area of variable size. This view is used to lay out the example's mobile UI.

manages three renderables: a header and a footer of specified size, and a content area of variable size. This view is used to lay out the example's mobile UI. EdgeSwapper is container that manages display of multiple renderables by sliding them in from the edge of the parent. The example mobile UI uses this view to display the two scrollable lists.

is container that manages display of multiple renderables by sliding them in from the edge of the parent. The example mobile UI uses this view to display the two scrollable lists. ScrollContainer is view that contains a Scrollview and a managed Surface that is used to clip the ScrollView 's displayed content. The example mobile UI uses a ScrollContainer in the HeaderFooterLayout 's content area to display lists of articles or videos.

The example mobile UI uses three widgets:

NavigationBar is a mini app-view that manages the display of a title surface and two clickable surfaces that represent the "back" and "more" buttons of a navigation bar. The widget emits back and more events.

is a mini app-view that manages the display of a title surface and two clickable surfaces that represent the "back" and "more" buttons of a navigation bar. The widget emits and events. TabBar manages a horizontally or vertically laid out bar of widgets. (The default is a toggle button.) When a managed widget is selected, its corresponding ID is emitted with a select event.

manages a horizontally or vertically laid out bar of widgets. (The default is a toggle button.) When a managed widget is selected, its corresponding ID is emitted with a event. ToggleButton is a button, either on or off, that displays two managed surfaces.

Using the available views and widgets, the mobile UI becomes the scene graph that is shown in Figure 13.

Figure 13. Scene graph for the mobile app UI

Although the context is still at the root of the tree, the modifiers and leaves of the tree are no longer easily discernable. Each composed Famo.us view encapsulates components' management details, providing the expected user interactions and behavior — and eliminating the need for you to code them.

To create the mobile UI, you code up the scene graph; the Famo.us engine then processes it and updates the DOM at rAF rate:

Create the list of articles — a Famo.us ScrollContainer containing a Scrollview managing a list of Surface s (cells in the list), one for each article: function createArticlesList() { artListSVC = new ScrollContainer({ scrollview: {direction: Utility.Direction.Y} }); var lines = []; artListSVC.sequenceFrom(lines); for (var i in articles) { var surf = new Surface({ content: '<div class="a-title">' + articles[i].title + '</div><div class="a-desc">' + articles[i].desc + '</div>', size: [undefined, 100], properties: { itemType: 'article', listIndex: i, textAlign: 'left', color: 'black' } }); surf.artIdx = i; surf.pipe(eh); lines.push(surf); } } function createWebSurface() { wb = new Surface( ); } Notice the highlighted content property, which is set to the HTML used to render a cell in the list, together with the associated CSS classes. itemType and listIndex are two custom properties that identify the actual data item that is selected in the click event handler. Create the list of videos. (The code, not shown here, is similar to the step 1 code). Create a Surface to display a selected article: function createWebSurface() { wb = new Surface( ); } Create a Surface to display a selected video: function createVideoSurface() { vs = new VideoSurface( { size: [undefined,undefined], autoplay: true } ); } Create the NavigationBar widget and add it to the header: function addHeader() { nb = new NavigationBar({ size: [undefined, 75], content: 'dW Famo.us', moreContent: '', backContent: '', properties: { lineHeight: '75px' } }); layout.header.add(nb); eh.subscribe(nb); eh.on('back', function() { rc.setOptions({ inTransition: false, outTransition: true }); if (backTarget !== undefined) rc.show(backTarget); setNavbarBack(false, undefined); }); } Create the EdgeSwapper view and add it to the content area. This controller swaps in the list of articles, the list of videos, the display of one article, or the display of one video: function addContent() { rc = new EdgeSwapper({ overlap: false, outTransition: false, size:[undefined, undefined] }); layout.content.add(rc); } Create the tab bar and add it to the footer: function addFooter() { var tb = new TabBar({ }); layout.footer.add(tb); tb.defineSection(0,{content: 'Articles', onClasses: ['tabbuton'], offClasses: ['tabbutoff']}); tb.defineSection(1,{content: 'Videos', onClasses: ['tabbuton'], offClasses:['tabbutoff']}); tb.select(0); eh.subscribe(tb); eh.on('select', function(but) { rc.setOptions({ inTransition: false, outTransition: false }); switch (but.id) { case 0: rc.show(artListSVC); break; case 1: rc.show(vidListSVC); break; } setNavbarBack(false, undefined); }); }

The CSS tabbuton class styles the button's on state, and tubbutoff styles the off state. The event handler for the select event displays the article list if button 0 is touched or the video list if button 1 is touched. Display the article list in the content area. Add an event handler for click events that are emitted from the selection within the Scrollview : function init() { rc.show(artListSVC); eh.on('click', function(obj) { rc.setOptions( { inTransition: true, outTransition: false }); var surfaceProps = obj.origin.getProperties(); if (surfaceProps.itemType === 'article') { wb.setContent('<iframe width="100%" height="100%" src="' + articles[surfaceProps.listIndex].url + '"></iframe>'); rc.show(wb); setNavbarBack(true, artListSVC); } else { // video vs.setContent(videos[surfaceProps.listIndex].url); rc.show(vs); setNavbarBack(true, vidListSVC); } }); }

The in-memory scene graph is now specified, and required event handlers are all wired, ready for the Famo.us engine to process.

Examine the Famo.us scene graph that is created in this example (see Figure 13). You might easily modify it to select and display other information — simply by changing the data source and modifying styles. Such modifications can even be parameterized and automated. Essentially, you can create a UI application template for a general class of "browse a list and select to show item" mobile apps. A comprehensive collection of such application templates can be built over time to cover a large variety of possible app areas.

Conclusion

Famo.us interview Learn about the inspiration behind Famo.us and the vision for its future in an interview that Sing Li conducted with Steve Newcomb, CEO and co-founder of Famo.us.

Creating native-code mobile apps is hard. You need to climb steep learning curves not only of multiple mobile operating systems (and revision differences), but also of different programming languages and hundreds of system APIs on each platform. Add proprietary or customized toolbelts, plus varying build-and-deployment pipelines and marketplaces, and you end up with a potpourri of fast-evolving technologies that you must keep up with and support. Even if you have ready-to-use boilerplate code for your application, adapting it to a new problem across mobile platforms can take weeks or months of coding and debugging.

Meanwhile, web technologies for mobile app development, despite their cross-platform promise, fall far short of delivering a native-code UX — until now. The Famo.us framework combines recent breakthroughs in browser optimization and mature concepts from 3D rendering libraries to deliver a high-performance, easy-to-use, highly automatable UI-creation platform for mobile web applications. Now JavaScript developers can easily create mobile apps that offer user experiences that rival native-code implementations.

Acknowledgments

The author would like to thank Andrew De Andrade and Larry Robinson of Famo.us for their kind assistance in reviewing the article; also Jeanne Feldkamp and Steve Newcomb for their time and the interview.

Downloadable resources

Related topics