1f2fad9b1SAman Mittal--- 2f2fad9b1SAman Mittaltitle: Add gestures 3f2fad9b1SAman Mittal--- 4f2fad9b1SAman Mittal 5f2fad9b1SAman Mittalimport { SnackInline, Terminal } from '~/ui/components/Snippet'; 6f2fad9b1SAman Mittalimport Video from '~/components/plugins/Video'; 73c9a6b96SBartosz Kaszubowskiimport { A } from '~/ui/components/Text'; 8a30150b3SAman Mittalimport { Step } from '~/ui/components/Step'; 99de0c686SAman Mittalimport { BoxLink } from '~/ui/components/BoxLink'; 109de0c686SAman Mittalimport { BookOpen02Icon } from '@expo/styleguide-icons'; 11f2fad9b1SAman Mittal 129de0c686SAman MittalGestures are a great way to provide an intuitive user experience in an app. The [React Native Gesture Handler library](https://docs.swmansion.com/react-native-gesture-handler/docs/) provides built-in native components that can handle gestures. 133c9a6b96SBartosz KaszubowskiIt uses the platform's native touch handling system to recognize pan, tap, rotation, and other gestures. 14f2fad9b1SAman Mittal 15f2fad9b1SAman MittalIn this chapter, we are going to add two different gestures using the React Native Gesture Handler library: 16f2fad9b1SAman Mittal 17f2fad9b1SAman Mittal- Double tap to scale the size of the emoji sticker. 18f2fad9b1SAman Mittal- Pan to move the emoji sticker around the screen so that the user can place the sticker anywhere on the image. 19f2fad9b1SAman Mittal 20a30150b3SAman Mittal<Step label="1"> 21a30150b3SAman Mittal 22a30150b3SAman Mittal## Install and configure libraries 23f2fad9b1SAman Mittal 243c9a6b96SBartosz KaszubowskiThe React Native Gesture Handler library provides a way to interact with the native platform's gesture response system. 25*72bb203aSDavid LeulietteTo animate between gesture states, we will use the [Reanimated library](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/handling-gestures). 26f2fad9b1SAman Mittal 27f2fad9b1SAman MittalTo install them, stop the development server by pressing <kbd>Ctrl</kbd> + <kbd>c</kbd> and run the following command in the terminal: 28f2fad9b1SAman Mittal 29f2fad9b1SAman Mittal<Terminal cmd={['$ npx expo install react-native-gesture-handler react-native-reanimated']} /> 30f2fad9b1SAman Mittal 31f2fad9b1SAman MittalNext, also install `@babel/plugin-proposal-export-namespace-from`, which is required to configure the Reanimated library: 32f2fad9b1SAman Mittal 33f2fad9b1SAman Mittal<Terminal cmd={['$ npm install -D @babel/plugin-proposal-export-namespace-from ']} /> 34f2fad9b1SAman Mittal 35f2fad9b1SAman MittalThen, add Reanimated's Babel plugin to **babel.config.js**: 36f2fad9b1SAman Mittal 37f2fad9b1SAman Mittal{/* prettier-ignore */} 38f2fad9b1SAman Mittal```jsx babel.config.js 39f2fad9b1SAman Mittalmodule.exports = function (api) { 40f2fad9b1SAman Mittal api.cache(true); 41f2fad9b1SAman Mittal return { 42f2fad9b1SAman Mittal presets: ['babel-preset-expo'], 43f2fad9b1SAman Mittal /* @info Add the plugins array and inside it, add the plugins.*/ 44f2fad9b1SAman Mittal plugins: [ 45f2fad9b1SAman Mittal "@babel/plugin-proposal-export-namespace-from", 46f2fad9b1SAman Mittal "react-native-reanimated/plugin", 47f2fad9b1SAman Mittal ], 48f2fad9b1SAman Mittal /* @end */ 49f2fad9b1SAman Mittal }; 50f2fad9b1SAman Mittal}; 51f2fad9b1SAman Mittal``` 52f2fad9b1SAman Mittal 53f2fad9b1SAman MittalNow, start the development server again: 54f2fad9b1SAman Mittal 55f2fad9b1SAman Mittal<Terminal cmd={['$ npx expo start -c']} /> 56f2fad9b1SAman Mittal 57f2fad9b1SAman Mittal> **Tip**: We are using `-c` option here because we modified the **babel.config.js** file. 58f2fad9b1SAman Mittal 59f2fad9b1SAman MittalTo get gesture interactions to work in the app, we'll render `<GestureHandlerRootView>` from `react-native-gesture-handler` to wrap the top-level component of our app (also known as the "root component"). 60f2fad9b1SAman Mittal 61f2fad9b1SAman MittalTo accomplish this, replace the root level `<View>` component in the **App.js** with `<GestureHandlerRootView>`. 62f2fad9b1SAman Mittal 63f2fad9b1SAman Mittal{/* prettier-ignore */} 64f2fad9b1SAman Mittal```jsx App.js 65f2fad9b1SAman Mittal/* @info Import GestureHandlerRootView from react-native-gesture-handler-library. */import { GestureHandlerRootView } from "react-native-gesture-handler"; /* @end */ 66f2fad9b1SAman Mittal 67f2fad9b1SAman Mittalexport default function App() { 68f2fad9b1SAman Mittal return ( 69f2fad9b1SAman Mittal /* @info Replace the root level View component with GestureHandlerRootView. */<GestureHandlerRootView style={styles.container}> /* @end */ 70f2fad9b1SAman Mittal /* ...rest of the code remains */ 71f2fad9b1SAman Mittal /* @info */</GestureHandlerRootView>/* @end */ 72f2fad9b1SAman Mittal ) 73f2fad9b1SAman Mittal} 74f2fad9b1SAman Mittal``` 75f2fad9b1SAman Mittal 76a30150b3SAman Mittal</Step> 77a30150b3SAman Mittal 78a30150b3SAman Mittal<Step label="2"> 79a30150b3SAman Mittal 80a30150b3SAman Mittal## Create animated components 81f2fad9b1SAman Mittal 82f2fad9b1SAman MittalOpen the **EmojiSticker.js** file in the **components** directory. Inside it, import `Animated` from the `react-native-reanimated` library to create animated components. 83f2fad9b1SAman Mittal 84f2fad9b1SAman Mittal```jsx EmojiSticker.js 85f2fad9b1SAman Mittalimport Animated from 'react-native-reanimated'; 86f2fad9b1SAman Mittal``` 87f2fad9b1SAman Mittal 88f2fad9b1SAman MittalTo make a double tap gesture work, we will apply animations to the `<Image>` component by passing it as an argument to the `Animated.createAnimatedComponent()` method. 89f2fad9b1SAman Mittal 90f2fad9b1SAman Mittal```jsx EmojiSticker.js 91f2fad9b1SAman Mittal// after import statements, add the following line 92f2fad9b1SAman Mittal 93f2fad9b1SAman Mittalconst AnimatedImage = Animated.createAnimatedComponent(Image); 94f2fad9b1SAman Mittal``` 95f2fad9b1SAman Mittal 96f2fad9b1SAman MittalThe `createAnimatedComponent()` can wrap any component. It looks at the `style` prop of the component, determines which value is animated, and then applies updates to create an animation. 97f2fad9b1SAman Mittal 98f2fad9b1SAman MittalReplace the `<Image>` component with `<AnimatedImage>`. 99f2fad9b1SAman Mittal 100f2fad9b1SAman Mittal{/* prettier-ignore */} 101f2fad9b1SAman Mittal```jsx EmojiSticker.js 102f2fad9b1SAman Mittalexport default function EmojiSticker({ imageSize, stickerSource }) { 103f2fad9b1SAman Mittal return ( 104f2fad9b1SAman Mittal <View style={{ top: -350 }}> 105f2fad9b1SAman Mittal /* @info Replace the Image component with AnimatedImage. */<AnimatedImage /* @end */ 106f2fad9b1SAman Mittal source={stickerSource} 107f2fad9b1SAman Mittal resizeMode="contain" 108f2fad9b1SAman Mittal style={{ width: imageSize, height: imageSize }} 109f2fad9b1SAman Mittal /> 110f2fad9b1SAman Mittal </View> 111f2fad9b1SAman Mittal ); 112f2fad9b1SAman Mittal} 113f2fad9b1SAman Mittal``` 114f2fad9b1SAman Mittal 115a30150b3SAman Mittal</Step> 116a30150b3SAman Mittal 117a30150b3SAman Mittal<Step label="3"> 118a30150b3SAman Mittal 119a30150b3SAman Mittal## Add a tap gesture 120f2fad9b1SAman Mittal 121f2fad9b1SAman MittalReact Native Gesture Handler allows us to add behavior when it detects touch input, like a double tap event. 122f2fad9b1SAman Mittal 1233c9a6b96SBartosz KaszubowskiIn the **EmojiSticker.js** file, import `TapGestureHandler` from `react-native-gesture-handler` and the hooks below from `react-native-reanimated`. 1243c9a6b96SBartosz KaszubowskiThese hooks will animate the style on the `<AnimatedImage>` component for the sticker when the tap gesture is recognized. 125f2fad9b1SAman Mittal 126f2fad9b1SAman Mittal```jsx EmojiSticker.js 127f2fad9b1SAman Mittalimport { TapGestureHandler } from 'react-native-gesture-handler'; 128f2fad9b1SAman Mittalimport Animated, { 129f2fad9b1SAman Mittal useAnimatedStyle, 130f2fad9b1SAman Mittal useSharedValue, 131f2fad9b1SAman Mittal useAnimatedGestureHandler, 132f2fad9b1SAman Mittal withSpring, 133f2fad9b1SAman Mittal} from 'react-native-reanimated'; 134f2fad9b1SAman Mittal``` 135f2fad9b1SAman Mittal 136f2fad9b1SAman MittalInside the `<EmojiSticker>` component, create a reference called `scaleImage` using the `useSharedValue()` hook. It will take the value of `imageSize` as its initial value. 137f2fad9b1SAman Mittal 138f2fad9b1SAman Mittal```jsx EmojiSticker.js 139f2fad9b1SAman Mittalconst scaleImage = useSharedValue(imageSize); 140f2fad9b1SAman Mittal``` 141f2fad9b1SAman Mittal 1423c9a6b96SBartosz KaszubowskiCreating a shared value using the `useSharedValue()` hook has many advantages. It helps to mutate a piece of data and allows running animations based on the current value. 1433c9a6b96SBartosz KaszubowskiA shared value can be accessed and modified using the `.value` property. It will scale the initial value of `scaleImage` so that when a user double-taps the sticker, 1443c9a6b96SBartosz Kaszubowskiit scales to twice its original size. To do this, we will create a function and call it `onDoubleTap()`, and this function will use the `useAnimatedGestureHandler()` hook 1453c9a6b96SBartosz Kaszubowskito animate the transition while scaling the sticker image. 146f2fad9b1SAman Mittal 147f2fad9b1SAman MittalCreate the following function in the `<EmojiSticker>` component: 148f2fad9b1SAman Mittal 149f2fad9b1SAman Mittal```jsx EmojiSticker.js 150f2fad9b1SAman Mittalconst onDoubleTap = useAnimatedGestureHandler({ 151f2fad9b1SAman Mittal onActive: () => { 15261b5371bSAman Mittal if (scaleImage.value !== imageSize * 2) { 153f2fad9b1SAman Mittal scaleImage.value = scaleImage.value * 2; 154f2fad9b1SAman Mittal } 155f2fad9b1SAman Mittal }, 156f2fad9b1SAman Mittal}); 157f2fad9b1SAman Mittal``` 158f2fad9b1SAman Mittal 1593c9a6b96SBartosz KaszubowskiTo animate the transition, let's use a spring-based animation. This will make it feel alive because it's based on the real-world physics of a spring. 1603c9a6b96SBartosz KaszubowskiWe will use the `withSpring()` hook provided by `react-native-reanimated`. 161f2fad9b1SAman Mittal 1623c9a6b96SBartosz KaszubowskiThe `useAnimatedStyle()` hook from `react-native-reanimated` is used to create a style object that will be applied to the sticker image. 1633c9a6b96SBartosz KaszubowskiIt will update styles using the shared values when the animation happens. In this case, we are scaling the size of the image, 1643c9a6b96SBartosz Kaszubowskiwhich is done by manipulating the `width` and `height` properties. The initial values of these properties are set to `imageSize`. 1653c9a6b96SBartosz KaszubowskiCreate an `imageStyle` variable and add it to the `EmojiSticker` component: 166f2fad9b1SAman Mittal 167f2fad9b1SAman Mittal```jsx EmojiSticker.js 168f2fad9b1SAman Mittalconst imageStyle = useAnimatedStyle(() => { 169f2fad9b1SAman Mittal return { 170f2fad9b1SAman Mittal width: withSpring(scaleImage.value), 171f2fad9b1SAman Mittal height: withSpring(scaleImage.value), 172f2fad9b1SAman Mittal }; 173f2fad9b1SAman Mittal}); 174f2fad9b1SAman Mittal``` 175f2fad9b1SAman Mittal 176f2fad9b1SAman MittalNext, wrap the `<AnimatedImage>` component that displays the sticker on the screen with the `<TapGestureHandler>` component. 177f2fad9b1SAman Mittal 178f2fad9b1SAman Mittal<SnackInline 179f2fad9b1SAman Mittallabel="Handling tap gesture" 180f2fad9b1SAman MittaltemplateId="tutorial/06-gestures/App" 181f2fad9b1SAman Mittaldependencies={['expo-image-picker', '@expo/vector-icons/FontAwesome', '@expo/vector-icons', 'expo-status-bar', '@expo/vector-icons/MaterialIcons', 'react-native-gesture-handler', 'react-native-reanimated']} 182f2fad9b1SAman Mittalfiles={{ 183f2fad9b1SAman Mittal 'assets/images/background-image.png': 'https://snack-code-uploads.s3.us-west-1.amazonaws.com/~asset/503001f14bb7b8fe48a4e318ad07e910', 184f2fad9b1SAman Mittal 'assets/images/emoji1.png': 'https://snack-code-uploads.s3.us-west-1.amazonaws.com/~asset/be9751678c0b3f9c6bf55f60de815d30', 185f2fad9b1SAman Mittal 'assets/images/emoji2.png': 'https://snack-code-uploads.s3.us-west-1.amazonaws.com/~asset/7c0d14b79e134d528c5e0801699d6ccf', 186f2fad9b1SAman Mittal 'assets/images/emoji3.png': 'https://snack-code-uploads.s3.us-west-1.amazonaws.com/~asset/d713e2de164764c2ab3db0ab4e40c577', 187f2fad9b1SAman Mittal 'assets/images/emoji4.png': 'https://snack-code-uploads.s3.us-west-1.amazonaws.com/~asset/ac2163b98a973cb50bfb716cc4438f9a', 188f2fad9b1SAman Mittal 'assets/images/emoji5.png': 'https://snack-code-uploads.s3.us-west-1.amazonaws.com/~asset/9cc0e2ff664bae3af766b9750331c3ad', 189f2fad9b1SAman Mittal 'assets/images/emoji6.png': 'https://snack-code-uploads.s3.us-west-1.amazonaws.com/~asset/ce614cf0928157b3f7daa3cb8e7bd486', 190f2fad9b1SAman Mittal 'components/ImageViewer.js': 'tutorial/02-image-picker/ImageViewer.js', 191f2fad9b1SAman Mittal 'components/Button.js': 'tutorial/03-button-options/Button.js', 192f2fad9b1SAman Mittal 'components/CircleButton.js': 'tutorial/03-button-options/CircleButton.js', 193f2fad9b1SAman Mittal 'components/IconButton.js': 'tutorial/03-button-options/IconButton.js', 194f2fad9b1SAman Mittal 'components/EmojiPicker.js': 'tutorial/04-modal/EmojiPicker.js', 195f2fad9b1SAman Mittal 'components/EmojiList.js': 'tutorial/05-emoji-list/EmojiList.js', 196f2fad9b1SAman Mittal 'components/EmojiSticker.js': 'tutorial/06-gestures/EmojiSticker.js', 197f2fad9b1SAman Mittal}}> 198f2fad9b1SAman Mittal 199f2fad9b1SAman Mittal{/* prettier-ignore */} 200f2fad9b1SAman Mittal```jsx 201f2fad9b1SAman Mittalexport default function EmojiSticker({ imageSize, stickerSource }) { 202f2fad9b1SAman Mittal // ...rest of the code remains same 203f2fad9b1SAman Mittal return ( 204f2fad9b1SAman Mittal <View style={{ top: -350 }}> 205f2fad9b1SAman Mittal /* @info Wrap the AnimatedImage component with TapGestureHandler*/ <TapGestureHandler onGestureEvent={onDoubleTap} numberOfTaps={2}>/* @end */ 206f2fad9b1SAman Mittal <AnimatedImage 207f2fad9b1SAman Mittal source={stickerSource} 208f2fad9b1SAman Mittal resizeMode="contain" 209f2fad9b1SAman Mittal /* @info Modify the style prop on the AnimatedImage to pass the imageStyle.*/ style={[imageStyle, { width: imageSize, height: imageSize }]} /* @end */ 210f2fad9b1SAman Mittal /> 211f2fad9b1SAman Mittal /* @info */</TapGestureHandler>/* @end */ 212f2fad9b1SAman Mittal </View> 213f2fad9b1SAman Mittal ); 214f2fad9b1SAman Mittal} 215f2fad9b1SAman Mittal``` 216f2fad9b1SAman Mittal 217f2fad9b1SAman Mittal</SnackInline> 218f2fad9b1SAman Mittal 2193c9a6b96SBartosz KaszubowskiIn the above snippet, the `onGestureEvent` prop takes the value of the `onDoubleTap()` function and triggers it when a user taps the sticker image. 2203c9a6b96SBartosz KaszubowskiThe `numberOfTaps` prop determines how many taps are required. 221f2fad9b1SAman Mittal 222f2fad9b1SAman MittalLet's take a look at our app on iOS, Android and the web: 223f2fad9b1SAman Mittal 224f2fad9b1SAman Mittal<Video file="tutorial/tap-gesture.mp4" /> 225f2fad9b1SAman Mittal 2263c9a6b96SBartosz Kaszubowski> For a complete reference on the tap gesture API, refer to the [React Native Gesture Handler](https://docs.swmansion.com/react-native-gesture-handler/docs/api/gestures/tap-gesture) documentation. 227f2fad9b1SAman Mittal 228a30150b3SAman Mittal</Step> 229a30150b3SAman Mittal 230a30150b3SAman Mittal<Step label="4"> 231a30150b3SAman Mittal 232a30150b3SAman Mittal## Add a pan gesture 233f2fad9b1SAman Mittal 234f2fad9b1SAman MittalA pan gesture allows recognizing a dragging gesture and tracking its movement. We will use this gesture handler to drag the sticker across the image. 235f2fad9b1SAman Mittal 236f2fad9b1SAman MittalIn the **EmojiSticker.js**, import `PanGestureHandler` from the `react-native-gesture-handler` library. 237f2fad9b1SAman Mittal 238f2fad9b1SAman Mittal{/* prettier-ignore */} 239f2fad9b1SAman Mittal```jsx EmojiSticker.js 240f2fad9b1SAman Mittalimport { /* @info */ PanGestureHandler,/* @end */ TapGestureHandler} from "react-native-gesture-handler"; 241f2fad9b1SAman Mittal``` 242f2fad9b1SAman Mittal 243f2fad9b1SAman MittalCreate an `<AnimatedView>` component using the `Animated.createAnimatedComponent()` method. Then use it to wrap the `<TapGestureHandler>` component by replacing the `<View>` component. 244f2fad9b1SAman Mittal 245f2fad9b1SAman Mittal{/* prettier-ignore */} 246f2fad9b1SAman Mittal```jsx EmojiSticker.js 247f2fad9b1SAman Mittal// ...rest of the code remains same 248f2fad9b1SAman Mittal/* @info */ const AnimatedView = Animated.createAnimatedComponent(View); /* @end */ 249f2fad9b1SAman Mittal 250f2fad9b1SAman Mittalexport default function EmojiSticker({ imageSize, stickerSource }) { 251f2fad9b1SAman Mittal // ...rest of the code remains same 252f2fad9b1SAman Mittal 253f2fad9b1SAman Mittal return ( 254f2fad9b1SAman Mittal /* @info Replace the View component with AnimatedView */<AnimatedView style={{ top: -350 }}>/* @end */ 255f2fad9b1SAman Mittal <TapGestureHandler onGestureEvent={onDoubleTap} numberOfTaps={2}> 256f2fad9b1SAman Mittal {/* ...rest of the code remains same */} 257f2fad9b1SAman Mittal </TapGestureHandler> 258f2fad9b1SAman Mittal /* @info */</AnimatedView>/* @end */ 259f2fad9b1SAman Mittal ); 260f2fad9b1SAman Mittal} 261f2fad9b1SAman Mittal``` 262f2fad9b1SAman Mittal 263f2fad9b1SAman MittalNow, create two new shared values: `translateX` and `translateY`. 264f2fad9b1SAman Mittal 265f2fad9b1SAman Mittal```jsx EmojiSticker.js 266f2fad9b1SAman Mittalexport default function EmojiSticker({ imageSize, stickerSource }) { 267f2fad9b1SAman Mittal const translateX = useSharedValue(0); 268f2fad9b1SAman Mittal const translateY = useSharedValue(0); 269f2fad9b1SAman Mittal 270f2fad9b1SAman Mittal // ...rest of the code remains same 271f2fad9b1SAman Mittal} 272f2fad9b1SAman Mittal``` 273f2fad9b1SAman Mittal 274f2fad9b1SAman MittalThese translation values will move the sticker around the screen. Since the sticker moves along both axes, we need to track the X and Y values separately. 275f2fad9b1SAman Mittal 2763c9a6b96SBartosz KaszubowskiIn the `useSharedValue()` hooks, we have set both translation variables to have an initial position of `0`. 2773c9a6b96SBartosz KaszubowskiThis means that the position the sticker is initially placed is considered the starting point. This value sets the initial position of the sticker when the gesture starts. 278f2fad9b1SAman Mittal 2793c9a6b96SBartosz KaszubowskiIn the previous step, we triggered the `onActive()` callback for the tap gesture inside the `useAnimatedGestureHandler()` function. 2803c9a6b96SBartosz KaszubowskiSimilarly, for the pan gesture, we have to specify two callbacks: 281f2fad9b1SAman Mittal 282f2fad9b1SAman Mittal- `onStart()`: when the gesture starts or is at its initial position 283f2fad9b1SAman Mittal- `onActive()`: when the gesture is active and is moving 284f2fad9b1SAman Mittal 285f2fad9b1SAman MittalCreate an `onDrag()` method to handle the pan gesture. 286f2fad9b1SAman Mittal 287f2fad9b1SAman Mittal{/* prettier-ignore */} 288f2fad9b1SAman Mittal```jsx EmojiSticker.js 289f2fad9b1SAman Mittalconst onDrag = useAnimatedGestureHandler({ 290f2fad9b1SAman Mittal onStart: (event, context) => { 291f2fad9b1SAman Mittal context.translateX = translateX.value; 292f2fad9b1SAman Mittal context.translateY = translateY.value; 293f2fad9b1SAman Mittal }, 294f2fad9b1SAman Mittal onActive: (event, context) => { 295f2fad9b1SAman Mittal translateX.value = event.translationX + context.translateX; 296f2fad9b1SAman Mittal translateY.value = event.translationY + context.translateY; 297f2fad9b1SAman Mittal }, 298f2fad9b1SAman Mittal}); 299f2fad9b1SAman Mittal``` 300f2fad9b1SAman Mittal 301f2fad9b1SAman MittalBoth the `onStart` and `onActive` methods accept `event` and `context` as parameters. In the `onStart` method, we'll use `context` to store the initial values of `translateX` and `translateY`. In the `onActive` callback, we'll use the `event` to get the current position of the pan gesture and `context` to get the previously stored values of `translateX` and `translateY`. 302f2fad9b1SAman Mittal 303f2fad9b1SAman MittalNext, use the `useAnimatedStyle()` hook to return an array of transforms.For the `<AnimatedView>` component, we need to set the `transform` property to the `translateX` and `translateY` values. This will change the sticker's position when the gesture is active. 304f2fad9b1SAman Mittal 305f2fad9b1SAman Mittal{/* prettier-ignore */} 306f2fad9b1SAman Mittal```jsx EmojiSticker.js 307f2fad9b1SAman Mittalconst containerStyle = useAnimatedStyle(() => { 308f2fad9b1SAman Mittal return { 309f2fad9b1SAman Mittal transform: [ 310f2fad9b1SAman Mittal { 311f2fad9b1SAman Mittal translateX: translateX.value, 312f2fad9b1SAman Mittal }, 313f2fad9b1SAman Mittal { 314f2fad9b1SAman Mittal translateY: translateY.value, 315f2fad9b1SAman Mittal }, 316f2fad9b1SAman Mittal ], 317f2fad9b1SAman Mittal }; 318f2fad9b1SAman Mittal}); 319f2fad9b1SAman Mittal``` 320f2fad9b1SAman Mittal 3213c9a6b96SBartosz KaszubowskiThen add the `containerStyle` from the above snippet on the `<AnimatedView>` component to apply the transform styles. 3223c9a6b96SBartosz KaszubowskiAlso, update the `<EmojiSticker>` component so that the `<PanGestureHandler>` component becomes the top-level component. 323f2fad9b1SAman Mittal 324f2fad9b1SAman Mittal<SnackInline 325f2fad9b1SAman Mittallabel="Handle pan gesture" 326f2fad9b1SAman MittaltemplateId="tutorial/06-gestures/App" 327f2fad9b1SAman Mittaldependencies={['expo-image-picker', '@expo/vector-icons/FontAwesome', '@expo/vector-icons', 'expo-status-bar', '@expo/vector-icons/MaterialIcons', 'react-native-gesture-handler', 'react-native-reanimated']} 328f2fad9b1SAman Mittalfiles={{ 329f2fad9b1SAman Mittal 'assets/images/background-image.png': 'https://snack-code-uploads.s3.us-west-1.amazonaws.com/~asset/503001f14bb7b8fe48a4e318ad07e910', 330f2fad9b1SAman Mittal 'assets/images/emoji1.png': 'https://snack-code-uploads.s3.us-west-1.amazonaws.com/~asset/be9751678c0b3f9c6bf55f60de815d30', 331f2fad9b1SAman Mittal 'assets/images/emoji2.png': 'https://snack-code-uploads.s3.us-west-1.amazonaws.com/~asset/7c0d14b79e134d528c5e0801699d6ccf', 332f2fad9b1SAman Mittal 'assets/images/emoji3.png': 'https://snack-code-uploads.s3.us-west-1.amazonaws.com/~asset/d713e2de164764c2ab3db0ab4e40c577', 333f2fad9b1SAman Mittal 'assets/images/emoji4.png': 'https://snack-code-uploads.s3.us-west-1.amazonaws.com/~asset/ac2163b98a973cb50bfb716cc4438f9a', 334f2fad9b1SAman Mittal 'assets/images/emoji5.png': 'https://snack-code-uploads.s3.us-west-1.amazonaws.com/~asset/9cc0e2ff664bae3af766b9750331c3ad', 335f2fad9b1SAman Mittal 'assets/images/emoji6.png': 'https://snack-code-uploads.s3.us-west-1.amazonaws.com/~asset/ce614cf0928157b3f7daa3cb8e7bd486', 336f2fad9b1SAman Mittal 'components/ImageViewer.js': 'tutorial/02-image-picker/ImageViewer.js', 337f2fad9b1SAman Mittal 'components/Button.js': 'tutorial/03-button-options/Button.js', 338f2fad9b1SAman Mittal 'components/CircleButton.js': 'tutorial/03-button-options/CircleButton.js', 339f2fad9b1SAman Mittal 'components/IconButton.js': 'tutorial/03-button-options/IconButton.js', 340f2fad9b1SAman Mittal 'components/EmojiPicker.js': 'tutorial/04-modal/EmojiPicker.js', 341f2fad9b1SAman Mittal 'components/EmojiList.js': 'tutorial/05-emoji-list/EmojiList.js', 342f2fad9b1SAman Mittal 'components/EmojiSticker.js': 'tutorial/06-gestures/CompleteEmojiSticker.js', 343f2fad9b1SAman Mittal}}> 344f2fad9b1SAman Mittal 345f2fad9b1SAman Mittal{/* prettier-ignore */} 346f2fad9b1SAman Mittal```jsx 347f2fad9b1SAman Mittalexport default function EmojiSticker({ imageSize, stickerSource }) { 348f2fad9b1SAman Mittal // rest of the code 349f2fad9b1SAman Mittal 350f2fad9b1SAman Mittal return ( 351f2fad9b1SAman Mittal /* @info Wrap all components inside PanGestureHandler. */<PanGestureHandler onGestureEvent={onDrag}>/* @end */ 352f2fad9b1SAman Mittal /* @info Add containerStyle to the AnimatedView style prop. */<AnimatedView style={[containerStyle, { top: -350 }]}>/* @end */ 353f2fad9b1SAman Mittal <TapGestureHandler onGestureEvent={onDoubleTap} numberOfTaps={2}> 354f2fad9b1SAman Mittal <AnimatedImage 355f2fad9b1SAman Mittal source={stickerSource} 356f2fad9b1SAman Mittal resizeMode="contain" 357f2fad9b1SAman Mittal style={[imageStyle, { width: imageSize, height: imageSize }]} 358f2fad9b1SAman Mittal /> 359f2fad9b1SAman Mittal </TapGestureHandler> 360f2fad9b1SAman Mittal </AnimatedView> 361f2fad9b1SAman Mittal /* @info */</PanGestureHandler>/* @end */ 362f2fad9b1SAman Mittal ); 363f2fad9b1SAman Mittal} 364f2fad9b1SAman Mittal``` 365f2fad9b1SAman Mittal 366f2fad9b1SAman Mittal</SnackInline> 367f2fad9b1SAman Mittal 368f2fad9b1SAman MittalLet's take a look at our app on iOS, Android and the web: 369f2fad9b1SAman Mittal 370f2fad9b1SAman Mittal<Video file="tutorial/pan-gesture.mp4" /> 371f2fad9b1SAman Mittal 372a30150b3SAman Mittal</Step> 373a30150b3SAman Mittal 3749de0c686SAman Mittal## Next step 375f2fad9b1SAman Mittal 376f2fad9b1SAman MittalWe successfully implemented pan and tap gestures. 377f2fad9b1SAman Mittal 3789de0c686SAman Mittal<BoxLink 3799de0c686SAman Mittal title="Take a screenshot" 3809de0c686SAman Mittal Icon={BookOpen02Icon} 3819de0c686SAman Mittal description="In the next chapter, we'll learn how to take a screenshot of the image and the sticker, and save it on the device's library." 3829de0c686SAman Mittal href="/tutorial/screenshot" 3839de0c686SAman Mittal/> 384