In this lesson we'll explore how to setup a draggable circle. Then we'll dive into using the call
method to create JavaScript callbacks whenever certain conditions are met inside of Reanimated.
Instructor: [00:00] We're going to start off with two sides. The first side will be the top part. That will be where our circle starts at. The second part will be our drop zone. Our drop zone is just going to be positioned at the bottom, left, and right, and it'll take up 50 percent of the screen.
[00:19] The first thing that we need to do is set up some draggability. With that, we're going to need a few different pieces to hold onto. We're going to say this.dragX is going to be a new value -- set it to zero -- this.dragY. These are going to hold onto the current drag position of the moving circle.
[00:41] Then we'll move onto our offset. We'll say this.offsetX, and we're going to set it to the central point of the screen. We'll say new value. We've already brought in the width of the screen here, so we'll say width divided by two. Then we'll say this.offsetY is equal to new value of just 100.
[01:05] Then because we are doing declarative approach with Reanimated, we need to hold onto the gesture state. We'll say this.gestureState is equal to new value. We'll set it to negative one, because that is not any gesture state within the React native gesture handler.
[01:23] Now, we need to set up the event handler for our pan gesture handler. We'll say this.onGestureEvent is equal to an event. This comes from Reanimated. For the first argument, we want to get the native event, which has a bunch of data on it.
[01:42] We want to tell the React native gesture handler to assign translationX to this.dragX, translationY to this.dragY, and then a state to this.gestureState. Now that we have all of this set up, we can set up our conditions so that the circle will actually move when it's grabbed.
[02:09] We need to do a this.transX, and use the condition from Reanimated. When our condition is met, which is when our drag state, our gesture state, is active. We'll say when it's equal, this.gestureState, to state.active, then we want to do certain things.
[02:32] We'll say this.transY. Say, when we're active again, we want to do something. Because Reanimated is declarative, we can save a lot of time writing different bits of code by just saving up the declarative bits.
[02:50] We can say const addY is just equal to add this.offsetY to this.dragY, and then the same for our X. With this, what we can do is when our gesture state is active, the next argument will run. In that case, we want to do our add X.
[03:23] This will just take the offsetX and the dragX, which the offset is just the position that it's moved across all drags. Then we add the current drag to it, and then return that to the translationX. Basically, we are just moving it around and returning a new value while dragging.
[03:42] However, when we are no longer active -- so, the person has stopped moving the circle -- then we want to save off the drag and the offset to the offset. We can then say set this.offsetX, and then just pass in our addX again.
[04:01] We'll do the same exact thing for our Y. We'll say addY. When the user's no longer dragging, we'll save off this.offsetY, which is a combination of addY. Now that we have our drag entirely set up, let's pipe these events in via props.
[04:22] We're going to come down to our components, and we'll say onGestureEvent is equal to this.onGestureEvent that we created. Then onHandlerStateChange, which will just provide single changes, where onGestureEvent is a stream of events, we'll still pass in onGestureEvent.
[04:39] For our box here, we need to provide it with our transforms. We'll say transform, which will take an array. We'll provide a translateX, say this.transX, and a translateY, this.transY. Now, you can see, we can grab, drag, and drop.
[05:03] However, we want to alert the user whenever this circle is dropped and released within here. With Reanimated being purely native, there's no callback to the JavaScript world. We'll need to implement that.
[05:20] We'll first need to grab the dimensions of our drop zone. We'll say on layout, we'll pass this.saveDropZone, which doesn't exist yet, and then we'll go ahead and create it. We'll say saveDropZone, and this will receive an event.
[05:42] That event will have a width, height, X, and Y, which we can use to calculate the dimensions of it on e.nativeEvent.layout. With that, we can get the top, bottom, left, and right of our drop zone. We'll say this.top is just equal to our Y.
[06:04] this.bottom is going to be equal to the Y, plus the height of our box. Our left will just be our X, and our right will be this.right is equal to X plus the width of our box. Now, we'll then need a function that will be called for whenever we drop our box.
[06:26] We'll create an onDrop, and we'll receive an X and Y. Then eventually, we'll do some logic here. First, we need to do two things. The first is we need to bind our onDrop to this, for this particular component. We'll say this.onDrop is equal to this.onDrop.bind(this).
[06:49] The reason that we're doing this in the constructor, versus using class properties, like we have here, is that we need it to be bound to this before we pass it into our Reanimated call. At the moment, you cannot pass in arbitrary stuff into Reanimated.
[07:09] We'll just pick one of these, and we'll just do our transY. For the condition when we are done dragging, where we save off of our stuff, we're going to pass an array in instead. What this will allow us to do is add additional condition to the inactive state whenever the user has completed their drag.
[07:34] As long as this is the end, then this will be the value that's passed back to our transY. Here, we can say, when the condition, and do another condition inside of our condition. Say when it's equal, our gesture state to state.end.
[07:52] Whenever the user has completed their drag, we'll then do a call. With that call, we want to pass in the values that we need, so in our case, the X and Y, and eventually, the second argument is the function that needs to be called.
[08:13] Because this happens, this saving and the ending of the drag happens in the same frame, we need to pass in our addX and addY. Then we'll pass in this.onDrop. What this is going to do is, in the same frame, doing the addition of the offsets during the drag.
[08:37] Then we'll just save off the offsetY. Now, if we add a comma here, we can reload this. Our onDrop will now be called whenever we drop our X and Y. We need to then do some logic to alert that you have dropped it within the zone.
[08:58] With our X and Y, we can do some simple conditions. Say if the X is greater than or equal to this.left, and the X is less than or equal to this.write, and the Y is greater than or equal to this.top, and the Y is less than or equal to this.bottom, then that means that the circle has been dropped within this particular square.
[09:30] With that, we can then just do an alert and say, "You dropped it in the zone." Now, if we move this around, we can see that our onDrop is not firing. Then as soon as we drop it within the zone, it will alert us that the user has moved the circle within the zone.