React Native Carousel에서 React Native에서 Carousel을 만듭니다.

조만간 너는 너의 한 종목에 회전목마가 필요할 것이다.그림 목록을 표시하고 싶을 수도 있고, 프로그램의 입문 여행일 수도 있고, 프로그램이 회전할 수 있는 화면이 몇 개 있다고 생각할 수도 있다.당신의 용례가 무엇이든지 간에 본문은 당신에게 도움이 될 수 있습니다.
시작합시다.회전목마의 받침대는 간단한 FlatList 부품이 될 것이다.이유는 간단합니다. - ScrollView 구성 요소를 기반으로 슬라이드를 슬라이딩할 수 있고, VirtualizedList 슬라이드에 이미지나 성능이 높은 UI 요소가 많을 때 최적화할 수 있습니다.
우선 가상 데이터를 만듭니다.우리는 Lorem Picsum를 사용하여 랜덤 이미지를 얻고 회전목마를 위한 슬라이드 30장의 랜덤 데이터를 만들 것이다.
const { width: windowWidth, height: windowHeight } = Dimensions.get("window");

const slideList = Array.from({ length: 30 }).map((_, i) => {
  return {
    id: i,
    image: `https://picsum.photos/1440/2842?random=${i}`,
    title: `This is the title! ${i + 1}`,
    subtitle: `This is the subtitle ${i + 1}!`,
  };
});
각 슬라이드의 무작위 이미지를 얻으려면 쿼리 매개 변수 random=${i} 를 추가해야 합니다.그렇지 않으면 React Native는 첫 번째 이미지를 캐시하고 회전목마의 모든 이미지를 대체합니다.
다음은 평면 목록을 만들고 slideList 아이템에 전달할 것입니다.우리는 또한 data 도구와 style 도구를 전달하여 전체 화면을 덮어씌울 것이다.마지막으로, 우리는 슬라이드의 외관을 정의해야 한다.이것은 flex: 1 아이템으로 완성되었습니다.
우리는 renderItem 구성 요소를 만들고 Slide 함수에서 사용할 것입니다.
function Slide({ data }) {
  return (
    <View
      style={{
        height: windowHeight,
        width: windowWidth,
        justifyContent: "center",
        alignItems: "center",
      }}
    >
      <Image
        source={{ uri: data.image }}
        style={{ width: windowWidth * 0.9, height: windowHeight * 0.9 }}
      ></Image>
      <Text style={{ fontSize: 24 }}>{data.title}</Text>
      <Text style={{ fontSize: 18 }}>{data.subtitle}</Text>
    </View>
  );
}

function Carousel() {
  return (
    <FlatList
      data={slideList}
      style={{ flex: 1 }}
      renderItem={({ item }) => {
        return <Slide data={item} />;
      }}
    />
  );
};

만약 우리가 지금 저장하고 있다면, 우리는 슬라이드를 볼 수 있지만, 스크롤 동작은 우리가 원하는 것이 아니다.스크롤 뷰를 각 슬라이드의 시작 부분으로 캡처해야 합니다.이를 실현하는 가장 간단한 방법은 renderItem 도구를 평면 목록에 추가하는 것이다.
또 다른 일-우리의 회전목마는 현재 수직-위아래로 굴러간다.대부분의 회전목마는 수평이기 때문에 방향을 바꾸자. 그러나 수직 회전목마를 구축하려면 가능하고 몇 번만 변경해야 한다는 것을 기억해라.
그러면 pagingEnabled={true} 도구를 평면 목록에 추가하여 왼쪽과 오른쪽으로 스크롤하고, 스크롤 표시기를 숨기기 위해 horizontal={true} 도구를 추가합니다.
function Carousel() {
  return (
    <FlatList
      data={slideList}
      style={{ flex: 1 }}
      renderItem={({ item }) => {
        return <Slide data={item} />;
      }}
      pagingEnabled
      horizontal
      showsHorizontalScrollIndicator={false}
    />
  );
}

