0

I'm trying to implement carousel using React Native Animated.FlatList, which should scroll automatically. But the problem is that, I cannot control the animation speed of scroll. When .scrollToIndex is called, FlatList items always move with one and the same speed.

Here's my code:

export const Carousel = ({ data }: CarouselProps) => {
  const width = Dimensions.get('window').width - 20;
  const flatListRef = useRef<FlatList>(null);
  const scrollX = useRef(new Animated.Value(0)).current;

  const [currentIndex, setCurrentIndex] = useState<number>(0);

  useEffect(() => {
    const interval = setInterval(() => {
      if (currentIndex === data.length - 1) {
        flatListRef.current?.scrollToIndex({
          index: 0,
          animated: true,
        });

        setCurrentIndex(0);
      } else {
        const nextIndex = Math.floor(currentIndex) + 1;
        flatListRef.current?.scrollToIndex({
          index: nextIndex,
          animated: true,
        });

        setCurrentIndex(nextIndex);
      }
    }, 3000);

    return () => clearInterval(interval);
  }, [currentIndex, data.length]);

  const renderItem = useCallback(
    ({ title }: { title: string }, index: number) => {
      return (
        <MotiView
          key={index}
          style={{ width, borderColor: 'red', borderWidth: 2 }}
          transition={{
            duration: 2000,
            type: 'timing',
          }}
        >
          <Text>{title}</Text>
        </MotiView>
      );
    },
    [currentIndex, width]
  );

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

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

return (
  <View style={{ height: 100 }}>
    <Text>Welcome to Carousel!</Text>
    <Animated.FlatList
      ref={flatListRef}
      data={data}
      keyExtractor={(item) => item.id}
      renderItem={({ item, index }) => renderItem(item, index)}
      horizontal={true}
      onScroll={handleScroll}
      pagingEnabled={true}
      showsHorizontalScrollIndicator={false}
      getItemLayout={getItemLayout}
    />
  </View>
  );
};

export default Carousel;
Arkadi
  • 1,153
  • 1
  • 14
  • 35

1 Answers1

0

The React Native's Animated API does not directly support controlling the speed of animations invoked through methods like scrollToIndex on the FlatList component. However, there's a workaround using the Animated API and scrollToOffset method instead. Here's a basic example:

import React, { useRef, useEffect } from 'react';
import { Animated, View, Dimensions } from 'react-native';

const { width } = Dimensions.get('window');

const items = [/* your data */]; 

const Carousel = () => {
  const scrollX = useRef(new Animated.Value(0)).current;
  const flatListRef = useRef();

  useEffect(() => {
    const interval = setInterval(() => {
      scrollX.addListener(({ value }) => {
        const nextIndex = Math.ceil(value / width);
        
        Animated.timing(scrollX, {
          toValue: nextIndex * width,
          duration: 500, // Duration of the animation
          useNativeDriver: true,
        }).start();
      });
    }, 3000); // Interval for changing items

    return () => {
      clearInterval(interval);
      scrollX.removeAllListeners();
    }
  }, []);

  return (
    <View>
      <Animated.FlatList
        ref={flatListRef}
        horizontal
        pagingEnabled
        data={items}
        keyExtractor={(item, index) => `carousel-item-${index}`}
        renderItem={({ item, index }) => /* Your rendering function */}
        scrollEventThrottle={16}
        onScroll={Animated.event(
          [{ nativeEvent: { contentOffset: { x: scrollX } } }],
          { useNativeDriver: true }
        )}
      />
    </View>
  );
};

This Carousel component automatically scrolls through its items every 3 seconds, and you can adjust the animation speed by modifying the duration parameter of the Animated.timing function.

But remember, this solution only works for a flatlist with items of the same width. For variable width, additional calculations would be needed.

And be sure that the useNativeDriver: true option is only compatible with certain styles, such as opacity and transform, and you must not try to animate non-layout properties when it is enabled.

Finally, please note that addListener function will likely have performance issues, and in the real production app, you might want to consider some debouncing or throttling.

Please replace /* your data / and / Your rendering function */ with your actual data and renderItem function respectively.

themaster
  • 345
  • 6
  • 15