[React-Native] Scroll Animation

박진·2022년 1월 7일
1

1. Animated 시작

스크롤 이벤트에 따른 이미지변화를 애니메이션으로 나타나는 예제를 만들어볼라한다.

우선 React Native에 animation을 주기위해선, Animated를 import해야한다.

import{ Animated} from 'react-native'

이렇게 해주면 애니메이션에대한 준비는 끝났다.

2. 생성

App.js

import React, {useRef} from 'react';
import {
  StyleSheet,
  Text,
  View,
  ScrollView,
  SafeAreaView,
  Animated,
  Image,
  Dimensions,
} from 'react-native';

const windowWidth = Dimensions.get('window').width;
const windowHeight = Dimensions.get('window').height;

const MAINIMG = windowHeight / 3.3;

const App = ()=> {
	return(
    	  <SafeAreaView style={styles.safeView}>
          	<ScrollView  style={styles.container}>
            		  <View style={styles.imgWrap}>
          			<Image style={styles.img} source={require('../assets/rn.png')}/>
        		</View>
                    <View style={styles.textWrap}>
          <Text style={styles.dummyText}>
            Lorem Ipsum is simply dummy text of the printing and typesetting
            industry. Lorem Ipsum has been the industry's standard dummy text
            ever since the 1500s, when an unknown printer took a galley of type
            and scrambled it to make a type specimen book. It has survived not
            only five centuries, but also the leap into electronic typesetting,
            remaining essentially unchanged. It was popularised in the 1960s
            with the release of Letraset sheets containing Lorem Ipsum passages,
            and more recently with desktop publishing software like Aldus
            PageMaker including versions of Lorem Ipsum. Lorem Ipsum is simply
            dummy text of the printing and typesetting industry. Lorem Ipsum has
            been the industry's standard dummy text ever since the 1500s, when
            an unknown printer took a galley of type and scrambled it to make a
            type specimen book. It has survived not only five centuries, but
            also the leap into electronic typesetting, remaining essentially
            unchanged. It was popularised in the 1960s with the release of
            Letraset sheets containing Lorem Ipsum passages, and more recently
            with desktop publishing software like Aldus PageMaker including
            versions of Lorem Ipsum. Lorem Ipsum is simply dummy text of the
            printing and typesetting industry. Lorem Ipsum has been the
            industry's standard dummy text ever since the 1500s, when an unknown
            printer took a galley of type and scrambled it to make a type
            specimen book. It has survived not only five centuries, but also the
            leap into electronic typesetting, remaining essentially unchanged.
            It was popularised in the 1960s with the release of Letraset sheets
            containing Lorem Ipsum passages, and more recently with desktop
            publishing software like Aldus PageMaker including versions of Lorem
            Ipsum. Lorem Ipsum is simply dummy text of the printing and
            typesetting industry. Lorem Ipsum has been the industry's standard
            dummy text ever since the 1500s, when an unknown printer took a
            galley of type and scrambled it to make a type specimen book. It has
            survived not only five centuries, but also the leap into electronic
            typesetting, remaining essentially unchanged. It was popularised in
            the 1960s with the release of Letraset sheets containing Lorem Ipsum
            passages, and more recently with desktop publishing software like
            Aldus PageMaker including versions of Lorem Ipsum.
          </Text>
        </View>
            	</ScrollView>
          </SafeAreaView>
    )}
    
  const styles = StyleSheet.create({
  safeView: {
    flex: 1,
    backgroundColor: '#1C1C1E',
  },
  container: {
    flex: 1,
    width: '100%',
    backgroundColor: '#1C1C1E',
    paddingBottom: '15%',
  },
  imgWrap: {
    alignItems: 'center',
    width: windowWidth,
    height: windowHeight / 3.3,
  },
  img: {
    width: windowWidth * 2,
    height: MAINIMG,
    resizeMode: 'center',
  },
  textWrap: {
    backgroundColor: 'white',
    padding: 20,
  },
  dummyText: {
    lineHeight: 20,
  },
});
    
export default App;
}

위의 이미지와 같은 이미지와 더미텍스트가 존재하는 예제를 만들었다.