이것은 보기에는 매우 좋지만, 우리가 빠뜨린 중요한 일이 하나 있다.활성 슬라이드의 인덱스가 필요할 수 있습니다.예를 들어, 만약에 우리가 응용 프로그램에 튜토리얼 구축 회전목마를 소개하고 있다면, 사용자가 마지막 슬라이드에 도착했을 때만 사용하거나, 이미지 라이브러리를 구축하고 있다면, 사용자가 얼마나 많은 이미지를 포함하고 있는지 알 수 있도록 페이지 구성 요소를 표시하기를 원할 수도 있습니다.
나는 다음 부분을 최적화하는 데 시간이 좀 걸렸기 때문에 좀 복잡해 보일 수도 있다.하지만 걱정하지 마세요. 제가 모든 것을 설명할게요.
function Carousel() {
  const [index, setIndex] = useState(0);
  const indexRef = useRef(index);
  indexRef.current = index;
  const onScroll = useCallback((event) => {
    const slideSize = event.nativeEvent.layoutMeasurement.width;
    const index = event.nativeEvent.contentOffset.x / slideSize;
    const roundIndex = Math.round(index);

    const distance = Math.abs(roundIndex - index);

    // Prevent one pixel triggering setIndex in the middle
    // of the transition. With this we have to scroll a bit
    // more to trigger the index change.
    const isNoMansLand = 0.4 < distance;

    if (roundIndex !== indexRef.current && !isNoMansLand) {
      setIndex(roundIndex);
    }
  }, []);

  // Use the index
  useEffect(() => {
    console.warn(index);
  }, [index]);

  return (
    <FlatList
      data={slideList}
      style={{ flex: 1 }}
      renderItem={({ item }) => {
        return <Slide data={item} />;
      }}
      pagingEnabled
      horizontal
      showsHorizontalScrollIndicator={false}
      onScroll={onScroll}
    />
  );
}
먼저 showsHorizontalScrollIndicator={false} 정의index - 회전목마의 활성 슬라이드 색인을 나타냅니다.그리고 우리는 useState - 색인 변수와 동기화된 참조 값 - indexRef 이 변경되면 index 의 값도 변경됩니다.
그럼 우리 왜 이러는 거야?답은 다음 줄에 있다.indexRef.current 리셋은 onScrolllayoutMeasurement 값을 사용하여 현재 인덱스를 스크롤하는 거리에 따라 계산합니다.계산된 색인이 바뀔 때마다 우리는 우리의 contentOffset 를 업데이트하기를 희망합니다.
문제는 우리가 indexindex 변수를 사용하여 계산된 인덱스가 현재 인덱스와 다른지 확인하려면 onScrollindex 의존수 그룹에 넣어야 한다는 것이다.이것은 반대로 색인이 바뀔 때마다 useCallback 함수도 바뀐다는 것을 의미하며, 이것이 도구로 FlatList에 전달될 때 목록이 다시 나타난다는 것을 의미한다.
회전목마가 수평이기 때문에 현재 인덱스를 계산하기 위해 onScrolllayoutMeasurement.width 를 사용합니다.만약 그것이 수직이라면, 우리는 높이와 y 편이를 사용해야 한다.
그 다음은 contentOffset.x 변수 뒤의 논리다.두 슬라이드 중간에 회전 목마를 드래그할 때, 이 논리적 방지 슬라이더는 일련의 isNoMansLand 호출을 촉발합니다.다음은 우리가 이 논리를 실현하지 않을 때 발생하는 상황입니다. 두 개의 슬라이드 중간에 있을 때 가장 가벼운 이동은 색인 변화를 촉발합니다.이것은 대량의 재렌더링을 초래할 수 있으므로 피하는 것이 가장 좋다.
솔루션과 관련: Schmitt trigger

지금까지, 우리가 구축한 것은 이미 약간 멋있었다. 당신의 용례에 있어서는 충분할 수도 있지만, 우리의 실현에는 숨겨진 성능 문제가 존재하기 때문에, 당신의 응용 프로그램을 늦추거나 붕괴시킬 수도 있다.이것은 미리 많은 슬라이드를 렌더링하고 이전의 슬라이드를 메모리에 보존했기 때문이다.기본적으로 우리가 목록을 빠르게 스크롤할 때, FlatList는 감지 성능을 향상시키기 위해서이지만, 우리의 경우 성능에 부정적인 영향을 미칠 수 있습니다.
나는 간단한 시각화 프로그램을 만들어서 어떤 슬라이드가 설치되었는지, 어떤 슬라이드가 설치되지 않았는지, 그리고 우리의 현재 색인도 돋보이게 했다.아래쪽의 녹색 점은 설치된 슬라이드, 검은색 점은 마운트 해제된 슬라이드, 빨간색 점은 현재 활성 슬라이드를 나타냅니다.

