我有一个奇怪的行为,我制作的Slider有时会返回到索引0(第一张幻灯片),但大多数时候它会返回到索引1.我做了一个沙箱,以防你需要它,但我不知道如何在沙箱中使expo 工作.

HERE是Sandbox

以下是该视图的代码

import React, { useEffect, useRef, useState } from "react";
import { StyleSheet, SafeAreaView, Animated, FlatList, View, Text, Image, Dimensions, Pressable } from "react-native";

const data = [
  {
    id: 1,
    text: "Making Testing Easier",
    img: "https://e1.pxfuel.com/desktop-wallpaper/85/884/desktop-wallpaper-beautiful-judo-throws-judoka-thumbnail.jpg",
  },
  {
    id: 2,
    text: "Instructional Videos",
    img: "https://i.pinimg.com/originals/2b/84/c9/2b84c904ec3373b90f4fcad956233211.jpg",
  },
  {
    id: 3,
    text: "Newaza Fully Expanded",
    img: "https://i.pinimg.com/474x/d4/55/8c/d4558cf325344900b9eead6cb74dab20.jpg",
  },
];

const { width } = Dimensions.get("screen");

const CustomButton = ({ onPress, title, btnstyle, textstyle }) => {
  return (
    <Pressable style={btnstyle} onPress={onPress}>
      <Text style={textstyle}>{title}</Text>
    </Pressable>
  );
};

const SlideItem = ({ item }) => {
  return (
    <View style={styles.slideitemcontainer}>
      <View style={styles.overlay}>
        <Image source={item.img} resizeMode="cover" style={styles.image} />
      </View>
      <View style={styles.txtcontent}>
        <Text style={styles.title}>{item.text}</Text>
      </View>
      <View style={styles.btncontent}>
        <CustomButton
          title="Get Started"
          btnstyle={styles.btnPrimary}
          textstyle={styles.btnPrimaryText}
        />
        <CustomButton
          title="Login"
          btnstyle={styles.btnSecondary}
          textstyle={styles.btnSecondaryText}
        />
      </View>
    </View>
  );
};

const Pagination = ({ data, scrollX }) => {
  return (
    <View style={styles.paginationcontainer}>
      {data.map((_, idx) => {
        const inputRange = [(idx - 1) * width, idx * width, (idx + 1) * width];

        const dotWidth = scrollX.interpolate({
          inputRange,
          outputRange: [12, 30, 12],
          extrapolate: "clamp",
        });

        const backgroundColor = scrollX.interpolate({
          inputRange,
          outputRange: [
            "rgba(255, 255, 255, .3)",
            "rgba(204, 204, 204, .7)",
            "rgba(255, 255, 255, .3)",
          ],
          extrapolate: "clamp",
        });
        return (
          <Animated.View
            key={idx.toString()}
            style={[styles.dot, { width: dotWidth, backgroundColor }]}
          />
        );
      })}
    </View>
  );
};

const App = () => {
  const [index, setIndex] = useState(0);
  const scrollX = useRef(new Animated.Value(0)).current;
  const flatlistRef = useRef(null);

  const handleScroll = (event) => {
    Animated.event(
      [
        {
          nativeEvent: {
            contentOffset: {
              x: scrollX,
            },
          },
        },
      ],
      {
        useNativeDriver: false,
      },
    )(event);
  };

  const handleOnViewableItemsChanged = useRef(({ viewableItems }) => {
    setIndex(viewableItems[0].index);
  }).current;

  const viewabilityConfig = useRef({
    itemVisiblePercentThreshold: 50,
  }).current;

  useEffect(() => {
    flatlistRef?.current?.scrollToIndex({
      behavior: "smooth",
      animated: true,
      index: index,
    });
  }, [index]);

  useEffect(() => {
    const timer = setTimeout(() => {
      const nextIndex = index === data.length - 1 ? 0 : index + 1;
      setIndex(nextIndex);
    }, 2000);
    return () => clearTimeout(timer);
  }, [index]);

  const getItemLayout = (data, index) => ({
    length: width,
    offset: width * index,
    index,
  });

  return (
    <SafeAreaView style={styles.container}>
      <FlatList
        data={data}
        ref={flatlistRef}
        getItemLayout={getItemLayout}
        renderItem={({ item }) => <SlideItem item={item} />}
        horizontal
        pagingEnabled
        snapToAlignment="center"
        showsHorizontalScrollIndicator={false}
        onScroll={handleScroll}
        onViewableItemsChanged={handleOnViewableItemsChanged}
        viewabilityConfig={viewabilityConfig}
      />
      <Pagination data={data} scrollX={scrollX} index={index} />
    </SafeAreaView>
  );
};

