由于疫情影響炼绘,春節(jié)在家足足呆了快二十天了,葛優(yōu)躺了幾天后覺(jué)得還是得做點(diǎn)什么妄田,于是想著把之前寫(xiě)的東西再總結(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效果圖
Android效果圖:
開(kāi)發(fā)思路:
由于是整體滑動(dòng)的標(biāo)簽頁(yè)堵漱,那首先得有一個(gè)標(biāo)簽頁(yè)組件,我之前正好封裝了一個(gè)涣仿,于是這個(gè)組件就在其基礎(chǔ)上進(jìn)行開(kāi)發(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)畫(huà)去做這件事爪幻,轉(zhuǎn)換思路后豁然開(kāi)朗菱皆,至少性能可以達(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)畫(huà)行為历造。
- 共享不同標(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組件的位置医吊。
- 問(wèn)題二比較簡(jiǎn)單钱慢,計(jì)算滑動(dòng)距離小于Head高度時(shí),通過(guò)區(qū)間動(dòng)畫(huà)決定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):
-
由于Tabbar初始位置在屏幕頂部,需要給它一個(gè)和滾動(dòng)距離成反比的動(dòng)畫(huà)逛绵,也就是說(shuō)初始位移值是Head的高度怀各,到最后位移值是0栗竖,也就是回到原位置頂在頂部。(如下圖)
滾動(dòng)距離超出Head高度后:
為了達(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ǔ)的方式壶栋。
由于標(biāo)簽頁(yè)子頁(yè)面是用高階組件HPageViewHoc包裹辰如,我用了Ref轉(zhuǎn)發(fā),能通過(guò)ref直接取到被包裹住的那個(gè)組件贵试,但是內(nèi)部用了const AnimatePageView = Animated.createAnimatedComponent(WrappedComponent)琉兜,你取到的ref會(huì)是一個(gè)動(dòng)畫(huà)對(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ū)
桑滩。