react native自定义滚动吸顶位置

自定义滚动吸顶位置

  1. 父组件中主要的工作使用的是动画Animated,对Animated.ScrollView组件的onScroll监听滚动位置,使用Animated.event 将偏移量保存
    import React, {useRef} from 'react';
    import {
      Text,
      View,
      ScrollView,
      Animated,
    } from 'react-native';
    // 这是我自定义的顶部图片
    import EveryDayRecommend from '@/view/DailyRecommendation/components/EveryDayRecommend/EveryDayRecommend';
    // 这个组件使用的是吸顶的组件,抽出来方便其他页面组件使用
    import StickyHeader from '@/view/DailyRecommendation/components/StickyHeader/StickyHeader';
    const DefaultRecommend = () => {
      // 初始化动画
      let scrollY = useRef(new Animated.Value(0)).current;
      // 指定到定位哪个位置停止滚动
      let headHeight = useRef(200).current;
    
      return (
        <View>
          <Animated.ScrollView
          // 通过OnScroll结合 Animated.event保存顶部偏移量
            onScroll={
              Animated.event(
                [
                  {
                    nativeEvent: {contentOffset: {y: scrollY}}, // 滑动距离
                  },
                ],
                {useNativeDriver: true},
              ) // 使用原生动画驱动
            }>
            {/* 自定义吸顶组件 */}
            <StickyHeader
              stickyHeaderY={headHeight} // 吸顶位置
              stickyScrollY={scrollY} // 滚动距离传入
            >
              <EveryDayRecommend
                source={require('@/assets/image/defaultRecommendGif.gif')}
                descBottom="默认的推荐风格"
                stickyHeaderY={headHeight} // 把头部高度传入
                stickyScrollY={scrollY} // 把滑动距离传入
              />
              <Text>全部播放</Text>
            </StickyHeader>
            <ScrollView>
              <View>
                {[...Array.from({length: 100}).keys()].map(item => {
                  return <Text key={item}>{item}</Text>;
                })}
              </View>
            </ScrollView>
          </Animated.ScrollView>
        </View>
      );
    };
    
    export default DefaultRecommend;
    
    
  2. 子组件StickyHeader 中接受了父组件传递的,
    1. stickyHeaderY = null 吸顶位置
    2. stickyScrollY 偏移量
    3. children 传递内容
    4. style 样式
    5. 在组件中使用了 Animated.View 属性onLayout 初始化会返回当前元素的属性,这里使用的情况是防止未传递吸顶高度,所以默认将其偏移量量作为吸顶高度
    import React from 'react';
    import {StyleSheet, Animated} from 'react-native';
    import {useRef} from 'react';
    
    const StickyHeader = ({
      stickyHeaderY = null,
      stickyScrollY,
      children,
      style,
    }) => {
      let stickyLayoutY = useRef(0).current;
      // 判断是否滚动是否存在吸顶高度,如果不存在将当前元素到高度设置为吸顶高度
      let y = stickyHeaderY !== null ? stickyHeaderY : stickyLayoutY;
    
      // 防止没有传头部高度
      const _onLayout = event => {
        stickyLayoutY = event.nativeEvent.layout.y;
      };
    
      return (
        <Animated.View
          onLayout={_onLayout} // 元素初始化时会返回当前元素的属性
          style={[
            style,
            styles.container,
            {
              // interpolate函数用于创建一个动画,
              // 将输入范围映射到输出范围 stickyScrollY是一个Animated.Value,正在被插值以创建一个新的Animated.Value。
              // inputRange数组指定了stickyScrollY值可以取的范围,outputRange数组指定了新的Animated.Value将取的值的范围。
              //  outputRange数组是[0, 0, 0, 1]。这意味着当stickyScrollY小于-1时,新的Animated.Value将为0。
              //  当stickyScrollY在-1和0之间时,新的Animated.Value将为0。当stickyScrollY在y和y + 1之间时,
              //  新的Animated.Value将为0。当stickyScrollY大于y + 1时,新的Animated.Value将为1。
              transform: [
                {
                  translateY: stickyScrollY.interpolate({
                    inputRange: [-1, 0, y, y + 1],
                    outputRange: [0, 0, 0, 1],
                  }),
                },
              ],
            },
          ]}>
          {children}
        </Animated.View>
      );
    };
    
    export default React.memo(StickyHeader);
    
    const styles = StyleSheet.create({
      container: {
        zIndex: 100,
      },
    });
    
    
    
  3. 并且使其组件中的图片会跟随,滚动逐渐隐藏,在父组件中有一个EveryDayRecommend 这里面是一个图片和日期
    1. 主要部分就是通过最后一个Animated.View 进行定位和整个元素重合,设置其透明度,透明度结束位置,就是触顶位置
    import React, {useRef, useState} from 'react';
    import {
      Dimensions,
      Image,
      StyleSheet,
      Text,
      View,
      Animated,
    } from 'react-native';
    import {useTheme} from '@react-navigation/native';
    
    const widthWindow = Dimensions.get('window').width;
    
    const EveryDayRecommend = props => {
      const {otherColor, fontSize} = useTheme();
      // 这个是获取当前日期
      const [currentDate] = useState(new Date());
      return (
        <View style={styles.everyDayRecommend}>
          <Image
            style={[{width: widthWindow, height: 300}]}
            source={props.source}
          />
          <View style={styles.desc}>
            <Text style={[styles.dateCurrent, {color: otherColor.bgFontColor}]}>
              <Text style={{fontSize: fontSize.superBig}}>
                {currentDate.getDate() >= 10
                  ? currentDate.getDate()
                  : '0' + currentDate.getDate()}
              </Text>
              <Text style={{fontSize: fontSize.small}}>
                /
                {currentDate.getMonth() >= 10
                  ? currentDate.getMonth() + 1
                  : '0' + (currentDate.getDay() + 1)}
              </Text>
            </Text>
            <Text style={{color: otherColor.bgFontColor}}>{props.descBottom}</Text>
          </View>
          {
          /* 相当于遮罩层 */
          }
          <Animated.View
            style={[
              styles.shelter,
              {
              // 使用外部传递的stickyScrollY,使用插值opacity
                opacity: props.stickyScrollY?.interpolate({
                  inputRange: [0, props.stickyHeaderY],
                  outputRange: [0, 1],
                }),
              },
            ]}
          />
        </View>
      );
    };
    
    export default EveryDayRecommend;
    
    const styles = StyleSheet.create({
      everyDayRecommend: {
        position: 'relative',
      },
      desc: {
        position: 'absolute',
        bottom: 20,
        paddingHorizontal: 10,
      },
      dateCurrent: {
        flexDirection: 'row',
        fontWeight: 'bold',
      },
      shelter: {
        width: widthWindow,
        height: 300,
        zIndex: 100,
        position: 'absolute',
        top: 0,
        left: 0,
        backgroundColor: '#fff',
      },
    });
    
    
    
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React Native中的自定义控件使用与React相同的组件方式进行实现。以下是自定义控件的基本步骤: 1. 创建一个自定义控件组件:在你的项目中创建一个新的组件,该组件包含你自定义的UI元素。 2. 定义控件属性:你可以在组件的props中定义一些属性,这些属性可以用来设置控件的外观和行为。 3. 实现控件逻辑:在组件的render方法中实现控件的逻辑,包括控件的事件处理、状态管理等。 4. 导出控件:将你的自定义控件组件导出,以便其他组件可以使用它。 以下是一个简单的例子,演示如何创建自定义控件: ``` import React, { Component } from 'react'; import { Text, TouchableOpacity } from 'react-native'; class CustomButton extends Component { constructor(props) { super(props); this.state = { pressed: false, }; } handlePress = () => { this.setState({ pressed: true }); }; handleRelease = () => { this.setState({ pressed: false }); }; render() { const { title, disabled } = this.props; const { pressed } = this.state; const buttonStyle = [ styles.button, disabled && styles.disabled, pressed && styles.pressed, ]; return ( <TouchableOpacity style={buttonStyle} onPress={this.handlePress} onPressOut={this.handleRelease} activeOpacity={0.6} disabled={disabled} > <Text style={styles.text}>{title}</Text> </TouchableOpacity> ); } } const styles = { button: { backgroundColor: '#007aff', paddingVertical: 10, paddingHorizontal: 20, borderRadius: 5, }, disabled: { opacity: 0.5, }, pressed: { backgroundColor: '#0051a8', }, text: { color: '#fff', fontSize: 16, fontWeight: 'bold', textAlign: 'center', }, }; export default CustomButton; ``` 在上面的例子中,我们创建了一个CustomButton组件,它包含一个TouchableOpacity,以及一些属性和状态来控制按钮的外观和行为。在render方法中,我们使用了一些简单的样式来设置按钮的外观,以及一些事件处理来处理按钮的行为。最后,我们将CustomButton组件导出,以便其他组件可以使用它。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值