Onboarding Animation
An easy to integrate onboarding screens with smooth color interpolation. Just pass Images, Texts and colors and it’s ready!!
import { StyleSheet, Text, View } from 'react-native'import React from 'react'import OnboardScreens from '../src/components/OnboardScreens'
//or if you're using npm package//import {OnboardScreens} from '@cascadeui/onboard-screens'
const App = () => { const pages = [ { image:require('../../assets/images/ill4.png'), // add path to your image heading:'Create anothing', subheading:'I made a typo I am sawwry :3',
}, { image:require('../../assets/images/ill5.png'), // add path to your image heading:'Create fast', subheading:'Why is this dude confused?',
}, { image:require('../../assets/images/ill6.png'), // add path to your image heading:'Create', subheading:'By just adding image, text and background colors in this predbuilt template',
} ]
return ( <View> <OnboardScreens Pages={pages} backgroundColors={['#FFEAA9','#CFEAD7','#D7EDFF']} onDone={()=>console.log('hello')} headingStyle={{color:'black',fontSize:36,fontWeight:'bold'}} subheadingStyle={{color:'grey',textAlign:'center',paddingHorizontal:30,marginTop:10}} buttonColor='#ff6347' pagerDotsColor={'#ff6347'} nextButton={<Text>Next</Text>} // or add an Icon /> </View> )}
export default AppInstallation
- Install component
Terminal window npm install @cascadeui/onboard-screens - Add react-native-reanimated plugin into
babel.configmodule.exports = {presets: [... // don't add it here :)],plugins: [...'react-native-reanimated/plugin',],};
- Install React Native Reanimated
Terminal window npm install react-native-reanimatedTerminal window yarn add react-native-reanimatedTerminal window npx expo install react-native-reanimated - Add Reanimated plugin into babel.config file
module.exports = {presets: [... // don't add it here :)],plugins: [...'react-native-reanimated/plugin',],};
- Copy core component
src/components/OnboardScreens.tsximport { Dimensions, StyleSheet, Text, TouchableOpacity, View, ImageSourcePropType } from 'react-native'import React, { useRef, useState} from 'react'import Animated, { Extrapolation, interpolate, interpolateColor, useAnimatedScrollHandler, useAnimatedStyle, useSharedValue, withDecay, withSpring, withTiming,runOnJS, withDelay } from 'react-native-reanimated'const {height , width} = Dimensions.get('window')type page = {image : ImageSourcePropType,heading : string,subheading : string}interface MyComponentProps {Pages:page[],backgroundColors:any[],onDone: () => void,headingStyle?:{},subheadingStyle?:{},buttonColor?:string,pagerDotsColor?:string,buttonTextStyle?:{},nextButton?:React.ReactNode}const OnboardScreens : React.FC<MyComponentProps> = ({Pages,backgroundColors,onDone,headingStyle,buttonColor,subheadingStyle,pagerDotsColor,buttonTextStyle,nextButton}) => {const translationX = useSharedValue(0)const bgColor = useSharedValue('red')const scrollPosition = useSharedValue(0)const [lastPage,setLastPage] = useState<boolean>(false)const scrollViewRef = useRef<Animated.ScrollView>(null);const scrollHandler = useAnimatedScrollHandler((event)=>{translationX.value = event.contentOffset.xscrollPosition.value = event.contentOffset.x})const handleNextPage = () => {if (scrollViewRef.current) {const nextPage = Math.ceil(scrollPosition.value / width) + 1;scrollViewRef.current.scrollTo({ x: nextPage * width, animated: true });const pageIndex = Math.round(translationX.value / width);if(pageIndex+1===Pages.length-1){setLastPage(true)}if(lastPage===true){onDone()}}};//Pager Dotsconst renderDots = () => {return Pages.map((_, index) => {const animatedDotStyle = useAnimatedStyle(() => {const inputRange = [(index - 1) * width, index * width, (index + 1) * width];const scale = interpolate(translationX.value, inputRange, [0.8, 1.5, 0.8], Extrapolation.CLAMP);const opacity = interpolate(translationX.value, inputRange, [0.3, 1, 0.3], Extrapolation.CLAMP);return {transform: [{ scale }],opacity,};});return (<Animated.View key={index} style={[styles.dot, animatedDotStyle,{backgroundColor:pagerDotsColor||'black'}]} />);});};const handleScrollEnd = (event: any) => {const pageIndex = Math.round(event.nativeEvent.contentOffset.x / width);if(pageIndex+1===Pages.length){setLastPage(true)}};return (<View ><Animated.ScrollView ref={scrollViewRef} horizontal onScroll={scrollHandler}scrollEventThrottle={16} pagingEnabled contentOffset={{ x: scrollPosition.value, y: 0 }}showsHorizontalScrollIndicator={false} onMomentumScrollEnd={handleScrollEnd} alwaysBounceHorizontal={false}>{Pages.map((item,index)=>{const animatedStyle = useAnimatedStyle(()=>{const inputRange = [0,width,width*2]const color = interpolateColor(translationX.value,inputRange,backgroundColors)bgColor.value = colorreturn{backgroundColor:bgColor.value}})const imageAnimatedStyle = useAnimatedStyle(()=>{const inputRange = [(index-1)*width,index*width,(index+1)*width]const scale = interpolate(translationX.value,inputRange,[0,1,0], Extrapolation.CLAMP)const translate = interpolate(translationX.value,inputRange,[100,0,100], Extrapolation.CLAMP)return{opacity:scale,transform:[{translateY:translate}],}})return(<Animated.View style={[animatedStyle,styles.container]} key={index}><View key={index}><Animated.Imagesource={item.image}style={[styles.image,imageAnimatedStyle]}key={index}/></View><Animated.Text style={[headingStyle||styles.text,imageAnimatedStyle]} key={item.heading}>{item.heading}</Animated.Text><Animated.Text style={[subheadingStyle||styles.subtext,imageAnimatedStyle]} key={item.subheading} >{item.subheading}</Animated.Text></Animated.View>)})}</Animated.ScrollView><View style={styles.footerContainer}><View style={styles.dotsContainer}>{renderDots()}</View><TouchableOpacity style={[styles.nextButton,{backgroundColor:buttonColor||"#ff6347"}]} onPress={handleNextPage}><Text style={buttonTextStyle||styles.buttonText}>{lastPage===true?'done':nextButton||'Next'}</Text></TouchableOpacity></View></View>)}export default OnboardScreensconst styles = StyleSheet.create({container:{height,width,alignItems:'center',justifyContent:'center',},image:{height:300,width:width,resizeMode:'contain',bottom:30},text:{fontSize:36,fontWeight:'bold',color:'black'},subtext:{fontSize:16,color:'black',textAlign:'center',width:width-40},nextButton: {position: "absolute",right: 20, // Adjust to position button on screenpaddingVertical: 10,paddingHorizontal:30,borderRadius: 100,alignItems:'center',justifyContent:'center'},buttonText: {color: "white",fontSize:16},dotsContainer: {flexDirection: "row",justifyContent: "center",position: "absolute",width: "100%",},dot: {width: 10,height: 10,borderRadius: 5,marginHorizontal: 5,},footerContainer: {position: "absolute",bottom: 80,width: "100%",flexDirection: "row",alignItems: "center",backgroundColor:'red'},})
Props
| Prop | Type | Description |
|---|---|---|
pages | page[] | Array of objects where each objects have three properties(image, heading, subheading). |
backgroundColors | any[] | Array of strings containing color for each screen respectively. |
onDone | function | A function which defines what will happen when user press next after reaching last screen. |
headingStyle | object | An object containing styling properties for headings of screens. |
subheadingStyle | object | An object containing styling properties for subHeadings of screens. |
buttonColor | string | color of button. |
buttonTextStyle | object | An object containing styling properties for the button Text. |
nextButton | ReactElement | Content in button (can be text or an icon). |