Multiple Drag and Sort List in Reanimated 2

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
if there is one thing hard to build with rec native it must be drug and sort a few of you have already asked me to do it and it didn't take me long to see how complex it usually is to learn how this can be done however you might be surprised at how easy it can be implemented with the right tools alright let's do this [Music] in this video we're going to work on a list of items just like the ones you might have in your music applications where you keep track of your songs if you want to play one of them before another you probably want to reorder the songs and for that you need the items to be movable if you're not familiar with gestures in rec native we usually go with the react native gesture handler library when dealing with complex gestures so i'll be using that today here's how this is going to work we'll keep a list of our songs each one represented by a song component and living inside a scroll view then we'll listen to pun gestures on each item meaning we'll call side effects whenever we move an item around for that i'll put all the gesture logic inside another component called movable song and that's pretty much the idea just so you can see what we're going to build here and because there are quite a few steps ahead of us this is the final result that you get a dragon sword for a list of elements for now let me show you a gist of what we're working with we have an array of daf bank songs that is shuffled every time just so we get something more natural but you could replace this with any other list right below is our song component very simple just showing the cover image title and artist all in a nice way then we have our songwrapper that will contain all the gesture logic movable song which is fairly empty at the moment as you can see it is positioned absolutely because that's how we can easily move our items around later by tweaking their top style attribute just imagine that when we'll start moving a song we should then make it move alongside the touch and you can do this if all the items are stacked normally on top of each other right now the top position is defined by this positions property it is basically an object that associates each item id to its position in the list so if we have three items a b c this is how positions could look like meaning a is the first item c the second and b the last one it's just a handy way of keeping track of each item's position and then also manipulating these values if i want to swap a and c i just need to swap the values and the ui should magically update based on this change alright so now you can understand how this works we just multiply the current position of the item by the height of each song component the first one will be at zero the second at song height times one etc if you look at what we have below movable song it's our main component that renders all the songs inside the scroll view and because all our items are positioned absolutely the scroll view can compute its height automatically so we need to manually set it in its container style property without this we couldn't scroll at all because the scroll view would think there's nothing relatively rendered inside okay one last thing is the positions we have at the top which is the object i mentioned earlier i have a helper function that takes a list of objects and then creates an item id to position object for now it's using use ref just so it gets created once so these are our foundations now we need to make all of this interactive it's always the same method we need to create a gesture handler that will detail how a gesture should work and then wrap our component with a pan gesture handler so first let's create an empty gesture handler using reanimated that will listen to when a gesture starts when it's active and when it's finished [Music] then we need to attach this handler to the component i just mentioned panchester handler that we get from react native gesture handler all right one last thing we need here is to wrap our song component with an animated view this is necessary for pandestra handler to work and the view should be the only child of the handler this time we'll use animated from reanimated just so we get full benefits from this combination just before we move on we should change something here the problem at this point is that the pan gesture handler will listen to the entire row for touch events on each item this means that we won't be able to scroll anymore simply because the scroll view won't get the touch events to keep it scrollable we can reduce the width of each item so that for example it will listen to touch events on eighty percent of the row and the twenty percent left will be sent to the scroll view in code all we need is to set a maximum width for each item that is being listened to let's stick to our 80 now because each item should be movable we need to change the styling just so we can see which item we're moving around for example there could be a drop shadow when we're interacting with a song but before doing this we need to keep track of when we're moving it let's add a state property that we should update based on when a touch is starting and when it ends we can now set it to true inside on start and to false and unfinish this will not work actually and this is related to how reanimated is structured to those of you who don't know about this reanimated is running on the separate thread called the ui thread so all the logic you put inside this gesture handler will be running inside a different box than the one used by the react native app code to make it work you need to tell reanimated that this specific code should run on the javascript thread instead of ui by default for this we can wrap set moving with run on js that you can import from reanimated all right now that we know when we're interacting with an item let's change a style accordingly and because we want to smoothly animate the styling changes we'll replace our view with an animated view this is the only way for us to animate style with reanimated we're also changing the z index because when we move the item it should be seen on top of all the others by default it will go behind the others that are below this one so we're setting it to one when it's moving and zero otherwise also right now the shadow won't animate if we don't tell reanimated how to transition from point 0.2 to 0 and the way around since the moving state is binary it will just go from a 0.2 opacity to 0 which is not exactly what we want instead we'll need to transition the value progressively and for that we can use a spring animation which should animate the property smoothly with reanimated all you need is to wrap the value with whispering which will transition nicely whenever the value changes and because this is not a regular style since we're using width spring here that comes from reanimated we need to take this whole style inside a used animated style hook the difference between a plain style and this one is that the latter will know how to deal with transitioning properties like shadow opacity for example it also optimizes your re-renders if you pass it a list of dependencies because we're using two outside variables positions and moving let's add these two alright you can now see that it smoothly animates our shadow using that spring animation nice halfway there let's focus on the gesture logic now how can we make a move well it is usually always the same thing we create an animated value that says the initial position of an element and we later update it using the gesture handlers because in our case the items should only move from top to bottom we can play with the top style property let's create an animated value called a shared value in reanimated that should equal to what we had inside that hook to read the value of a shared value we just need to call dot value on it so we now have this shared value but it is not just being updated yet let's do this inside the gesture handler when the touch is active meaning we're moving our fingers we need to do two things first to update our top value according to the new touch position and second to update the positions of the items as we're moving our song over the others so let's start by updating the top value to get our current touch position we can use the absolute y property of the event which is where our touch is on the screen at the moment but we're missing something here we're not taking the scrolling into account indeed if we're 30 pixels down inside the scroll view we need to add these 30 pixels to our top position but we don't have the scroll position right now so let's get it from the scroll view first let's create a shared value to store our current scroll position this value should be updated when we scroll the list we can get that from the unscroll property of the scroll view and in reanimated you can use an animated scroll handler for this all this is doing is setting the show y value to the current content offset y of the scroll view whenever it's scrolled scroll even throttle will only go unscroll every 16 milliseconds which is recommended not to miss any important scroll event oh and before we forget we also need to turn the scroll view into an animated one since handle scroll is using a reanimated scroll handler the callback wouldn't work otherwise so now scroll y contains the current scroll position which can be passed to our movable song component that needed it if you remember we needed scroll y for the current position of our touch we can now add it to absolute y and great let's update the temperature value with this and we can animate the touch event just like we did with a spring animation earlier but with a timed one this time since we don't want it to bounce in every direction it has a short duration and i also subtract song height to the y position since we want the element not to be right below our touch finger but rather on top of it if we don't subtract it the item will jump right at the position of our finger which is not what we want alright if i give this a try we can now move the items but the other elements aren't changing position along with the gesture which they should this is our second important step here and you'll see it's actually quite simple what we need at this point is whenever we move our touch we should check if our current position has changed or not to compute the next one we can use our current position on the y-axis and divide it by the height of a song element i floored the value just so we don't get a decimal number also in case we take our item too far on the screen we need to make sure the new position is kept between 0 and our number of items otherwise our new position could be let's say 15 with 10 songs for this i created a simple clamp function that keeps a value within a range of numbers so if it's below zero it'll be set to zero and if it's higher than the maximum value it won't go higher this code is using the worklight instruction which means that this function will run on the ui thread and not on the javascript thread even if it's been declared outside of a reanimated handler okay so let's clamp our new position [Music] and to get the upper bound which is the maximum value this position can be we'll need to pass the number of songs we have down to movable song now to prevent our app to change positions when it's not necessary we need to check if the new position is different from the item's current position the problem with this approach is that we need a system where whenever positions are changed within one component which should be the case here all the other components that are impacted should update so at this point you might think we need to use a state property for this to keep track of all the positions and then set a new value for the other component to update accordingly this could work but it would all run on the javascript side of things not on the ui thread and just imagine if something is happening in our app at the moment you're making this gesture it wouldn't be smooth anymore that's why we should really take advantage of reanimated here and make all these updates on the ui thread and then make the other components react to these changes the closest thing we have from a state in reanimated is a shared value so let's begin with replacing the positions inside the app component and we'll use a shared value instead alright so this will also change a couple of things inside movable song so let's now call dot value every time we're reading from positions and so now we can safely update our positions for all the other components object move here takes an object our id to position object here called positions and take the element at the given position to a new one so here it's swapping our current item position to the next one by the way object move is also a worklet function so it will be able to run on the ui thread so this shouldn't change much for now since we're not updating our top value when the positions have changed the idea is to listen to positions and whenever it's updated we should also update top in reanimated we have another hook for this called an animated reaction that computes something based on shared values every time these values change you can think of it as a use effect for shared values here whenever shared changes it will compute shared times 10 and then pass the result to the following callback in our case we want to listen to position changes for that we'll replace the computation by checking our current position with positions.value and then do something if the position has changed if our item has a new position we should then update its top value this will use a spring animation to animate our song to its new position the only problem now is that it will also take the current element to its new position while we're moving it and create this weird effect also the item will stay where we left it and won't snap to its new position so that's two things first let's only update the top value for an element that isn't moving we'll also add moving to the animated reaction dependencies since it's a state property and when we release the item we'll set its top value to the new position which is going to snap the element to where it now belongs perfect and as you can see it also works with multiple elements okay that was a long one wasn't it there's still a few things to add for scrolling and the final touches but i think it's better if we take a break now we'll finish up this project in the next part of this episode which should be much shorter than this one remember to subscribe if you don't want to miss it thanks for watching and see you next [Music] time
Info
Channel: evening kid
Views: 4,667
Rating: undefined out of 5
Keywords: react native, react, tutorial, reanimated, reanimated 2, reanimated2, reactnative, learn, learn reanimated 2, reanimated 2 tutorial
Id: Ia2y0GDEuPc
Channel Id: undefined
Length: 16min 59sec (1019 seconds)
Published: Thu Apr 15 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.