반응 본기 경사 회전 목마 애니메이션

51650 단어 reactreactnative

개요


React Native에는 FlatList 구성 요소가 있습니다. 이 구성 요소는 프로젝트 목록을 가져와 목록의 모든 항목에 하나의 요소를 지능적으로 보여줍니다.언뜻 보기에, 이 구성 요소는 단지 대형 원소 목록을 나타내는 데 쓰이는 것 같다.사실이 증명하듯이, React Native FlatLists는 멋진 일을 많이 할 수 있습니다!이 글에서 우리는 기이한 경사 효과가 있는 회전목마를 구축하는 것 중 하나를 볼 것이다.다음 예제를 참조하십시오.

이 예시의 소스 코드 here을 찾을 수 있습니다.

약간의 설정


작은 설정부터 시작합시다.회전 목마 슬라이드를 보여주는 데이터 목록을 만들 것입니다.또한 React Native의 Dimensions API를 사용하여 화면의 폭과 높이를 얻을 수 있습니다.
import { Dimensions } from "react-native";

// Set up our slides
type Slide = {
  image: any;
  title: string;
  subtitle: string;
};
const SLIDES: Slide[] = [
  {
    image: require("./img/summer.jpg"),
    title: "Summer",
    subtitle: "Warm days, fun nights.",
  },
  {
    image: require("./img/fall.jpg"),
    title: "Fall",
    subtitle: "Sweater weather, baby.",
  },
  {
    image: require("./img/winter.jpg"),
    title: "Winter",
    subtitle: "The season to be jolly.",
  },
  {
    image: require("./img/spring.jpg"),
    title: "Spring",
    subtitle: "April showers, may flowers.",
  },
];

// Utils
const { width, height } = Dimensions.get("window");

회전목마 구축


현재 우리는 이미 데이터 설정을 완성했으니, 우리는 계속 회전목마의 골격을 함께 놓을 것이다.여기에는 FlatList 데이터와 회전목마의 슬라이드를 렌더링하는 사용자 정의 구성 요소 SLIDES 데이터가 포함됩니다.CarouselSlide의 대상이 회전목마를 포함하는 스타일이 있지만, 지금은 이런 세부 사항을 걱정할 필요가 없다.
/**
 * Tilt Carousel View
 */
export const TiltCarousel: React.FC = () => {
  return (
    <View style={styles.background}>
      <FlatList
        data={SLIDES}
        renderItem={({ item, index }: { item: Slide; index: number }) => (
          <CarouselSlide slide={item} />
        )}
        horizontal
        snapToInterval={width}
        showsHorizontalScrollIndicator={false}
        scrollEventThrottle={16}
        decelerationRate={-1}
        bounces={true}
        keyExtractor={(slide: Slide) => slide.title}
      />
    </View>
  );
};

/**
 * Slide item
 */