export default App;

const styles = StyleSheet.create({
  overlay: {
    position: "absolute",
    width: "100%",
    height: "100%",
    flex: 1,
    left: 0,
    top: 0,
    opacity: 0.7,
  },
  slideitemcontainer: {
    width,
    height,
    alignItems: "center",
    backgroundColor: "black",
  },
  image: {
    flex: 1,
    width,
  },
  txtcontent: {
    position: "absolute",
    top: "25%",
    paddingHorizontal: "2%",
  },
  btncontent: {
    position: "absolute",
    bottom: "25%",
    width: "70%",
  },
  title: {
    fontSize: 45,
    fontWeight: "bold",
    color: "#fff",
  },
  btnPrimary: {
    alignItems: "center",
    justifyContent: "center",
    paddingVertical: 8,
    paddingHorizontal: 36,
    borderRadius: 8,
    elevation: 3,
    backgroundColor: "#0079D1",
    borderColor: "#0079D1",
    borderStyle: "solid",
    borderWidth: 2,
    marginBottom: 10,
  },
  btnPrimaryText: {
    fontSize: 13,
    fontWeight: "600",
    color: "white",
    textTransform: "uppercase",
  },
  btnSecondary: {
    alignItems: "center",
    justifyContent: "center",
    paddingVertical: 8,
    paddingHorizontal: 36,
    borderRadius: 8,
    elevation: 3,
    backgroundColor: "white",
    borderColor: "#0079D1",
    borderStyle: "solid",
    borderWidth: 2,
  },
  btnSecondaryText: {
    fontSize: 13,
    fontWeight: "600",
    color: "#0079D1",
    textTransform: "uppercase",
  },
  usjacontainer: {
    width,
    display: "flex",
    alignItems: "center",
    position: "absolute",
    bottom: "13%",
  },
  usja: {
    width: 40,
    height: 40,
    alignItems: "center",
    display: "flex",
    justifyContent: "center",
  },
  paginationcontainer: {
    position: "absolute",
    bottom: 35,
    flexDirection: "row",
    height: 64,
    width: "100%",
    justifyContent: "center",
    alignItems: "center",
  },
  dot: {
    height: 12,
    width: 12,
    borderRadius: 6,
    marginHorizontal: 3,
    backgroundColor: "rgba(255, 255, 255, .4)",
  },
  dotActive: {
    backgroundColor: theme.colors.secondary,
  },
});

更具体地说,useEffect可能是我搞砸的地方

useEffect(() => {
    const timer = setTimeout(() => {
        const nextIndex = index === data.length - 1 ? 0 : index + 1;
        setIndex(nextIndex);
    }, 2000);
    return () => clearTimeout(timer);
}, [index]);

SideNote-有没有办法让动画只向前移动,而不是向后滑动到0?无限大的卷轴总是令人愉快得多.

推荐答案

在我看来,这种情况的发生可能有两个原因:

  • Synchronous Update of index (Race Condition):当调用setIndex(NextIndex)时, 索引的更新不会立即发生.它被安排在 下一次渲染.这意味着在2000毫秒间隔内,其他 您的部分代码可能会与索引交互,可能 导致意外的行为.
  • Dependence on index:每当索引更改时,该效果都会运行.如果 代码中的其他内容更改了索引(如用户交互), 效果将运行,并且可能不会像预期的那样运行.

Suggested Solution

useEffect(() => {
  const timer = setInterval(() => {
    setIndex(prevIndex => (prevIndex === data.length - 1 ? 0 : prevIndex + 1));
  }, 2000);
  return () => clearInterval(timer);
}, []);
  • 使用setInterval而不是setTimeout.这允许该函数 在不重新初始化计时器的情况下每2000毫秒调用一次 每index个变化.
  • 依赖关系数组为空,确保间隔设置一次 而不是每改变index次就重置.
  • 使用功能更新(setIndex(prevIndex => ...))来确保我们 始终拥有正确的先前索引.

无限循环(圆形 carousel )

要创建这种效果,需要考虑三件事:

  1. Duplicating First and Last Items:将最后一项添加到开头 数据数组的第一项和末尾的第一项.这将创建 carousel 无限循环的错觉.
  2. Adjusting the Initial Index:在第一个"真正"处开始 carousel Item(现在位于索引1,因为您已在 开始).
  3. Handling the End of the Scroll:当用户到达 carousel ,重置到没有动画的第二项.同样, 当它们滚动到开头时,重置为倒数第二项.

