1. 程式人生 > >react-native ScrollView 實現上拉滑動全屏,下拉恢復原先大小

react-native ScrollView 實現上拉滑動全屏,下拉恢復原先大小

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, 所以都是一些虛擬碼。如果遇到問題可以在下方留言,歡迎一同討論!