UNPKG

react-native-walkthrough-swiper

Version:

A walkthrough swiper component for React-Native. Can be used in onboarding/walkthrough screens. Uses Reanimated API to create smooth animations.

158 lines (141 loc) 4.2 kB
import React, { useEffect, useRef } from 'react'; import { StyleSheet, Text, View, Dimensions } from 'react-native'; import Reanimated, { Easing, useAnimatedStyle, useSharedValue, withDelay, withSpring, withTiming, } from 'react-native-reanimated'; const width = Dimensions.get('window').width; const TEXT_OUT_DURATION = 160; const TEXT_OUT_OPACITY_DURATION = 100; const TEXT_IN_DURATION = 200; const SUBTITLE_IN_DELAY = 40; const ElasticText = ({ data, currentSlide, titleStyle, subTitleStyle }) => { const oldSlide = useRef(currentSlide); const animationVars = useRef( data.map(() => ({ title: { alpha: useSharedValue(0), x: useSharedValue(0), }, subtitle: { alpha: useSharedValue(0), x: useSharedValue(0), }, })) ); useEffect(() => { moveText(oldSlide.current, currentSlide); oldSlide.current = currentSlide; }, [currentSlide]); const moveText = (prev, active) => { const direction = active < prev ? -1 : 1; const textInSpring = { damping: 14, mass: 1, stiffness: 100, }; data.forEach((_, index) => { const { title, subtitle } = animationVars.current[index]; title.x.value = withTiming( direction * (index === prev ? -1 : 1) * width, { easing: Easing.inOut(Easing.linear), duration: TEXT_OUT_DURATION, } ); subtitle.x.value = withTiming( direction * (index === prev ? -1 : 1) * width, { easing: Easing.inOut(Easing.linear), duration: TEXT_OUT_DURATION, } ); title.alpha.value = withTiming(0, { easing: Easing.inOut(Easing.linear), duration: TEXT_OUT_OPACITY_DURATION, }); subtitle.alpha.value = withTiming(0, { easing: Easing.inOut(Easing.linear), duration: TEXT_OUT_OPACITY_DURATION, }); title.x.value = withSpring( index === active ? 0 : direction * width, textInSpring ); subtitle.x.value = withDelay( SUBTITLE_IN_DELAY, withSpring(index === active ? 0 : direction * width, textInSpring) ); title.alpha.value = withTiming(index === active ? 1 : 0, { easing: Easing.inOut(Easing.exp), duration: TEXT_IN_DURATION, }); subtitle.alpha.value = withDelay( SUBTITLE_IN_DELAY, withTiming(index === active ? 1 : 0, { easing: Easing.inOut(Easing.exp), duration: TEXT_IN_DURATION, }) ); }); }; return ( <> {data.map((slide, index) => { const { title, subtitle } = animationVars.current[index]; const titleAnimatedStyle = useAnimatedStyle(() => { const transX = title.x.value; return { transform: [{ translateX: transX }], opacity: title.alpha.value, }; }); const subtitleAnimatedStyle = useAnimatedStyle(() => { const transX = subtitle.x.value; return { transform: [{ translateX: -1 * transX }], opacity: subtitle.alpha.value, }; }); return ( <View style={styles.container} key={`content_${index}`}> <Reanimated.View key={`title_${index}`} style={[{ width: width - 40 }, titleAnimatedStyle]} > <Text style={[{ fontSize: 32, marginBottom: 12 }, titleStyle]}> {slide.title} </Text> </Reanimated.View> <Reanimated.View key={`subtitle_${index}`} style={[styles.widthWithMargin, subtitleAnimatedStyle]} > <Text style={[{ fontSize: 14.4 }, subTitleStyle]}> {slide.subTitle} </Text> </Reanimated.View> </View> ); })} </> ); }; const styles = StyleSheet.create({ container: { position: 'absolute', flexDirection: 'column', alignItems: 'center', alignSelf: 'center', width: width - 40, bottom: 72, }, widthWithMargin: { width: width - 40, }, }); export default React.memo(ElasticText);