Duolingo Drag-and-drop - β€œCan it be done in React Native?”

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

Candillion rocks

πŸ‘οΈŽ︎ 9 πŸ‘€οΈŽ︎ u/bender_3000 πŸ“…οΈŽ︎ Oct 13 2020 πŸ—«︎ replies

ello react native develowwwperz :)

πŸ‘οΈŽ︎ 10 πŸ‘€οΈŽ︎ u/Zeppelin2 πŸ“…οΈŽ︎ Oct 13 2020 πŸ—«︎ replies

Great content as usual

πŸ‘οΈŽ︎ 3 πŸ‘€οΈŽ︎ u/daavidaviid πŸ“…οΈŽ︎ Oct 14 2020 πŸ—«︎ replies

I really like the idea of this series, thanks for the content.

πŸ‘οΈŽ︎ 1 πŸ‘€οΈŽ︎ u/Aewawa πŸ“…οΈŽ︎ Oct 14 2020 πŸ—«︎ replies

Man, this guy is awesome!!!!

πŸ‘οΈŽ︎ 1 πŸ‘€οΈŽ︎ u/gowthamm πŸ“…οΈŽ︎ Oct 14 2020 πŸ—«︎ replies
Captions
hello react native developers what is going on welcome to can it be on rec native in this episode we build a flexible drag to sort user interaction [Music] guys i hope you are well william here recording from beautiful switzerland in this episode we are looking at the duolingo drag to sort user interaction so we have a world bank here at the bottom and we can drag words onto the lines and we can change the order of the words and put a word back into the word bank let me see if i can do this one she's eating an apple and they are eating bread so the east einen apfel and the essen brought so overall the user experience field is very intuitive very smooth the visual design is beautiful and that begs the question how would we do this in react native well we're going to do this in three steps step one we render the words and calculate their dimensions each word has a different dimension and we need this information to calculate the layout step two we need two functions one to reorder the words and another to calculate the layout depending on the order of each word and finally step three we're going to wire these functions to a pan gesture handler so that we can build the final drag and drop user interactions for step one we're simply gonna render the words attached the dimensions to some data this is how the data model looks like we need the position which we're going to calculate dynamically depending on the order we need the order of the word within the sentence and the position of the word when in the word bank so if we drag the word outside the sentence for step two we need two functions one to move an item from one place of the array to the other and that will enable us to recalculate the order of each words and the second function we use to calculate the layout of the words given their order step three we connect the functions from step two to a gesture each style as a pen gesture handler if the gesture is not active we transition to the offset x and y value if the gesture is active we drag the tile around instead if the tile position is over another tile we move its position in the array and recalculate the layout which will force the other tile to transition from one position to the other when we release the gesture we transition nicely to their offset position but what do you guys think can it be doing react native let's have a look before we get started one thing if you're interested to learn the fundamentals of gestures and animations in react native i hope that you check out my online course at start react native dot dev my goal with this course is to provide you with all the tools and knowledge necessary in order to be incredible user experiences in react native that will run at 60fps even on low-end devices and we are covering both reanimated version 1 and version 2 in the course so if you are interested to learn the fundamentals i hope that you will check it out at start react native.dev here is the boilerplate project which is also available with the source code on github in case you want to code along with me in the video so we have the list of words and we have the word list component which texts as children the world component in simply a static layout for each word and as you can see each word has a different size and if we go into the word list we have a couple of things already we have a state ready so if we don't know the measurements of each word we don't display the final rendering yet we first need to calculate the dimensions of each one and once we have the dimensions we show the final layout and we have the offset variable so we know that to each word we need to attach some animation values the order of the word within the sentence and if the word is in the word bank so it has no order because we didn't drag it yet onto the sentence its order will be minus one the dimensions which we are going to measure using on layout the position which we are going to calculate dynamically depending on the order and original x original y which is the position of the word when it's in the world bank if the measurements hasn't been made yet we display this layout that you see here if not we display the final layout so if ready is true where the words will be layout into the word bank and then we can drag and drop them onto the sentences here you see since we don't even know the dimensions of the world there's like an absolute position fields are all overlaid on top of each other and so we're gonna do step one measure these words also get the original x original y position and once these measurements have been done we will set ready to true and by the way just a small knot here you'll notice that i use animation values for all of these data i don't need to right because original x original y with height are not things which are going to change dynamically it's something we calculate up front what will change dynamically and that what need to be an animation value is order x and y but here i use animation values for everything just to be consistent because i would just need to have like a so i would need to use for instance for original x original word use ref so instead of dot value we would have dot current i just thought it would be easier to use animation values for all of these properties but obviously we only react so we only need animation values for x y and order so for each child here we're going to add on layout handler so we will have actually only out so it's a native event and i think it's yeah the layout property and we have x y weave and height now the offset is offsets at index and we can set offset got value to be with offset dot i dot value to be height this is a really cool autocompletion dot by done by type 9 original x dot value equals x and offset original y value equals y this plugin is really very very good once this assignment is done we need to check if um all the assignments have been done and if yes we need to do uh so we need to check if this was the last measurement and if it is we set ready to true and for that so i'm gonna show you how we do it so we are gonna loop on the offsets filter if the offset and by the way sorry we need also to set the order to be minus one there's no order yet it's in the word bank order dot value so here we have offset let's say so here default value is zero so if i do offset order that value is different from minus one if the length equals at zero it means that um all the measurements has have been done is that correct yeah i think so all the measurements have been done and we can set ready to true set ready true and as you can see this is not working because we need to run this check on the ui thread so we're going to use run on ui so it's we're going to invoke a function so here we add the worklet annotation so that the function gets compiled and run onto the ui thread and we execute it so now it should work nicely yes perfect so we have the measurements for each tile so step two we're gonna calculate so here actually if i go sorry before we get to step two if i go to the world here and so we have the offset so we can i'm gonna remove this here and we can do weave equals offset dot weave that value same for height and you see here so the size is taken into consideration they're all overlaid on top of each other and this is where we're going to use a function to calculate the layout depending on uh the size and the position but also here since order is -1 they should be in the world bank and so actually we could build this function already so we can create a variable called is in bank and so it's going to be a derived value so we're going to look at offset dot order dot value equals minus one and we're gonna so okay we're gonna create translate x and translate y to move these into the word bank so translate x so use the right value is a hook where we can execute so javascript code onto the ui thread to calculate to derive an animation value based on other animation values so here also we're going to use use derived value and if it's in the bank is in bank we return we're gonna return dot original x dot value same for y offset dot original y dot value and if not we're going to return offset dot x dot value a dot y dot value and here dot x dot value so we can apply the transform here so let's translate x that value translate y that value so here i need to create two objects and also here it's i'm sure there's a way in typescript to configure this so here it should complain because i'm trying to check if something is trophy should be that value i'm sure there is probably a way to statically complain about this so we don't have to do such mistake so let's have a look okay so almost so here we have merging top and merging left so we need to add merging top so merging top is ma and merging left are the offset of the world bank from the lines from the top and the left side so here i'm gonna do plus merging top and here because these positions original x original y were calculated already with the margin we're gonna remove merging left and so now it looks good and so now we can go to step two where we're gonna build our functions to move an item into the array and calculate the layout so to test if our layout function is going to work without actually using the gesture there is one thing we can do here instead of using order is in the bank so minus one we're going to set the order to be index and here so would be different from so equals minus one and default would be minus one but we'll change that later it's just for testing so let's see okay perfect so now what we want to do here is to calculate the layout and actually i could do it here so once everything has been measured we want to calculate the layout just to test we don't need it here but just to test the function um so we pass offsets as parameter and so we have our layout file and we can create calculate layout and the input ah an array of offsets and we need the weave of the container here so to know when we break lines okay so container with and this function is a worklet function [Music] so the first thing we need to do is to filter the words which are not in the word bank so we want only the word which in the sentence and we need to sort them by the order so we're gonna do offset squares input and we're gonna do filter ease in word bank and we're gonna sort sort by order so this is the offset we're gonna need to calculate the layout so is in world bank again we need to not forget the worklet directive so the function gets properly compiled and executed on the ui thread so we have offset and we return offset order.value is different from -1 and then by order so we have a and b again it's a worklet so if a dot order the value is greater than b dot order dot value we return one if not minus one so here we have the offsets filtered and ordered as we want so we can calculate the layout from from there so we need the current line so the line number and we need the line break we also need the height of each word here each word has the same height so actually let me if offset dot length equals zero we don't do anything and then the height is of sets so we know here we have at least one item dot height dot value okay and now we can go over the offsets so yeah offsets for each and we also have the index so we need to calculate the x position so we're gonna do a offset a reduce so we're gonna slice the offset from line break because if we are at the second line the line break it goes back to is not zero but it's whatever was the number of words on the first line so we're gonna do a line break to index and we're gonna add the weave of each element so we can do total equals reduce um okay offset we start at zero and we add offset dot we've done value oops okay so here we have the x position if total plus offset dot with that value is greater than container with it means that we should break line because we don't have enough space to put the word so we would do line number plus one and the line break will be index and since we are breaking line offset dot x that value is zero if not offset.x value is total and then we need to add y which is height times number of lines line number let's have a look so this should display the words properly now we so it looks like the function is not executed and in fact it's not easy in word bank is is not in one bank he's not in bank i don't know and so different from we want the one where the order is different from minus one [Music] yeah so you see here the layout was nicely calculated and we need a second function which is going to be to you know we're going to drag the words it's going to change the order and we need to recalculate the layout depending on that so we need a function to say okay we want to move one item from this position here for instance order one to let's say order six five and four and um yeah so we can build this function quickly so um we're gonna call it reorder and so we have the offsets as parameter this one here and we have a from position and a two position so to move uh an item from one position in the array to the other there are cop there are many functions that do it there are some also npm package which provides functions to do it here we cannot use an npm package to do it because we the npm package will enter the worklet directive so the function couldn't be executed in the ui thread so i shipped a function to do this where i copied the implementation from an npm package and i am shipping it part of redash so that we can execute it in the ui thread so here again it's a worklet and we need to do essentially the same selection here where we want the words which are which are not in the bank so which have an order and we want to sort them by order so that we the position on the array matches the order then we're going to do the array move which is a function provided by redash and then apply the index to the order property so that it's in sync and so i'm gonna do new offset is move from redash [Music] yes we pass the offsets from two parameter and we get a copy of the array and now we can map the array and have so we have offset index and we copy to offset dot order.value index and so this is the function so when we're going to drag an item we're going to detect always the item into this new position we're going to reorder the offsets and then calcul recalculate the layout and that's step three we're going to wire these functions to the punches handler so let's create open gesture handler and the first thing maybe i'm gonna do here let's put back this one to zero and zero this was just for testing so here it would be if different from minus one and harder is gonna be minus one so everything by default should be in the world bank perfect and um [Music] yeah let's do it uh okay so uh sortable word and we're gonna wrap this into a pan gesture handler and you'll notice you know sometimes so we have a pound gesture handler which drive drives some sort of translations sometimes we want the pan gesture handler to be outside the translation so we don't want the position of the pendulum to move depending on the translation sometimes you want the other way around which is the case here we want the translation to be outside and we need the on gesture event so we're going to create the on gesture event and uh yeah let's do it so use animated gesture handler and maybe so what we want to do we need to do many things here but uh to start step by step we're going to create a variable to detect if the gesture is active or not if the gesture is active we want the position translate x and translation translate y to be the drag of the gesture if not we want basically to be this logic so let me create a variable is gesture active so use shared value and default value is false and when the gesture is active we are going to calculate some sort of drag position so which we're gonna store into a vector called translation it's simply a short use vector it's a simple shortcut from re dash to create a x and y value shared value so on start the um is gesture active that value is true and on end it's force and on active translation dot x dot value so here we get in the event translation x translation y and we're gonna when we start of course we need to remember the position before the gesture started so we're going to use so there is a concept in the use animated gesture handler so here it's going to be pan i think it's spanisher and there's a concept of context which is a state that we can maintain across gesture events and it's all done on the ui thread and we're going to remember the x and y position i think no it's not and gesture i'm not getting here unfortunately the up-to-date version of the types so maybe that's why i'm yes okay so i'm using an old version of the typescript types that's okay but this was updated i think in alpha 7 and we're using alpha 6 with expo that's okay so here we're going to remember the position so we're going to have context x equals should be i would say offset dot x that value i guess depending if the so is so if we are in the bank the offset is original x and original y if not it's indeed the position on the sentence which is these two things here and the context we get event and context perfect and so here it knows about the types based on this generic so here we do context dot x plus translation x and translation why the value is context dot y plus translation y perfect so context we get here um now if the gesture is active we return translation dot exit value and same for y let's have a look no so the the offset is not correct there is something also we need to do i mean z index if the gesture is active that index is 100 we want the element to be above all elements if not zero but the starting offset is incorrect interesting should i'm wondering it's if it's because we set this one to true so it returns translation x value but translation x value hasn't been updated yet so i'm wondering if this is the issue so i think what i should do is probably is the issue so is to do translate so context is translation x dot value same for y and these are the values we update so that when this one is set to true the values are immediately correct let's have a look yes no there's still some um we still have the margin top and margin left so essentially what we have here margin left and plus margin top [Music] we could refactor it's okay so we could refactor these expressions all in this i use the right value but i guess let's try there will be original y here perfect so now and one thing we can do also immediately you see how it doesn't transition back into the world bank so we can do return weave spring it's in the bank we transition to this position if not we transition back to the offset value and same for y looks funny so plus margin top okay when you see now it snaps nicely back to position that's perfect so we have here the basic gesture integration if the word is in the bank if the word is in bank and translation y is below 100 pixels so within the region of the sentence we want to add the word to the sentence so first its order is gonna be the last available order so we're gonna do offset dot order dot value to be so we can call it last order and then once there is more we're gonna calculate after uh what's the real order but we need to add some order and first here we don't know based on the position so we're just going to pick the last one and then fix it after so we pass here or sets as parameters so we need to create this function so again it needs to be a worklet and so i think here we're simply gonna return so worklet the length so we sort the one which are not in the world bank actually just this and the length so that would give us one order which is non-taken and from there we can calculate the layout of sets container width um and when so this will calculate the offset x so when we end the gesture we need to transition to the offset value so our translation.x value equals that weave spring to offset x dot value and translation dot y dot value is with spring of offset dot y dot value so you see here the word was placed so here it will always be in the end but we're gonna add the logic not to if we go over one of these regions and so maybe but before we get into it we can so here we can go from the world bank to the sentence you see nicely up but now we should be able also to go back to the one bank so we can do else if uh is in yeah that's good [Music] is in bank dot value and translation dot y dot value is greater than 100 we set the order value to minus one and we need also to recalculate the layout so let's have a look so we go in and we go back and just put some words here and i can take it back here perfect so now let's say you see i mean here there is a yeah i know actually okay there is a bug there is a bug let me first where to start there's many things we still need to do here and i'm thinking what we should do is calculate if we overlay the position of one of the word we change position recalculate the layout and then there is obviously here [Music] and you've seen it there is a bug with last order because here you see the the order is not correct because we remove one when we remove it when we remove this one we need to recalculate the order so we recalculate the layout but the order of the word is wrong because we removed one one so it's not enough to just put this one to minus one we need um we need to move all the other ones so actually let me try to see if we can do um should i do remove so remove offsets and the position here we have [Music] um so here we do export const remove we have the input index and so we're going to do the same here so we filter we want the one which in the sentence we do want to sort by order we want to remove and here i can just get a splice of that index [Music] and then we have the offsets and now the position should max match the index and before we do this we could do offsets index dot order dot value to b minus 1. let's have a look [Music] so we remove it here let me try so we add some wards we broke something [Music] so offset order equals minus one that value [Music] we do it here no because index is not correct oh okay so maybe here we first need to filter with the index so where e is different from index and then we filter not in bank sorter by order and we should have the so is it strange what i'm doing so instead of working with functions to remove reorder which work with order directly i map the order to an array do the operation and remap the indexes of the array to the order that's what i do here is that i think it's good but let me know what you think maybe i'm not approaching this in the most efficient way but that should fix the bug and then finally we can look at the real piece which is to the reordering so let's say i put this one back and now this one should go last which was not the case before this is very cool yes cool guys it's a final stretch now okay we do all this fancy stuff we're going to look with the drag if we are over one of the tiles and we are we switch the position recalculate the layout so four and here i'm using i never i never use the for loop here we need it because we want to break so if we do a move that's it we don't need to go over all of the offsets so that's why i'm using the for loop i never use it but here we really need the brake capability if and continue also because if e i equals index we skip because it's the same tile and now if the so we are [Music] translation dot x dot value so we have the offset we're comparing two so i guess i can call it o so it's going to be offset i if we are so over dot x and also if it's in the bank so o dot order that value is different from minus one and it's over a tile so we do the x and y axis so o that x that value plus the weave so in between right [Music] so it's translation and we do the same for the y axis [Music] so here would be height [Music] so i'm still missing uh like this okay if we are so if this happens um we need to move so offsets so from index to the new position which is i and we calculate the layout of sets container with and if this is done we can break because that's it we are done essentially so we don't need to continue the loop let's have a look [Music] so air east einen so this is not working this is not working at all i'm an upfall so here it's not the index of course because this is static it's offset.order.value and here would be offset or dot order that value should be forbidden to promote such poor naming conventions on youtube i feel like i really need to work on this this is really poor naming conventions a couple of other names also which don't really make sense he's in bank i'm not sure is this should be really cool offsets i'm not sure could be maybe more explicit original x maybe should be more explicit it's posi position x in the bank i don't know let me know what you think in the comments below um so if we skip if it's the same index that makes sense we should also skip here if the order is we can do it here maybe it's much easier let's have a look what am i doing it's not move it's reorder move is from redash so we cannot use this one directly so now i hope this should work air east after einen einen apfel isn't that cool kind of nice isn't it guys i hope you enjoyed this example what a delightful user interaction and this is really where reanimated2 shines because we can execute these somewhat complex javascript functions on the ui thread and integrate them very easily with the pungest handler as you've guessed already all of our next videos are going to be based on reanimated version 2 so i am looking forward to talk to you soon and in the meantime happy hacking [Music] you
Info
Channel: William Candillon
Views: 19,290
Rating: 4.9848866 out of 5
Keywords: React, React Native, Can it be done in React Native?, JavaScript, TypeScript, App Development
Id: tHWGKdpj1rs
Channel Id: undefined
Length: 53min 3sec (3183 seconds)
Published: Tue Oct 13 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.