Creating a Fan-out menu in a Hybrid mobile app

The other day, I set out to create a fan-out menu in an Ionic app.  You know the type – you tap on a button such as Share and options for things like Facebook, Twitter, etc. appear in a fan of icons.  Tapping on one of the icons would then post your message to the selected social media site.

fan-out menu

Per my usual modus operandi, I started Googling, hoping to find a nice tutorial or example I could “repurpose”, but surprisingly, I didn’t find much.  So this post will document the solution I came up with.  The completed code can be found at https://github.com/dschultz-mo/Ionic-fan-out-menu-demo.

Create a starter app

Let’s start by creating a blank Ionic app.  If you are new to Ionic, it is really simple to get started.  Follow the brief Getting Started instructions to install Node and Ionic.

Now create a project based on the tabs template:

$ ionic start demoApp tabs

For now, you can just say ‘no’ when asked if you want to create an ionic.io account, but I would highly recommend you revisit this and dig into Ionic a bit more if you are doing hybrid development.

Change directory into the new app project and start the ionic server.  This will launch the app in your default browser.

$ cd demoApp
$ ionic serve

Add a button

Let’s add a button to the bottom of the first tab.  Tapping this button will open and close our fan-out menu.

Open the file www/templates/tab-dash.html.  Add the code

<button id=share-button>
Share
</button>

just before   </ion-content>.

Open the file www/css/style.css.  Add the code

#share-button {
position: relative;
top: 0px;
}

Assigning a relative position with no offset may seem strange, but play along for a moment.  Hopefully the reason will become clear in a few more steps.

Save the files.  Because of live reload, the browser will instantly refresh and your new button will appear.

Share button.png

Add a single menu item

Let’s start with one menu item so we see how this works, then build from there.

Inside the button tag in tab-dash.html, add the following code:

<img class=menu-fan
src="https://www.facebookbrand.com/img/fb-art.jpg"
style="left:40px;top:40px"/>

This will add a Facebook icon image inside the button offset down and to the right.

Add some styling to the css file for the class called menu-fan that is applied to the item in our fan-out menu:

.menu-fan {
height: 20px;
width: auto;
position: absolute;
z-index: 1000;
opacity: 1;
}

Save the files.  You should now see the Facebook icon.

Facebook icon.png

Next, we need a way to have tapping the Share button to alternately hide and show the Facebook icon.  Angular has a built-in way to do that with ng-show. We will have taps on the share-button toggle the boolean value of a scope variable we will name showMenu using the ng-click directive.  We will then bind the ng-show directive of the Facebook img to that variable.  The html should look like this:

html-01.png

Save.  Now clicks will cause the Facebook icon to alternately appear and disappear.

A note on positioning… The icon img is being positioned using absolute positioning with the left and top offsets being specified in the style attribute.  This is why the Share button needed to be explicitly positioned as ‘relative’.  The rule is that absolute positioning is relative to the img’s first positioned (not static) ancestor element.  Positioning the Share button as relative stops the ancestor search for a non static element.

Add some bling

Without a lot of imagination, you can see how we could create several icons for multiple social media outlets, fan them out in an arc and associate a scope action to each so that tapping on any one of them would invoke a routine that does what we want.  We will do that in a moment.  But right now, the Facebook icon just appears and disappears.  Not very exciting. Let’s add a little pizazz to this one icon first, then add in the others.

The ng-hide directive simply adds an ng-hide class to the img when the user wants it hidden.  By default, all that does is apply the style “display: none !important” to the img.  But, you can actually override what the ng-hide class does!

Go back to the style.css stylesheet.  Add the following to the menu-fan class:

    -webkit-transition:all linear 0.2s;
-moz-transition:all linear 0.2s;
-o-transition:all linear 0.2s;
transition:all linear 0.2s;

Also, create a new .menu-fan.ng-hide class like this:

.menu-fan.ng-hide {
opacity: 0;
-ms-transform: rotate(360deg); /* IE 9 */
-webkit-transform: rotate(360deg); /* Safari */
transform: rotate(360deg); /* Standard syntax */
}

The entire stylesheet should look like this now:
stylesheet-complete.png

Save the file and click on Share.  Now when the Facebook icon appears, it spins as it moves into position from the upper left corner of the Share button.  It spins back and disappears on the next click.  Let’s look at how the CSS we added does that.

First, we added a transition to the menu-fan class.  What this says is that any time an attribute is changed on the object, it should transition according to the given rule (there are four rules here to ensure cross-browser compatibility).  This particular rule says that it should apply to all attribute changes and that the change should be applied linearly over 0.2 seconds.  There are a lot of transition options that you could experiment to add even more pizazz.

Next, we defined some attributes that will apply to the object only when the .ng-hide class is applied to it.  The first two are positional – when the object hides, we want it to move to the upper-left corner of the Share button.  The transition attribute means that it will move back and forth linearly over 0.2 seconds.  Opacity of 0 means it is transparent, so as it moves, the img will also fade in and out.

The transform attribute is probably the least obvious.  rotate(360deg) means the element will spin around one revolution as it moves and fades over 0.2 seconds.  Here again, you have a number of transforms you can apply if you are looking for a different effect.

Expand to multiple menu items

Ok, so let’s scale this out.  Let’s say you want to add four social outlets – Facebook, Instagram, Pinterest and Twitter.  You could calculate the top and left values required for each to make a nice arc and add an img definition for each one of them in the html – hard coding the position, image source location and action you want performed.  But there may be a better way.  First off, the Angular ng-repeat directive is made for this sort of stuff.  It repeats over an array of items and builds the html you need.  We should really be using that.  So instead of code that looks like this:

html-02.png

It will look like this:

html-03.png

  • ng-repeat – creates the img element for each item in a scope array called socialFanButtons.  We will define that array in a minute.
  • ng-src – the src of the img will be whatever the value of the src property of the array element is.
  • ng-click – clicking this image will invoke the function defined by the action property.
  • style – This will now pick up the left and right properties of the array element.

So now, we could just create an array in the DashCtrl controller like:

controllerArray

Well, that does work, but let’s use the computer to do what it was made for – to compute.  Let’s create a routine that will compute the left and top values so we don’t have to.  We will define an array with the src and action fields we need, but then create a function that will compute the left and top values we need based on a direction(degrees), spread angle(degrees) and distance(radius).

Conclusion

So here you see that by leveraging AngularJS directives, some creative CSS and some geometry, we have created a reusable pattern including a procedure and a stylesheet to generate fan-out menus.