In this lesson we'll explore using Reanimated and React Native Gesture Handler to create a touchable opacity button using TapGestureHandler
. With declarative animations in Reanimated we'll use tools like timing
, Clock
, set
cond
, and eq
to create a purely native opacity animation.
Instructor: [00:00] We'll start off with tap gesture handler in an animated view from Reanimated already setup with some styling to make it look like an orange box. In order to get going, we need to destructure some stuff from Animated. Let's say const equals animated. We're going to pull off value, clock, event. That will be it for now.
[00:24] We first need an animated value that we're going to call gesture state. We'll say new value negative one. This is going to hold on to the gesture state from the Tap Gesture Handler, whether it's active or it's ended.
[00:40] The next thing we're going to do is set up a clock. The clock will be used to drive our animations. We'll basically create something that ticks on the native side every roughly 16 milliseconds. Let's say clock equals new clock.
[00:57] We need to create an event for our Tap Gesture Handler. Let's say onStateChange is equal to event. With event, it will take an array. It will then destructure the arguments that this callback call is called with. It is just called with a single event with a native event property.
[01:20] Tap Gesture Handler from React Native Gesture Hander will then have a special state, which we'll pass to gesture state. This.gestureState will be receiving the state of the Tap Gesture Handler whenever that changes. We can wire that up now. We'll say onHandlerStateChange is equal to this.OnStateChange.
[01:40] We can get into the meat of our operation, we'll say timing opacity animation. We're going to create an opacity and call run opacity timer. This is a function we haven't created yet, but we're going to pass in our clock, as well as our this.gestureState. That doesn't exist. Let's go ahead and create it. Let's say const run opacity timer. This will receive our clock and our gesture state.
[02:16] The first thing we need to do is set up our configuration for our clock. We're going to say const state, and it takes four particular animated values. The first one will be finished. Let's say new value is zero. This one will be set once the animation has been completed.
[02:37] We'll set our position, which is a new value of zero. This is going to hold on to the transition from the to value of the timing to the position that it's currently at. Let's start at zero and then translate and animate stepping at the correct intervals to the to value.
[02:57] We'll have our time and then our frame time which, for our purposes, is not important. It is essentially holding on to the current frame of time over the current progress that the animation is making. We'll then need to set up our configuration for our timing animation.
[03:26] We'll say const config. We'll need to do a duration. We'll say duration of 300. We'll do a to value, which we'll set to negative one. We also need to set up easing. We'll say easing, which we'll do an easing in out within easing is.
[03:52] Because of the way that Reanimated works, and its declarative nature that sends over everything over the bridge just once and then handles everything natively so there's not a lot of chat over the bridge back and forth, we need to import a lot of these comparison and instructions for the animation.
[04:09] We're going to pull in block, condition, and, equal, not equal, set, start clock, timing, stop clock, as well as interpolate. With all of these imported, we can finally set up our return value for our opacity. We're going to return a block of conditions. We're going to say return block, which takes an array of things.
[04:47] The order at which these are constructed don't really matter. All that matters is that the last value of this array block is a value that's going to be fed to opacity. That means it's going to need to be between zero and one whatever we happen to put in this last slot. We're going to first set up our timing.
[05:06] Let's say timing, which takes our clock that we passed in, as well as our state that we set up for the clock, as well as the configuration for our timing animation. The next thing we need to do is set up a condition for when the timing animation and the clock has been completed.
[05:26] We need to say condition when the state is completed, then we need to call stop clock. Make sure you add comma here, so we have valid JavaScripts. Now that we have our timing and our condition for finishing set up, we need to define animation for when we press on it.
[05:50] When we press on it, what we want to happen is for it to slowly fade out over a duration of 300. However, at any point that we release it, we want it to fade back to our full opacity of one. We need to do that with a condition block. We need to do that with a condition block based upon the state of our gesture.
[06:10] We also need to utilize our config to value because what happens is, when you have condition, it will look at all the nodes that are depending on that particular thing. Every time the clock changes, that condition will be reevaluated. How are we going to have to set this up is we're going to say condition. We'll use an and statement.
[06:29] Whatever comes after the and statement, or after the second argument here, is going to be what is evaluated for whatever this is evaluated as true. What we want to do is then do a set state.finished to zero. We want clear out that the animation has been completed. We're going to do a set to state.time of zero. We're resetting the animation.
[06:54] We're going to state.frameTime to zero because that's also going to reset the progress of the frame. We're going to set our config.to value to one. As you can see here, we started with negative one. We'll get to that in a second. We're going to start our clock, so we'll pass in our clock, and then add a comma here so we don't break everything.
[07:21] Our two comparisons are going to be if the gesture state is equal state.begin, once the tap has happened. Also, when config.to value doesn't equal one. You see we're setting our config value in here to one. Because this block is depending on clock, this will always run. If we didn't add this "not equals to one" clause, then our animation would just do nothing.
[08:00] If we add a comma here, we can reload this and everything will work. If we pass this, this is being returned to our opacity. We can add this here. Let's say opacity, this.opacity. Nothing's going to happen, because if you look, our last thing is returned, is our state.finished, and then we call stop clock. This will eventually evaluate to zero.
[08:26] What we want to do then is put the last thing here at as our state.position. However, our state.position is going to start at zero. We want our button to show up in the beginning. What we need to do is use our interpolate. We'll also bring in extrapolate here just to solidify our animation.
[08:46] We're going to say interpolate. We're just going to flip our values. When we say state.position, it's going to take an object. We need to define our input range. This is going to be input values that we expect state.position to be. In this case, we expect it to be anywhere between zero and one.
[09:07] We want to flip that, say output range. When our initial state.position is zero like it is in the beginning, we want to output one. When it's at one, we want it to be zero. We will add our extrapolate with our extrapolate.clamp.
[09:32] What this will do is just always say that it goes between zero and one and never be on one or zero. As you can see that we are at state position zero and our opacity is now one. If we go ahead and press on this, we can see that it animates out, but it does not animate in. Let's fix that.
[09:49] We need to do the same thing that we've done here, but for when the tap ends. We're going to copy this. Instead of begin, we say end. Instead of the to value which we then animate our position back to zero. We're going to say zero here and config.to value, zero here, and then we're going to start our clock.
[10:13] If we refresh, we have the complete animation, but we'll animate our to value of one which, with our interpolation, will be flipped to zero. Or when we animate back to zero, which is our starting position, we'll go back to an opacity of one. We press and then release, it comes right back.