How to create onboarding screens with React Native reanimated

Nandhu_writes
4 min readApr 20, 2023

Offer a coffee ☕️ before a clap👏🏻

Hi guys,

Toaday we are going to build an onBoarding screen for react native apps, using react native reanimated npm ( yarn add react-native-reanimated), Follow the link for installation

React Native Reanimated provides a more comprehensive, low level abstraction for the Animated library API to be built on top of and hence allow for much greater flexibility especially when it comes to gesture based interactions.

So After setting the react native animated npm , we can directly jump into creating the onboarding structure

Nb: it is a raw structure, you need to refactor and customise as you need

create a file on src/screens/onboarding.js

import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
import Animated, { useAnimatedScrollHandler, useSharedValue } from 'react-native-reanimated';
import { Dot, Page } from '../Custom/slide';
import { CommonStyle, PAGE_HEIGHT, PAGE_WIDTH } from '../Style/stylesheet';
import { Color } from '../Globals/colors';
import { screenDiagonal } from '../Utils/helperFunction';

/**calculate diagonal of the screen of device */
const dgl = screenDiagonal();

const OnBoarding = (props) => {

const WORDS = [{ head: 'if you see this', des: 'your code is working..' }, { head: 'Jump in', des: 'create and customize ' }, { head: 'finally', des: 'you have a working code' }];
const translateX = useSharedValue(0);
/** handle Translation on scroll event */
const scrollHandler = useAnimatedScrollHandler((event) => {
translateX.value = event.contentOffset.x;
});

const Skipable = () => {
return (
<View style={{ flex: 1, position: 'absolute', bottom: 25, alignSelf: 'flex-end', paddingRight: dgl * 0.020 }}>
<Text onPress={() => { props.navigation.push('<Your-screen>') }} style={CommonStyle.input}>Skip</Text>
</View>
)
}
return (
<>
<Animated.FlatList
data={WORDS}
onScroll={scrollHandler}
initialScrollIndex={0}
snapToInterval={PAGE_WIDTH}
decelerationRate="fast"
scrollEventThrottle={16}
horizontal
style={style.container2}
pagingEnabled={true}
bounces={true}
bouncesZoom={true}
initialNumToRender={1}
keyExtractor={(item, index) => item.key}
renderItem={({ index, item }) => {
return (
<Page
key={index.toString()}
index={index}
title={item}
translateX={translateX}
/>
)
}}
>
</Animated.FlatList>
<Dot data={WORDS} translateX={translateX} color={Color.blue} />
<Skipable />

</>
)
}
const style = StyleSheet.create({
pageContainer: { height: PAGE_HEIGHT, width: PAGE_WIDTH, justifyContent: 'center' },
container2: { flex: 1, backgroundColor: Color.theme1 },
text: { fontSize: 30, color: 'rgba(0,0,250,1)', fontWeight: '700', textAlign: "center" },
})

export default OnBoarding
scrollEventThrottle={16}

This controls how often the scroll event will be fired while scrolling (as a time interval in ms). A lower number yields better accuracy for code that is tracking the scroll position, but can lead to scroll performance problems due to the volume of information being sent over the bridge. The default value is zero, which means the scroll event will be sent only once each time the view is scrolled.

now create the component src/screens/slide.js

This file contain the animation to interpolate between the Flatlist that acts like pages

//src/screens/slide.js

import React from "react"
import { StyleSheet, Text, Dimensions, View, Image } from "react-native";
import Animated, { Extrapolate, interpolate, useAnimatedStyle } from "react-native-reanimated";
import { Color } from "../Globals/colors";
import { PAGE_HEIGHT, PAGE_WIDTH } from "../Style/stylesheet";
import { screenDiagonal } from '../Utils/helperFunction';

// const PAGE_WIDTH = Dimensions.get('window').width;
// const PAGE_HEIGHT = Dimensions.get('window').height;

const SIZE = PAGE_WIDTH * 0.7;
const dgl = screenDiagonal();

