News

Get faster animations with Famo.us

Build an image gallery super quickly with Famo.us’ surfaces, modifiers and animations

famous

Build an image gallery super quickly with Famo.us’ surfaces, modifiers and animations

famous


Ever since the advent of computing, we’ve been pushing for more power, more speed, more memory, more storage. Even though it arrived 40 odd years after the first computers, the web is not free of this demand, we need more!

Then JavaScript was added to a browser, but still we’ve been pushing our browsers to do things quicker and quicker – from face detection to generating whole 3D worlds, we can do it all. One thing, however, still remains a little slow – DOM manipulation. Manipulating the DOM in almost any way triggers redraws and reflows that can seriously affect performance.

Now that we have CSS3, JS and hardware acceleration, there are ways around this and we now have the tools to squeeze the best out of every cycle, but as with all things browser-based, each approach and browser has its quirks. But now, we have Famo.us, a JavaScript library that abstracts away all of the mucking about with DOM reflows and animations. We make the web app with the transitions we need, and it makes the transitions with the 60FPS we deserve.


GET THE TUTORIAL CODE HERE

Set it up

We’re going to need Famo.us.js to get started with the image gallery project for our tutorial. For simplicity’s sake, you can download and retrieve the starter project from our dedicated FileSilo site (filesilo.co.uk/webdesigner). In the root of the folder is a simple Node.js/Express app. Just enter:

001    npm install; node server.js;      

 
and you’ll be able to view the project files at http://localhost:8118;

Get global

In the src folder, open the main.js file. To help us keep track of the bits of Famo.us that we will end up using, we’re going to create some references for the global objects that Famo.us declares as we import them into our project.

001    var Engine = famous.core.Engine;
002    var Modifier = famous.core.Modifier;
003    var Transform = famous.core.Transform;    
004    var ImageSurface = famous.surfaces.    ImageSurface;
005    var ContainerSurface = famous.surfaces.    ContainerSurface;
006    var EventHandler = famous.core.        EventHandler;                            
007    var Transitionable = famous.transitions.    Transitionable    
008   

 

Create a context

In order to add things to our Famo.us app, we will need to first create a context for our Famo.us app. The Famo.us context is kind of similar to a <canvas> context, but it works more like a virtual DOM than a series of points. We can create a context by adding the following line to main.js

001   var mainContext = Engine.createContext();

Make the gallery

We will need to use a lot of images to really tax the Famo.us rendering engine. The assets provided on FileSilo contain 20 Creative Commons images that can be used to create a mock gallery. Replace these with your own images to create a more personal gallery.  In main.js, we create an images array with the name of the files that we are going to add to our project and then some simple variables that will then help us to position them inside of our window:

001    var images = ["1.jpg", "2.jpg", "3.jpg",     "4.jpg", "5.jpg", "6.jpg", "7.jpg", "8.jpg",     "9.jpg", "10.jpg", "11.jpg", "12.jpg", "13.    jpg", "14.jpg", "15.jpg", "16.jpg", "17.jpg",     "18.jpg", "19.jpg", "20.jpg"];
002    var thumbSize = Math.ceil(window.innerWidth / 7),
003    columns = window.innerWidth / thumbSize,    
004    rows = window.innerHeight / thumbSize,
005    idx = 0,
006    count = 0;

The ImageSurface

With Famo.us, we don’t need to create any nodes in the DOM, we create surfaces that correspond to various DOM elements instead. We can then adjust the properties and appearance of the surfaces before we add them to our context. This will enable Famo.us to optimise element rendering ahead of time. We can create an image surface for each of our gallery images like the snippet below:

