DevGang
Авторизоваться

Как создать карусель с автоматической прокруткой, бесконечным циклом, разбивкой на страницы в React Native

Создание собственной карусели в React Native — это отличный способ сделать ваше приложение более привлекательным и интерактивным. В этом блоге мы подробно разберем, как создать карусель с функцией автоматической прокрутки, используя анимированные и реанимированные библиотеки React Native. Мы также добавим систему разбивки на страницы с анимированными точками и элегантным эффектом перехода изображений.

В этом руководстве мы рассмотрим все этапы создания карусели: от настройки пользовательского компонента до реализации плавной анимации и интерполяции с помощью Reanimated, а также добавления функции автоматической прокрутки, которая приостанавливается при взаимодействии пользователя. Мы также создадим систему разбивки на страницы с анимированными точечными индикаторами, которые будут обновляться в зависимости от текущего видимого элемента. В результате у вас будет горизонтальная карусель с анимированными переходами, автоматической прокруткой и точками разбивки на страницы, которые обеспечивают комфортный просмотр контента.

Настройка компонента Carousel (Карусель)

Сначала мы создадим компонент CustomCarousel, который будет содержать основную логику нашей карусели. Основные элементы включают в себя:

  1. Animated.FlatList для отображения элементов.
  2. Механизм автоматической прокрутки с использованием setInterval.
  3. scrollTo и useSharedValue из Reanimated для анимации переходов.
/* eslint-disable react-native/no-inline-styles */
import React, { useEffect, useRef, useState } from 'react';
import { StyleSheet, View, useWindowDimensions } from 'react-native';
import Animated, {
  scrollTo,
  useAnimatedRef,
  useAnimatedScrollHandler,
  useDerivedValue,
  useSharedValue,
} from 'react-native-reanimated';
import { hpx } from '../../helpers';
import Pagination from './Pagination';
import RenderItem from './RenderItem';
import { animals } from './constants';

const CustomCarousel = () => {
  const x = useSharedValue(0);
  const [data, setData] = useState(animals);
  const { width } = useWindowDimensions();
  const [currentIndex, setCurrentIndex] = useState(0);
  const [paginationIndex, setPaginationIndex] = useState(0);
  const ref = useAnimatedRef();
  const [isAutoPlay, setIsAutoPlay] = useState(true);
  const interval = useRef();
  const offset = useSharedValue(0);

  console.log('CURRENT_CAROUSEL_ITEM👉', paginationIndex);

  const onViewableItemsChanged = ({ viewableItems }) => {
    if (viewableItems[0].index !== undefined && viewableItems[0].index !== null) {
      setCurrentIndex(viewableItems[0].index);
      setPaginationIndex(viewableItems[0].index % animals.length);
    }
  };

  const viewabilityConfig = {
    itemVisiblePercentThreshold: 50,
  };

  const viewabilityConfigCallbackPairs = useRef([{ viewabilityConfig, onViewableItemsChanged }]);

  const onScroll = useAnimatedScrollHandler({
    onScroll: (e) => {
      x.value = e.contentOffset.x;
    },
    onMomentumEnd: (e) => {
      offset.value = e.contentOffset.x;
    },
  });

  useDerivedValue(() => {
    scrollTo(ref, offset.value, 0, true);
  });

  useEffect(() => {
    if (isAutoPlay === true) {
      interval.current = setInterval(() => {
        offset.value += width;
      }, 4000);
    } else {
      clearInterval(interval.current);
    }
    return () => {
      clearInterval(interval.current);
    };
  }, [isAutoPlay, offset, width]);

  return (
    <View style={styles.container}>
      <Animated.FlatList
        ref={ref}
        style={{ height: hpx(194), flexGrow: 0 }}
        onScrollBeginDrag={() => {
          setIsAutoPlay(false);
        }}
        onScrollEndDrag={() => {
          setIsAutoPlay(true);
        }}
        onScroll={onScroll}
        scrollEventThrottle={16}
        horizontal
        bounces={false}
        pagingEnabled
        showsHorizontalScrollIndicator={false}
        viewabilityConfigCallbackPairs={viewabilityConfigCallbackPairs.current}
        onEndReached={() => setData([...data, ...animals])}
        onEndReachedThreshold={0.5}
        data={data}
        keyExtractor={(_, index) => `list_item${index}`}
        renderItem={({ item, index }) => {
          return <RenderItem item={item} index={index} x={x} />;
        }}
      />
      <Pagination paginationIndex={paginationIndex} />
    </View>
  );
};

export default CustomCarousel;

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  buttonContainer: {
    justifyContent: 'center',
    alignItems: 'center',
    flexDirection: 'row',
    gap: 14,
  },
});

Компонент Pagination

Компонент разбивки на страницы визуализирует точки, которые соответствуют слайдам карусели. Активная точка, соответствующая текущему слайду, выделяется более насыщенным цветом, в то время как остальные точки становятся полупрозрачными.

