Membina Karusel Boleh Disesuaikan dengan Tatal Auto, Gelung Infinite, Penomboran dalam React Native menggunakan Reanimated

Patricia Arquette
Lepaskan: 2024-10-23 06:21:29
asal
257 orang telah melayarinya

Building a Customisable Carousel with Auto-Scroll, Infinite Loop, Pagination in React Native using Reanimated

Mencipta karusel tersuai dalam React Native ialah cara yang bagus untuk menambah bakat visual dan interaktiviti pada aplikasi anda. Dalam blog ini, kami akan meneroka cara membina karusel yang merangkumi fungsi tatal automatik menggunakan pustaka Animated dan Reanimated React Native. Kami juga akan melaksanakan sistem penomboran dengan titik animasi dan kesan peralihan imej yang elegan.

Gambaran keseluruhan

Dalam tutorial ini, kami akan membincangkan perkara berikut:

  • Menyediakan komponen Karusel Tersuai.
  • Menggunakan Beranimasi semula untuk animasi dan interpolasi yang lancar.
  • Fungsi tatal automatik untuk memutar antara imej secara automatik.
  • Membina sistem Penomboran dengan penunjuk titik animasi.

Apa yang Kami Akan Bina:

  • Karusel menatal mendatar dengan peralihan animasi.
  • Tatal automatik yang berhenti seketika apabila pengguna berinteraksi dengan karusel.
  • Titik penomboran yang dikemas kini berdasarkan item yang kelihatan pada masa ini.

Mari mulakan!


1. Menyediakan Komponen Karusel

Kami bermula dengan mencipta komponen CustomCarousel, yang akan menempatkan logik teras karusel kami. Elemen utama termasuk:

  • Animated.FlatList untuk memaparkan item.
  • Mekanisme tatal automatik menggunakan setInterval.
  • Tatal ReanimatedKe dan gunakanSharedValue untuk menganimasikan peralihan.
/* 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,
  },
});
Salin selepas log masuk
Salin selepas log masuk

2. Komponen Penomboran

Komponen penomboran memaparkan titik untuk menunjukkan slaid aktif semasa. Setiap titik berubah dalam kelegapan bergantung pada indeks semasa karusel.

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',
  },
});
Salin selepas log masuk

3. Komponen Titik

Komponen Dot mengendalikan penampilan setiap titik individu dalam sistem penomboran. Ia menukar gayanya berdasarkan sama ada titik itu aktif (indeks semasa) atau tidak.

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,
  },
});
Salin selepas log masuk

4. Komponen RenderItem

Komponen RenderItem memaparkan setiap item karusel. Ia menggunakan fungsi interpolasi Reanimated untuk menghidupkan kelegapan item semasa ia menatal.

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),
  },
});
Salin selepas log masuk

5. Tatal Auto

Ciri tatal automatik dilaksanakan dengan bantuan setInterval. Kaedah ini memastikan bahawa karusel bergerak secara automatik dari satu slaid ke slaid seterusnya setiap 4 saat. Jika pengguna berinteraksi dengan karusel dengan menyeret, tatal automatik akan dijeda.

6. Fail pemalar

/* 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,
  },
});
Salin selepas log masuk
Salin selepas log masuk

7. Kesimpulan

Dalam tutorial ini, kami membina karusel tersuai menggunakan React Native's FlatList, bersama-sama dengan Reanimated untuk animasi dan interpolasi yang lancar. Kami menambahkan sistem penomboran dengan titik animasi, kefungsian tatal automatik dan memastikan interaksi pengguna akan menjeda dan menyambung semula ciri tatal automatik.

Dengan komponen ini, anda boleh memanjangkan karusel untuk menyertakan ciri lain seperti kandungan dinamik, item boleh klik dan animasi yang lebih canggih. Fleksibiliti React Native dengan Reanimated membolehkan karusel yang sangat disesuaikan dengan kos prestasi minimum, yang bagus untuk mencipta apl mudah alih yang menarik secara visual.

Jangan ragu untuk mencuba ini dalam projek anda, dan sesuaikan gaya serta tingkah laku agar sepadan dengan keperluan reka bentuk anda!

Atas ialah kandungan terperinci Membina Karusel Boleh Disesuaikan dengan Tatal Auto, Gelung Infinite, Penomboran dalam React Native menggunakan Reanimated. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:dev.to
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel terbaru oleh pengarang
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan
Tentang kita Penafian Sitemap
Laman web PHP Cina:Latihan PHP dalam talian kebajikan awam,Bantu pelajar PHP berkembang dengan cepat!