Reanimated 라이브러리를 사용한 React Native 사용자 지정 진행률 표시기

안녕하세요 여러분! 이 튜토리얼에서는 react-native-reanimate 라이브러리를 사용하여 고성능 사용자 지정 진행률 표시기를 만드는 방법을 보여드리고자 합니다.

이 자습서의 비디오 버전은 YouTube에서 사용할 수 있습니다.



github 또는 expo snack 에서 다음 템플릿부터 시작하겠습니다.

export const ProgressIndicator: FC<{
  count?: number;
  itemWidth?: number;
  itemHeight?: number;
  duration?: number;
  itemsOffset?: number;
  topScale?: number;
}> = ({
  count = 8,
  itemWidth = 16,
  itemHeight = 4,
  duration = 5000,
  itemsOffset = 4,
  topScale = 4,
}) => {
  return (
    <View
      style={{
        flexDirection: "row",
        justifyContent: "space-between",
        alignItems: "center",
        height: itemHeight * topScale,
        width: (itemWidth + itemsOffset) * count,
      }}
    >
      {[...Array(count)].map((x, index) => (
        <ProgressItem
          key={`progressItem${index}`}
          index={index}
          width={itemWidth}
          height={itemHeight}
          count={count}
          topScale={topScale}
        />
      ))}
    </View>
  );
};

export const ProgressItem: FC<{
  index: number;
  count: number;
  width: number;
  height: number;
  topScale: number;
}> = ({ index, width, height, count, topScale }) => {
  return (
    <View
      style={[
        {
          width,
          height,
          backgroundColor: "black",
        },
      ]}
    />
  );
};


따라서 기본적으로 ProgressIndicator 구성 요소 중 count를 렌더링하는 스타일링 소품이 있는 ProgressItem가 있습니다. ProgressItem는 단순히 검은색 사각형입니다. 최종 결과를 얻기 위해 사각형의 Y축을 순서대로 확장합니다.

먼저 애니메이션 값을 추가하고 react-native-reanimated 라이브러리의 타이밍 애니메이션 함수로 변경해야 합니다.

export const ProgressIndicator = ({duration, ...props}) => {
  const progress = useSharedValue(0);

  useEffect(() => {
    progress.value = withTiming(1, {
      duration,
    });
  }, []);
  // ...
};


우리는 shared value progress 값을 0으로 생성했습니다. 그리고 duration밀리초 안에 withTiming을 0에서 1로 변경하기 시작합니다.

이제 progress 값을 ProgressItem에 전달하고 Y축의 스케일을 애니메이트해 보겠습니다.

// ...
<ProgressItem
  // ...
  progress={progress}
/>
// ...


애니메이션 보기에서 애니메이션 스타일을 사용합니다.

  export const ProgressItem = ({ index, width, height, count, topScale, progress }) => {
  const animatedStyle = useAnimatedStyle(() => {

    const scaleY = interpolate(
      progress.value,
      [0, 1], // input progress value from 0 to 1
      [1, topScale], // output scale from 1 to 4 (topScale = 4)
      Extrapolation.CLAMP
    );
    return {
      transform: [{ scaleY }],
    };
  });
  return (
    <Animated.View
      style={[
        {
          width,
          height,
          backgroundColor: "black",
        },
        animatedStyle
      ]}
    />
  );
};


기본적으로 우리가 interpolate scaleY 값을 duration밀리초 동안 내부useAnimatedStyle에서 4로 조정했습니다.



이제 모든 항목의 크기를 동시에 조정하는 대신 하나씩 수행해 보겠습니다. 이를 위해 항목 수에 대한 애니메이션 진행을 분할해야 합니다.

const scaleY = interpolate(
  progress.value,
  [index / count, (index + 1) / count],
  [1, topScale],
  Extrapolation.CLAMP
);




다음 단계는 다음 항목에 애니메이션을 적용하기 전에 축소하는 것입니다. 이 경우 각 항목에 대해 3개의 출력 포인트[1, 4, 1]가 필요합니다.

const scaleY = interpolate(
  progress.value,
  [index / count, (index + 1) / count, (index + 2) / count],
  [1, topScale, 1],
  Extrapolation.CLAMP
);


그리고 여기 있습니다



이제 웨이브를 더 부드럽게 만들어 봅시다. 다음 항목 애니메이션을 더 일찍 시작하겠습니다. 이를 위해 각 진행률 조각을 3으로 나눕니다.

const parts = 3;
const wholeCount = count * 3; 
const scaleY = interpolate(
  progress.value,
  [index / wholeCount, (index + parts) / wholeCount, (index + 2 * parts) / wholeCount],
  [1, topScale, 1], 
  Extrapolation.CLAMP
);


거의 우리가 원하는 것이지만 애니메이션이 우리보다 약간 일찍 완료되었습니다duration. 전체duration를 사용하려면 마지막 인덱스에 대한 3포인트 값이 1이어야 합니다.

(index + 2 * parts) / wholeCount = 1
// where 
index = count - 1
// so
wholeCount = count - 1 + 2 * parts;


그 다음에

const parts = 3;
const wholeCount = count - 1 + 2 * parts; 
const scaleY = interpolate(
  progress.value,
  [index / wholeCount, (index + parts) / wholeCount, (index + 2 * parts) / wholeCount],
  [1, topScale, 1], 
  Extrapolation.CLAMP
);




엄청난! 거의 다 왔습니다. 우리의 애니메이션은 좋아 보이지만 이제 구성 요소 마운트 후 한 번만 재생됩니다. 이 문제를 해결하려면 withRepeat 애니메이션 기능을 사용하십시오. 그리고 withTiming 함수를 다음과 같이 래핑합니다.

useEffect(() => {
  progress.value = withRepeat(
    withTiming(1, { duration }),
    -1,
    true
  );
}, []);


첫 번째 인수는 withTiming 함수입니다. 두 번째는 반복 횟수가 음수이면 무한 루프가 됩니다. 세 번째는 reverse 매개변수로 애니메이션을 앞뒤로 재생합니다. 기본적으로 진행률 값을 0에서 1로 변경한 다음 1에서 0으로 변경하고 반복합니다. reverse가 false인 경우 값을 0에서 1로 변경하고 뒤로 이동한 다음 다시 0에서 1로 변경합니다.



그게 다야! 댓글로 어떻게 생각하는지 알려주시고 질문이 있으면 언제든지 물어보세요. 최종 코드는 snackgithub에서 사용할 수 있습니다.

좋은 웹페이지 즐겨찾기