React Native에서 구현하는 기분 좋은 탭 바 애니메이션

안녕하세요. 하라 켄타입니다 (@hellokenta_ko)

자기소개



현재 Standard Cognition ( htps : //s 단지 rd. 아이 )이라는 회사에서 주로 React Native나 ReactJS로 앱 개발을 하고 있습니다.
샌프란시스코에 본사를 두는 「무인 편의점」을 만들고 있는 회사로, 지금은 비자가 잡힐 때까지 일본의 지사에서 일하고 있습니다.
잠시 일본에 있기 때문에 부담없이 밥이나 가자.

기분 좋은 하단 탭 바



여러분, 하단 탭 바를 아십니까?
여러분이 아무렇지도 않게, 당연하게 구현하고 있는 탭 바. 궁리의 여지가 없다고 생각되기 쉬운 탭 바.
그러나 나는 Spotify의 앱을 만졌을 때 충격을 받았다. 마치 "살아있는 것 같다"고. (덧붙여서 Twitter 앱이라든지 이런 느낌입니다)

Apple Music 탭 표시줄(일반 탭 표시줄)



보통의 극한군요.


Spotify의 탭 바



푸니 뿌니하고 귀엽다! ! ! 몇 번이라도 만지고 싶어집니다!


잡담



덧붙여서 나는 Spotify의 앱을 아주 좋아하고, 통근시에는 「통근에 추천의 곡」이 표시되어, 비오는 날에는 「비오는 날에 듣고 싶은 플레이리스트」를 표시해 준다.
그런, 저에게 붙어 주는 Spotify 앱을 좋아합니다.

소스 코드



Spotify의 홍보처럼 보였지만, Spotify와는 전혀 관련이 없습니다.
이제 소스 코드를 공유합니다. 우선 아래는 탭 바의 각각의 버튼을 나타내는 컴퍼넌트입니다.
여기에서 푸니 푸니 애니메이션을 실현하고 있습니다.
* 이대로의 코드로 움직이는 것은 아닙니다. 또, react-navigation 의 라이브러리를 이용하고 있습니다.

tabbar-button.js
class TabbarButton extends React.Component {
  onPress = () => {
    const {
      navigation,
      route: { key },
    } = this.props
    navigation.navigate(key)
  }

  onPressIn = () => {
    Animated.timing(this.scale, {
      toValue: 0.95,
      duration: 40,
      useNativeDriver,
    }).start()
  }

  onPressOut = () => {
    Animated.spring(this.scale, {
      toValue: 1,
      friction: 0,
      useNativeDriver,
      overshootClamping: true,
    }).start()
  }

  scale = new Animated.Value(1)

  render() {
    const {
      currentIndex,
      routeIndex,
    } = this.props

    const focused = currentIndex === routeIndex
    return (
      <TouchableOpacity
        activeOpacity={0.8}
        onPress={this.onPress}
        onPressIn={this.onPressIn}
        onPressOut={this.onPressOut}
        style={{ flex: 1 }}
      >
        <Animated.View
          style={[styles.tabBarButton, { transform: [{ scale: this.scale }] }]}
        >
          <Icon color={focused ? black : gray} />
        </Animated.View>
      </TouchableOpacity>
    )
  }
}

포인트


  • TouchableOpacity 컴포넌트의 onPressIn , OnPressOut 를 이용한다.
  • Animated.spring timing 보다 spring 쪽이 개인적으로는 기분 좋았습니다.
  • overshootClampingtrue 로 바운스하지 않도록 한다.
  • useNativeDrivertrue 로 하는 것으로, 네이티브의 UI thread로 애니메이션을 움직일 수가 있어 퍼포먼스가 향상합니다. 매 프레임마다 상태를 브리지하지 않아도 된다. → 참고 URL

  • 기타 코드



    나머지는 위에서 만든 탭 바의 버튼을 표시하기만 하면 됩니다. 참고까지 다른 코드도 올려 둡니다.

    tabbar.js
    export default function Tabbar({ navigation, renderIcon, getLabelText }) => {
      const state = navigation.state
      const routes = state.routes
    
      const currentIndex = state.index
    
      return (
        <SafeAreaView style={styles.container}>
          {routes.map((route, routeIndex) => (
            <TabBarButton
              key={route.key}
              route={route}
              routeIndex={routeIndex}
              navigation={navigation}
              renderIcon={renderIcon}
              currentIndex={currentIndex}
            />
          ))}
        </SafeAreaView>
      )
    }
    

    routes.js
    import Tabbar from 'components/tabbar'
    const Tabs = createBottomTabNavigator(
      {
        Shopping: {
          screen: Shopping,
        },
        Receipts: {
          screen: Receipts,
        },
        Settings: {
          screen: Settings,
        },
      },
      {
        initialRouteName: 'Shopping',
        tabBarComponent: Tabbar,
      }
    )
    

    저자


  • 이름: 하라 켄타
  • Twitter: htps : // 라고 해서 r. 코 m / 헤이 켄타 _
  • LinkedIn: ㅡㅡㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜ 코m/인/켄타-하라-18003797/
  • 좋은 웹페이지 즐겨찾기