Building an Apple Wallet Clone with React Native Reanimated

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what's up not just developers welcome back to a new live stream today today we're going to build the Apple wallet in rack native using rack native ranimated and rack native gesture Handler and instead of talking let me actually show you a quick demo of what exactly we're going to build so here is the end result that you're going to achieve by following this tutorial we are rendering a list of cards similar to how Apple wallet is doing and we are uh actually implementing our own version of scrolling through these items and as you can see we also have some special effects on the scrolling as the cards are stacking on top of each other at the top as we are scrolling another interesting animation that we're going to work on today is the animation when we select one of these cards whenever that happens the card pops up at the top and the rest of them are covering here on the bottom and when we deselect everything moves back to the normal and everything works with maintaining the same scroll position so when we go back we are at the same place everything works very smooth on both IOS and Android with 60 frames per second so very excited to uh build this application together with you and as you can see the application is highly focused on animations and that's exactly going to be the focus of today's tutorial for that we're going to use re native reanimated to power our animations and react native gesture Handler to uh respond to the gestures that the user are doing swiping tapping and so on these two libraries are very important part of a rnate of building interactive applications in react native they are built by software mansion and actually if you want to learn more about this I would like to invite you to this year's edition of ABDO j conference this conference is also organized by softare mansion and it's happening in kov in Poland uh from 22nd to 24th May so um it's three days uh event we're at more than 26 speakers a lot of atende DS so if you want to connect with the community this is the right place I've been there the last two years and I really enjoyed and this year there are even better speakers from which you can learn there are also workshops uh most of them are already sold out but there are two of them that are really interesting and there are couple of spots left uh this is the accelerated development and distribution uh workflows with Expo and the one that most probably uh you will get a lot of value out of is the export router Workshop make sure to check out the uh the page of a conference going at AB dogs.com Co and you can use the promo code not just devs 10 for 10% discount at checkout and with that I'm going to probably see you at ABDO Jazz because I'm also going to be there and I'm actually going to celebrate my birthday during this uh event so let's meet let's catch up there and now back to the video thank you very much software Manion for sponsoring this video and let's get started [Music] all right so uh we're going to start everything from scratch so let me go ahead and close everything here and let's start together by initializing a new Expo project I'm going to open up a terminal I'm going to navigate here to the place where I keep my project and here let's go ahead and initialize the Expo project using npx and using the create Expo application uh by running npx create Expo up at latest and we we can call it wallet up let's go ahead and press enter and this will initialize an empty and blank Expo project from where we can get started all the libraries are working out of a box in Expo so we can continue working with Expo go throughout the whole tutorial that we're going to do today really like animations you can actually use this kind of Animation in a lot of applications in a lot of finances application so very excited about this one but while this our application is being uh set up um if you're new on this channel hello welcome here my name is Vadim and on this channel my mission is to help you build impactful mobile applications we have a lot of Project based tutorial similar to this one or even bigger projects where we take something from scratch and build all the way till production together with front end and back end so if you're interested in that if you want to learn and master mobile development with rck Native and all the tools around it make sure to subscribe to the channel that would help us a lot and uh yeah welcome welcome to the channel um yes uh now the project is actually finished initializing so I'm going to open it up in Visual Studio code wallet up this will open the visual studio code or you can use any editor of your choice perfectly fine and before we actually start the application what I like to do is I'd like to switch from JavaScript to typescript uh you don't have to do that this is really optional I'm not even sure that we're going to use any typescript today but this is a um something that I follow in most of the tutorials to do that I will simply rename the up. GS file to up. TSX and in the next step we are going to open a terminal let's open a visual studio terminal inside our project and here let's run npm start npm start will execute Expo start and then Expo will detect that we have a typescript file but we don't have yet typescript configured so what it will do is it will ask us to uh if we want to install and configure typescript let's just press enter and Expo will take care of everything for ourself installing typescript other packages and configuring it here so now our application is a typescript application and when we see this menu that means that our application is ready for us to get started and working on V just way to um start working on this application uh would be to download the expoo application on your physical device from App Store or Google Play and then simply scan this QR code that's the simplest one because in that case you don't need any Android Studio or X code on your machine uh so you can just get up and running really really fast in my case I have IOS simulator and also Android emulator uh so I'm going to press I here in this terminal to run it on my iOS simulator you you can follow this tutorial both with Android and iOS and at the end I'm going to test to make sure that it works perfectly for both of them and after I press I it opens here on the side in my emulator we see open upgs to start working on your application and we can do that here if I open this file we're going to see the entry point of our application from here I can simply start start with a Hello World save make sure that everything reloads and refresh uh automatically on our device and that means that we're ready to get started uh hello everyone who joining us live how are you doing guys are you excited about this tutorial hello Rogelio hello Kamal hello weip yes is asking when will you release the new course I'm ready to pay I'm very happy to hear that uh however we're still working very hard on that we're going to have a waiting list up and running hopefully in one week and then you're can join the waight list and probably in a couple of months we're going to have a first version when when you will be able to join the course um it takes a lot of time to build a really high quality course because these are not like YouTube videos where I improvise in a lot of cases this is something that I prepare a lot I do a lot of research I make sure that all the projects that we build in the course are kind of built on top of each other so that you can learn really in depth everything about react native and Expo uh without like spending like $100 or something like that I want to help you in the fastest way possible to master re native development with modern tools so yeah that's that's our upcoming course it's it's coming soon uh and when I will have more information I'm going to let you know but thank you for asking uh okay guys let me um ask a question in the chat and I will stop from time to time to answer them and uh let's actually go back and continue the tutorial uh before we actually get started and write any code one second I want to sneeze oh come on okay sorry um before we get started there are a couple of assets that I prepare for you uh that you can download following this URL you can find this URL also in the description below and after you download this uh you're going to get here you download the asset bundle and in the asset bundle let me also Al find it we will find some where is it here is the asset bundle so inside this asset bundle you will find uh some uh assets the presentation and the source code at the end I'm going to includ it here as well what I'm interested in is in the cards images and that's going to be the first step that we're going to do so let's go ahead and drag and drop this cards uh folder in our assets directory inside rck native so I'm going to do it like this so under assets I have cards and all the cards are pngs that we can see here so we can work with them okay perfect now um what I'm going to do next is I will want to render these cards on the screen but I don't want to do everything in the app. TS file I want to separate them in separate files uh so that is going to be very easier for us to manage and work on the application so let's go ahead and set up quickly the structure of our project I like to create a source folder where I keep all the source uh well the source code and inside the source directory let's go ahead and create a folder called component and a folder called I'm not even it's supposed to be a folder for screens but probably I'm going to do them right away in the components not to have a lot of things so for now a folder with components is enough and the first component that we are going to build is going to be the list of cards so let's go ahead and inside the components folder create a new file called cards list. TSX now here we're going going to create our first custom component the custom component in react is a simple function let's call it the same name as the file cards list um I'm going to Define it as a arrow function you can also do it with normal function and what makes this a component is uh we have to return something to render on the screen things to render on the screen we can import from react so we can start with a text uh from Rea native actually and let's go ahead and render a text here cards list this is going to allow us just to see if everything is connected and then we're going to work on rendering the actual cards the last step in this file is to go ahead and Export uh this component cards list from this file so that we can go into up. TSX import this component let's import cards list from Source component cards list and instead of this text I'm going to delete the text completely let's go ahead and render our custom component called cards list if I do that I should see on the screen cards list text that means that our Uh custom component that we created here is rendering correctly um okay now let's go ahead and actually render our cards so I'm going to import a view and a image component because this image component is going to help us to render our card images uh instead of a text I'm going to actually remove it I'm going to open the brackets and start with a view because that's where we will keep our list of cards and then inside this view we're going to render the actual cards but first we need to import all of these images and I'm going to import them here at the top by defining an array of cards this is going to be an array and for each item I'm going to import the image using require and then the relative puff from where we are here in the components folder I need to go back one time into the source folder then I need to go one more time into the root folder and after that I'm going to go into the assets then cards then here I'm going to choose the card 1.png card one.png okay so that's our first card and I can do the same for all the rest of the cards by simply copy pasting it nine times we have nine cards and changeing the digit of a card like this have one more extra let's remove it so now we have an array with nine images um before we render all the images let's try to render at least one image so I'm going to I'm going to go here inside our view and let's start by rendering the image component that we imported from react native now inide the image component we can give a source which uh expects an image and we're going to give it our cards at position zero to take the first image from this array if I save this we should already see something um but uh the the image is quite large uh and what I'm going to do is let's go ahead and provide some styles to this image what I want to do is I want to make sure that the width of this image is 100% the problem right now is that the image completely disappeared and I can test if the problem is with height by setting a custom height here like 200 but in fact it's not about Pro the problem is not with a height the problem is actually on our app. TSX uh that lines and justify conter on the center let's remove these two from here and now our card should be displayed at the top of the screen with 100% uh with while actually I was at here in ab. TSX let's do a couple of small changes here that will we can do and forget about them one of these changes is I would like to change the background color to Black like this and the second change what I want to do is I want to make sure that the content of our application is not displayed behind the uh behind the notch so that's called unsafe area uh to to fix that we're going to import a component called safe area view from Rec native and I'm going to Simply replace our root view in app.js and up. TS with a safe area view now that will make sure that our image is displayed below the Notch and now we kind of lost uh all the um status bar icons at the top uh and that's because here in status bar instead of Auto we can set it to light and now they are displayed with white so now in up TSX everything is correct I can remove these two Imports to keep it clean and let's go back to our cards list okay so um let's talk a little bit about the sizing of this image at the moment I said that the WID of this image should be 100% uh that's fine but how do we calculate the height at the moment we hardcoded it but what we actually want to do is the height we want the height to depend on the width so if the screen is larger both the WID and the height is going to be larger so we want a relative sizing and that can be accomplished by setting aspect ratio for example one over one that will make a square as we can see here uh actually the aspect ratio that I want we're going to take it from by looking at the aspect ratio of this image if we look here at the bottom we see that it's 700 pixels by 400 pixels so that means that the aspect ratio is 700 over 400 or 7 over 4 it's doing something but the problem is that now it messed up the wave uh with remote images this are this syntax is working perfectly fine but with local images what we should do to fix this issue is we need to set the um the height of a image to undefined that's a little like uh probably it's a bug because I would expect like if we don't provide the aspect ratio for it to also work but in this case it messes up uh it keeps the aspect ratio but it enlarges the WID so I need to uh hard code height with undefine and then by providing the aspect ratio our height is going to be calculated based on the Wei using this aspect ratio and this way our card is actually displayed correctly and if I'm going to add I don't know background color red here we're going to see that the Box in which it renders is exactly the size of a image that's what we want okay perfect so now we render one image we styled it we sized it now let's go ahead and render a list of images so we have an array of cards and for every single card here I want to render one of these images so in in JavaScript or in R native what we can do for in this case where we have an array of things and we want to render an array of elements on the screen the easiest way to do that is using a do map operation so I'm going to start by opening the corly bracket I'm going to use the cards array and let's do cards. map so we are going to map over all the cards in the array and for every card we are going to ROR turn an image like this and if we do that now we see a list of images uh the think is that now the source or the image itself that we want to render should be the card or better called card image and if we replace the source with card we see that all the images are different one thing to remember is that whenever we are doing this mapping we should also give here a key uh for react to be able to properly optimize our list in our case um we don't have any identifiers for our cards in a real scenario you would have like some kind of an ID but for this demo we only have images so I'm going to go ahead and actually take the index of the element that we're currently rendering and I'm going to use it for the key just like that it should be fixed and we should also get rid of a of a key um warning um let's go ahead and add some maybe margin vertical to this uh image and we're going to do five and that will add some spacing around them and and and the next thing is what maybe to the whole view we can give it a style and add a ping 10 to also have some spacing around on the left and the right okay so that's our list at the moment we cannot scroll yet you might think that to make it scrollable we can replace this view with a scroll view or you might ask why didn't we simply put this um render this using a flat list well we render it this way because in this way we're going to have much more control of how the scroll is working and we need that in order for us to have like really control over like where the elements are positions like for example when we select a card we want the card to go on the top the rest of them to go to the bottom so it's going to be a lot of work with the scroll position wave translation so I think it's going to be easier if we actually will manage the scrolling ourself so that's why I don't have anything here our application it's not scrollable yet but that's going to be the next step now that we have it uh this way let me go ahead and uh commit it get add get commit render list of cards um okay yeah I think we can move on and now we are going to start working with reanimated and gesture Handler because as I said our next step is going to be to implement the to make this list scrollable and we're going to do it ourself and do it yourself way uh and for that we're going to need the library gesture Handler to detect when the user is dragging over this list and we're going to need ranimated to make sure to smoothly um move the elements on the UI on on the UI to move them up and down so let's go ahead and go ahead and install these two libraries let's start with gesture Handler I'm going to search for gesture Handler should be this one it's the latest version and let's go into the installation guide to yeah basically to install it I'm going to just grab the name r native no R native gesture Handler come on and let's go ahead in the terminal and install it using npx install react native gesture Handler like this let's press enter And while this is installing npx npx Expo install I forgot about that npx Expo install R native gesture Handler and while this is installing I'm going to go back into the documentation and check the next steps that we have to do so what we have to do is we have to wrap our whole application in this gesture Handler root view inside our app. TSX so let's just do that let's open up the TSX let's import here gesture Handler root view from RE native gesture Handler and let's wrap our hor Up application in this gure Handler root View and usually what we also should do is we need to give it style and a flex one to make sure that this view will take the whole amount of space on the screen so now everything is working working as it was working before we a difference that now we are going to be able to handle the gestures that's going to come in a moment but uh now let's go ahead and also install and set up uh Rec native reanimated so let's search rect native reanimated let's go ahead and open up the documentation of re native reanimated and press get started to install we're going to do npx Expo install R native reanimated so let's go ahead in the terminal using Expo we're going to install re native reanimated and as as I said before both of these libraries are part of the um over the Expo uh SDK and they are available in uh Expo go so we don't have to do anything extra let's follow the instruction guides uh for setting up reanimated and what we have to do is we have to set this reanimated plug-in in Babel do config so let's open the files we're going to find here Babel config I don't have plugins yet so I'm going to paste plugins and the react native reanimated plugin here if you have already some plugins make sure that you add the reanimated as the last one in the list and with that we are ready to restart our application and we need to restart our server and make sure to clear the cache for that I'm going to go back to where my Expo is running I'm going to stop it by pressing command or contrl C and I'm going to run it with npm start then I'm going to do das Dash space-- clear this way we are sending with clear flag towards Expo start and and this will also clear the bundler cache meaning that everything is going to be um fresh let's see let's run it again on iOS by pressing I and if our application is running which hopefully it will then we have successfully installed gesture Handler and renim yes yes here we have them so now we can go ahead into the cards list and start working on making this list scrollable I think I'm too zoomed in or no it's good hopefully it's going to be good so um first step the first step is to detect gestures we want to make to detect when the user is uh dragging on the screen or this um this gesture is also called panning when there is a pen gesture for that we need two things a gesture detector so let's import a a gesture detector and the gesture from RE native gesture Handler first we need to Define our configuration for the gesture so let's define this as in our cards list as pen equal gesture do pen and later we're going to provide more configuration here for now we simply uh created this pan gesture we're not handling anything yet now we need to uh to wrap the area or the component that where we want to handle these gestures in our case that's going to be our whole um list so that's why I'm going to wrap the whole view into a gesture detector I'm going to close it at the end and the gesture that we are going to detect is going to be this pan gesture that we Define here now nothing is happening yet but now we can set up and configure different configuration of this gesture and different callbacks callback functions that um will be called whenever something happens for example if we have a on start um on start and we provide here a call back and inside a callback we say console log panning started and here we need to close it and after that we can chain multiple uh call backs for example and our event is going to be on um change and this is going to be called every time we are moving an hour uh finger like changes like the position of the finger changes on the screen let's do here console log panning and then I'm going to add one more uh event here on end and this is going to be called whenever the gesture ends so let's do console log panning ended now let's go ahead and open the terminal clear it and go ahead and try to press and start dragging and release as we can see we have panning started and after that as we moved our Mouse on the screen it's panning panning panning and at the end we have also the panning ended not only do we know like these events we can also get some information about what's happening for example in the on change we can take the event and we can also console log for example the uh what we are actually interested is how much did we uh did we swipe up or down because that our scroll um list is going to be a vertical scroll list so the only thing we are interested right now is the how much scrolled on y let's console log the event Dot and here you're going to see a lot of properties that the event has the absolute X and Y how much it changed the state how much it translated the velocity and so on what I'm interested here is in the change Y how much did we scroll up and down how much did our pan change from the previous time it was called not from the beginning of the gesture but from the previous time the on change was called so if I open up the terminal now and start Banning we see that as I move up we are scrolling with around like five pixels or three pixels we moving up if I scroll down we're going to see that the values have positive value so just like that we know like how much it changed but as I said there is a lot of things you can have in this event and for more information make sure to go to the reanimated or no to the gesture Handler here we're talking about gesture Handler and look at the P gesture it allows you to do these kind of drag and drops um in our case we are doing like scrollable and we only handle like the why translation like up and down but you can do also drag and drop as well and you're going to see like all of the things here you can configure a lot of things and if you look at the callbacks here you're going to see on begin on start on end on finalize and a lot of more other events that you can hook in and execute something there is the callbacks uh when we are actually dragging there are two callbacks the on update this is going to be called every time the gesture receives an update and the on change that we are using which is the call back um it's the same on change the only difference is that it has information about the change in the value in relation to the last received event so it's basically besides the information that the update is giving it's also giving G information how much did that did did the how to call it the gesture changed on the on the screen so perfect so knowing knowing this information as you can imagine we can power a scroll scroll view uh for for that we're going to have to keep track of a value of how much we scrolled and every time we are panning on the screen we are going to update that scroll value with this change y um translation and then using that scroll value we're going to move our cards up and down um depending on it so now we are moving into the reanimated part so probably I'm going to do a get ad and now we're going to start working with reanimated the first thing that we need from reanimated is going to be to uh we're going to have to keep track of the scroll position the scroll position um cannot be a St State a react State variable because the state is used to reender our component in our case our scroll Position will change so often often that it will be very inefficient to reender our component every time it changes so for that reason we need to execute or keep track of this scroll position not on our usual JavaScript thread that is handling our rendering life cycle but on the UI FR which is uh handling the rendering part the UI part to do that that's called the shared value uh from shared value from react native reanimated so a shared value is let's say quite similar to a state variable however this value is um is stored on the UI thread and it's perfect for running animation for running uh yeah UI animations so let's define it as uh scroll Y how did they call it yeah the scroll y position and we're going to Define it as use shared value equal to zero now we can uh simply update this scroll y value uh inside the on change by saying scroll y. value is equal to a new value so updating it is even easier we don't have to call any Setters we right away update that do value property of our shared value so what we want to do with a scroll y when we are panning up and down is we want to um subtract the change like how much it changed from the current scroll value so we're going to take the current scroll value and subtract their the event dot change Y and we can uh we can also do a console log of scroll and here let's console log with scroll y. value I'm going to remove this console log and let's check uh now we start at zero and I'm going to I'm going to simulate that we are scrolling down by moving the finger up the screen so we see that as we move up our scroll increases and then as we will move down the scroll decreases and it doesn't matter where we start because everything is relative like we're working with simply with change even if we start from the bottom or from the top and so on it automatically keeps track and if I will do more times to the bottom it's going to go really it's going to increment so with this scroll value minus event change okay perfect uh but what can we do with this scroll y knowing the scroll position what we should do is we need to translate this image a translation allows us to move an element on the screen and is defined on the style using a transform and then we need an array with an object where we put the Translate Y so if we translate all the items 100 pixels to the top they will will all together move to the Top If we increase it to 120 30 and so on you can see that they move to the top in other way around if we have a positive value they will move to the bottom so that means that we can actually go ahead and use the the scroll y position here scroll y value uh scroll y but in order to use the scroll Y which is a shared value which runs on the UI Fred our component that is using this value to sty for The Styling should be an animated component so the only thing that we should do is we're going to import from RE native reanimated the animated library and there we already have some default animated components such as animated oh come on animated do image and just like that now this image is uh we are being able to animate it using shared values in directly inside the style so now if I'm going to start scrolling up all the cards are moving down and if I will do up they are moving up so it's a bit it should be other way around it should be inverted um one way to do it is instead of doing minus here to do plus and in that case it will work correctly but this way is not actually logically correct because the scroll y like as we move down it should be a positive value but in now now the scroll y if we look in the logs is a negative value so it's a bit uh counterintuitive so I'm going to actually leave it to minus one minus event. change in order to have in order to have positive values when we scroll down and the only thing that I will have to do is when using it here I'm going to reverse it I cannot do it right away here like minus scroll y because this is a shared value I cannot even do minus scroll y. value because now it's not animated we could use a um we could use a how is called um use animated styles on this component and in that situation we would be able to apply any any changes to this shared value uh that would be an option another option would be to create a derived variable a derived value based on the scroll y that is simply the uh that be simply like uh a value with opposite sign compared to the scroll y uh but in fact what I'm actually going to do because we're going to actually need this a little bit more control over this Translate Y a bit later so let's right away do this together what I want to do is I want to create a separate component for this card component and there we're going to manage how we can transform from the scroll y to our Translate Y so um let's go ahead and start by creating a c a separate component in our components folder called card. TSX here I'm going to do const card equal something then we're going to return and what we are going to return is this animated image because at the moment our card is a simple animated image now in the card let's make sure to import animated from react native we are going to need index the index and the card image let's call it and the index we're going to receive Pro properties of our component um I rename it to card image or maybe we can do simply card here and what else the scroll Y is not defined here so the scroll Y is another thing that we are going to receive through properties now that we separated everything into custom component let's go ahead and Export this card export default card from here so that we can import it in our card list at the top I'm going to import card fromt card and I'm going to render it here like this card and we need to send some properties here one of them is the card itself which is actually the image then we also need the index to know which position in the list it has is going to be important in a lot of our animation in a moment and the scroll y because the card know needs to know how much it the whole list is scroll so that it knows how much to translate it up or down so let's send there a scroll y scroll y okay so now that we have scroll y here if I reload the application what's going on reading from value directly directly on the UI Fred animated scroll y I [Music] need scroll Y is sh value scroll Y what am I reading on there oh and animated not from react native that's the mistake we need to import animated from react native reanimated now thing works it's still like in the opposite direction but we're going to change it in a shortly um actually let me show you how you can do it with a derived value and later we're going to change it to a shared value so our Translate Y uh can be defined as a derived value which is quite similar to a shared value but a derived value cannot be changed a derived value is calculated based on our shared values so so let's call it Translate Y is equal to use derived value and here we simply Define a function and we return that value that we want to derive derive so in our case that's going to be scroll y do value and only with a minus sign in front where you can multiply it by minus one and now we can take this derived value use it in our Styles and the scroll dire will be uh correct now perfect um yes so everything is good so far that it derive Valu is basically yeah we derived a new shared value based on our shared values but the think is that now we cannot go ahead and do Translate value equal to something it's not allowed to reassign it's only recalculated why are you always doing mobile apps and not web application is this more profitable um well everyone is using by mobile phone every day all day so I think there is a bigger Market with mobile applications that's why I decided to focus on mobile applications hello John how are you doing hello loish all right so let's continue um our list is scrolling really nice when we actually like scroll it but but it doesn't have any momentum and what I mean is when I scroll up fast and release it it stops abruptly like very fast instantly stops as soon as we end um as soon as we end our pun gesture it doesn't do anything more what we want to do is whenever we end the gesture I want the list to still continue moving and slowly slow down this way it's a nice effect of a list moving and slowing down so that's how we normal list is um is working how the scroll thing is working so to do that what we are going to do is we're going to go ahead and on the on end call back of a pesture we can get the event and this event will also contain an a property called velocity y velocity y will represent present in simple terms the speed of our um of our gesture when we release it so if I'm going to release it slowly it's going to be minus 1,400 if I'm going to release it fast it's going to minus 5,000 and in the other way as well slow and fast and this way knowing the velocity of the gesture we can go ahead and and um and run knowing the velocity we can go ahead and run how fast we want to slow down the movement of our um scroll animation so in this case we are still going to change the scroll value and we're going to change it not instant because if I change it instantly to something like I don't know event do velocity or even something else you're going to see that it's going to basically jump so it jumps out of a view completely what I want to do here is use a animation modifier from reanimated to run a smooth animation there are different animation modifiers and you can find them in the documentation of reanimated for example with spring modifier we'll go ahead and animate something uh with a spring like animation with timing is a simple time based animation and there is one interesting an um one interesting modifier which is called with Decay and it's actually used for this slowing down of things so something has speed and when we release it it's going to simulate that it slows down slow slowly so the animation decays over time and moves slower slower slower until it reaches the end goal so let's go ahead and use this with Decay and it actually works with velocity so what we're going to do is we're going to use here we're going to set the scroll y value using with Decay and the velocity is going to be our event velocity y if now I'm going to release it it's going the other way around so it should be minus if I release it as you can see it moves smooth until it stops and the other way as well if I do it slow it moves slow if I do it really fast it really moves fast as you can expect so it's really simple like we don't have to over engineer every anything it's just an animation um modifier with Decay and it already Powers this smooth slowing down of us our scroll animation perfect um now that our scrolling is working more or less correctly The Next Step that I want to do and the next feature of most of this animation is being able to stop the scroll for example if I scroll very fast and I see what I need I want to press on the screen and at that moment I want the animation to stop uh so for example if I scroll very fast and click I want it to stop at that moment because I want to stop animation maybe go back or something like that what we can do in that situation uh now our scroll Y is animated with this with Decay which takes a bit of time like I know a second or half a second for the animation to run until the end what I want to do is when I start is it when I start or when I begin yeah um I'm going to use another uh call back function here on begin I'm going to explain in a moment like what's the difference between on begin and on start and when I begin another gesture handle later I want to cancel all the animation on the scroll y position in order to stop it so to do that I will use cancel animation function that was imported for me from react native animated and we can pass here a shared value on each on which we are running animations currently we are running animation on our scroll y position so let's pass here scroll Y and this will cancel all animations that are running there uh and make sure that the scroll y basically stops and just with this line of code now if I'm going to scroll very fast and then click again the animation stops in order for me to take control over it in a way hopefully you're following along together with me and you can see how this works and I would highly recommend for you to actually run it on a physical device because all of these animations and gestures there are going to be much more user friendly when you're doing this on an actual device with your uh in your hand and just like that now our uh scroll scroll view basically we recreated a scroll view um with really powerful like and a lot of configuration that we can get uh into and the difference between on begin and on start you can read here in the documentation um on start will basically be triggered even when the gesture Handler is not sure what type of gesture is going to be happening and on where is it on begin so on begin when the gesture Handler starts receiving touches and at this moment the the Handler is not yet in an active State because we don't know yet if it will be recognized as a gesture or not and only when it recognize that okay this is going to be a pen gesture it's it's calling the on Star so that's why we are using on begin to make sure that we call this as soon as we touch the screen in in the on start this particular like stopping animation is not going to work as we expect because usually we just tap we're not panning at that moment okay uh hello John P spoiler flat list and in English why are uh don't you use a flat list I don't use a flat list in this situation because uh in The Next Step that we're going to do uh we will need much more control over the position of our elements on the screen for example when I select a card I want that card to be on the top I want the rest to be on the bottom and I believe is going to be [Music] [Music] is working perfectly fine how to run on a physical device without Expo go uh without Expo go why don't you want Expo go what's the problem with Expo go just trying to understand eager thank you all right let's continue um another thing that I want to do is I want to clamp uh our scroll positions for example I don't want to be able to scroll infinitely to the bottom and infinitely to the top I want to stop when the card reaches the top of a screen and when the cards reaches the bottom of a screen to do that what we're going to do is we're going to clamp the scroll y position whenever we are changing it so clamping is basically defining the boundaries like the minimum value and the maximum value that we can do that using the clamp functions from rect native reanimated so whenever we change the scroll y value when we are actually scrolling we can say that change it to this value but make sure to clamp it so I'm going to do clamp uh to the following values now the first parameter of a clamp is the actual value that we want to clamp then we have to provide the minimum and the minimum in our case is going to be zero we don't want to go below zero on scroll Y and maximum it should be calculated but I'm going to go with 1,000 for a second uh just to test it out and now if I'm going to scroll I can scroll down but when scrolling back up it stops at this moment and when scrolling down it's also going to stop somewhere at at 1,000 the only problem is that now we are only clamping on the on change so when we are actively scrolling but if I'm going to uh dragon release is going to go here because this is hand happening in the on end here for the scroll value in this situation we oh I think we can simply uh clamp velocity no we don't want to clamp velocity we want to we want to clamp the output of this Decay function we want to clamp the output of this Decay function so for that uh we will have to use a animation modifier similar to like weave Decay and similar like clamp put together there is a with clamp and with clamp is uh be is used to clamp the output of a animation modifier so I'm going to use weave clamp on the one on end for the momentum scroll and here we what's the API here for the weave clamp it has a object configuration it needs minimum value of zero maximum value of 1,000 and then the second parameter is the clamped animation the actual animation that we want to run so the actual animation is this with Decay so these are two different ways of clamping things let me actually first check if it's working so scrolling up I cannot and if I drag it it's going to scroll and stop here as fast as I can it's not going to go further and and on top as well so as we can see there are two ways of clamping in this situation we are clamping v a value like a number in second situation we're clamping an an animation so the output of an animation like with Decay or with timing with spring and so on because when when we change we set right away the value here we animate the value um and what's up with this maximum 1,000 um this should be the maximum scroll position and the maximum scroll position depends on the size of the whole cards container of all the list before we do that I will I forgot to provide the key to this card as index I moved it in another place so now the key should be on this card and not on the animated image and I can remove it from here going back to cards list as I was saying the maximum scroll positions should depend on the size of a whole container uh we can calculate the size of a whole container by uh by taking the the height when the component renders and then storing it into a state variable so let's store here um list height and set list height use State initially we can set it to zero and then the view that we want to measure we can measure it using the on layout which is a callback function that is being called whenever the component renders or whenever the component mounts now VC vent uh contains the I'm going to set our list height using the event dot native event and the native event has layout and the layout has height the height with X and Y position so this way we can basically take dynamically the size of a container without h having to calculate it or some on so now that we are setting this list height we can use it in the both clamps instead of 1,000 I want to to scroll maximum to the least height and here as well maximum to the list height now I will reload and check if I scroll down we should be able to scroll until yeah actually the last least height uh but but probably it's not doing this like it allows us to scroll until the end of a list is at the top of a screen so that means that we will also have to take the size of a screen and adjust it accordingly so let's go ahead and take the size of a screen using the use window dimensions and this is a hook imported from rack native that allows us to get get uh access to the height of a screen which I will call it height and let's call it screen height now knowing the screen height we want to allow the scroll to get to the least height minus screen height will that work minus screen height so now if I will scroll it stops here yep uh and that's because that's probably because of the how is it called the safe area view maximum least height if it's there yeah look most probably uh the I think is that the safe area view on our parent component is messing with uh with us if I'm going to transform it back to a view because we would have to take into consideration that as well will this now yes now it Scrolls until the bottom here the only thing that now the top card is not actually correctly uh scrolled so I will go back to Safe area view actually and in this card in this uh card list let's simply add manually like I don't know 100 pixels here and here as well plus 100 pixels and just like that we have it properly so I think I will better store it into a variable called const Max scroll y equal to this value and I'm going to use it in both of these cases where we clamp it so to the top it works perfectly to the bottom it works perfectly this 100 probably you should take it like based on the sizes of the but I'm not going to worry about that now all right so now our list is properly clamped to the top and the bottom so we cannot scroll outside that okay let's go ahead and make sure to do a g status G ad uh get ad and get commit scroll animation that was our scroll animation isn't there a way to measure the safe area insets yes there is uh it's um I think that would be the let me see if I have it you could do that use Save area insets and that is coming from react native safe area context basically you need an uh a separate library to install called re native safe area context and then you will be able to with a hook to get the insets to take them Auto atically because my list is not going to work properly I mean it's going to have a little bit more space on the bottom if it's going to run on a device without um notches but I'm okay with that U for you you can install this library that I just showed you and already lost it and take dynamically insets sizes okay so all right the next step is going to be the fun animation of being able to select one of these cards from the list oh no I think the the first step for us is going to be to stack together on top of each other the cards on the top so as we scroll up we don't want to see the cards leaving the screen we want them to stack on top of each other uh uh to do that it's quite easy to achieve I think um and we are going to achieve that on the card itself on the card component itself because we don't have want to mess with a scroll y we're going to mess around with the Translate Y the scroll position we already figured everything out it works properly now we're going to work with the Translate Y to make sure that we are clamping it at the top of a screen so clamping it on top of a screen so again we're going to work with clamping because we want this Translate Y to change but we want it to not exceed the top of a screen to stop here so um it's a bit tricky to uh to to understand everything but I'm going to try to make it as clear as possible so the first thing that I'm going to do is when calculating this derived value for our Translate Y let's clamp it so let's clamp this value and let's import clamp from R native ranimated and here we need to provide the value like the minimum amount of translation to the top and our minimum amount is not going to be zero and then 1,000 for example it's going to be our way around it's going to be here a negative value because items are moving to the top and they get Negative translation as they are being scrolled from where ini position and the maximum value is going to be one so I'm not sure what's going to happen right now but probably nothing if I will change here to minus 100 what that will do is it will allow the items to only be scrolled 100 pixels um let's think about how do we want to clamp how much should we allow every item to be translated up think about the initial position of the of items the top card let's say has a position zero the next and if the height of one of these cards is 200 maybe I can uh Draw Something to make it a bit easier so this is our list uh this is our list and we're going to have the first item we're going to consider that the height of one card is 200 pixels 200 pixels 200 pixels the initial position of this first item is at zero pixels the initial position of the second item is 200 because we assume the height of this item is 200 pixels and the position of this item is going to be 400 if I can do simple math now knowing where initial position and we can understand how much should we allow the cars to move the first card should not move more than zero pixels to the top it should stay here always the second card should be able to move 200 pixels to the top to get to this position and then stay here and be stacked on top of each other and the third card should be able to be scrolled 400 pixels and stop here so as you can see the amount of translation depends on the initial position but the initial position in fact is can be calculated by multiplying index by the height of one C this is the simple formula index multipli by the height and because this is our index zero the initial position is zero because we multiply Z by 200 index one and index two so in this case the initial position is going to be index 2 multiply by the height of this card which is 400 which is 200 so we get 400 and that is also the amount of translation that we should allow this C to move to the top in order for it to stop as soon as it reaches the top and stack on top of each other so let's try this maximum value of translation to clamp it at index multipli by the height let's go here so so the maximum amount of translation here should be minus 1 * by index multiply by the height let's say 200 so now it's not going to be perfect because this height is hard coded for now but if I scroll we see that the first item doesn't move at all because its index is zero so it will not allow it any movement the second item will move move move until it stops and when the second stops and then stops stops stops and they this way they stack all on top of each other like this so we are simply limiting how much translation each card will get depending on its position the closer it is to the top the less translation it will need until it stops and the to the bottom it will have more because index is going to be more and and yeah this minus one can be simply put near one of this either minus index or minus 200 it doesn't matter it's going to work similar so now in this case like as you can see we're not messing with a scroll y we leave a scroll y to represents how much we are scrolling we are only transforming how we are calculating the amount of translation needed for every single card in this list and in in order to get the height of a card we can take it dynamically as well uh as we saw previously by creating a state variable card height Set Card height equal use state zero let's import use State and when we're rendering this image we can have this on layout that will give us an event from where we can take the height of this image and set it in our card height State taking it from event dot uh Native event. layout. height now if I'm going to take instead of 200 here that I hardcoded if I'm going to take Card height in that case our cards I think will Stack Up perfectly but probably not because I will show you in a second why not let's let's reload for the on layout to be called yeah they will be still have a bit of overlap why because we have margin vertical on this image so five pixels to the top five five pixels to the bottom that's in total 10 pixels I think we can simply add 10 pixels to the card height when we are setting it here so now if I reload and the height is going to take into consideration both the image height and the margin that we have on top and the bottom and now it will perfectly stack on top of each other and actually I'm not sure if you want them to perfectly stack on top of each other I'm not sure how Apple wallet is doing probably because I don't have so many cards no actually it's uh Apple wallet is stacking like right on top of each other as I see here as we build right now but it's up to you how you want to do it if you want to leave 10% of visible area on top what you can do is do card height multiplied by 0.9 this way 10% of uh of a previous card is going to be visible and this way it all collects here I believe this is a bit risky if you have a lot of a lot a lot of items so as you can see the the stack becomes higher and higher you might increase it like this you might limit how many items there are like there are a lot of possibilities here but I'm going to remove this and let the cards stack exactly on top of each other oh perfect so this was our stacking get commit minus M Stack cards when we scroll up [Music] and the last animation that we have to implement and this is the most interesting one is going to be selecting one of these items and displaying it at the top and the rest of cards we want to put together here on the bottom in also some kind of a stack like this so this is uh this is quite an interesting animation and we're going to take it step by step so to be able to select one of these items and displate is as active uh what we first have to do is keep track of which of the current cards is selected we're going to keep track of it in cards list and we are going to use a shared value for that let's call it active card index use shared value let's initialize it with null because by default no cards is selected let's take this shared value and send it to every single card so that from inside the card when we click on the card itself we can update which is the current selected active card index um for that I used another gesture detector on top of our animated image now I'm thinking I'm not exactly sure what would be give a benefit of using a tab gesture detector compared to um compared to a pressible let's say consider using the gesture API instead the old API is not actively supported tap yeah tab gesture oh no this is is Legacy yeah yeah I was not looking at the right place I should look here tab gesture so I'm not really sure what's the benefit of using a tab gesture compared to a pressible but I would believe it's much more performant but that's just my understanding well it gives you a lot more um configuration like you need to with a tab gesture you can handle like how many pointers the maximum duration the delay the number of steps like this is uh of course like if you need this configuration um definitely like we need to use a TP gesture but for a simple like press event I believe we can still use a pressable anyway I'm going to go ahead and use a tap gesture to handle gestures as we we saw in pre previous previously we're going to import from react native gesture Handler let's import two things the gesture and the gesture detector using gesture we're going to Define this gesture it's funny how I say gesture isn't it am I saying it correctly gesture uh and we're going to use a not a pen but a tap gesture just to handle tap events and now now having this gesture we can wrap our animated image in a gesture detector and let's close it at the end and let's do gesture equal gesture or let's call it simply tab to Define like the what kind of gesture is that so again like as with all gesture there are a number of call backs like on beginning on start on end and I think I'm going to hook into the on end because this is called when the gesture that was recognized finishes so a tap event when it finishes when the user tabs I want to do something on end let's do console Warr index just to know on which like if I press here I see index one if I press here index two index zero index three so that means that we are properly uh handling the gestures and whenever we click on one of the cards it displays the the index of that card so what we should do with that is whenever we press on a card we need to update the active card index do value we need to set it the current index of the card that is being pressed so if I press here I want to update the active card index to this one and and later yeah I'm going to do that in a moment uh now having um having the active card index there are two things that should happen when this H when we tap on one card the card itself that we tap should go to the top and the rest of them should go to the bottom so there are two things like whenever it's the active card we need to move it to the top whenever it's the non-active card we need to move it to the bottom that means that we need a way to upd dat this Translate Y value and I said that uh a derived value cannot be updated um for that reason our Translate Y is no longer going to be a derived value but is going to be a shared value because we in some cases we want to update it um use shared value in in most cases is going to depend on the scroll y when we are scrolling but when we are currently in an active State we need to take control of this Translate Y of the card so initially it's going to be zero now we lost the possibility to scroll how can we update the Translate Y every time our scroll y value is changing for that we can define a anim use animated reaction a use animated reaction is a way for us to hook into the updates of some shared values and the use animated reaction expects two functions the first one in the first one we should return the value on which we need to react on so in our case we need to react on the scroll y meaning every time the scroll y do value changes come on we need to do something and to do something that's going to be the second method and is going to have two uh values current and previous value and this current and previous value will allow us to compare how much or how did the value that we return here changed from the previous time it was called well we only need to update our translate y. value with um with this clamp scroll y so basically the way we derived it previously but this time it's not going to work with scroll y but with corrent and yes that should make I will re update as expecting it to work but it doesn't return scroll y do value translate value equal and it shouldn't be minus current it should be not current but minus current because we want to revert yes yes it works we want to revert the uh scroll position basically if it's positive scroll we need to translate on the negative side so now it still works as it was working before but it's not a our Translate Y is no longer a derived value it's a shared value that is being updated with uh as we uh as we move as the scroll y value updates and here we don't need the previous and we can even simplify this function to remove return return and the brackets and this value is going to be returned right away this is a bit cleaner okay that is one animation animated reaction that will drive our Translate Y the next event that should change the Translate Y is going to be whenever we select one of these cards and it becomes active card so let's create another animated reaction and this one will look at the Active card index do value and quent and then previous uh yeah we're in this scenario we're going to look at the Active card index value uh I'm not sure if in any case it's going if it will ever be called when the current is equal to previous but anyway I'm going to check if current is equal well to previous I don't want to do anything because I want to do something only when this value changes so I will return here otherwise I'm going to console console log change active card changed from let's say corrent to uh previous no no no change from previous to current let's look in the console and try to tap on something active card change from n to one if I do again active card change from one to three if I do again from three to zero and so one we see more of them because our animated reaction is inside every single element here uh okay okay okay uh another thing that I we we're going to do now is if I press on the first item we see that it changed from two to zero and if I press it again it will not call it at all but what I want to do is if I press on the same element again I want to deselect it so that's going to happen here in the T gesture and we're going to check if active card index do value if it's equal to null in that case we are going to select the value otherwise if it has already a value I'm going to do value equal to null and this way if I click it goes from null to zero if I click again it goes to from zero to null if I click again it goes from null to two and so one that's exactly what we need in a moment you're going to understand like a bit it's a bit weird because we cannot go from one active card to another active card right away we're going to go from one active card to no active card and then go back into active card because that's actually how apple apple is working and it's going to make a bit more sense like when we see the animation working um okay okay okay now our work is to run some animations when the active current index changes and there will be uh two cas cases or actually three cases there will be the case where um this card becomes active another card be is active move to the bottom let's start with a second and another um and another case is going to be uh the no card selected move to list view something like that so basically here I want to reset it back to a scrollable list let's go ahead and start with the last one the last one is what we want to do is whenever there is an active card I want to move um move the cards to the bottom so that means that I'll have to update the translate value let's go ahead and take translate y. Val equal to I don't know let's start with zero and see what happens if I press on one of these cards what happens translate y equal Z yeah basically all of them come uh come together come at their initial position if I want to Stack all of them at the top remember how we did the stacking when scrolling I want to translate the items based on their position based on their index multiply by height and this way we're going to stack all of them at the top so let's start by stacking all of them at the top that means that we have to translate them using index multiply by the card height and with negative values because we want to move up so if I press on one of the cards they all move to the Top If I press again if I press again nothing happens because if I press again we need to handle this situation but let me not confuse you I'm going to work with refreshing from time to time just for us to be able to see them the cards at the top in our case when we stack together the cards I want to make them a little bit visible like 90% like this but the thing is that I don't want to stack them at the top of a screen I want to stack them at the bottom of the screen so for that I'm going to Simply add to this value the screen height height which I don't have yet but I can take it using the hook use window dimensions I can take the height rename it to screen height and now if I click all of them are moved to the bottom of the screen but they are not visible so I'm going to add only let's say half of a screen's height screen height multipli by 0.5 this way our cards when stacked together they move here so they are stacked at the bottom okay so with this formula we found out the final position of our translate but we don't want the cars to jump there we want them to smoothly move there so for that we need a uh to add an animation modifier uh such as with spring or with timing I checked the animations on Apple uh application and a weave timing animation is what is being used there so I'm going to put this final value inside a weave timing and this way if I refresh and then click the cards will simply move to the bottom maybe I can even increase here to 0.7 if I if I click now all of them are moving to the bottom here um let's go ahead and do an if statement here because we kind of M handle the animation when uh to move things to the Bottom now when we [Music] are when no card is active we want to go back to the scrollable normal list view so in that situation our active card index Dov value is going to be null if it's null we should go back to the normal noral to handle the normal scenario and the normal scenario is going to be also translate value we want to go back to the normal position but also using a weave timing in order not to jump but to slowly move there and we're going to move to uh I think it's I think it's going to be this clamp position because this is how we're scrolling so we need to move back to this one with timing clamp it's not is not only it's not going to be current but it's going to be scroll y.v value and now what's going what's happening and now we should also make sure to put this animation in an else statement because if we are in normal mode we need to run this animation if we go into an active mode we need to run this animation and if I press again it moves back press again moves back and even if I have some scroll and from here I go to this Visa card and go back it keeps the previous core uh like this it keeps the scroll position whenever we are going from one mode to another uh okay but the thing is that the last step in this animation to make this animation work is to highlight the card basically to move the card that is being selected to the top of the screen so in that is going to be the thir case and I'm going to do it here with else if uh and if this active value is equal to the current index meaning that if this card that we are animating is the card that is being selected is going to be this car becomes active so in this situation we need to do something it's not going to at the moment it's simply going to keep it in the same position I guess so if you press on the blue green it keeps in the same position and so one but what we want to do is simply we want to move it to the top of the screen so it's going to be something similar to this one I believe but the only thing is that I'm we're not going to add the screen height in order for it to simply be at the top and even no 0.9 because we simply want it at the top of a screen right this as simple as that and now everything works we can move from one item to a list and the car that we're clicking goes to the top the rest goes to the bottom we can go back to the scroll thing and so on oh my God looks so good perfect so does it make sense did I manag to make this animation simple and clear to understand I hope so let me know in the comments you can play with it on phone you can improve it I think what I want to improve here let me check the actually no it's like this with timing maybe the with timing animation it can also have different um time where is it with timing it can have different easing meaning how the time affects the animation so a linear easing basically will mean that the speed of Animation is constant from the beginning to end in this situation as we see here in this preview I think the easing is this one is also the default but we can change this using method to a different uh yeah using in out what I want to do is yeah I want to maybe simply start fast and uh when the animation comes to an end to reduce the the SP speed please add Shadows to the card yes I think I'm going to add I was looking like what what's not working bounce no I don't want a bounce think out is what I'm interested in oh my God this so there is so many configuration so let's simply copy this one and we're going to pass it to all the with timing do we want to all of them at least to the card that is be becoming active let's go ahead here and provide options to the with timing we're going to provide the easing and we need to import it from not from react native but make sure to import it from react reanimated and you can also change the duration in order to see the effect so if you change it to 1 second you're going to see that the card slowly goes but the thing is that it starts fast and it ends a little bit slower so I'm going to leave the duration to normal doesn't seem to change much but it's there and you can do the same here to the cars that are moving to the bottom just to make sure that it works maybe we can do something like a jump no how is it called Bounce what's going to happen if we Bounce It hello Carlos Carlos Jr thank you very much Carlos oh that's V was something definitely not what what I want should I reduce the speed because it's too too fast maybe to reduce the duration to half a second and the same here yeah I think it looks good good nice perfect so that was our animation here let me go ahead and do get OD get commit minus M active um or selected card animation and someone recommended to add some Shadow will I be able to add let's do react native Shadow generator come on and let's try to add it where on our IM here I'm going to do it after the transform does it have Shadow I think it does or maybe I should do it what's going on with elevation maybe I should do it on a view it's on a view that wraps around our image let's import this View and let's add Shadows on that view cannot understand if it added or not yeah on a white background it's clear that it added Shadows on the white background it works pretty well but when it's on top of each [Music] other probably it still works yeah it still works and I can change the color here to a white H I don't think a white shadow is like this no I don't like the white shadow maybe it should be some kind of a purple I'm spending too much time with this shadow I just need a color okay perfect and maybe at this moment it's better to add them to a Styles not to not to over complicate our component Styles equal Styles shet do create container and image so the styles for container I'm going to take them from here and do Styles container and here from the image I'm going to take most of the things I'm going to put them here these are the Statics Styles like weave height and so on and I'm going to put them into an array together with animated Styles styles. image and the transform will we will keep it here because it's a dynamic property and because these two have a same name I can remove it like this maybe I can simplify this as well I can the same line just like that and even well this work y it works so yeah the component is much cleaner now it's recommended to use box Shadow okay I'm going to check it out later I'm not going to stop it for for this um this live will it be uploaded to YouTube yes the live will be available on YouTube uh all our live streams are here so yeah you can check it out you can follow at your own base and also I'm going to make sure to upload the source code so you can check out the the source code it's going to be available through the assets so make sure to go to assets. no.app wallet and you're going to download that bundle as well and before we uh if you have any questions let me know maybe you want us to implement uh some more features but I want to say a big thank you to um software Manion team for sponsoring this video and I want to uh remind you that on 22nd May to 24th of May I'm going to be in kov in Poland and and I would love to meet you as well so if you want to attend ABDO Jaz conference which is one of the best conferences for rec native developers make sure to buy your ticket use the no just devs 10 promo code you can also follow the link in the description for a 10% discount and let's let's meet in kov all right so that was it thank you very much have a nice day and I'm going to see you next week bye-bye
Info
Channel: notJust․dev
Views: 5,095
Rating: undefined out of 5
Keywords: vadim savin, not just development, notjust.dev, live coding, react tutorial, react native tutorial, react native for beginners, React Native, step-by-step tutorial, react native, react native reanimated, apple wallet clone, animations in react native, swipe gestures, state management, react hooks, coding tutorial, apple wallet app clone, react native reanimated tutorial, how to add animations in react native, react native animations, reanimated tutorial
Id: 3OgZzRe57P4
Channel Id: undefined
Length: 120min 6sec (7206 seconds)
Published: Sat Mar 30 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.