Scroll To Top 버튼 개발기

dudgus5766·2022년 11월 9일
0

Junior Developer

목록 보기
9/18

이번 새로운 탭 페이지를 구현하면서 페이지 상단 이동 버튼을 추가해달라는 요청이 있어 스크롤 버튼 컴포넌트 및 그에 대한 로직을 구현하게 되었습니다 👆

scroll to top 버튼은 보통 이렇게 네이버처럼 우측 하단에 두고 있는 것을 알 수 있습니다.


시행착오

1️⃣ state값으로 scrollY 값 업데이트

FlatList에 onScroll 메서드를 넣어 scroll 이벤트에 발생될 때마다 native.contentOffset.y 값을 저장했습니다. 사용자가 scroll을 할 경우, 매 scroll 이벤트가 이러날때마다 y값을 저장하는 함수가 호출되어 다른 방법을 찾게 되었습니다.

2️⃣ isScrolling boolean state값으로 top 버튼 노출

다른 페이지에서도 isScrolling state값을 사용한 코드가 있어 그대로 진행했습니다.

const handleScroll = (event) => {
    const positionY = event.nativeEvent.contentOffset.y;
    if (positionY > 100) {
      setIsScrolling(true);
    } else {
      setIsScrolling(false);
    }
  };

isScrolling boolean 값으로 top 버튼 노출 여부를 판단하는 코드였습니다. 이대로 진행할수도 있었지만 디자인 팀에서 애니메이션 효과를 부탁한 이유도 있었고 기존 state 추가 및 변화없이 top 버튼을 제어하고 싶었습니다.

3️⃣ React Native Animated 사용

React Native에서 Animated를 사용하면 컴포넌트에 움직임을 넣거나 색상, 투명도(opacity) 등이 변경되도록 애니메이션을 만들 수 있습니다.

Animated는 State나 변수 등을 직접 제어하지 않고, Animated 객체로 생성된 value를 제어하는 것으로 애니메이션을 만듭니다. 애니메이션을 실행하면 시간에 따라 value가 변하는데, 이때 리렌더링은 이루어지지 않으며 실시간으로 반영됩니다.(rerender와 연관이 없으므로 케이스에 따라 shouldComponentUpdate를 false로 해놓는 것도 좋습니다.)

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

처음 scrollY값이란 변수에 useRef를 사용해 ref값(value)을 저장합니다.

새롭게 렌더링 되지 않고 값이 유지 되게 됩니다. 애니메이션이 되고 원래 위치로 돌아가는 것을 방지하는 데 사용하게 되는.. 즉, 리렌더링 시 값이 초기화 되는 것을 막기 위해서 사용합니다.

<FlatList
ref={flatList}
onScroll={Animated.event([{ nativeEvent: { contentOffset: { y: scrollY } } }], { useNativeDriver: true })}
>

FlatList에서 onScroll 메서드에 Animated.event를 걸고 offset값을 scrollY ref값에 저장합니다.

🚨 이 때 주의해야할 점
Animated.event를 쓰기 위해서는 Animated.FlatList로 설정을 해주어야 합니다.
그렇지 않으면…

AnimatedComponent로 감싸주지 않았다는 이런 에러를 띄웁니다🚫

그리고 scrollY 값으로 애니메이션을 넣어주는 단계로 넘어갑니다.🏃‍♂️


Interpolation

일단, 보간(interpolation)이란 무엇이고 어떻게 사용하는지 알아보겠습니다.

경우에 따라 값을 n -> m 으로 변경 한 결과로는 충분하지 않습니다. 일부 오브젝트 스타일은 정수가 아닌 다른 형식을 받아들이기 때문에 커스텀 값을 가져와야 할 수도 있습니다. 예를 들면 colors 와 rotation degrees가 있습니다. (저는 opacity 와 scale에 사용했습니다.)

이 경우 interpolate() 함수를 호출하여 사용자 정의 값 범위를 작성할 수 있습니다.

예를 들어 화면의 퍼센트 사이즈를 사용하여 상자의 크기를 조정하려는 경우에 보간(interpolation)을 실행 할 수 있습니다.

value.interpolate({
  inputRange: [0, 1],
  outputRange: ['0%', '100%'],
});

이 함수는 두 개의 키가있는 오브젝트를 받습니다.

  • inputRange - 애니메이션 값의 범위
  • outputRange - 보간 된 값이 inputRange에 맵핑 된 결과

이 예제에서 value값이 0에서 1로 변경되면 퍼센트가 0%에서 100%로 점차 변경됩니다.

작성한 코드로 살펴보게 되면,

// top 버튼 opacity
  const IconOpacity = scrollY.interpolate({
    inputRange: [250, 300],
    outputRange: [0, 1],
    extrapolate: 'clamp',
  });

  // top 버튼 scale
  const iconScale = scrollY.interpolate({
    inputRange: [0, 300],
    outputRange: [0, 1],
    extrapolate: 'clamp',
  });

top버튼에서는 opacity값을 scrollY값이 250일 때 0, 300일 때 1로 변경되게 됩니다. 사용자가 scroll을 어느 정도까지 하고 나서 top 버튼이 나타나야하는지 디자인 팀과 상의 후에 결정할 수 있었습니다.

scale값 같은 경우는 scroll을 내렸을 시에 버튼이 생기는 애니메이션 UI를 구현하기 위해 사용하였습니다.


👋 마치며

처음엔 요구사항만 보고 간단한 작업이라고 생각했습니다. (페이지 상단 버튼만 만들면 되겠지..)

하지만 실제 개발로 들어가게 되니 기술적으로 복잡도가 높은 기능은 아니었지만 페이지 성능 최적화, 리랜더링 방지, UXUI적 애니메이션 추가 등 기능 개발 뿐 아니라 고려해야할 부분이 많다라는 생각이 들었습니다.

profile
RN App Developer

0개의 댓글