애니메이션 추가하기

React Native에서는 animation을 사용할수있는 컴포넌트가 제한적이다.

View, Text, Image, ScrollView, FlatList and SectionList

만약, 이외의 컴포넌트를 사용할경우에는 Animated.createAnimatedComponent()를 이용하여 만들면된다.

이글에서는 위에 나온 컴포넌트로 애니메이션을 이용해보자.

그리고 애니메이션은 타입이 존재하는데,

  • Timing : 시간이 지남에 따라 값을 애니메이트한다.
  • Spring : 간단한 스프링 물리 모델을 제공한다.
  • Decay : 초기속도로 시작하여 점차 정지한다.

Timing타입이 가장 기본적인 접근방식이다.

애니메이션 초기값 설정
애니메이션을 사용하기 위하여 먼저 초기값을 정의해야한다.

설정된 초기값을 useRef를 이용하여 만든다.

import React, {useRef} from 'react';
...

const App = () =>{

const scrollA = useRef(new Animated.Value(0)).current;

...

그다음은 컴포넌트로 Animate를 렌더링해야한다.

<Animated.ScrollView>
</Animated.ScrollView>

이벤트 발생
스크롤 이벤트가 발생할때 애니메이션이 작동하기위해, ScrollView에 onScroll 이벤트를 걸어준다.

여기서 useNativeDriver:true 는 Animated 모듈은 React Native의 JavaScript 측에서 애니메이션을 수행하고 단일 스레드를 공유한다. 그래서 애니메이션이 부드럽게 작동하지 않을 수 있기에 useNativeDriver:true 옵션을 사용하여 애니메이션을 부드럽게 작동시킨다1.

수직으로 스크롤이벤트가 일어났을때 애니메이션함수가 호출된다.

<Animated.ScrollView 
	style={styles.container}
	onScroll={Animated.event([{nativeEvent:{contentOffset:{y:scrollA}}}],
	{useNativeDriver:true})}
>
</Animated.ScrollView>

interpolate

interpolate() 함수를 통해 정의값의 범위를 지정할수있다.

  • inputRange : 애니메이션 값의 범위
  • outRage : interpolate된 값이 inputRage에 맵핑된 결과 (string값도가능)
value.interpolate({
  inputRange: [0, 1],
  outputRange: ['0px', '100px'],
});

** 애니메이션값이 0 에서 1로 변할때 0px에서 100px로 변한다

그후 style에서 scrollA를 props로 받아 이벤트를 처리한다

const styles = StyleSheet.create({
 ...
 img: scrollA => ({
    width: windowWidth * 2,
    height: MAINIMG,
    resizeMode: 'center',
    transform: [
      {
        translateY: scrollA.interpolate({
          inputRange: [-MAINIMG, 0, MAINIMG, MAINIMG + 1],
          outputRange: [-MAINIMG / 2, 0, MAINIMG * 0.75, MAINIMG * 0.75],
        }),
      },
      {
        scale: scrollA.interpolate({
          inputRange: [-MAINIMG, 0, MAINIMG, MAINIMG + 1],
          outputRange: [2, 1, 0.5, 0.5],
        }),
      },
    ],
  }),
  textWrap: {
    backgroundColor: 'white',
    padding: 20,
  },
  dummyText: {
    lineHeight: 20,
  },
  )}

scale: scrollA.interpolate({
inputRange: [-MAINIMG, 0, MAINIMG, MAINIMG + 1],
outputRange: [2, 1, 0.5, 0.5],
}),
스크롤을 아래로 내렸을때, 이미지 크기는 2, 현위치일때 1, 스크롤을 올렸을때 이미지 스케일이 반값이된다.

3. 마무리

import React, {useRef} from 'react';
import {
StyleSheet,
Text,
TouchableOpacity,
View,
ScrollView,
SafeAreaView,
Animated,
Image,
Dimensions,
} from 'react-native';

const windowWidth = Dimensions.get('window').width;
const windowHeight = Dimensions.get('window').height;

const MAINIMG = windowHeight / 3.3;

const App = ({
counter,
handleAddCounter,
handleRemoveCounter,
handleIncrement,
handleDecrement,
}) => {
// scroll Animation
const scrollA = useRef(new Animated.Value(0)).current;


return (
 <SafeAreaView style={styles.safeView}>
   <Animated.ScrollView
     style={styles.container}
     onScroll={Animated.event(
       [{nativeEvent: {contentOffset: {y: scrollA}}}],
       {useNativeDriver: true},
     )}
     scrollEventThrottle={16}>
     <View style={styles.imgWrap}>
       <Animated.Image
         style={styles.img(scrollA)}
         source={require('../assets/rn.png')}
       />
     </View>
     <View style={styles.textWrap}>
       <Text style={styles.dummyText}>
         Lorem Ipsum is simply dummy text of the printing and typesetting
         industry. Lorem Ipsum has been the industry's standard dummy text
         ever since the 1500s, when an unknown printer took a galley of type
         and scrambled it to make a type specimen book. It has survived not
         only five centuries, but also the leap into electronic typesetting,
         remaining essentially unchanged. It was popularised in the 1960s
         with the release of Letraset sheets containing Lorem Ipsum passages,
         and more recently with desktop publishing software like Aldus
         PageMaker including versions of Lorem Ipsum. Lorem Ipsum is simply
         dummy text of the printing and typesetting industry. Lorem Ipsum has
         been the industry's standard dummy text ever since the 1500s, when
         an unknown printer took a galley of type and scrambled it to make a
         type specimen book. It has survived not only five centuries, but
         also the leap into electronic typesetting, remaining essentially
         unchanged. It was popularised in the 1960s with the release of
         Letraset sheets containing Lorem Ipsum passages, and more recently
         with desktop publishing software like Aldus PageMaker including
         versions of Lorem Ipsum. Lorem Ipsum is simply dummy text of the
         printing and typesetting industry. Lorem Ipsum has been the
         industry's standard dummy text ever since the 1500s, when an unknown
         printer took a galley of type and scrambled it to make a type
         specimen book. It has survived not only five centuries, but also the
         leap into electronic typesetting, remaining essentially unchanged.
         It was popularised in the 1960s with the release of Letraset sheets
         containing Lorem Ipsum passages, and more recently with desktop
         publishing software like Aldus PageMaker including versions of Lorem
         Ipsum. Lorem Ipsum is simply dummy text of the printing and
         typesetting industry. Lorem Ipsum has been the industry's standard
         dummy text ever since the 1500s, when an unknown printer took a
         galley of type and scrambled it to make a type specimen book. It has
         survived not only five centuries, but also the leap into electronic
         typesetting, remaining essentially unchanged. It was popularised in
         the 1960s with the release of Letraset sheets containing Lorem Ipsum
         passages, and more recently with desktop publishing software like
         Aldus PageMaker including versions of Lorem Ipsum.
       </Text>
     </View>
   </Animated.ScrollView>
 </SafeAreaView>
);
};

const styles = StyleSheet.create({
safeView: {
 flex: 1,
 backgroundColor: '#1C1C1E',
},
container: {
 flex: 1,
 width: '100%',
 backgroundColor: '#1C1C1E',
 // paddingTop: '15%',
 paddingBottom: '15%',
}
imgWrap: {
 alignItems: 'center',
 width: windowWidth,
 height: windowHeight / 3.3,
},
img: scrollA => ({
 width: windowWidth * 2,
 height: MAINIMG,
 resizeMode: 'center',
 transform: [
   {
     translateY: scrollA.interpolate({
       inputRange: [-MAINIMG, 0, MAINIMG, MAINIMG + 1],
       outputRange: [-MAINIMG / 2, 0, MAINIMG * 0.75, MAINIMG * 0.75],
     }),
   },
   {
     scale: scrollA.interpolate({
       inputRange: [-MAINIMG, 0, MAINIMG, MAINIMG + 1],
       outputRange: [2, 1, 0.5, 0.5],
     }),
   },
 ],
}),
textWrap: {
 backgroundColor: 'white',
 padding: 20,
},
dummyText: {
 lineHeight: 20,
},
});

export default App;

profile
Hello :)

0개의 댓글