React Native中如何实现卡片的无缝无限横向滚动动画?
React Native中如何实现卡片的无缝无限横向滚动动画?
从你描述的动图效果能看到,现在的卡片动画是从右到左滚动,但结束后会直接跳回初始位置,没法实现无缝的无限循环——也就是希望最后一张卡片从左侧滚出屏幕时,第一张卡片立刻从右侧进来,一直重复这个过程对吧?别担心,咱们只需要做两个关键调整就能搞定这个问题!
核心思路
要实现无缝无限滚动,关键是让用户察觉不到动画的“重置”动作:
- 首先在滚动容器里放置两组完全相同的卡片,这样当第一组卡片滚出屏幕时,第二组刚好接上,视觉上不会有断层;
- 然后调整动画逻辑:当第一组卡片完全滚出后,瞬间把动画的初始位置重置,同时重新启动动画——因为两组卡片内容完全一致,用户根本察觉不到这个重置操作,看起来就像是卡片在无限循环滚动。
修改后的完整代码
import React, { useEffect, useRef } from 'react'; import { Animated, StyleSheet, Text, View, Dimensions, Image } from 'react-native'; import anonim_icon from "../img/anonim.png"; const cardData = [ { id: 0, title: "Feeling Happy" }, { id: 1, title: "Feeling Calm" }, { id: 2, title: "Feeling Angry" }, { id: 3, title: "Feeling Sad" }, ]; const App = () => { const translateX = useRef(new Animated.Value(width)).current; // 计算单张卡片的总宽度(卡片本身宽度 + 左右margin) const singleCardTotalWidth = width / 1.7 + 20; // marginHorizontal是10,左右各10 const Cards = ({ item, index }) => { return ( <View style={styles.cards_con} key={index}> <Image style={styles.cards_ico} source={anonim_icon} resizeMode="contain" /> <Text style={styles.card_title}>{item.title}</Text> </View> ); }; useEffect(() => { const startInfiniteAnimation = () => { translateX.setValue(width); // 重置初始位置到屏幕右侧外 Animated.timing(translateX, { // 滚动距离为一组卡片的总宽度,确保第一组完全滚出屏幕 toValue: -singleCardTotalWidth * cardData.length, duration: 17000, useNativeDriver: true, }).start(() => { // 动画结束后立刻重新启动,实现无缝循环 startInfiniteAnimation(); }); }; startInfiniteAnimation(); }, [translateX, width, singleCardTotalWidth]); return ( <View style={styles.container}> <Animated.View style={{ flexDirection: 'row', transform: [{ translateX }], }} > {/* 渲染两组相同的卡片,实现无缝衔接 */} {[...cardData, ...cardData].map((item, index) => ( <Cards key={index} item={item} index={index} /> ))} </Animated.View> </View> ); }; const { width, height } = Dimensions.get('window'); const styles = StyleSheet.create({ container: { flex: 1, justifyContent: "center", alignItems: "center", backgroundColor: "#000", }, cards_con: { flexDirection: "row", alignItems: "center", justifyContent: "space-around", backgroundColor: "#323232", height: height / 13, borderRadius: 10, marginHorizontal: 10, padding: 10, width: width / 1.7, }, cards_ico: { width: 30, height: 30, borderRadius: 10, }, card_title: { color: "#FFFFFF", marginLeft: 10, fontSize: 16, }, }); export default App;
关键修改点说明
- 复制卡片数组:用
[...cardData, ...cardData]渲染两组卡片,这样第一组滚完后第二组刚好跟上,避免出现空白; - 修正滚动距离计算:原来直接用
width * cardData.length不准确,我们计算了单张卡片的实际总宽度(卡片宽度+左右margin),确保第一组卡片能完全滚出屏幕; - 替换循环逻辑:去掉
Animated.loop,改用动画结束后递归调用启动函数的方式,这样在动画结束时瞬间重置位置并重新启动——因为两组卡片内容一致,视觉上完全无缝。
备注:内容来源于stack exchange,提问作者selcukctn
相关产品推荐
相关产品推荐

