react-native-reanimated-flat-list
Version:
React Native Flatlist with scroll animation and loop ability
133 lines (117 loc) • 3.4 kB
JavaScript
/**
* 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;