この記事はレコチョク Advent Calendar 2021の10日目の記事となります。
レコチョク・ラボの松木です。
仕事では主にレコチョク・ラボの取り組みをサポートしたり検証のためのプロトタイプを作ったりしています。
2021年も残り少しですが、おうち時間で楽器を楽しむ方が増えたというデータもあり、今年から新しく楽器をはじめた方もいるのではないでしょうか。
私は以前からサンプリングというものに興味があったので、サンプラーとマイクを使った遊びをはじめました。
作ったもの
今回作ったものはReact Nativeアプリケーションで動作する以下のようなアニメーションになります。
https://snack.expo.dev/@y-matsuki/wallet-like-ui

解説
今回実装するUIは react-native-snap-carousel をベースに実装しています。
コンポーネントの利用方法は以下です。
| const { height } = Dimensions.get('window'); return (   <Carousel     ref={carouselRef}     data={items}     renderItem={renderItem}     vertical={true}     enableSnap={false}     layout={'default'}     activeSlideAlignment={'start'}     inactiveSlideScale={1}     inactiveSlideOpacity={1}     sliderHeight={height}     itemHeight={54}     scrollInterpolator={scrollInterpolator}     slideInterpolatedStyle={animatedStyles}     useScrollView={true}   /> ); | 
今回の実装で重要なポイントを以下に列挙します。
その他の属性についてはドキュメントを参照ください。
| 名称 | 説明 | 
|---|---|
| data | リストのデータ | 
| renderItem | データを元にReactコンポーネントを返す関数(今回はカードを返す) | 
| itemHeight | 今回はカードの高さ300に対して54を指定することで重なりを表現する | 
| scrollInterpolator | スクロール位置に応じた挙動をカスタマイズする | 
| slideInterpolatedStyle | スクロール位置に応じたスタイルをカスタマイズする | 
ドキュメントを参考にスクロールした際の挙動のカスタマイズします。「scrollInterpolator」についてはほぼそのまま利用したため「slideInterpolatedStyle」の話をします。
| const animatedStyles = (   index: number,   animatedValue: Animated.Value,   carouselProps: CarouselProps<any> ): StyleProp<ViewStyle> => {   const cardHeight = Math.floor((width / 16) * 9);   return {     zIndex: carouselProps.data.length - index,     transform: [       {         translateY: animatedValue.interpolate({           inputRange: [-1, 0, 1, 2],           outputRange: [             -cardHeight * 0.2,  // <- アクティブなカードの前のカード             0,                  // <- アクティブなカード(今回は一番上のカード)             cardHeight * 1,     // <- アクティブなカードの次のカード             cardHeight * 1.2,   // <- アクティブなカードの次の次のカード           ],         }),       },     ],   }; }; | 
「animatedValue.interpolate()」関数を用いてスクロール時のスタイルの挙動アニメーションを定義します。「inputRange」がリスト内でのカードの位置を表しており「outputRange」がそれに対応するカードのスタイルを表します。
ちなみに「outputRange」を全て「0」にすると以下のようになります。(itemHeight = 54のため高さ54のリストとして描画される)

「transform.translateY」は「コンポーネントのY座標の位置を移動する」ことを表しており「cardHeight * 1.2」は「カードの高さの1.2倍の長さ(画面下部に)移動する」という意味になります。
アクティブなカードの前後を少しずつ上下に移動させてあげることで、アクティブなカードにアクセントを付けたUIを作ることができます。

また、zIndexの部分を以下のよう指定すると、カードの重なり順を逆にできます。
| zIndex: carouselProps.data.length - index | 

まとめ
iOSだとヌルヌル動くのですがAndroidの場合はshadowが効かなかったり動きが硬い感じでややイマイチでした。
今回の実装方法だとカードの枚数が多いとメモリを食うので、RecyclerView的な実装に改善するとより実用的と思います。
明日のレコチョク Advent Calendar 2021は11日目「家計簿のデータをNode.jsでスプレッドシート連携してみた」です。お楽しみに!
この記事を書いた人

最近書いた記事
 2021.12.10React NativeでWallet風UIを実装する 2021.12.10React NativeでWallet風UIを実装する
 2018.11.19Elasticsearchで簡単な検索とscoreを調整する方法 2018.11.19Elasticsearchで簡単な検索とscoreを調整する方法
 2018.10.05ECSをEC2からFargateに切り替える際の注意点 2018.10.05ECSをEC2からFargateに切り替える際の注意点
 2018.09.12AKB48グループ映像倉庫のWeb版をリリースしました 2018.09.12AKB48グループ映像倉庫のWeb版をリリースしました







