Reputation: 1145
I have flatlist horizontal like below:
const DATA = [
{
id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba',
title: 'First Item',
},
{
id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63',
title: 'Second Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d72',
title: 'Third Item',
},
{
id: 'bd7acbea-c1b1-46c2-aed5-3ad353abb28ba',
title: 'Fourth Item',
},
{
id: '3ac68afc-c605-48d3-a4f8-fbd291aa97f63',
title: 'Fifth Item',
},
{
id: '58694a0f-3da1-471f-bd961-145571e29d72',
title: 'Sixth Item',
},
];
const Item = ({ title }) => (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
const App = () => {
const renderItem = ({ item }) => (
<Item title={item.title} />
);
return (
<SafeAreaView style={styles.container}>
<FlatList
horizontal
data={DATA}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</SafeAreaView>
);
}
Whenever Item entered the viewport, I want to add animation to that element. I can get X and Y position of scroll with onScroll
, now how do I get the positions of items to check if its in view port or if it went away from viewport...
Upvotes: 1
Views: 4653
Reputation: 3540
I implemented the basic fade in/out animation example into the Item
component. Whether it fades out or in is decided by the prop isViewable
// Item.js
const Item = (props) => {
const {
item:{title, isViewable}
} = props
/*
I copied and pasted the basic animation example from the react-native dev page
*/
const fadeAnim = useRef(new Animated.Value(1)).current;
const fadeIn = () => {
Animated.timing(fadeAnim, {
toValue: 1,
duration: 1000,
useNativeDriver:false
}).start();
};
const fadeOut = () => {
Animated.timing(fadeAnim, {
toValue: 0,
duration: 1500,
useNativeDriver:false
}).start();
};
/* end of animation example*/
// fade in/out base on if isViewable
if(isViewable || isViewable == 0)
fadeIn()
else
fadeOut()
const animation = {opacity:fadeAnim}
return (
//add animation to Animated.View
<Animated.View style={[style.itemContainer,animation]}>
<View style={style.item}>
<Text style={style.title}>{title}</Text>
</View>
</Animated.View>
);
}
Create a FlatListWrapper (to avoid the onViewableItemChange on fly error). By doing this, as long as you don't make changes to FlatListWrapper, you won't get the on the fly error:
// FlatListWrapper.js
const FlatListWrapper = (props) => {
// useRef to avoid onViewableItemsChange on fly error
const viewabilityConfig = useRef({
// useRef to try to counter the view rerender thing
itemVisiblePercentThreshold:80
}).current;
// wrapped handleViewChange in useCallback to try to handle the onViewableItemsChange on fly error
const onViewChange = useCallback(props.onViewableItemsChanged,[])
return (
<View style={style.flatlistContainer}>
<FlatList
{...props}
horizontal={true}
onViewableItemsChanged={onViewChange}
/>
</View>
);
}
const style = StyleSheet.create({
flatlistContainer:{
borderWidth:1,
borderColor:'red',
width:'50%',
height:40
},
// main FlatList component
const FlatListAnimation = () => {
// store the indices of the viewableItmes
const [ viewableItemsIndices, setViewableItemsIndices ] = useState([]);
return (
<SafeAreaView style={style.container}>
<FlatListWrapper
horizontal={true}
//{/*give each data item an isViewable prop*/}
data={DATA.map((item,i)=>{
item.isViewable=viewableItemsIndices.find(ix=>ix == i)
return item
})}
renderItem={item=><Item {...item}/>}
keyExtractor={item => item.id}
onViewableItemsChanged={({viewableItems, changed})=>{
// set viewableItemIndices to the indices when view change
setViewableItemsIndices(viewableItems.map(item=>item.index))
}}
//{/*config that decides when an item is viewable*/}
viewabilityConfig={{itemVisiblePercentThreshold:80}}
extraData={viewableItemsIndices}
/>
{/* Extra stuff that just tells you what items should be visible*/}
<Text>Items that should be visible:</Text>
{viewableItemsIndices.map(i=><Text> {DATA[i].title}</Text>)}
</SafeAreaView>
);
}
const style = StyleSheet.create({
container:{
padding:10,
alignItems:'center'
},
flatlistContainer:{
borderWidth:1,
borderColor:'red',
width:'50%',
height:40
},
item:{
borderWidth:1,
padding:5,
},
itemContainer:{
padding:5,
}
})
By wrapping your FlatList in a separate file, you won't encounter the "onViewableItemsChange on the fly" error as long as you don't modify FlatListWrapper.js
Upvotes: 1
Reputation: 16122
Use onViewableItemsChanged this is called when the items in the flatlist changes.
const handleViewableItemsChanged = (viewableItems, changed) => {}
<Flatlist
...
onViewableItemsChanged={handleViewableItemsChanged}
Upvotes: 2