現(xiàn)象:最近開發(fā)RN項(xiàng)目蛛枚,需要開發(fā)一個(gè)頁面滑動(dòng)過程中的tab吸頂?shù)男Ч?/p>
解決方案: 使用Animated創(chuàng)建動(dòng)畫
具體代碼如下:
- index.tsx
import React, { Component } from 'react'
import { View, Text, ViewStyle, StyleSheet, ImageBackground, Animated, RefreshControl } from 'react-native'
import StickyHeader from "./StickyHeader"
import { images, dimensions } from '../../../../res'
interface IState {
refreshing: boolean,
scrollHeight: number,
scrollY: Animated.Value
}
export class PrivateCollectScreen extends Component<any, IState> {
readonly state: IState = {
scrollY: new Animated.Value(0),
scrollHeight: -1,
refreshing: false
}
render() {
return (
<View style={styles.container}>
<Animated.ScrollView
style={{ flex: 1 }}
onScroll={
Animated.event( // 映射動(dòng)畫值
[{
// 垂直滾動(dòng)時(shí)扭吁,將 event.nativeEvent.contentOffset.y映射到this.state.scrollY,以此記錄滑動(dòng)距離
nativeEvent: { contentOffset: { y: this.state.scrollY } }
}],
{ useNativeDriver: true }) // 啟用原生動(dòng)畫驅(qū)動(dòng)盲镶。默認(rèn)不啟用(false)
}
scrollEventThrottle={1} // 滾動(dòng)條距離視圖邊緣的坐標(biāo)
refreshControl={ // 下拉刷新功能
<RefreshControl
style={{ backgroundColor: 'transparent' }}
tintColor={'white'}
refreshing={this.state.refreshing}
onRefresh={() => { // 再刷新時(shí)調(diào)用
this.setState({ refreshing: true })
}} />
}
>
<View onLayout={(e) => {
this.setState({ scrollHeight: e.nativeEvent.layout.height }) // 獲取頭部的高度
}}>
<ImageBackground
style={{ width: dimensions.screenWidth, height: 190 }}
source={images.icReact}
>
{/* 頭部內(nèi)容 */}
</ImageBackground>
</View>
<StickyHeader
stickyHeaderY={this.state.scrollHeight} // 將頭部高度傳入組件
stickyScrollY={this.state.scrollY} // 將滑動(dòng)距離傳入組件
>
<View style={{ height: 50, backgroundColor: '#3356d9' }}>
<Text style={{ fontSize: 20, textAlign: 'center', color: '#fff', lineHeight: 50 }}>固定欄</Text>
</View>
</StickyHeader>
{/* 底部內(nèi)容 */}
{
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].map((item, index) => {
return (<View style={{ height: 100 }}><Text>底部內(nèi)容{index}</Text></View>)
})
}
</Animated.ScrollView>
</View>
)
}
}
interface Styles {
container: ViewStyle
}
const styles = StyleSheet.create<Styles>({
container: {
flex: 1,
backgroundColor: '#fff'
}
})
- StickyHeader.tsx
import * as React from 'react'
import { StyleSheet, Animated } from 'react-native'
interface IState {
stickyLayoutY: number,
stickyHeaderY: number,
stickyScrollY: Animated.Value
}
/**
* 滑動(dòng)吸頂效果組件
* @export
* @class StickyHeader
*/
export default class StickyHeader extends React.Component<any, IState> {
readonly state: IState = {
stickyHeaderY: -1,
stickyScrollY: new Animated.Value(0),
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
}
})
-
效果圖