Simple physics with Reanimated — Part 2

Krzysztof Magiera
Software Mansion
Published in
6 min readOct 31, 2018

--

In the first part of this series we learned how to define a simple interaction that’d allow us to move an object along x-axis on a screen. If you missed the first part, it is the best if you start from it as a lot of the concepts we are referring to here have been introduced and explained in it.

We talked a lot about positioning elements in response to gesture in the previous part. However, physics is not only about space — it is about space and time. In this part we will learn how to take time into account when defining interactions using Reanimated library.

In this and the next part, we use terms and equations from classical mechanics. Whenever we do, we purposefully omit the fact some of the quantities should be represented as vectors. We allow for that, as in our example we consider motion in one dimension only (along x-axis).

Time-based physics simulation

The concept that represents a change of position in time is called velocity. Just like position can change over time (motion), velocity can change as well (acceleration). However, let us first consider what happens when velocity does not change over time. In such a case, the position at time t can be calculated by multiplying velocity v by time t, that is x = x₀ + v⋅t, where x₀ is the object’s initial position. The case when velocity is a function of time turns out to be way more complicated and requires understanding of calculus (integrals, derivatives, etc.). Thankfully, we don’t need all of that. Because our screens refresh only a number of times every second (for mobile devices it is usually 60 times per second, that is at a refresh rate of 60Hz), we can assume velocity remains constant for the duration of each frame (in between screen refresh). This assumption is commonly used in physics simulation engines and is also perfect for our use-case. As a result, we will modify previous equation a bit, and in each animation frame we will calculate x = xᴾ + v ⋅ dt, where xᴾ is the position at previous frame and dt is the time duration between the current and previous frames (note that even with a fixed frame rate dt may vary, e.g., in a case when some frames are dropped).

Let us change our code from Part 1 such that when we release the view it starts moving towards the initial position with a constant velocity. Note that our block is laid out at the center. Therefore when the position is equal to 0 (x-axis translation is also at0), the object is in the center of the screen, when the position is negative it is on the left half of the screen, and when it is positive it is on the right half of the screen.

For that to work, we introduce another state variable to our interaction, that is velocity. We also need to know how much time has elapsed since the previous frame. For that we will use a special Reanimated node called Clock. Clock nodes, when evaluated, give us a current timestamp in milliseconds. To get a delta between the previous value of a timestamp and the current one we can use diff node that does exactly that. We use it as follows:

Since we want to express dt in seconds, we use divide node to divide the value given in milliseconds by 1000 in order to convert it to seconds.

As we mentioned in the previous part, Reanimated tries to optimize the number of updates that need to run every frame. For that reason clock nodes are treated in a special way. Even though the clock node value updates every frame, it won’t trigger its dependant expressions to evaluate unless the clock is started. To start and stop the clock one can use startClock and stopClock Reanimated nodes. In our example, we want to start the clock after we finish dragging the box, and then stop the clock when we start dragging again or when the animation has reached its final destination.

We now have both dt and velocity variables available in our interaction. Let us use them in order to update position accordingly:

The above Reanimated code fragment will update position every frame based on the previous position, current velocity and delta time between frames. It reflects the equation we discussed earlier.

On top of that, right next to updating position, we would like to have some code responsible for starting and stopping our clock. One way to handle that is by using startClock when the gesture is inactive, and then stop it when certain conditions are met. Let us create a method stopWhenNeeded, that we call after all the position calculations are done. That method will take clock as an argument and when needed will call stopClock. For our use-case, we can stop the clock when the current position is close enough to 0 (falls within some threshold around 0). It is not an ideal solution, and we will revisit this topic later on. However, for simplicity let us follow this approach for now:

We use two new Reanimated nodes in the above code. First one is and, which works like && (logical AND) operator in JavaScript. It takes a number of arguments and returns true if all of these evaluate to true, otherwise it returns false. The second node is lessThan which works like < operator in JavaScript. It compares values of nodes passed as arguments and returns true if the first value is less than the second. Finally, POSITION_THRESHOLD is just a constant value that we set to 1. On top of that when stopWhenNeeded method detects we should stop our animation, it resets velocity and position to 0.

The final piece that is missing is updating velocity such that when the box is on the left half we set it to some positive value (we want the object to start moving to the right and reach 0). Otherwise, when the object is on the right half want velocity to be set to a negative value and hence make the object start moving to the left.

Let us create new method calledforce. It will take frame duration (dt), current position and velocity, and is expected to update velocity in a way described above. We will then call force from our main interaction code:

What the above code does it is essentially updates velocity to either VELOCITY or -VELOCITY depending on position being lower or greater than 0 respectively.

Check this snack for a complete code for this part: https://snack.expo.io/@kzzzf/time-based-physics-simulation

Here is how our example works:

As you can see, we can start pulling the box at any moment even if it hasn’t yet reached the stationary point.

Until now we have only used a constant velocity, but as you might have already noticed our interaction code is pretty much ready for force method to update velocity every frame. Turns out that updating velocity each frame is sufficient to model an impact of different physical forces like gravity, friction or spring compression/extension on our object. In the next part of this series we are covering that in detail.

Check out Part 3 to learn how to model spring and build more complex physical systems using Reanimated.

Part 3: Simulating forces

This image is here because medium doesn’t handle gifs as featured images well :-)

--

--

Director of engineering at Software Mansion. React Native contributor and former member of React Native team at Facebook.