const CarouselSlide: React.FC<{
  slide: Slide;
}> = ({ slide }) => {
  return (
    <View style={styles.cardContainer}>
      <ImageBackground source={slide.image} style={{ flex: 1 }}>
        <Spacer height={0.7 * height} />
        <View style={styles.cardContentContainer}>
          <AppText style={styles.title}>{slide.title}</AppText>
          <AppText style={styles.subtitle}>{slide.subtitle}</AppText>
        </View>
      </ImageBackground>
    </View>
  );
};
여기에는 몇 가지 주의해야 할 점이 있다.
  • 우리는 styleshorizontal 아이템을 사용하여 목록을 수평으로 렌더링할 것입니다.
  • FlatList을 설정하여 snapToInterval={width}이 특정 간격으로 포착되도록 합니다.중요한 것은 우리가 FlatList, 화면의 폭을 포착할 것이다.각 회전목마 슬라이드의 폭도 width이므로 각 회전목마 슬라이드를 보기에 포착할 수 있습니다.
  • 우리는 widthscrollEventThrottle={16}을 사용하여 회전목마의 미끄럼 물리를 제어할 것이다.
  • 이때 decelerationRate={-1}은 슬라이드 이미지와 일부 텍스트로만 이미지 배경을 렌더링합니다.사용자 정의 SlideCarouselAppText 구성 요소를 사용하여 텍스트와 공간을 렌더링합니다.
  • 이제 우리는 좋은 출발을 했다.이것이 바로 우리가 가진 것이다.

    그러나 우리는 사용자 정의 애니메이션으로 이 일의 흥미를 높일 것이다.

    일부 애니메이션에 뿌리기


    우리는 회전목마 슬라이드에 좋은 경사 효과를 줄 것이다. 그들이 보기에 들어가고 떠날 때.그러기 위해서는 다음과 같은 몇 가지 작업이 필요합니다.
  • Spacer에서 사용자의 스크롤 위치를 추적합니다.
  • 은 이 스크롤 위치를 사용하여 회전목마 슬라이드의 일부 변환 설정 애니메이션을 설정합니다.
  • 사용자의 스크롤 위치를 추적합니다.

    FlatList 스크롤 주위에 애니메이션을 추가하기 위해서는 FlatList 값을 만들고 추적해야 합니다. 이 값은 사용자가 Animated에서 스크롤하는 거리에 대응합니다.React Native에는 애니메이션 값을 만드는 데 사용할 FlatList API가 있습니다(애니메이션 값을 설정하고 스타일에서 사용하여 애니메이션 효과를 만들 수 있습니다).우리는 이 애니메이션 값을 사용하여 사용자가 목록에서 얼마나 멀리 굴렀는지 추적할 것이다.이를 위해 Animated.ValueonScroll 도구와 FlatList API를 사용합니다.Animated.event 값을 각 scrollX 요소에 계속 전달하고 전달합니다.
    export const TiltCarousel: React.FC = () => {
    +  const scrollX = React.useRef(new Animated.Value(0)).current;
    
      return (
        <View style={styles.background}>
          <Animated.FlatList
            data={SLIDES}
            renderItem={({ item, index }: { item: Slide; index: number }) => (
    -         <CarouselSlide slide={item} />
    +         <CarouselSlide slide={item} scrollX={scrollX} index={index} />
            )}
            horizontal
            snapToInterval={width}
            showsHorizontalScrollIndicator={false}
            scrollEventThrottle={16}
            decelerationRate={-1}
            bounces={true}
            keyExtractor={(slide: Slide) => slide.title}
    +       onScroll={Animated.event(
    +         [{ nativeEvent: { contentOffset: { x: scrollX } } }],
    +         { useNativeDriver: true },
    +       )}
          />
        </View>
      );
    };
    
    const CarouselSlide: React.FC<{
      slide: Slide;
    + scrollX: Animated.Value;
    + index: number;
    -}> = ({ slide }) => {
    +}> = ({ slide, scrollX, index }) => {
    
    이 변경 사항은 시각적 변경 사항을 추가하지 않았지만, 현재 <CarouselSlide />을 방문하여 사용자의 수평 스크롤 위치를 추적할 수 있습니다. 이 변경 사항을 사용하여 회전 목마 슬라이드의 애니메이션을 설정할 것입니다.

    회전 목마 슬라이드 애니메이션 설정


    우리는 회전목마 슬라이드에 약간의 기울기 효과를 추가할 것이다.

    이를 위해 scrollX의 값(사용자가 scrollX에서 스크롤하는 거리)과 FlatList의 값(슬라이드 항목별 인덱스)을 사용합니다.우리는 최종적으로 애니메이션을 만드는 회전목마 슬라이드에 약간의 변환을 적용할 것이다.이제 설정을 좀 해 봅시다.
    const CarouselSlide: React.FC<{
      slide: Slide;
      scrollX: Animated.Value;
      index: number;
    }> = ({ slide, scrollX, index }) => {
      // Transforms
      const opacity = 1;
      const scale = 1;
      const perspective = 800;
      const translateX = 0;
      const rotateY = '0deg';
    
      return (
        <Animated.View
          style={[
            styles.cardContainer,
            {
              opacity,
              transform: [{ scale }, { perspective }, { translateX }, { rotateY }],
            },
          ]}
        >
          <ImageBackground source={slide.image} style={{ flex: 1 }}>
            <Spacer height={0.7 * height} />
            <View style={styles.cardContentContainer}>
              <AppText style={styles.title}>{slide.title}</AppText>
              <AppText style={styles.subtitle}>{slide.subtitle}</AppText>
            </View>
          </ImageBackground>
        </Animated.View>
      );
    };
    
    위의 코드 세그먼트에서 우리는 index, opacity, scale, perspective, translateXrotateY의 기본 값을 설정한 후에 이 값을 우리의 슬라이드 포장기에 응용하였다.우리는 또한 ViewAnimated.View으로 전환할 것이다. 왜냐하면 우리는 상술한 속성 값을 애니메이션 값으로 전환할 것이다.
    이제 우리가 애니메이션을 제작할 이러한 속성과 왜 그런지 이야기해 봅시다.
  • 우리는 opacity의 애니메이션을 설정할 것입니다. 이렇게 하면 슬라이드가 보기를 들어가거나 떠날 때'사라지기'효과를 추가할 수 있습니다.
  • 우리는 scale의 애니메이션을 설정합니다. 이렇게 하면 슬라이드가 보기를 들어가거나 떠날 때 애니메이션은 축소/증가합니다.
  • 에서 perspective, translateXrotateY의 애니메이션을 설정하여 슬라이드를 기울게 합니다.카드를 왼쪽으로 미끄러뜨리면 화면 왼쪽을 중심으로 기울어져야 합니다.만약 당신이 오른쪽으로 카드를 미끄러뜨린다면, 그것은 화면의 오른쪽을 둘러싸고 기울어져야 한다.
  • 이 애니메이션 값을 채워서 뒤에 있는 논리를 봅시다.
    const inputRange = [(index - 1) * width, index * width, (index + 1) * width];
    
    const opacity = scrollX.interpolate({
      inputRange,
      outputRange: [0, 1, 0],
    });
    const scale = scrollX.interpolate({
      inputRange,
      outputRange: [0.6, 1, 0.6],
    });
    const perspective = scrollX.interpolate({
      inputRange,
      outputRange: [1200, 800, 1200],
    });
    const translateX = Animated.subtract(scrollX, index * width);
    const rotateY = scrollX.interpolate({
      inputRange,
      outputRange: ["-45deg", "0deg", "45deg"],
    });
    
    이때 기울기 효과가 생성되었습니다!그러나 우리는 우리가 어떻게 이런 애니메이션 값을 구축했는지 되돌아봐야 한다.

    애니메이션 보간


    React 원본 애니메이션 값은 interpolate 방법으로 애니메이션 값을 변환할 수 있습니다.이것은 매우 강력한 도구다!우리는 우리의 scrollX 값을 변환할 것이다.interpolate 방법은 inputRange과 정의 변환의 outputRange을 채택한다.
    우리의 예에서 우리는 이런 줄을 가지고 있다.
    const inputRange = [(index - 1) * width, index * width, (index + 1) * width];
    
    우리는 그것을 세 개의 중단점으로 볼 수 있으며, 우리는 그 중에서 각 슬라이드에 비추게 될 것이다.예를 들어, 첫 번째 슬라이드에서는 다음과 같이 생각할 수 있습니다.

    왼쪽으로 미끄러지면 scrollXindex * width에서 (index - 1) * width으로 바뀐다.만약 네가 오른쪽으로 미끄러진다면, 그것은 index * width에서 (index + 1) * width까지 같지 않을 것이다.따라서 이 inputRange을 정의된 중단점으로 볼 수 있습니다. 슬라이드가 화면 보기에서 완전히 벗어날 때(왼쪽), 슬라이드가 화면 보기에서 완전히 중간에 있을 때, 그리고 슬라이드가 화면 보기에서 완전히 벗어날 때(오른쪽).
    그리고 우리는 이러한 중단점을 바탕으로 다른 값을 변환하는 방법을 고려할 수 있다.예를 들어, 슬라이드가 화면 뷰에 없을 때(왼쪽 또는 오른쪽으로) opacity: 0을 적용하여 "사라지기"를 원합니다.슬라이드가 화면 보기에 있으면 opacity: 1이 필요합니다.따라서 opacity에서 다음과 같은 변환을 통해 scrollX을 정의할 수 있습니다.
    const opacity = scrollX.interpolate({
      inputRange,
      outputRange: [0, 1, 0],
    });
    
    이와 유사한 방식으로 슬라이드가 보기에서 벗어날 때 약간 수축되기를 원하기 때문에 우리는 다음과 같이 정의할 수 있다.
    const scale = scrollX.interpolate({
      inputRange,
      outputRange: [0.6, 1, 0.6],
    });
    
    우리는 이렇게 생각할 수 있다.
  • 미끄럼틀이 왼쪽으로 미끄러진다고요?scale: 0.6 .
  • 슬라이드가 완전히 표시되었습니까?scale: 1 .
  • 미끄럼틀이 오른쪽으로 미끄러진다고요?scale: 0.6 .
  • 은 그 사이의 모든 설정에 애니메이션을 매끄럽게 설정합니다(선형 삽입값 사용).
  • 경사변환


    이제 다음 항목만 남았습니다.
    const perspective = scrollX.interpolate({
      inputRange,
      outputRange: [1200, 800, 1200],
    });
    const translateX = Animated.subtract(scrollX, index * width);
    const rotateY = scrollX.interpolate({
      inputRange,
      outputRange: ["-45deg", "0deg", "45deg"],
    });
    
    나는 이런 무료한 세부 사항을 토론하고 싶지 않지만, 화면 가장자리에서'경사'효과를 만들려면, 우리는 세 가지 연속적인 변환 (그리고 순서!) 을 실행해야 한다.고위층에서 볼 때 우리는 다음과 같은 방식으로 이러한 전환을 고려할 수 있다.
  • perspective을 사용하여 슬라이드를 화면에서 이동할 때 사용자가 밀어내는 것처럼 보입니다.
  • translateX을 사용하여 슬라이더를 왼쪽/오른쪽으로 이동하여 기울기의 회전축을 결정합니다.
  • rotateY을 사용하여 회전 효과를 생성합니다.
  • 이런 것들이 있어서 우리는 경사 효과를 얻었다.이 강좌는 여기서 끝냅니다.

    결론


    전체 "경사"효과는 복잡한 수학/변환이 있지만, 이러한 변환 설정이 도움이 되고 도구를 제공하여 React Native FlatLists를 바탕으로 자신의 기이한 애니메이션을 만들기 시작합니다!전면적으로 보기 위해 다음은 모든 코드입니다.
    import * as React from "react";
    import {
      Animated,
      Dimensions,
      ImageBackground,
      StyleSheet,
      View,
    } from "react-native";
    import { AppText } from "../../components/AppText";
    import { Spacer } from "../../components/Spacer";
    
    // Set up our slides
    type Slide = {
      image: any;
      title: string;
      subtitle: string;
    };
    const SLIDES: Slide[] = [
      {
        image: require("./img/summer.jpg"),
        title: "Summer",
        subtitle: "Warm days, fun nights.",
      },
      {
        image: require("./img/fall.jpg"),
        title: "Fall",
        subtitle: "Sweater weather, baby.",
      },
      {
        image: require("./img/winter.jpg"),
        title: "Winter",
        subtitle: "The season to be jolly.",
      },
      {
        image: require("./img/spring.jpg"),
        title: "Spring",
        subtitle: "April showers, may flowers.",
      },
    ];
    
    // Utils
    const { width, height } = Dimensions.get("window");
    
    /**
     * Tilt Carousel View
     */
    export const TiltCarousel: React.FC = () => {
      const scrollX = React.useRef(new Animated.Value(0)).current;
    
      return (
        <View style={styles.background}>
          <Animated.FlatList
            data={SLIDES}
            renderItem={({ item, index }: { item: Slide; index: number }) => (
              <CarouselSlide slide={item} scrollX={scrollX} index={index} />
            )}
            horizontal
            snapToInterval={width}
            showsHorizontalScrollIndicator={false}
            scrollEventThrottle={16}
            decelerationRate={-1}
            bounces={true}
            keyExtractor={(slide: Slide) => slide.title}
            onScroll={Animated.event(
              [{ nativeEvent: { contentOffset: { x: scrollX } } }],
              { useNativeDriver: true },
            )}
          />
        </View>
      );
    };
    
    /**
     * Slide item
     */
    const CarouselSlide: React.FC<{
      slide: Slide;
      scrollX: Animated.Value;
      index: number;
    }> = ({ slide, scrollX, index }) => {
      const inputRange = [(index - 1) * width, index * width, (index + 1) * width];
    
      const opacity = scrollX.interpolate({
        inputRange,
        outputRange: [0, 1, 0],
      });
      const scale = scrollX.interpolate({
        inputRange,
        outputRange: [0.6, 1, 0.6],
      });
      const perspective = scrollX.interpolate({
        inputRange,
        outputRange: [1200, 800, 1200],
      });
      const translateX = Animated.subtract(scrollX, index * width);
      const rotateY = scrollX.interpolate({
        inputRange,
        outputRange: ["-45deg", "0deg", "45deg"],
      });
    
      return (
        <Animated.View
          style={[
            styles.cardContainer,
            {
              opacity,
              transform: [{ scale }, { perspective }, { translateX }, { rotateY }],
            },
          ]}
        >
          <ImageBackground source={slide.image} style={{ flex: 1 }}>
            <Spacer height={0.7 * height} />
            <View style={styles.cardContentContainer}>
              <AppText style={styles.title}>{slide.title}</AppText>
              <AppText style={styles.subtitle}>{slide.subtitle}</AppText>
            </View>
          </ImageBackground>
        </Animated.View>
      );
    };
    
    /**
     * Styling
     */
    const styles = StyleSheet.create({
      background: { flex: 1, backgroundColor: "rgba(30,30,30,0.8)" },
      cardContainer: {
        width,
        flex: 1,
        justifyContent: "center",
        borderRadius: 30,
        overflow: "hidden",
      },
      cardContentContainer: {
        alignItems: "center",
        backgroundColor: "rgba(0,0,0,0.3)",
        padding: 16,
      },
      title: {
        color: "white",
        fontWeight: "bold",
        fontSize: 64,
        textShadowColor: "black",
        textShadowRadius: 4,
        textShadowOffset: {
          width: 1,
          height: 1,
        },
      },
      subtitle: {
        color: "rgb(230,230,230)",
        fontWeight: "600",
        fontSize: 18,
      },
    });
    

    좋은 웹페이지 즐겨찾기