前言
讓客戶滿意是我們工作的目標,不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領域值得信任、有價值的長期合作伙伴,公司提供的服務項目有:域名注冊、虛擬空間、營銷軟件、網(wǎng)站建設、崇仁網(wǎng)站維護、網(wǎng)站推廣。
最近公司開發(fā)方向偏向移動端,于是就被調(diào)去做RN(react-native),體驗還不錯,當前有個需求是首頁中間吸頂?shù)男Чm然已經(jīng)很久沒寫樣式了,不過這種常見樣式應該是so-easy,沒成想翻車了,網(wǎng)上搜索換了幾個方案都不行,最后去github上復制封裝好的庫來實現(xiàn),現(xiàn)在把翻車過程記錄下來。
需求效果

翻車過程
第一種方案 失敗
一開始的思路是這樣的,大眾思路,我們需要監(jiān)聽頁面的滾動狀態(tài),當頁面滾動到要吸頂元素所處的位置的時候,我們設置它為固定定位,不過很遺憾,RN對于position屬性只提供了兩種布局方式:absolute和relative,既沒有fixed也沒有仍處于試驗的api:sticky。尷尬了😅
第二種方案 失敗
不過也不慌,看網(wǎng)上有第二種方案,把圖上第二 三塊地方作為ScrollView,然后ScrollView滑動監(jiān)聽距離,把第一塊的marginTop設為負值,但是這樣第一部分不能滑動,不符合需求,pass
第三種方案 完全失敗
從網(wǎng)上找到第三種方案,就是一二三部分作為ScrollView,
第一部分position設為absolute,剩下的不設置,默認是relative
第二部分(吸頂部分)marginTop設置(setState)為第一部分高度的state,
添加滑動onScroll事件=》滑動距離y等于第二部分marginTop的state,但是當滑動超過第一部分高度的時候把第二部分(吸頂部分)position設為absolute,并把其marginTop設為0,看起來不錯,實際用ios模擬器一跑就無語了😅,效果很奇葩,手指滑動時不吸頂直接劃上去隱藏掉大半,一松突然吸頂了。。。
見下圖