以下是一个基本的指导方针:

import React, { useEffect, useRef, useState } from "react";
import {
  StyleSheet, SafeAreaView, Animated, FlatList,
  View, Text, Image, Dimensions, Pressable
} from "react-native";

// Original data array
const originalData = [
  {
    id: 1,
    text: "Making Testing Easier",
    img: "https://e1.pxfuel.com/desktop-wallpaper/85/884/desktop-wallpaper-beautiful-judo-throws-judoka-thumbnail.jpg",
  },
  {
    id: 2,
    text: "Instructional Videos",
    img: "https://i.pinimg.com/originals/2b/84/c9/2b84c904ec3373b90f4fcad956233211.jpg",
  },
  {
    id: 3,
    text: "Newaza Fully Expanded",
    img: "https://i.pinimg.com/474x/d4/55/8c/d4558cf325344900b9eead6cb74dab20.jpg",
  },
];

// Modify the data array for infinite loop
const loopedData = [originalData[originalData.length - 1], ...originalData, originalData[0]];

const { width } = Dimensions.get("screen");

// ... Rest of your components (CustomButton, SlideItem, Pagination)

const App = () => {
  const [index, setIndex] = useState(1); // Start from the second item (first real item)
  const scrollX = useRef(new Animated.Value(0)).current;
  const flatlistRef = useRef(null);

  // Automatic index change
  useEffect(() => {
    const timer = setInterval(() => {
      setIndex(prevIndex => (prevIndex === loopedData.length - 1 ? 1 : prevIndex + 1));
    }, 2000);
    return () => clearInterval(timer);
  }, []);

  // Handle index change for looping
  useEffect(() => {
    if (index === 0 || index === loopedData.length - 1) {
      // No animation when resetting the index
      flatlistRef.current.scrollToIndex({ index: index === 0 ? loopedData.length - 2 : 1, animated: false });
    } else {
      flatlistRef.current.scrollToIndex({ index: index, animated: true });
    }
  }, [index]);

  // ... Rest of your code

  return (
    <SafeAreaView style={styles.container}>
      <FlatList
        data={loopedData}
        ref={flatlistRef}
        keyExtractor={(item, idx) => `slide-${idx}`}
        renderItem={({ item }) => <SlideItem item={item} />}
        horizontal
        pagingEnabled
        snapToAlignment="center"
        showsHorizontalScrollIndicator={false}
        onScroll={Animated.event(
          [{ nativeEvent: { contentOffset: { x: scrollX } } }],
          { useNativeDriver: false }
        )}
        onViewableItemsChanged={({ viewableItems }) => {
          if (viewableItems.length > 0) {
            setIndex(viewableItems[0].index);
          }
        }}
        viewabilityConfig={{ itemVisiblePercentThreshold: 50 }}
        initialScrollIndex={1}
        getItemLayout={(data, index) => ({
          length: width,
          offset: width * index,
          index,
        })}
      />
      <Pagination data={loopedData} scrollX={scrollX} />
    </SafeAreaView>
  );
};

export default App;

// ... Styles

Javascript相关问答推荐

为什么从liveWire info js代码传递数组我出现错误?

如何在使用fast-xml-parser构建ML时包括属性值?

我试图实现用户验证的reduxstore 和操作中出了什么问题?

MongoDB中的引用

如何找出摆线表面上y与x相交的地方?

使搜索栏更改语言

自定义高图中的x轴标签序列

分层树视图

从Node JS将对象数组中的数据插入Postgres表

对网格项目进行垂直排序不起作用

如何在使用rhandsontable生成表时扩展数字输入验证?

禁用.js文件扩展名并从目录导入隐式根index.js时,找不到NodeJS导入模块

如何在 Select 文本时停止Click事件?

向数组中的对象添加键而不改变原始变量

为什么我的按钮没有从&q;1更改为&q;X&q;?

在Odoo中如何以编程方式在POS中添加产品

P5JS-绘制不重叠的圆

如何在Reaction中设置缺省值, Select 下拉列表,动态追加剩余值?

如何在css中裁剪成一定Angular 的圆的一部分,而不需要复杂的多边形

JavaScript structuredClone在Chrome/Edge中获得了非法调用,但在NodeJS中没有