繁体   English   中英

如何在不使用任何 npm 包的情况下在本机反应中制作自定义轮播或图像幻灯片

[英]how can I make a custom carousel or image slide in react native without using any npm packages

我在不使用任何包的情况下在本机反应中制作自定义图像轮播或图像滑块,问题是底部的点指示器在 3 秒间隔后变化良好,但图像不会自动滑动。 有一些我找不到的小问题。 如果您可以在不更改大部分代码的情况下帮助解决此问题,那就太好了。 提前致谢。

这是完整的组件代码。

import React, {useEffect, useState, useRef} from 'react';
import {StyleSheet, ScrollView, View, Dimensions, Text} from 'react-native';
import {ActivityIndicator} from 'react-native';
import {Image} from 'react-native-elements';

const HomeCarousel = () => {
  const [dimension, setDimension] = useState(Dimensions.get('window'));
  const [selectedIndex, setSelectedIndex] = useState(0);

  const scrollRef = useRef();

  const onChange = () => {
    setDimension(Dimensions.get('window'));
  };

  useEffect(() => {
    Dimensions.addEventListener('change', onChange);
    return () => {
      Dimensions.removeEventListener('change', onChange);
    };
  });

  useEffect(() => {
    setInterval(() => {
      setSelectedIndex(prevSelectedIndex =>
        prevSelectedIndex === carouselImages.length - 1
          ? 0
          : prevSelectedIndex + 1,
      );
      () => {
        scrollRef.current.scrollTo({
          animated: true,
          y: 0,
          x: dimension.width * selectedIndex,
        });
      };
    }, 3000);
  }, []);

  const carouselImages = [
    {url: 'https://i.ibb.co/FDwNR9d/img1.jpg'},
    {url: 'https://i.ibb.co/7G5qqGY/1.jpg'},
    {url: 'https://i.ibb.co/Jx7xqf4/pexels-august-de-richelieu-4427816.jpg'},
    {url: 'https://i.ibb.co/GV08J9f/pexels-pixabay-267202.jpg'},
    {url: 'https://i.ibb.co/sK92ZhC/pexels-karolina-grabowska-4210860.jpg'},
  ];

  const setIndex = event => {
    let viewSize = event.nativeEvent.layoutMeasurement.width;
    let contentOffset = event.nativeEvent.contentOffset.x;
    let carouselIndex = Math.floor(contentOffset / viewSize);
    setSelectedIndex(carouselIndex);
  };

  return (
    <View style={{width: dimension.width}}>
      <ScrollView
        horizontal
        ref={scrollRef}
        onMomentumScrollEnd={setIndex}
        showsHorizontalScrollIndicator={false}
        pagingEnabled>
        {carouselImages.map((value, key) => (
          <Image
            source={{uri: `${value.url}`}}
            style={{width: dimension?.width, height: 250, resizeMode: 'cover'}}
            PlaceholderContent={<ActivityIndicator />}
          />
        ))}
      </ScrollView>
      <View
        style={{
          flexDirection: 'row',
          position: 'absolute',
          bottom: 0,
          alignSelf: 'center',
        }}>
        {carouselImages.map((val, key) => (
          <Text
            key={key}
            style={key === selectedIndex ? {color: 'white'} : {color: '#888'}}>
            ⬤
          </Text>
        ))}
      </View>
    </View>
  );
};

const styles = StyleSheet.create({});

export default HomeCarousel;

由于两件事,您的图像没有更新:

  1. 您正在将更新设置在未被调用的匿名 function 中:
// You're just defining the function here and not actually calling it
() => {
  scrollRef.current.scrollTo({
    animated: true,
    y: 0,
    x: dimension.width * selectedIndex,
  });
};
  1. 您的useEffect没有任何依赖关系,这意味着它的回调不会改变,因此其中的selectedIndex将始终为 0:
useEffect(() => {
  setInterval(() => {
    setSelectedIndex(prevSelectedIndex =>
      prevSelectedIndex === carouselImages.length - 1
        ? 0
        : prevSelectedIndex + 1,
    );
    () => {
      scrollRef.current.scrollTo({
        animated: true,
        y: 0,
        // Since there are no dependencies, this callback won't ever be changed and selectedIndex will always be 0
        x: dimension.width * selectedIndex,
      });
    };
  }, 3000);
  // You have no dependencies here
}, []);

我对改进/修复的建议是:

  1. 提取 function 以从useEffect回调中更新幻灯片并应用带有selectedIndex依赖项的useCallback挂钩。 您还应该在setSelectedIndex挂钩之外计算新索引的值,因为setState/useState不会立即更新
// You should also use an intervalId so that you can clear the interval/reset when needed
let intervalId = null;

