Instructor: We're going to start with a pan gesture handler. It'll allow us to track the touch movements. We're going to allow the max pointers of one, so only one touch will be allowed on this view, which is an animated view, from React Native Reanimated.
The first thing that we'll need to do is set up a few animated values to hold onto the position that the user has dragged. We're going to need to destructure value from animated. With our value, we can set up a dragX to be a new value -- we'll just initialize it with zero -- and a dragY.
We'll also need to hold onto some gesture state. This will just allow us to trigger the drag whenever the pan gesture handler becomes active. We'll say gestureState is equal to new value. I'll just initialize it with negative one, which is no particular gesture state.
The pan gesture handler will always kick back new animated values based upon the delta translation of this particular circle that we'll be dragging. However, if we don't save off the accumulation of all those drags in an offset, whenever you go to touch on it, it will cause the circle to jump.
We need to create an offsetX, which we're going to set to a new value. Because we want to just start the circle in the center of the screen, we're going to use the width of the screen that we've pulled off here, and say width divided by two.
Then the same goes for the offsetY. We'll start it in the middle, and say new value, with height divided by two. Finally, to capture the gesture state, as well as the translation X and translation Y, we're going to need to set up an onGesture event, which is equal to an event.
We'll use this array to say that we are receiving an array of arguments. The first argument will be an object that we want to grab that will have a structure of native event. The pan gesture handler will kick back translation X, which we'll pass into our this.dragX, our translation Y, which we'll pop into this.dragY.
Then our state, which we'll pass into this.gestureState. The event call is from Reanimated, so we'll make sure that we import that and destructure that from animated. We can take this.onGesture event, and we'll pass it into the onGesture event, as well onHandlerStateChange.
The onGesture event will always fire, and the onHandlerStateChange will only fire when the particular states have changed. Now that we have this all set up, we need to actually control what happens when the touch is initiated.
We're going to do that with a transX, which will then be a conditional block from Reanimated. Because Reanimated is declared, if we need to pull off a few different actions, so that we can declare how the animation should work. They're going to be condition, equal, add, set, and then eventually, interpolate.
Now that we have those, we can take care of this, since we'll say when the condition is met. Our condition will be whenever the gesture state is active. Dismiss this. We want to use the equality operator, and say whenever this.gestureState is equal to state.active.
This state comes from the React Native gesture handler. It's just a helper to compare the actual states. Then whenever that is the case, therefore, the tap is active, this next argument will run. What we want to do is say add this.offsetX with this.dragX.
Then whenever the gesture state isn't active, what we want to do then is save off the particular offset dragX to the offsetX. We're going to use the set command, and say we want to set this.offsetX with add this.offsetX and this.dragX.
The way that this works is that the final value that gets returned to this condition block will be applied to the transX. In our case, when we are active, the value being piped into the transX, the translate X, is the offset plus the current drag state.
However, when we are officially done, and we've released, then we will pass in this.offsetX into the addition offsetX and dragX, which will then be our final position that we have dropped. What that means is we can take this.transX, and for our animated view, we'll say transform, say translate X is equal to this.transX.
We'll do the same exact thing for the translate Y. Rather than transX, it'll be transY. Here, it will be offsetY, dragY, offsetY, Y, and Y. Then we can take that and pass into the translate Y.
Now, if we refresh our simulator, we'll see that we are appearing right in the middle. That's because the offset X and offset Y have been set to the center point. Whenever this is not active, which it is not active, then we'll add the offset X or offset Y with the drag Y.
Basically, we're adding zero to the center point. Now, if we grab this and drag it around, we can see that it is following. We can stop, and our offset X and offset Y will be updated with the current drag. We can do it again from the same exact point without jumping.
Now, we can control effects based upon the transX and transY. We're going to also then pull in extrapolate, which will be working in conjunction with our interpolate. We're going to set up two interpolations.
The first is going to be the opacity. We'll say opacity, and we're going to use our interpolate on this.transY. Whenever we go any direction on the y-axis, we're going to trigger a new opacity. We're going to take our input range, and because our input range runs from zero to the height, we'll say zero and height.
That will translate and interpolate it to an output range of 01 to 1. When it's at 0or close to it, it'll be .1 opacity. When it's down here, it will be at a full opacity. We can take this, pass it into here, and say opacity, this.opacity.
Now, it's at a roughly .5 opacity. As we drag it up, it'll get lighter. Down, it'll get more full. Finally, we'll add another effect that just controls the border width. We'll say borderWidth, and we'll then interpolate on the X, the translate X.
Say interpolate this.transX. Our input range will be zero to the width, because as we go left or right, we want it to increase or decrease. Then our output range will have a border width of zero or five. Then we will use our extrapolate to extrapolate.clamp.
What that is going to do is make sure that if we go any further, then it will never go past the zero or five of the output range for our border width. Then we can now supply borderWidth with this.borderWidth. See, it starts here. As we go left, it'll shrink down to zero. As we go right, it will slowly grow, and we still have our opacity working.