import React from 'react';
import { StyleSheet, View } from 'react-native';
import { hpx } from '../../helpers';
import Dot from './Dot';
import { animals } from './constants';

const Pagination = ({ paginationIndex }) => {
  return (
    <View style={styles.container}>
      {animals.map((_, index) => {
        return <Dot index={index} key={index} paginationIndex={paginationIndex} />;
      })}
    </View>
  );
};

export default Pagination;

const styles = StyleSheet.create({
  container: {
    flexDirection: 'row',
    marginTop: hpx(16),
    justifyContent: 'center',
    alignItems: 'center',
  },
});

Компонент Dot

Компонент Dot отвечает за внешний вид каждой точки в системе разбивки на страницы. Он изменяет свой стиль в зависимости от того, активна ли точка (соответствует текущему индексу) или нет.

import React from 'react';
import { StyleSheet, View } from 'react-native';
import { Colors } from '../../assets';
import { hpx, wpx } from '../../helpers';

const Dot = ({ index, paginationIndex }) => {
  return <View style={paginationIndex === index ? styles.dot : styles.dotOpacity} />;
};

export default Dot;

const styles = StyleSheet.create({
  dot: {
    backgroundColor: Colors.white,
    height: hpx(3),
    width: wpx(12),
    marginHorizontal: 2,
    borderRadius: 8,
  },
  dotOpacity: {
    backgroundColor: Colors.white,
    height: hpx(3),
    width: wpx(12),
    marginHorizontal: 2,
    borderRadius: 8,
    opacity: 0.5,
  },
});

Компонент RenderItem

Компонент RenderItem отвечает за отображение каждого элемента карусели. Он применяет функцию interpolate из библиотеки Reanimated для плавной анимации прозрачности элементов во время прокрутки.

import React from 'react';
import { StyleSheet, useWindowDimensions, View } from 'react-native';
import Animated, { Extrapolation, interpolate, useAnimatedStyle } from 'react-native-reanimated';
import { hpx, nf, SCREEN_WIDTH, wpx } from '../../helpers/Scale';

const RenderItem = ({ item, index, x }) => {
  const { width } = useWindowDimensions();

  const animatedStyle = useAnimatedStyle(() => {
    const opacityAnim = interpolate(
      x.value,
      [(index - 1) * width, index * width, (index + 1) * width],
      [-0.3, 1, -0.3],
      Extrapolation.CLAMP
    );
    return {
      opacity: opacityAnim,
    };
  });

  return (
    <View style={{ width }}>
      <Animated.Image
        resizeMode="cover"
        source={{ uri: item.image }}
        style={[styles.titleImage, animatedStyle]}
      />
    </View>
  );
};

export default RenderItem;

const styles = StyleSheet.create({
  titleImage: {
    width: SCREEN_WIDTH - wpx(32), // adjust the width of the image and horizontal padding
    height: hpx(194),
    alignSelf: 'center',
    borderRadius: nf(16),
  },
});

Автоматическая прокрутка

Автоматическая прокрутка карусели реализована с помощью таймера setInterval. Эта функция обеспечивает плавное перемещение карусели с одного слайда на другой каждые 4 секунды. При взаимодействии пользователя с каруселью, например, путем перетаскивания, автоматическая прокрутка временно приостанавливается.

Файл Constant

export const animals = [
  { text: 'cat', image: 'https://i.imgur.com/CzXTtJV.jpg' },
  { text: 'dog', image: 'https://i.imgur.com/OB0y6MR.jpg' },
  { text: 'cheetah', image: 'https://farm2.staticflickr.com/1533/26541536141_41abe98db3_z_d.jpg' },
  { text: 'piano', image: 'https://farm4.staticflickr.com/3224/3081748027_0ee3d59fea_z_d.jpg' },
  // { text: 'apple', image: 'https://farm8.staticflickr.com/7377/9359257263_81b080a039_z_d.jpg' },
  // { text: 'flower', image: 'https://farm9.staticflickr.com/8295/8007075227_dc958c1fe6_z_d.jpg' },
  // { text: 'mushroom', image: 'https://farm2.staticflickr.com/1449/24800673529_64272a66ec_z_d.jpg' },
  // { text: 'coffee', image: 'https://farm4.staticflickr.com/3752/9684880330_9b4698f7cb_z_d.jpg' },
];

Вывод

Мы добавили систему разбивки на страницы с анимированными точками, функцию автоматической прокрутки и позаботились о том, чтобы взаимодействие пользователя приостанавливало и возобновляло автопрокрутку.

С помощью этих компонентов вы можете расширить карусель, добавив другие функции, такие как динамическое содержимое, кликабельные элементы и более сложную анимацию. Гибкость React Native с Reanimated позволяет создавать высоконастраиваемые карусели с минимальными затратами на производительность, что идеально подходит для создания визуально привлекательных мобильных приложений.

Не стесняйтесь попробовать это в своем проекте и настроить стили и поведение в соответствии с вашими дизайнерскими потребностями!

Источник:

#JavaScript #ReactNative
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу