UNPKG

react-native-reanimated-flat-list

Version:

React Native Flatlist with scroll animation and loop ability

133 lines (117 loc) 3.4 kB
/** * Sample React Native App * https://github.com/facebook/react-native * * @format * @flow strict-local */ import React, { useState, useRef, useEffect } from 'react'; import { SafeAreaView, StyleSheet, View, FlatList, StatusBar, Dimensions, } from 'react-native'; import ReAnimated from 'react-native-reanimated'; import { Transitioning, Transition } from 'react-native-reanimated'; const transition = <Transition.Change duration={50} interpolation={'easeInOut'}/>; const screenHeight = Math.round(Dimensions.get('window').height); type Props = { defaultData: any, itemHeight: number } function getRandomColor() { let letters = '0123456789ABCDEF'; let color = '#'; for (let i = 0; i < 6; i++) { color += letters[Math.floor(Math.random() * 16)]; } return color; } function initData() { let data = []; for (let i = 0; i < 20; i++) { data.push(getRandomColor()); } return data; } function initItemsActiveList(dataLength = 0, itemHeight) { let itemsActiveList = []; for (let i = 0; i < dataLength; i++) { if (itemHeight * i < screenHeight) { itemsActiveList.push(true); } else { itemsActiveList.push(false); } } return itemsActiveList; } export default function AnimatedFlatList( { defaultData = initData(), itemHeight = 60, }) { const [data, setData] = useState(defaultData); const [itemsActiveList, setItemsActiveList] = useState(initItemsActiveList(data?.length, itemHeight)); const [lastActiveIndex, setLastActiveIndex] = useState(14); const itemsRef = useRef([]); useEffect(() => { itemsRef.current = itemsRef.current.slice(0, data.length); }, [data]); console.log(itemsRef.current) return ( <View style={styles.container}> <StatusBar barStyle="dark-content"/> <FlatList snapToInterval={itemHeight} vertical showsVerticalScrollIndicator={false} data={data} renderItem={({item, index}) => { return renderItem(item, index); }} onScroll={onScroll} keyExtractor={item => item.id} /> </View> ); function onScroll({nativeEvent}) { if (isCloseToBottom(nativeEvent)) { setData([...data, ...data]); } console.log(nativeEvent.contentOffset.y, screenHeight, (lastActiveIndex ) * itemHeight, lastActiveIndex); if ((nativeEvent.contentOffset.y + screenHeight > (lastActiveIndex) * itemHeight) && !itemsActiveList[lastActiveIndex + 1]) { let tempItemsActiveList = itemsActiveList; tempItemsActiveList[lastActiveIndex] = true; setItemsActiveList([...tempItemsActiveList]); console.log(itemsRef.current) itemsRef.current[lastActiveIndex]?.animateNextTransition(); setTimeout(() => { setLastActiveIndex(lastActiveIndex + 1); }, 50); } } function isCloseToBottom({layoutMeasurement, contentOffset, contentSize}) { const paddingToBottom = 20; return layoutMeasurement.height + contentOffset.y >= contentSize.height - paddingToBottom; }; function renderItem(item, index) { return <Transitioning.View key={index} ref={el => itemsRef.current[index] = el} transition={transition}> {itemsActiveList[index] ? <View style={{height: itemHeight, width: '100%', backgroundColor: item}}/> : <View style={{height: itemHeight, width: '100%', backgroundColor: item, marginLeft: -150}}/>} </Transitioning.View>; } }; const styles = StyleSheet.create({ container: { ...StyleSheet.absoluteFillObject, }, }); export default App;