スマホアプリでよくある、上スクロールしたときだけ、上からタブとかロゴが出てくる実装をReactNativeで書きました。
参考にした記事
https://reactnative.dev/docs/animated
https://hrk-ys.blogspot.com/2017/07/react-nativeanimation.html
https://qiita.com/rrrroji/items/35b659728a953307c040
完成イメージ
完成コード
import React from 'react';
import {Animated, StyleSheet, Text, ScrollView, Dimensions} from 'react-native';
const TestScreen = () => {
const scrollY = React.useRef(new Animated.Value(0)).current;
return (
<>
<Animated.View
style={[
styles.box,
{
transform: [{
translateY: Animated.diffClamp( Animated.multiply(scrollY, -1), -60, 0)
}],
},
]}
>
<Text style={styles.text}>ロゴ</Text>
</Animated.View>
<ScrollView
contentContainerStyle={{ marginTop: 60 }}
scrollEventThrottle={16}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: scrollY } } }],
{
useNativeDriver: false,
},
)}
>
<Text style={styles.text}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
minim veniam, quis nostrud exercitation ullamco laboris nisi ut
aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.
</Text>
</ScrollView>
</>
);
}
const { width } = Dimensions.get('window');
const styles = StyleSheet.create({
box: {
position: 'absolute',
top: 0,
right: 0,
zIndex: 10,
backgroundColor: '#FFF',
height: 60,
width: width,
justifyContent: 'center',
alignItems: 'center',
},
text: {
fontSize: 42,
fontWeight: 'bold',
},
});
export default TestScreen;
ポイント
浅い知識で解説するので、興味ある方だけ見てください。
変数宣言
const scrollY = React.useRef(new Animated.Value(0)).current
イベント発火
<ScrollView
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: scrollY } } }],
{
useNativeDriver: false,
},
)}
>
スクロールすると現在の位置に合わせてscrollYの値が変化する。
Animated.event([{nativeEvent:{contentOffset:{y:scrollY}}}])}は決まった書き方らしい。言われるがままに書く。
https://reactnative.dev/docs/animated#handling-gestures-and-other-events
useNativeDriverはtrueだと、スマホでの操作性が上がるらしいが開発環境similatorでエラーが出たので、falseにした。何も書かないと警告がでる。
https://reactnative.dev/docs/animations#using-the-native-driver
タブのstyle
<Animated.View
style={{
position: 'absolute',
top: 0,
right: 0,
transform: [{
translateY: Animated.diffClamp( Animated.multiply(scrollY, -1), -60, 0)
}],
}}
>
絶対位置で指定する。
scrollYの値に合わせて移動するようにtransformで位置を動かす。Animated.multiplyは第一引数と第二引数の乗算を返す(scrolly × (-1))ので、これを使いスクロール方向と値の正負を逆転させる。Animated.diffClampは第二引数で最小値を、第三引数で最大値を設定できるので、これでタブの動ける範囲を指定する。
https://reactnative.dev/docs/animated#multiply
スクロールのオプション
<ScrollView
contentContainerStyle={{ marginTop: 60 }}
scrollEventThrottle={16}
>
この2つのオプションは必須ではない。contentContainerStyleを指定することで、ページを最初に表示したときに、タブとコンテンツがかぶらないようになる。
https://reactnative.dev/docs/scrollview#contentcontainerstyle
scrollEventThrottle={16}を指定することで、操作性が上がるらしい。