react-native ScrollView 實現上拉滑動全屏,下拉恢復原先大小
阿新 • • 發佈:2018-12-09
ScrollView 系列的都可以完成, 比如 FlatView 和 SectionList 都可以。
1 需求
大概就是一個 scroll 元件向上滑動的時候可以完全展示出來。完全展示之後下滑再恢復縮小時的高度。
1.1需求分析
- 縮小時不允許滾動,只有上滑動能喚醒動畫,移至指定位置
- 完全展示出來後可以內部進行滑動,當滑動到頂部,再向上滑(手勢是從上至下)時,縮小整個 list
1.2 技術分析
- list 採用 react-native 元件 ScrollView / FlatView / SectionList
- 動畫: react-native 自帶一套動畫系統,效能尚可
- 手勢: react-native 自帶手勢響應系統
1.3
2 Let’s do it!
2.1 列表頁
<ScrollView
style={
{
position: 'absolute',
top: 500,
height: '展開後的高度'
}
}
>
// children
<ScrollView>
ScrollView 沒有什麼好說的, 設定好樣式,定位在頁面中下方就可以了。 需要注意的是高度設定成展開後的高度,要麼無法滾動。
2.2 動畫
如果要使用動畫的話,需要改變的是 ScrollView 的 top 值。 首先在 constructor 中定義 state, 為初始化的 top 值
this._top = 500
this.state = {
topValue: new Animated.Value(this._top)
}
之後來寫兩個動畫開始的函式
Animated.timing(
this .state.fadeAnim,
{
toValue: 100,
duration: 500,
}
).start(() => {
this.setState({listScroll: false}) // 開啟 or 關閉 ScrollView 滾動
}
)
向上 / 向下的函是一樣的,值不一樣而已,就不多贅述了。
2.3 手勢響應
元件 & 動畫設計好了,就差什麼時候觸發動畫了! 首先先給 ScrollView 一套自定義的手勢響應
this._panResponder = PanResponder.create({
onMoveShouldSetPanResponderCapture:this._handleMoveShouldSetPanResponderCapture,
onPanResponderGrant: this._handlePanResponderGrant,
onPanResponderMove: this._handlePanResponderMove,
onPanResponderRelease: this._handlePanResponderEnd
})
這幾個函式都沒得說,在文件裡已經寫得很清楚了。下面我們來看看什麼時候觸發向上的動畫。前面說了在初始畫面裡向上滾動,列表會變長,那麼就判斷是不是向上滾動。
_handlePanResponderEnd = (event, gestureState) => {
// 我們只需要在這裡判斷 gestureState.dy 的值是否為正負
if (gestureState.dy < 0) {
// 執行向上移動畫
}
}
向上很簡單,向下移動(縮小) 的話需要判斷,使用者已經在在 ScrollView 滾動到頂部,並且手勢是向下滑動的,才可以收回 ScrollView。
<ScrollView
{...this._panResponder.panHandlers} // 繫結手勢響應
style={
{
position: 'absolute',
top: 500,
height: '展開後的高度'
}
}
onScroll={this._handleScrollEnd} // 每次滾動記錄滾動位置
scrollEventThrottle={16} // 設定 onScroll 觸發頻率,一般為 16
>
// children
<ScrollView>
this._handleScrollEnd = (e) => {
this._switchScrollBottom = e.nativeEvent.contentOffset.y // 儲存最後滾動位置
}
// 在申請成功時候儲存上次滾動
_handlePanResponderGrant = (event, gestureState) => {
this._previousScrollValue = this._switchScrollBottom
}
_handlePanResponderEnd = (event, gestureState) => {
// 如果上次滾動和這次滾動位置的值一樣,證明沒有滾動, 已經到達頂部了。
// 這時候則觸發移動至 bottom 的動畫
if (gestureState.dy > 0) {
if (this._previousScrollValue == this._switchScrollBottom) {
this._scrollAnimToBotton()
}
}
總結
rn 自帶的動畫系統性能還是可以的,而且使用起來也比較方便, 和手勢系統一同使用可以解決很多需求。本文章只是大概提供了思路,由於時間緊迫,沒來得及寫 dome, 所以都是一些虛擬碼。如果遇到問題可以在下方留言,歡迎一同討論!