슬라이드가 이미 10개의 슬라이드를 미리 설치했다는 것을 알 수 있습니다.또한 이전 10장의 슬라이드는 제거되지 않았습니다.이것은 모두 평면 목록의 기본 최적화의 일부분으로 더 긴 목록은 효과가 좋지만 우리의 용례에 있어서는 그렇지 않다.
우리가 약간의 최적화를 실현합시다.우리는 최적화 도구를 하나의 대상에 나누어 평면 목록에 전달할 것이다.
  const flatListOptimizationProps = {
    initialNumToRender: 0,
    maxToRenderPerBatch: 1,
    removeClippedSubviews: true,
    scrollEventThrottle: 16,
    windowSize: 2,
    keyExtractor: useCallback(e => e.id, []);
    getItemLayout: useCallback(
      (_, index) => ({
        index,
        length: windowWidth,
        offset: index * windowWidth,
      }),
      []
    ),
  };

  <FlatList
    data={slideList}
    style={{ flex: 1 }}
    renderItem={({ item }) => {
      return <Slide data={item} />;
    }}
    pagingEnabled
    horizontal
    showsHorizontalScrollIndicator={false}
    onScroll={onScroll}
    {...flatListOptimizationProps}
  />
다음은 이 모든 의미에 대한 해석이다.setIndex - 첫 번째 슬라이드부터 렌더링된 슬라이드의 수를 제어합니다.이것은 목록에서 매우 유용합니다. 프로그래밍을 통해 맨 위로 스크롤할 수 있습니다. 이 경우 이전 슬라이드의 렌더링을 기다리고 싶지 않기 때문에 FlatList는 항상 렌더링을 유지합니다.우리는 이 기능을 필요로 하지 않기 때문에 안전하게 initialNumToRender 여기에 놓을 수 있다.0 - 렌더링된 슬라이드 개수를 조정합니다.많은 요소를 포함하는 평면 목록이 있고 사용자가 평면 목록에 데이터가 로드되지 않은 영역으로 빠르게 스크롤할 수 있을 때도 유용합니다.maxToRenderPerBatch - 평면 목록 뷰포트 이외의 뷰가 삭제됩니다.안드로이드는 기본적으로true로 설정됩니다. iOS에서도 이렇게 설정하는 것을 권장합니다.메모리에서 구성 요소를 삭제하고 자원을 절약할 수 있습니다.removeClippedSubviews - 회전 목마를 드래그할 때 발생하는 스크롤 이벤트를 제어합니다.16으로 설정하면 이벤트가 16ms마다 촉발됩니다.우리는 이것을 더 높은 숫자로 설정할 수 있을지도 모르지만, 16은 좋은 것 같다.Image - 이것은 앞에 설치된 슬라이드의 수와 현재 색인 뒤에 설치된 슬라이드의 수를 제어합니다.
이것은 프로젝트를 렌더링하는 데 VirtualizedList의 창 폭을 제어합니다. 창 안의 모든 내용은 렌더링되고 창 밖의 모든 내용은 공백입니다.예를 들어, 만약 우리가 이 속성을 2로 설정한다면, 창은 평면 목록의 너비의 두 배가 될 것이다.다음 시각화의 분홍색 선은 창을 표시합니다.

이 회전목마의 예시에서 값 2는 매우 유용하지만, 마음에 드시면 사용해 보십시오.scrollEventThrottle - React를 내부 최적화에 사용합니다.이렇게 하지 않으면 슬라이드 추가 및 삭제가 중단될 수 있습니다.그 밖에 경고를 삭제했습니다. 좋습니다.windowSize - 항목의 크기(높이 또는 너비)를 미리 알고 있으면 동적 내용의 측정을 건너뛸 수 있는 선택적 최적화입니다.우리의 예에서 항목의 폭은 시종 keyExtractor 이다.회전 목마를 수직으로 하려면 getItemLayout 를 사용해야 합니다.
마지막으로, 우리는 스타일을 구성 요소 정의 밖으로 옮기고, windowWidth 함수를 windowHeight 로 포장하여 평면 목록을 불필요하게 다시 렌더링하지 않도록 할 수 있다.
회전목마를 더욱 최적화하기 위해 우리가 할 수 있는 또 다른 일은 슬라이드 요소를 renderItem에 봉인하는 것이다.
그렇습니다!나는 페이지 구성 요소를 추가하고 스타일을 약간 조정했다. 다음은 최종 제품의 외관이다.

직접 해보세요: https://snack.expo.io/@hrastnik/carousel

좋은 웹페이지 즐겨찾기