const Page = (props) => {
const { index, title, translateX, onPress } = props;

const inputRange = [(index - 1) * PAGE_WIDTH, index * PAGE_WIDTH, (index + 1) * PAGE_WIDTH]

const rstyle = useAnimatedStyle(() => {
const scale = interpolate(
translateX.value,
inputRange,
[0, 1, 0],
Extrapolate.CLAMP
)

const borderRadius = interpolate(
translateX.value,
inputRange,
[0, SIZE / 2, 0],
Extrapolate.CLAMP
)

return {
transform: [{ scale: scale }],
// transform: [{ rotate: scale }],
borderRadius,
}
})
const textStyle = useAnimatedStyle(() => {
const scale = interpolate(
translateX.value,
inputRange,
[PAGE_HEIGHT / 2, 0, PAGE_HEIGHT / 2],
Extrapolate.CLAMP
)
const opacity = interpolate(
translateX.value,
inputRange,
[-4, 1, -4],
Extrapolate.CLAMP
)
return {
opacity,
transform: [{ translateY: scale }],
}
})

return (
<>
<Animated.View style={[rstyle, Style.square]} />
<View style={Style.pageContainer}>
<View style={Style.container2}>
<Image style={{ width: dgl * 0.26, height: 170, alignSelf: 'center', borderRadius: 5, resizeMode: 'contain' }} source={require('../Assets/images/Frame.png')} />
</View>

<Animated.View style={[textStyle, Style.container]}>
<Text style={Style.text}>{title?.head}</Text>
<Text style={Style.text2}>{title?.des}</Text>
</Animated.View>

</View>

</>
)
}

const Dot = (props) => {
const { data, translateX, style,color } = props;

return (
<View>
<View style={style ? style : { flex: 1, flexDirection: 'row', justifyContent: 'space-between', backgroundColor: 'transparent', position: 'absolute', bottom: 25, marginLeft: 25 }}>
{data.map((_, index) => {
const inputRange = [(index - 1) * PAGE_WIDTH, index * PAGE_WIDTH, (index + 1) * PAGE_WIDTH]
const textStyle = useAnimatedStyle(() => {
const size = interpolate(
translateX.value,
inputRange,
[10, 25, 10],
Extrapolate.CLAMP)
const opacity = interpolate(
translateX.value,
inputRange,
[0.5, 2, 0.5],
Extrapolate.CLAMP)
return {
width: size,
opacity,
backgroundColor: color
}
})
return (<Animated.View style={[Style.dot, textStyle]} key={index.toString()} />)
})}
</View>
</View>
)
}

const Style = StyleSheet.create({
pageContainer: {flex: 1, height: PAGE_HEIGHT, width: PAGE_WIDTH, alignItems: 'center', justifyContent: 'center'},
container: { flex: 2, justifyContent: 'center', alignItems: 'center', position: 'absolute' },
container2: { flex: 1, top: 200, position: 'absolute' },
square: {height: SIZE,width: SIZE,backgroundColor: Color.theme3,position: "absolute",justifyContent: 'center',alignSelf: 'center',top: SIZE/3},
text: {fontSize: 18,color: Color.blue,textTransform: 'uppercase',fontWeight: '700',textAlign: "center",width: PAGE_WIDTH - 120,},
text2: {fontSize: 12,color: Color.blue,fontWeight: '700',width: PAGE_WIDTH - 120,textAlign: "center"},
dot: {height: 8, margin: 5, borderRadius: 10}
})
export { Page, Dot };
useAnimatedStyle(()=>{
/** your animtions here*/
})

you may feel difficulty in animating at starting, once you get used to it , you can smoothly and continuously build interactive UI

Thats all you are there !!

Mention your thoughts and modification on comments.!!

So we can learn together..

Why Should You Follow My Blog?

  • Stay Informed: By following my blog, you’ll never miss an update. Stay informed about the latest trends, tips.
  • Exclusive Content: Subscribers get access to exclusive content, including in-depth articles into our creative process.
  • Engage and Connect: Joining our community allows you to engage with like-minded individuals who share your interests. Connect, ask questions, and share your thoughts in the comments section.

byeee……

--

--

Nandhu_writes

React Native developer | Still a learner | blogger : Tech life lessons || Buy me a coffee, and I'll write : https://www.buymeacoffee.com/infoappmakk