RN - 实现页面中间部分吸顶效果

在FlatList 和ScrollView 中有一个stickyHeaderIndices 可以轻松实现吸顶效果。

<ScrollView
  	showsVerticalScrollIndicator={false}
 	style={styles.container}
 	ListHeaderComponent={...}
  	stickyHeaderIndices={[0]}//第一个子元素即头部组件,上滑时吸顶 	
/>

由于头部组件是一个整体,无法单独使组件内的元素吸顶,不满足我的需求。
RN - 实现页面中间部分吸顶效果_第1张图片

实现方案1–来源于网上

实现的图中第二部分吸顶功能的核心代码

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 }) // 使用原生动画驱动
  }
  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>

但是将这FlatList/SectionList中的任何一个放在 ScrollView 中,它们将无法计算当前窗口的大小,而是会尝试渲染所有内容,这可能会导致性能问题。

实现方案2–个人想法

使用SectionList分组列表,ListHeaderComponent头部组件放置不需吸顶但需要跟着上滑的部分,renderSectionHeader放需要吸顶的部分组件,且包含空列表时需展示的组件,renderSectionFooter根据是请求下一页还是没有更多了进行显示。

<SectionList
          ref={(c) => {
            this.longlist = c
          }}
          style={[comStyles.box, { height: '100%' }]}
          sections={[{ data: list }]}
          renderItem={({ item, index }) => this.renderItem(item)}
          onEndReached={() => {
            if (page * 5 < total) {
              this.setState({ isNext: true })
              this.getList('next')
            }
          }}
          onRefresh={() => this.init(true)}
          refreshing={refreshing}
          stickySectionHeadersEnabled={true} //安卓需手动设置
          onEndReachedThreshold={0.01}
          ListHeaderComponent={this.renderSwiper()}
          renderSectionHeader={() => {
            return (
              <>
                <Tabs
                  tabs={cateList}
                  isScroll
                  selTab={cateId}
                  onChange={(item) => this.setState({ cateId: item.value })}
                  itemStyle={{ marginRight: scaleSize(20) }}
                  style={{ backgroundColor: '#f5f5f5' }}
                />
                {!list?.length && <DefaultPage style={{ height: 200 }} />}
              </>
            )
          }}
          renderSectionFooter={() => {
            return isNext ? <ActivityIndicator size='small' color='#000' /> : page * 5 < total || !total ? <View /> : <Text style={[comStyles.fontGrayS, comStyles.textCenter, { padding: 10 }]}>没有更多了~</Text>
          }}
        />

注意:onEndReached需要判断是否进行请求,否则会造成循环。


方案一缺点:一次渲染所有数据,造成性能问题

方案二缺点:对于复杂的数据,如不同tab显示不同列表,且不同操作,后期维护较为麻烦。

有更好的方法欢迎提出,谢谢!

你可能感兴趣的:(#,React,Native,react,native,react.js,javascript)