代码之家  ›  专栏  ›  技术社区  ›  selcukctn

如何在react native中制作无限动画?

  •  0
  • selcukctn  · 技术社区  · 5 月前

    我的卡片从右到左,动画效果很好。但是当动画倒退时,卡的位置会倒退,并且没有无限的动画图像。我想要的是,当最后一张牌离开屏幕并出现无穷大时,第一张牌再次进入。我该怎么做?正如你在gif中看到的,动画结束了,我希望最后一个元素从屏幕左侧退出,第一个元素再次进入。

    enter image description here

    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;
    
        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 startAnimation = () => {
                translateX.setValue(width); // Animasyon başlangıç pozisyonu (sağ dışı)
                Animated.loop(
                    Animated.sequence([
                        Animated.timing(translateX, {
                            toValue: -width * cardData.length, // Sol dışına çıkış
                            duration: 17000, // Hareket süresi
                            useNativeDriver: true,
                        }),
                    ]),
                ).start();
            };
    
            startAnimation();
        }, [translateX, width]);
    
        return (
            <View style={styles.container}>
                <Animated.View
                    style={{
                        flexDirection: 'row',
                        transform: [{ translateX }],
                    }}
                >
                    <View style={{ width: width * 3 }}/>
                    {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;
    
    1 回复  |  直到 5 月前
        1
  •  1
  •   Sowmo0509    5 月前

    这是修改后的代码。

    为了达到你的效果,我必须做的改变:

    1. 将卡片向左移动
    2. 立即重置位置以开始
    3. 重新启动动画
    4. 在useEffect清理函数中使用stopAnimation()添加了适当的清理
    5. 添加了一个带有溢出的clipContainer:“隐藏”以防止可见的跳跃
    6. 调整了卡片宽度计算以考虑利润
    7. 使用两套卡片(原件和复印件)实现无缝过渡
    8. 为两组卡添加了唯一密钥,以防止React警告

    请确保您重新添加了图像/图标,因为我没有该文件,我删除了图标/图像代码和CSS进行调试。

    import React, { useEffect, useRef } from 'react';
    import { Animated, StyleSheet, Text, View, Dimensions } from 'react-native';
    
    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(0)).current;
      const { width } = Dimensions.get('window');
    
      const Cards = ({ item }) => {
        return (
          <View style={styles.cards_con}>
            <Text style={styles.card_title}>{item.title}</Text>
          </View>
        );
      };
    
      useEffect(() => {
        const cardWidth = 220; // Width of card + horizontal margin
        const contentWidth = cardWidth * cardData.length;
    
        const startAnimation = () => {
          translateX.setValue(0); // Reset to start position
          
          Animated.sequence([
            Animated.timing(translateX, {
              toValue: -contentWidth,
              duration: 17000,
              useNativeDriver: true,
            }),
            // Reset the position instantly
            Animated.timing(translateX, {
              toValue: 0,
              duration: 0,
              useNativeDriver: true,
            })
          ]).start(() => {
            // Restart the animation when complete
            startAnimation();
          });
        };
    
        startAnimation();
    
        return () => {
          // Cleanup animation when component unmounts
          translateX.stopAnimation();
        };
      }, []);
    
      return (
        <View style={styles.container}>
          <View style={styles.clipContainer}>
            <Animated.View
              style={{
                flexDirection: 'row',
                transform: [{ translateX }],
              }}
            >
              {/* Original set of cards */}
              {cardData.map((item, index) => (
                <Cards key={`original-${index}`} item={item} />
              ))}
              {/* Duplicate set for seamless transition */}
              {cardData.map((item, index) => (
                <Cards key={`duplicate-${index}`} item={item} />
              ))}
            </Animated.View>
          </View>
        </View>
      );
    };
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        justifyContent: "center",
        alignItems: "center",
        backgroundColor: "#000",
      },
      clipContainer: {
        overflow: 'hidden',
        width: '100%',
      },
      cards_con: {
        flexDirection: "row",
        alignItems: "center",
        justifyContent: "center",
        backgroundColor: "#323232",
        height: 50,
        borderRadius: 10,
        marginHorizontal: 10,
        padding: 10,
        width: 200,
      },
      card_title: {
        color: "#FFFFFF",
        fontSize: 16,
      },
    });
    
    export default App;