Native Mobile to Web #2: Path Menu

Native Mobile to Web #2: Path Menu

Here it is, another post where I take some cool native UI and reproduce using only HTML5 technologies. The first one was the Facebook Login page. The result was a pixel perfect UI, just as nice as the native one. Now I’m taking the Path animated menu!

For those who don’t know Path, it’s a social network for connecting with family and close friends, available for iOS and Android. It has very interesting features, but the thing that really caught my attention was their “fan-out animated menu”. When you tap the menu, the options fan out, with a nice circular animation. It’s so cool that I had to take this UI and port it. I translated <divs> to Ext.Buttons, created the CSS3 animation and calculations, so everything works properly with Touch components.

Original iOS Interface

This is the result below! You need to use a WebKit Browser in order to see it (Smartphone browsers, Google Chrome or Safari).

*Touch compatibility webkit only

Animation

Inside the path menu container I have one main button, and other small ones. They all are absolute positioned, and stacked together. So the main button actually covers the small ones, having a z-index: 2. When this button is tapped, either fanOut or fanIn methods are triggered. Those methods iterate over each item, animating them with methods fanOutItem or fanInItem.

The fanInItem is easy. We just have to take the items and translate to the origin, which is 0,0. The tricky method is fanOutItem. There is a couple of math involved for calculating the X,Y position of the item on top of the arch line. You can check all those methods below:

onPathBtnTap: function(btn) {
    var pressedCls = Ext.baseCSSPrefix + 'button-pressed';
    
    btn.pressed = !btn.pressed;
    
    if (btn.pressed) {
        btn.addCls(pressedCls);
        this.fanOut();
    }
    else {
        btn.removeCls(pressedCls);
        this.fanIn();
    }
},

fanOut:  function() {
    this.getComponent(0).items.each(this.fanOutItem, this);
},

fanIn:  function() {
    this.getComponent(0).items.each(this.fanInItem, this);
},

//@private
fanOutItem: function(item, index, len) {
    var angle, rad, sin, cos, x, y, style, difCenter,
        arc     = 90,
        distance= 150;
    
    //ignore main button
    if (index === 0) {
        return;
    }
    index--;
    len--;
    
    //calculate angle using items count
    if (len === 1) {
        angle = 0;
    }
    else {
        angle = (arc/(len-1)) * index;
    }
    
    //transform angle to rad
    rad = angle * Math.PI/180;
    
    //calculate cos and sin
    cos = Math.cos(rad);
    sin = Math.sin(rad);
    
    //find x,y using distance
    x = Math.ceil(distance * cos);
    y = Math.ceil(distance * sin * -1);
    
    style = {
        '-webkit-transition-delay': (30 * index) + 'ms',
        '-webkit-transform': 'translate3d('+x+'px, '+y+'px, 0)'
    };
    
    if (!item.rendered) {
        item.style = style;
    }
    else {
        item.element.applyStyles(style);
    }
},

//@private
fanInItem: function(item, index, len) {
    //ignore main button
    if (index === 0) {
        return;
    }
    
    var style = {
        '-webkit-transition-delay': (30 * index) + 'ms',
        '-webkit-transform': 'translate3d(0px, 0px, 0)'
    };
    
    if (!item.rendered) {
        item.style = style;
    }
    else {
        item.element.applyStyles(style);
    }
}

Theming

Path menu buttons

I’m just using a couple gradients and box-shadows. Since I don’t want to change the HTML structure of the component itself, I had to use CSS pseudo elements :before and :after.

The :before was used for the outer gray and white box-shadows. They are not borders, but 2 box-shadows. The :after was used for the pressed state, putting a black mask on top of the button.

Wrapping Up

Currently everything is very hard coded, and my next task is make this more extensible, allowing other developers to take this animation and plug to any other containers. Also allow customizations, like change the arch degree and items distance to origin.

Hey, if you found this interesting don’t forget to tweet and share it with others :) I’m always looking for new cool UIs to reproduce with HTML5.