A gentle introduction to React Motion

React is great, I am having a lot of fun playing around with it for the past few weeks. I decided to take a shot and try out React Motion. It was a little tricky in the beginning to wrap my head around the API, but eventually it all started to make sense, it took time though. Sadly there are no proper React Motion tutorials I could find online, so I decided to write this one up, as a resource for fellow devs and as a reference for myself.

React Motion exports 3 main components, Motion, StaggeredMotion and TransitionMotion. In this tutorial we’ll be taking a look at the Motion component, the one you will find yourself using most of the time.

Since this is a React Motion tutorial, I assume that you are somewhat acquainted with React as well as ES2015.

We’ll explore the API while recreating this Framerjs example using React Motion. You can find the final version of code here.

Final Result

We’ll be doing a little bit of Math first, don’t fret though, I’ll try to explain every single one of the steps in as much detail as possible. You can skip directly to the React.start(); part if you want to :-).

Ready? Let’s do this…

Math.start();

Let’s call the Big Blue Button - main button, and the buttons that fly out from it the child buttons.

Fig. 1

Child buttons have two states of position, 1) where they are all hidden behind the main button, 2) where the child buttons arrange themselves in a circle around the main button.

That is where the math comes in, we will have to come up with a way to evenly arrange the child buttons around the main button in a perfect circle. You could hard code those values by trial and error, but seriously, who does that? Plus once you get the math right, you can have any number of child buttons you like and they all will automagically arrange themselves.

First of all let us get acquainted with a few terms.

M_X, M_Y

Fig. 2

M_X, M_Y represents the x and y coordinates respectively of the center of the main button. This point (M_X, M_Y) will be used as a reference from where the distances and directions of each child button will be calculated.

Each child button initially hides behind the main button with their centers at M_X, M_Y.

Separation angle, Fan angle, Fly out radius

Fig. 3

Fly out radius is the distance away from the main button the child buttons fly out to. Everything else looks pretty self explanatory.

Also notice that,

Fan angle = (number of child buttons-1) * Separation angle

Now, we need to devise a function that takes in the index of a child button (0, 1, 2, 3 …) and returns the x and y coordinates of the new position (after flying out) of the child button.

Base angle, Index

Fig. 4

Since in general trigonometry angles are measured from the positive x axis, we will start numbering our child buttons from the opposite (right to left) direction. This way, later we won’t have to deal with multiplying a negative 1 each time we need to find the final position of a child button.

While we’re at it, notice that (refer Fig. 3)

Base angle = (180 — Fan angle)/2

(In degrees of course).

Angle

Fig. 5

Each child button will have its very own angle, which I will call Angle, yes just Angle. This angle is the final piece of information we need to calculate the final position of the child buttons.

Notice that, (refer Fig. 3, Fig. 4)

Angle of child button with index i = Base angle + ( i * Separation angle)

Now, once we have the angle for each child button,

Fig. 6

We will be able calculate deltaX and deltaY for that child button.

Notice that (refer Fig. 2),

Final X position of child button = M_X + deltaX
Final Y position of child button = M_Y-deltaY

(We subtract deltaY from M_Y because unlike general coordinate system where the origin is in the lower left corner, browsers have origin at the top left, so to move something up you decrease the value of their y coordinate.)

That is it, that’s all the math needed, now we have two things the initial position of each of the child buttons (M_X, M_Y) and the final positions of the child buttons. Let’s let React do rest of the magic.

React.start();

In the following gist you’ll see what happens is, on clicking the main button we set the state variable of isOpen to true ( line 85 ). Once isOpen is true a different set of styles for the child buttons is passed ( line 97, line 66, line 75).

The result :

Fig. 7

Alright, we’re pretty much done here, we’re setting the initial and final positions of the child buttons on click, all we need to do now is add React Motion to the mix to animate the child buttons between their initial and final positions.

React-Motion.start();

<Motion> takes in several parameters one of which is optional, we don’t really care about the optional parameter here since we’re not doing anything that this parameter is concerned with.

One of the parameter <Motion> takes in is style, this style is then passed down as parameter into the callback function, which takes in interpolated values and does whatever magic it does.

(line 8 : Since we’re iterating React requires you to pass in a key parameter to the child components)

Something like this,

Even after doing this the result won’t be any different from Fig. 7. Why so you ask? Well, we need to perform one last step, spring.

As mentioned earlier, the callback function takes in interpolated values, that is what spring does it helps interpolate the style values.

We will need to edit the initialChildButtonStyles and the finalChildButtonStyles to

notice the top and left values wrapped around spring. Those are the only changes, and now,

Fig. 8

optionally spring takes a second parameter, which is an array containing two numbers [Stiffness, damping], this defaults to [170, 26] resulting in what you see above in Fig. 8.

See Stiffness as the speed at which the animation occurs, not a very accurate assumption, but larger the value greater the speed. And dampness is like wobbliness but opposite, smaller the value more wobbly the animation.

Take a look at these

[320, 8] — Fig. 9
[320, 17] — Fig. 10

We’re close, but not there yet. What if we added delay each time before the next child button starts to animate? That’s exactly what we need to do, to achieve the final effect. Doing so wasn’t so straightforward though, I had to store each motion component as an array in a state variable. Then change the state one by one for each of the child button to achieve the desired effect, the code goes something like this

this.state = {
 isOpen: false,
 childButtons: []
 };

Then on componentDidMount fill the childButtons

componentDidMount() {
 let childButtons = [];
 range(NUM_CHILDREN).forEach(index => {
 childButtons.push(this.renderChildButton(index));
 });
this.setState({childButtons: childButtons.slice(0)});
 }

and openMenu function becomes :

There we are. After doing some aesthetic tweakings like adding icons and a little rotation, we get the following.

You can have any number* of child buttons you want, math has you covered

NUM_CHILDREN = 1
NUM_CHILDREN = 3
NUM_CHILDREN = 8

Pretty cool huh? Again, you can find the code here, feel free to contribute/improve. Do hit the Recommend button below if you found the article helpful.

Got any questions, comments, suggestions or just wanna chat? Find me on Twitter @NashVail or shoot me an email at hello@nashvail.me.


One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.