001    for(var y = 0; y <= rows; y += 1){
002    for(var x = 0; x < columns; x += 1){
003    var img = new ImageSurface({        
004    size : [thumbSize, thumbSize],
005    content : '/assets/img/gallery/' +     images[idx],
006    classes: ['pointer']
007    });

The various surfaces

There are seven different types of surfaces for us to make use of. The standard surface equates to a <div>, imageSurface – <img>, inputSurface – <input type=”text”>, textareaSurface – <textarea>, submitInputSurface – <input type=”submit”>, canvasSurface – <canvas> and videoSurface – <video>. Note that you can interface with each of the created elements just as you would in any ordinary HTML, JavaScript or CSS web page, but the problem there is that it’s not like Famo.us will get to do its nifty optimisations. By interfacing with these elements through Famo.us, we’re trusting that we are going to get the best performances out of using this method.

Understand the modifiers

Modifiers will enable us to modify the properties of a surface in Famo.us. We can use them to create animations, transform positions, size and skews, and we can also chain them together to generate cumulative effects! That sounds pretty neat huh? Here, we will create two modifiers, one for positioning our ImageSurface in our context, and then another modifier for animating into its rightful place.

001    var posMod = new Modifier({ 
002    origin : [0, 0]
003    });        
004    var animateToPosition = new Modifier({transform : Transform.identity});
005    animateToPosition.setTransform(Transform.translate(x * thumbSize, y * thumbSize, 0),   
006   {curve : 'easeInOut', duration : 1000});   

 
                       

Animate with modifiers

Our animateToPosition modifier is how we tell a surface to move about with our context once the modifier has been added to it. First, what we will do is we create an object to access the Modifier method. Next we will then call the setTransform() function to do exactly that: set the transform we want the modifier to perform. The first argument is a Transform object which will describe the transformation, the second argument are the properties for timing the animation. In this instance we are creating a transformation that will last one second, and this will accelerate and then slow down to complete the transform.

Piping events

We’re working through a finite amount of images that we want to use to fill our context, and as such, we need to update some simple indexes that will end up making our lives a little simpler:

001    idx += 1; 
002    count += 1;
003    if(idx > images.length – 1){
004    idx = 0;
005    }
006    

If we run out of new images, start again.

Bind to EventHandlers

When we click or tap an image, we want to see a bigger version of it come up. This sounds pretty simple, just addEventListener(), right? Well Famo.us doesn’t use event binders. Instead, Famo.us we will ‘pipe’ (or pull) events to and from our surfaces. First, we create an EventHandler (which is one of the Global Famo.us objects that we imported at the start), then we ‘pipe’ that EventHandler through to the surface that we want to interact with, and in this scenario, we want it to interact with our ImageSurface:

001    var eventHandler = new EventHandler();    
002    img.pipe(eventHandler);

Create an overlay

Famo.us EventHandlers can both emit and listen for events. This is great as it enables us to use interactions to trigger behaviours on elements that aren’t directly associated with the object interacted with (which is basically everything in Famo.us) and keep our code nice and modular. Binding functions to events is very similar to addEventListener().

001 eventHandler.on('click', function(e) {  // ...Code to do stuff... });

Fade in

Much as when we created our image surfaces, for our overlay we need to create a different kind of surface: a ContainerSurface. Where an ImageSurface is equivalent to an <img> element, a ContainerSurface is a <div> with the sole purpose of containing other surfaces/elements. For our overlay, we need two container elements, one for the semiopaque black background, which goes over all of our gallery images, and nested within that another container to contain the larger version of our image.

001    var overlay = new ContainerSurface({
002    size : [window.innerWidth, window.    innerHeight],
003    properties : {
004    background : "rgba(0,0,0,.7)"
005    }    
006    });        
007    var focusHolder = new ContainerSurface({
008    size: [500, 500],
009    properties: { 
010    background: 'rgba(255,255,255,1.0)' 
011    }
012    });
013

Set surfaces

Earlier, we used transforms to animate the position of an element on our context, but we can also animate opacity and sizes with a similar bit of code. If we create another modifier, opacityMod, we can use it on any of our surfaces, and, when applied to a surface the modifier will cause it to fade in from zero opacity (transparent) to one opacity (opaque).

001        var opacityMod = new Modifier({        
002             transform : Transform.identity,
003              opacity : 0    
004        });
005        opacityMod.setOpacity(1, {duration : 1500});

Layer it up

Unlike the DOM, Famo.us does not have a concept of flow. Elements are positioned on top of one another without regard for their own position or that of elements around them. Although it defeats the purpose of Famo.us to implement browser flow, we can use modifiers to control the points from which our surfaces draw themselves and how they position themselves relevant to their parents. In this case, we want everything to be in the middle of our screen. By changing our origin to [0.5, 0.5] we’re setting our surfaces to draw from the centre of themselves, and by setting ‘align’ to [0.5, 0.5] we’re setting those surfaces to align their origin points to the centre of the parent surface:

001    var centerModifier = new Modifier({    
002    align: [0.5, 0.5],
003    origin: [0.5, 0.5]     
004    });
005

Hide overlays

Now that we have all of our surfaces and modifiers, it’s time to add them all together so they display properly when we click on a thumbnail. First we want to add the centerModifier to our overlay (so that everything is centrally alligned).

001  overlay.add(centerModifier).add(focusHolder);
002

Get focused

Next, we add our focusHolder (the white div element) to our overlay, to which we then add the centerModifier (one more time), the opacityMod modifier and then the image. This will create a fading in effect for our image after we’ve selected it from the gallery. Finally, we will add the centerModifier with overlay to our mainContext, which will draw it to our screen.

001    focusHolder.add(centerModifier).add(opacityMod).add(imgSurf)
002    mainContext.add(centerModifier).add(overlay);    
003

Hide overlays

Once our overlay displays, we want to be able to hide it. That’s easy, all we need to do is create another EventHandler to delete the element. When the handler is triggered (in this case by clicking outside of the element) we look up the parent container and remove its child element (our white box) from the DOM.

001    var exitHandler = new EventHandler();
002    overlay.pipe(exitHandler);
003    exitHandler.on('click', function(e){
004    console.log(overlay);
005    overlay._element.parentNode.removeChild(overlay._element);        
006    });        
007

Full circle

That’s it. We’ve now covered the basics of Famo.us’ surfaces, modifiers and properties. We’ve only scratched the surface here – there are tons of things that Famo.us is capable of doing at superhigh frame rates. Go! Make something awesome.

GET THE LATEST ISSUE OF WEB DESIGNER NOW

×