React Native整體滑動(dòng)的標(biāo)簽頁(yè)(標(biāo)簽頁(yè)嵌套,微博熱搜頁(yè))

由于疫情影響,春節(jié)在家足足呆了快二十天了,葛優(yōu)躺了幾天后覺(jué)得還是得做點(diǎn)什么,于是想著把之前寫的東西再總結(jié)下和學(xué)點(diǎn)新東西。
年前一段時(shí)間,RN項(xiàng)目需要用一個(gè)整體可以縱向滑動(dòng)的標(biāo)簽頁(yè)。網(wǎng)上沒(méi)有找到讓人滿意的組件,于是打算自己封裝一個(gè),期間遇到了一些問(wèn)題,后面算是能達(dá)到比較好的效果,并且兼容ios、android平臺(tái)了,決定趁著這段空閑時(shí)間分享出來(lái)和大家一起探討,共同進(jìn)步。
大家可以直接點(diǎn)擊跳轉(zhuǎn)源碼
組件名:react-native-head-tab-view

iOS效果圖


demo_ios.gif

Android效果圖:

demo_android.gif

開發(fā)思路:
由于是整體滑動(dòng)的標(biāo)簽頁(yè),那首先得有一個(gè)標(biāo)簽頁(yè)組件,我之前正好封裝了一個(gè),于是這個(gè)組件就在其基礎(chǔ)上進(jìn)行開發(fā)了,下面是我當(dāng)時(shí)的一些思路。
我在設(shè)計(jì)之初,最先想到是用ScrollView包裹標(biāo)簽頁(yè),達(dá)到整體滑動(dòng)的效果,但是會(huì)有諸多問(wèn)題,列舉一二,比如幾個(gè)標(biāo)簽頁(yè)高度不同,ScrollView必須能容納最長(zhǎng)的那個(gè)標(biāo)簽頁(yè),那從最長(zhǎng)的標(biāo)簽頁(yè)滑動(dòng)到比較短的標(biāo)簽頁(yè)時(shí)就會(huì)有白條,需要另外處理,比較消耗RN的性能;再就是頂部滑出屏幕和滑入屏幕時(shí),需要在ScrollView和標(biāo)簽頁(yè)直接切換手勢(shì),非常影響流暢性。
后面決定用動(dòng)畫去做這件事,轉(zhuǎn)換思路后豁然開朗,至少性能可以達(dá)到原生級(jí)別。
接下來(lái)拆解需求,無(wú)非是要達(dá)到以下幾個(gè)目的:

  • 標(biāo)簽頁(yè)左右滑動(dòng)功能正常,頂部加了一個(gè)頭部Head組件。
  • 任何一個(gè)標(biāo)簽頁(yè)滑動(dòng)時(shí),計(jì)算距離頂部的距離,決定Head組件的動(dòng)畫行為。
  • 共享不同標(biāo)簽頁(yè)的垂直滑動(dòng)距離,達(dá)到共同操控Head組件的目的
  • 兼容標(biāo)簽頁(yè)下官方的所有滑動(dòng)組件(ScrollView,F(xiàn)latList,SectionList)

那只需要針對(duì)以上問(wèn)題一一解決就可以了。

  • 首先問(wèn)題一,需要加Head組件,那怎么加,加到哪里,可以看下面(左),標(biāo)簽頁(yè)組件正常布局,只讓所有標(biāo)簽頁(yè)子頁(yè)面加了個(gè)paddingTop,空出一個(gè)Head組件的位置。
    third.png
  • 問(wèn)題二比較簡(jiǎn)單,計(jì)算滑動(dòng)距離小于Head高度時(shí),通過(guò)區(qū)間動(dòng)畫決定Head的軌跡。
  • 解決問(wèn)題三只需要將記錄Y軸位置的對(duì)象提升到更高一級(jí),放在標(biāo)簽頁(yè)組件中,然后下發(fā)到各個(gè)標(biāo)簽頁(yè)子頁(yè)面,由當(dāng)前正在滾動(dòng)的子頁(yè)面去記錄和維護(hù)這個(gè)對(duì)象。
  • 問(wèn)題四的解決辦法只需要封裝一個(gè)高階組件:接受當(dāng)前使用的滑動(dòng)組件,返回一個(gè)滿足以上功能的新組件,也能同時(shí)達(dá)到封裝內(nèi)聚的目的。

補(bǔ)充幾個(gè)點(diǎn):

  1. 由于Tabbar初始位置在屏幕頂部,需要給它一個(gè)和滾動(dòng)距離成反比的動(dòng)畫,也就是說(shuō)初始位移值是Head的高度,到最后位移值是0,也就是回到原位置頂在頂部。(如下圖)


    move.png

    滾動(dòng)距離超出Head高度后:


    end.png
  2. 為了達(dá)到效果,我們?cè)跐L動(dòng)標(biāo)簽Tab1頁(yè)面時(shí),是需要Tab2、Tab3也同時(shí)滾動(dòng)的,那就會(huì)出現(xiàn)如果Tab2、Tab3未加載出數(shù)據(jù),或者加載的數(shù)據(jù)少,不夠滾動(dòng)的情況。我采取的是通過(guò)動(dòng)態(tài)計(jì)算,用占位高度去彌補(bǔ)的方式。

  3. 由于標(biāo)簽頁(yè)子頁(yè)面是用高階組件HPageViewHoc包裹,我用了Ref轉(zhuǎn)發(fā),能通過(guò)ref直接取到被包裹住的那個(gè)組件,但是內(nèi)部用了const AnimatePageView = Animated.createAnimatedComponent(WrappedComponent),你取到的ref會(huì)是一個(gè)動(dòng)畫對(duì)象,請(qǐng)?jiān)偻ㄟ^(guò)getNode()獲取實(shí)際的FlatList組件,如下面代碼。

import { HPageViewHoc } from 'react-native-viscous-tabview'
const HFlatList = HPageViewHoc(FlatList)

render() {
        const { data } = this.state;
        return (
            <HFlatList
                {...this.props}
                ref={_ref=>this.list=_ref}
                data={data}
                renderItem={this.renderItem.bind(this)}
                keyExtractor={(item, index) => index.toString()}
            />
        )
    }
handleRef(){
    this.list.getNode()...
}

如果大家有什么疑問(wèn)可以在文章下方評(píng)論區(qū)留言,如果有bug請(qǐng)?zhí)嵩?a target="_blank">issues區(qū)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容