const HomeCarousel = () => {
  // ... rest of the HomeCarousel component

  const onSlideChange = useCallback(() => {
    // Calculate newIndex here and use it to update your state and to scroll to the new slide
    const newIndex = selectedIndex === carouselImages.length - 1 ? 0 : selectedIndex + 1;

    setSelectedIndex(newIndex);

    scrollRef?.current?.scrollTo({
      animated: true,
      y: 0,
      x: dimension.width * newIndex,
    });
  }, [selectedIndex]);

  const startInterval = useCallback(() => {
    intervalId = setInterval(onSlideChange, 3000);
  }, [onSlideChange]);

  useEffect(() => {
    startInterval();

    return () => {
      // Clear the interval when component unmounts, otherwise you could have memory leaks
      clearInterval(intervalId);
    };
  }, [onSlideChange]);  

  // ... rest of the HomeCarousel component
};
  1. 我建议您完成此轮播的另一件事是在用户开始滑动时清除间隔/停止自动滑动,否则会导致奇怪的行为和幻灯片失控。 用户完成滑动操作后,您可以重新启动自动滑动:
const onTouchStart = () => {
  // As soon as the user touches the slide, stop the automatic sliding
  clearInterval(intervalId);
};

const onTouchEnd = () => {
  // As soon as the user stops touching the slide, releases it, start the automatic sliding again
  startInterval();
};

return (
  <ScrollView
    // ... Other scrollview props
    onTouchStart={onTouchStart}
    onTouchEnd={onTouchEnd}>
    {/* ... Rest of the component */}

 const carouselImages = [ carousel1, carousel2, carousel3, carousel4, carousel5 ]; const Carousel = () => { const [current, setCurrent] = useState(0); useEffect(() => { const next = (current + 1) % carouselImages.length; const id = setTimeout(() => setCurrent(next), 3000); return () => clearTimeout(id); }, [current]); return ( <div className="banner"> <div className="slider"> <img src={carouselImages[current]} alt="carousel" /> </div> <div className="overlay"></div> </div> );

这是完整的工作代码。

import React, {useEffect, useState, useRef, useCallback} from 'react';
import {StyleSheet, ScrollView, View, Dimensions, Text} from 'react-native';
import {ActivityIndicator} from 'react-native';
import {Image} from 'react-native-elements';

const HomeCarousel = () => {
  const [dimension, setDimension] = useState(Dimensions.get('window'));
  const [selectedIndex, setSelectedIndex] = useState(0);

  const scrollRef = useRef();
  let intervalId = null;

  const onChange = () => {
    setDimension(Dimensions.get('window'));
  };

  useEffect(() => {
    Dimensions.addEventListener('change', onChange);
    return () => {
      Dimensions.removeEventListener('change', onChange);
    };
  });

  const onSlideChange = useCallback(() => {
    // Calculate newIndex here and use it to update your state and to scroll to the new slide
    const newIndex =
      selectedIndex === carouselImages.length - 1 ? 0 : selectedIndex + 1;

    setSelectedIndex(newIndex);

    scrollRef?.current?.scrollTo({
      animated: true,
      y: 0,
      x: dimension.width * newIndex,
    });
  }, [selectedIndex]);

  const startInterval = useCallback(() => {
    intervalId = setInterval(onSlideChange, 3000);
  }, [onSlideChange]);

  useEffect(() => {
    startInterval();

    return () => {
      // Clear the interval when component unmounts, otherwise you could have memory leaks
      clearInterval(intervalId);
    };
  }, [onSlideChange]);

  const onTouchStart = () => {
    // As soon as the user touches the slide, stop the automatic sliding
    clearInterval(intervalId);
  };

  const onTouchEnd = () => {
    // As soon as the user stops touching the slide, releases it, start the automatic sliding again
    startInterval();
  };

  const carouselImages = [
    {url: 'https://i.ibb.co/FDwNR9d/img1.jpg'},
    {url: 'https://i.ibb.co/7G5qqGY/1.jpg'},
    {url: 'https://i.ibb.co/Jx7xqf4/pexels-august-de-richelieu-4427816.jpg'},
    {url: 'https://i.ibb.co/GV08J9f/pexels-pixabay-267202.jpg'},
    {url: 'https://i.ibb.co/sK92ZhC/pexels-karolina-grabowska-4210860.jpg'},
  ];

  const setIndex = event => {
    let viewSize = event.nativeEvent.layoutMeasurement.width;
    let contentOffset = event.nativeEvent.contentOffset.x;
    let carouselIndex = Math.floor(contentOffset / viewSize);
    setSelectedIndex(carouselIndex);
  };

  return (
    <View style={{width: dimension.width}}>
      <ScrollView
        horizontal
        ref={scrollRef}
        onMomentumScrollEnd={setIndex}
        showsHorizontalScrollIndicator={false}
        onTouchStart={onTouchStart}
        onTouchEnd={onTouchEnd}
        pagingEnabled>
        {carouselImages.map((value, key) => (
          <Image
            source={{uri: `${value.url}`}}
            style={{width: dimension?.width, height: 250, resizeMode: 'cover'}}
            PlaceholderContent={<ActivityIndicator />}
          />
        ))}
      </ScrollView>
      <View
        style={{
          flexDirection: 'row',
          position: 'absolute',
          bottom: 0,
          alignSelf: 'center',
        }}>
        {carouselImages.map((val, key) => (
          <Text
            key={key}
            style={key === selectedIndex ? {color: 'white'} : {color: '#888'}}>
            ⬤
          </Text>
        ))}
      </View>
    </View>
  );
};

const styles = StyleSheet.create({});

export default HomeCarousel;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM