React Native自定义下拉刷新动画:蓝点平移实现需求问询
自定义FlatList下拉刷新动画实现方案
效果说明
红色区域为HEADER,蓝色圆点是View或Icon,灰色区域是FlatList列表项;Refresh Control的高度等于HEADER与首个列表项之间的间距。
核心需求
- 下拉FlatList时,蓝点随下拉距离通过
Animated translateX向右移动,最小位移为0,最大位移为WINDOW_WIDTH - BLUE_DOT_WIDTH(即HEADER的中间位置) - 初始状态(Refresh Control高度为0)时,蓝点的
translateX为0;当下拉到Refresh Control高度达到最大值时,蓝点移动到中间位置并触发刷新 - 刷新完成后,蓝点停止加载动画,随Refresh Control高度降低向左移动,直到高度归0时回到初始位置(
translateX=0)
完整实现代码
import React, { useState, useRef } from 'react'; import { View, FlatList, Animated, StyleSheet, Dimensions, ActivityIndicator } from 'react-native'; // 获取屏幕宽度,定义蓝点尺寸和最大刷新高度 const { width: WINDOW_WIDTH } = Dimensions.get('window'); const BLUE_DOT_WIDTH = 30; const MAX_REFRESH_HEIGHT = 80; const CustomRefreshFlatList = () => { const [refreshing, setRefreshing] = useState(false); // 监听滚动偏移量的动画值 const scrollY = useRef(new Animated.Value(0)).current; // 蓝点的横向位移动画值 const dotTranslateX = useRef(new Animated.Value(0)).current; // 处理滚动事件,实时计算蓝点位移 const handleScroll = Animated.event( [{ nativeEvent: { contentOffset: { y: scrollY } } }], { useNativeDriver: false, listener: (event) => { const scrollOffsetY = event.nativeEvent.contentOffset.y; // 仅在下拉且未处于刷新状态时计算位移 if (scrollOffsetY < 0 && !refreshing) { const pullDistance = Math.abs(scrollOffsetY); // 计算位移比例,限制在0-1区间内 const ratio = Math.min(pullDistance / MAX_REFRESH_HEIGHT, 1); const targetX = ratio * (WINDOW_WIDTH - BLUE_DOT_WIDTH); // 实时更新蓝点位置 Animated.timing(dotTranslateX, { toValue: targetX, duration: 0, useNativeDriver: false, }).start(); } }, } ); // 模拟刷新操作 const onRefresh = () => { setRefreshing(true); setTimeout(() => { setRefreshing(false); // 刷新完成后将蓝点平滑移回初始位置 Animated.timing(dotTranslateX, { toValue: 0, duration: 300, useNativeDriver: false, }).start(); }, 2000); }; // 自定义刷新控件 const CustomRefreshControl = () => ( <View style={styles.refreshContainer}> <Animated.View style={[ styles.blueDot, { transform: [{ translateX: dotTranslateX }] } ]} > {/* 刷新时显示加载指示器 */} {refreshing && <ActivityIndicator color="#ffffff" />} </Animated.View> </View> ); // 生成模拟列表数据 const listData = Array.from({ length: 20 }, (_, idx) => ({ id: idx.toString(), label: `列表项 ${idx + 1}` })); return ( <View style={styles.container}> <View style={styles.header} /> <FlatList data={listData} renderItem={({ item }) => ( <View style={styles.listItem}> {/* 可自行添加列表项内容 */} </View> )} keyExtractor={item => item.id} onScroll={handleScroll} scrollEventThrottle={16} // 保证滚动事件触发频率 refreshControl={<CustomRefreshControl />} onRefresh={onRefresh} refreshing={refreshing} contentContainerStyle={{ paddingTop: 0 }} /> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1 }, header: { height: 60, backgroundColor: '#ff4444' }, refreshContainer: { height: MAX_REFRESH_HEIGHT, justifyContent: 'center' }, blueDot: { width: BLUE_DOT_WIDTH, height: BLUE_DOT_WIDTH, borderRadius: BLUE_DOT_WIDTH / 2, backgroundColor: '#2196F3', justifyContent: 'center', alignItems: 'center' }, listItem: { height: 60, backgroundColor: '#f0f0f0', marginVertical: 5, marginHorizontal: 10, borderRadius: 8 } }); export default CustomRefreshFlatList;
内容的提问来源于stack exchange,提问作者SAI
相关产品推荐
相关产品推荐