ios的系統(tǒng),手指在屏幕上滾動時,onScroll一直在觸發(fā),如果里面有setState方法,也會不停執(zhí)行并計算state,但是改變react的state是異步的,只要手指不離開屏幕,改變的state就無法生效(觸發(fā)界面渲染)
實現(xiàn)方案
我最終意識到由于ios的機制,react的state機制不能滿足需求,RN里面肯定有借助原生渲染的方式,于是github找了現(xiàn)成的代碼實現(xiàn)之后,反過來進行研究,大家有RN豐富經(jīng)驗的也可以直接看最下面代碼👇
RN的Animator
RN的Animator動畫庫旨在解決動畫問題,由于js橋接過程,動畫通常不能很好展現(xiàn),最好是把動畫的 數(shù)據(jù) 和 變化方法 一次性發(fā)給原生,由原生進行處理,這就是Animator庫的核心作用。
記得原來RN的動畫一直被吐槽,不過現(xiàn)在效果還挺不錯的,可能與近年來手機硬件提升也越來越大也有關(guān)系吧。
簡單用法
由于Animator內(nèi)部封裝了這四個組件,所以默認可以導出<Animator.View/>,<Animator.Text/>,<Animator.Image/>,<Animator.ScrollView/>
在這幾個組件里面想做一些動畫處理,數(shù)據(jù)方面也是react的state,但是賦值要給Animated.Value,如下👇
this.state = {
scrollY: new Animated.Value(0)
}這里雖然使用的還是原生state,但是經(jīng)過Animated處理,渲染機制完全不一樣了
簡單原理
經(jīng)過Animator包裝后的組件,會遍歷傳入的props和自身的state,查找是否有Animated.Value的實例,并綁定進相應的原生操作。
props和自身的state變化時,將Animated.Value值逐個轉(zhuǎn)化為普通數(shù)值,再交給原生進行渲染,但是值得注意的是,這里并不會觸發(fā)react 的 render,更不會有什么domdiff ,是一種特殊處理,類似于Animated.Value改變時每次的shouldUpdateComponent返回都是false(毫秒級的渲染react性能扛不住),shouldUpdateComponent函數(shù)里面判斷Animated.Value,然后會把數(shù)據(jù)變化發(fā)給原生組件
完整的介紹請移步中文官網(wǎng)Animator庫介紹
實現(xiàn)思路
既然用了Animator組件了,渲染的問題解決了,下面思路是動態(tài)設置吸頂組件的translateY屬性。style:{ transform: [{ translateY:translateY }] }
下面利用插值來實現(xiàn)
const translateY = ScrollY.interpolate({
inputRange: [-1, 0, headerHeight, headerHeight + 1],
outputRange: [0, 0, 0, 1],
});插值interpolate略難理解,需要一點基礎,這里再細說起來這篇文章就太長了官網(wǎng)介紹,
如果還不懂可以去網(wǎng)上找找這方面的資料
實現(xiàn)源碼
實現(xiàn)的圖中第二部分吸頂功能的核心代碼
import * as React from 'react';
import { StyleSheet, Animated } from "react-native";
/**
* 滑動吸頂效果組件
* @export
* @class StickyHeader
*/
export default class StickyHeader extends React.Component{
static defaultProps = {
stickyHeaderY: -1,
stickyScrollY: new Animated.Value(0)
}
constructor(props) {
super(props);
this.state = {
stickyLayoutY: 0,
};
}
// 兼容代碼,防止沒有傳頭部高度
_onLayout = (event) => {
this.setState({
stickyLayoutY: event.nativeEvent.layout.y,
});
}
render() {
const { stickyHeaderY, stickyScrollY, children, style } = this.props
const { stickyLayoutY } = this.state
let y = stickyHeaderY != -1 ? stickyHeaderY : stickyLayoutY;
const translateY = stickyScrollY.interpolate({
inputRange: [-1, 0, y, y + 1],
outputRange: [0, 0, 0, 1],
});
return (
<Animated.View
onLayout= { this._onLayout }
style = {
[
style,
styles.container,
{ transform: [{ translateY }] }
]}
>
{ children }
</Animated.View>
)
}
}
const styles = StyleSheet.create({
container: {
zIndex: 100
},
});
頁面里實際用法如下
// 在頁面constructor里聲明state
this.state = {
scrollY: new Animated.Value(0),
headHeight:-1
};<Animated.ScrollView
style={{ flex: 1 }}
onScroll={
Animated.event(
[{
nativeEvent: { contentOffset: { y: this.state.scrollY } } // 記錄滑動距離
}],
{ useNativeDriver: true }) // 使用原生動畫驅(qū)動
}
scrollEventThrottle={1}
>
<View onLayout={(e) => {
let { height } = e.nativeEvent.layout;
this.setState({ headHeight: height }); // 給頭部高度賦值
}}>
// 里面放入第一部分組件
</View>
<StickyHeader
stickyHeaderY={this.state.headHeight} // 把頭部高度傳入
stickyScrollY={this.state.scrollY} // 把滑動距離傳入
>
// 里面放入第二部分組件
</StickyHeader>
// 這是第三部分的列表組件
<FlatList
data={this.state.dataSource}
renderItem={({item}) => this._createListItem(item)}
/>
</Animated.ScrollView>收尾
具體代碼就是這樣實現(xiàn)了,算是比較完美的方案,特別是照顧了性能,各位可以基于這個封裝來實現(xiàn)更復雜的需求,原理大概就是這個原理了,在前端動畫領域,自己確實也就剛?cè)腴T水平,如有問題,請直接指出。
另外,這是我找的那個 組件 github的代碼地址:https://github.com/jiasongs/react-native-stickyheader,原地址附上,建議如果項目用了給人家一個star
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對創(chuàng)新互聯(lián)的支持。
標題名稱:react-native滑動吸頂效果的實現(xiàn)過程
網(wǎng)站URL:http://www.chinadenli.net/article0/igggio.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供Google、營銷型網(wǎng)站建設、動態(tài)網(wǎng)站、云服務器、軟件開發(fā)、小程序開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)