Create an Animated Social Media Share Button with NativeScript
In this tutorial, I will show you how to create an animated social media share button using NativeScript and Angular. The button will react to a tap, by showing the different share buttons, and translating them in a circular manner around the main one. This behaviour is shown in the following video:
The complete source code is also available on GitHub.
Let’s get to work !
a bit of setup to start. Let’s create the project using the –ng flag to create an angular application:
Our application is going to use Nathan Walker’s ng2-fonticon plugin to display the buttons and the icons, so make sure to install it by following the instructions on the github page.
We’re also going to use the lodash package. Go ahead and download the package, along with the types:
We’re now ready to dive into the code of our application.
Create the SocialShareButtonComponent
The core of our logic will reside in a Component we’ll call SocialShareButtonComponent. The template will be composed of a main button, and the share buttons. Tapping for the first time on the main button triggers the display of the share buttons, and tapping a second time will bring them back at their default position. Form the button forms, we will use the font awesome ‘circle’ icon. Font icons are great because they look good on any device screen and resolution. The thing we have to keep in mind though, is that their size are controlled through their font-size property. In order to keep the control over the overall size of our Component, we will have to make some calculations, because not all the font icons composing it will have the same dimensions.
We’ll also accept an array of icon titles in input. We will use them to create the corresponding share buttons. The names will have to be picked up from the font awesome icons list. With all of this in mind, let’s create the component, in a new folder social-share-button:
The Input size will be our reference to calculate the different dimensions, we set its default value to 75. We will use it as the font-size for the main button. The mainIconSize is the size of the icon displayed inside the main button. The shareButtonSize correspond to the size of the different share buttons, and the shareIconSize, the icons inside. The viewHeight and viewWidth porperties correspond to the dimension of the whole view. We need enough space to display the main button, as well as the share buttons once translated. We will have a maximum of one share button displayed below the main button, so the height will never be bigger that size + shareButtonSize. For the width though, we can have one button at each side, hence size + shareButtonSize x 2. We use some coefficients in order to add a slight additional space.
Create the corresponding template with the following content:
The buttons are wrapped into GridLayout in order to display the icons on top of the circles. The whole content is in turn wrapped into a GridLayout, to which we apply the width and height style properties dynamically.
For the share buttons, we loop through the given icons. The text of the icon is then the result of the concatenation of ‘fa-‘ and the shareIcon value.
Next, create the corresponding stylesheet:
Nothing too fancy here. We just make sure that we get everything centered inside the GridLayouts, and set some colors. We also make sure that the icons are vertically centered inside the buttons.
Before going any further in the implementation, let’s display the result in the AppComponent. First, add the Component to the list of the AppModule declarations:
Then, open up the AppComponent, and do a bit of cleanup
Create the app.component.html template file with the following content:
And the CSS file:
The result should look like this:
Animate the Share Buttons
Let’s now work on the animations around the main button. First, let’s create a @ViewChildren() property to retrieve all the share button GridLayouts:
We want the animation to be performed in two steps. First, the buttons get out of inside the main button, in a simple linear translation. Then, we need to do something a bit more tricky. We want the buttons to be translated in a circular manner, around the main button. The final circular translation position will depend on the position of each button, or in other words, their position within the shareButtons array.
To create the circular translation, let’s recall how to calculate the coordinates x, y of a point on the edge of a circle, in function of the angle:
where and are the coordinates of the origin of the circle, is its radius and is the angle.
To be able to see an actual circular animation, we can’t just translate from one point within the edge of the circle, to another though. Doing this would result in a cross of the circle:
Instead, we need to do multiple sequential translations, using small steps (small variations of ):
Let’s now translate this into code.
Animate the Share Buttons out of the Main One
Let’s hook the tap of the main button with a onMainButtonTap() method of the Component:
And the corresponding method in the Component:
The translation on the x-axis uses a value directly related to the size of the main button. This value will be the starting point for all the rotations. If you go back to the definition for the coordinates calculations, it will also be the radius, around which we want to rotate the buttons. With this in mind, let’s create a getter property to wrap this value:
At this point, the angle is equal to 0. Let’s now move on to the fun part: the rotation of the buttons.
Circular Translations of the Buttons
Before we continue the implementation of this method, let’s think about what we’ll need. We said that we want to be able to translate from small angle values, or steps. We also need to calculate the maximum angle value for the translation of each button, according to its index position. Let’s do just this, and create the two following methods:
The maxAngleFor() takes an index as input, and return its value multiplied by 45. This means that every button will be separated by a quarter of the circle, which keeps things symmetric.
The angleIntervals() method takes a maxAngle, and return an array of values separated by 5, until maxAngle. These will be our steps for the rotation.
Let’s also implement the method to retrieve the coordinates of a point, according to an angle value:
Now, the challenge is, for every button, to chain the translations of every angle step value. One solution would be to create an array of AnimationDefinition like we did for the previous section, and trigger the animation with the playSequentially flag. Unfortunately, doing so forces the position of the view being translated to be reseted between each animation step, which is not really useful for the result we want to get. Another solution is to take each step value and chain the result of the Promise returned by the translation animation through the then() method. We can achieve this using the reduce() method called on the angleIntervals() method. A few lines of codes speak better that a thousand words, so let’s implement this:
For every button, we retrieve its corresponding maxAngle value. We use it to calculate the angle steps, and call the reduce method, chaining the animation Promises together (we start with the result of an empty Promise). Every animation is a bit of 0.8, taking the button to the coordinates corresponding to the current angle. Recall that we initially start from angle of 0.
(The animation might look jerky on an iPhone emulator, but works smoothly on the actual device)
With a bit of refactoring, this turns into:
Tidy up Back the Buttons
Once the share buttons are displayed, we would be glad to have a way to bring them back where they come from. To do this, we will introduce a flag indicating if the share buttons are displayed or not:
The animation to bring the buttons back home will be pretty similar to the translateShareButtonsOutOfMainButton(), so we’ll extract its content to make it a bit more reusable:
which allows us to write:
We can now transform the onMainButtonTap() as follow:
The problem with the current implementation, is that the user can mess up the animations while playing. To prevent him from doing so, we’ll introduce a State enumeration, indicating wether the Component is iddle, animating or settled. Before doing this, we need to change the rotateShareButtonsAroundMainButton() method to return a Promise as well. For this method, we want to return the result of all the animation Promises, so that we catch the moment where the global animation is done (settled). Let’s change the method to the following:
Let’s turn the flag into a state enumeration:
And the final implementation:
Make the Button Customizable
Right now, the button has some boring black and white colors. Let’s make them customizable. Add the two following Input (with some default values):
And bind them in the Template:
We can also clean things up a bit in the stylesheet:
Add a Shadow effect using Native Code
Let’s enhance the styling of the button by adding a shadow to it. NativeScript doesn’t support displaying a shadow to a view out of the box, so we’re going to have to reach out to native code. We will do this through the use of a Directive, that will be implemented for both iOS and Android platforms.
Let’s create a new directory to host the code related to the Directive, called label-shadow. We will now create the abstract base directive that will be inherited by each of the platforms:
We need to wait for the Label to be properly set up through the FontIcon plugin, so we’re adding an event handler. When it’s ready, we apply the abstract method displayShadowOn().
Before tackling the implementation, let’s create the type definition, that will indicate to TypeScript that the directive is there at compile time (even though the actual one will be loaded according to the runtime platform):
Now, let’s create the Android implementation:
And the iOS one:
Next, add the Directive to the AppModule declarations:
We can now add the directive to the FontIcon Labels representing our buttons:
Let’s now showcase the Component using different sizes and colors. Edit the AppComponent:
And the template:
Which gives us:
Get the Result of a Tapped Share Button
The button now looks good, but won’t be of any help if we can’t get the result of which button has been tapped. Let’s introduce an EventEmitter Output that will emit the icon name once its corresponding button has been tapped:
Then, bind the (tap) hook of the share button GridLayout to a onShareButton() method, accepting the icon string:
And create the corresponding method, emitting the event with the icon as parameter:
This allows you to subscribe to the event in the AppComponent:
Add Some Validation
For this last step, we want to provide some validation in order to prevent the Component from behaving incorrectly.
Open up the SocialShareButton again, and make it implements the OnInit interface:
then, implement the ngOnInit() hook with the validation logic:
And our Component is now done!
If you enjoyed this tutorial, please go ahead and share it 🙂 any questions or ideas to make it better are really welcome.Angular2, NativeScript and tagged android, Animation, IOS, nativescript. Bookmark the permalink.