VIP用户交流群:462197261 收藏本站北冥有鱼 互联网前沿资源第一站 助力全行业互联网+
在线客服:78895949
tonglan
  • 当前位置:
  • react-native滑动吸顶效果的实现过程

    前言

    最近公司开发方向偏向移动端,于是就被调去做RN(react-native),体验还不错,当前有个需求是首页中间吸顶的效果,虽然已经很久没写样式了,不过这种常见样式应该是so-easy,没成想翻车了,网上搜索换了几个方案都不行,最后去github上复制封装好的库来实现,现在把翻车过程记录下来。

    需求效果

    翻车过程

    第一种方案 失败

    一开始的思路是这样的,大众思路,我们需要监听页面的滚动状态,当页面滚动到要吸顶元素所处的位置的时候,我们设置它为固定定位,不过很遗憾,RN对于position属性只提供了两种布局方式:absolute和relative,既没有fixed也没有仍处于试验的api:sticky。尴尬了😅

    第二种方案 失败

    不过也不慌,看网上有第二种方案,把图上第二 三块地方作为ScrollView,然后ScrollView滑动监听距离,把第一块的marginTop设为负值,但是这样第一部分不能滑动,不符合需求,pass

    第三种方案 完全失败

    从网上找到第三种方案,就是一二三部分作为ScrollView,

    第一部分position设为absolute,剩下的不设置,默认是relative

    第二部分(吸顶部分)marginTop设置(setState)为第一部分高度的state,

    添加滑动onScroll事件=》滑动距离y等于第二部分marginTop的state,但是当滑动超过第一部分高度的时候把第二部分(吸顶部分)position设为absolute,并把其marginTop设为0,看起来不错,实际用ios模拟器一跑就无语了😅,效果很奇葩,手指滑动时不吸顶直接划上去隐藏掉大半,一松突然吸顶了。。。

    见下图

    ios的系统,手指在屏幕上滚动时,onScroll一直在触发,如果里面有setState方法,也会不停执行并计算state,但是改变react的state是异步的,只要手指不离开屏幕,改变的state就无法生效(触发界面渲染)

    实现方案

    我最终意识到由于ios的机制,react的state机制不能满足需求,RN里面肯定有借助原生渲染的方式,于是github找了现成的代码实现之后,反过来进行研究,大家有RN丰富经验的也可以直接看最下面代码👇

    RN的Animator

    RN的Animator动画库旨在解决动画问题,由于js桥接过程,动画通常不能很好展现,最好是把动画的 数据 和 变化方法 一次性发给原生,由原生进行处理,这就是Animator库的核心作用。

    记得原来RN的动画一直被吐槽,不过现在效果还挺不错的,可能与近年来手机硬件提升也越来越大也有关系吧。

    简单用法

    由于Animator内部封装了这四个组件,所以默认可以导出<Animator.View/>,<Animator.Text/>,<Animator.Image/>,<Animator.ScrollView/>

    在这几个组件里面想做一些动画处理,数据方面也是react的state,但是赋值要给Animated.Value,如下👇

    this.state = {
      scrollY: new Animated.Value(0)
    }

    这里虽然使用的还是原生state,但是经过Animated处理,渲染机制完全不一样了

    简单原理

    经过Animator包装后的组件,会遍历传入的props和自身的state,查找是否有Animated.Value的实例,并绑定进相应的原生操作。

    props和自身的state变化时,将Animated.Value值逐个转化为普通数值,再交给原生进行渲染,但是值得注意的是,这里并不会触发react 的 render,更不会有什么domdiff ,是一种特殊处理,类似于Animated.Value改变时每次的shouldUpdateComponent返回都是false(毫秒级的渲染react性能扛不住),shouldUpdateComponent函数里面判断Animated.Value,然后会把数据变化发给原生组件

    完整的介绍请移步中文官网Animator库介绍

    实现思路

    既然用了Animator组件了,渲染的问题解决了,下面思路是动态设置吸顶组件的translateY属性。style:{ transform: [{ translateY:translateY }] }

    • 当向下滑动时,不管它
    • 向上滑,但是当头部还没有完全隐藏时,也不管它
    • 向上滑,头部完全不见了,这时向上再滑一点,那么他的translateY就应该 = 上划总距离 - 头部高度,这样越往上滑,把吸顶组件使劲往下推,这样吸顶组件就牢牢固定在顶部了

    下面利用插值来实现

    const translateY = ScrollY.interpolate({
      inputRange: [-1, 0, headerHeight, headerHeight + 1],
      outputRange: [0, 0, 0, 1],
    });

    插值interpolate略难理解,需要一点基础,这里再细说起来这篇文章就太长了官网介绍,

    如果还不懂可以去网上找找这方面的资料

    实现源码

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

    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>

    收尾

    具体代码就是这样实现了,算是比较完美的方案,特别是照顾了性能,各位可以基于这个封装来实现更复杂的需求,原理大概就是这个原理了,在前端动画领域,自己确实也就刚入门水平,如有问题,请直接指出。

    另外,这是我找的那个 组件 github的代码地址:https://github.com/jiasongs/react-native-stickyheader,原地址附上,建议如果项目用了给人家一个star

    总结

    以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对北冥有鱼的支持。

    您可能感兴趣的文章:

    • react-native 封装选择弹出框示例(试用ios&android)
    • 详解React Native开源时间日期选择器组件(react-native-datetime)
    • React-Native 组件之 Modal的使用详解
    • react-native 完整实现登录功能的示例代码
    • React-Native中使用验证码倒计时的按钮实例代码
    • react-native动态切换tab组件的方法
    • 探究react-native 源码的图片缓存问题
    • React-Native中一些常用组件的用法详解(一)

    广而告之:
    热门推荐:
    JS逻辑运算符短路操作实例分析

    本文实例分析了JS逻辑运算符短路操作。分享给大家供大家参考,具体如下: js逻辑运算符有三个:逻辑非!、逻辑与&&、逻辑或||。 所谓短路操作就是,当&&的第一个操作数的值是false时,直接返回第一个操作数的值,不再对第二个操作数进行计算; <script> ···

    javascript基于牛顿迭代法实现求浮点数的平方根【递归原理】

    本文实例讲述了javascript基于牛顿迭代法实现求浮点数的平方根。分享给大家供大家参考,具体如下: 今天在网上看到一则利用牛顿迭代法求浮点数的平方根的方法,发现很好,比一些语言自带的sqrt方法运行要快,在这里备份一下,以待后用,这里稍微做了些改动. 首先是牛顿迭代法原理: ···

    PHP小白必须要知道的php基础知识(超实用)

    很多人看到PHP就以为是程序员,就以为钱很多(虽然是事实),但是也要考虑下自己是不是适合这一行,知道PHP是什么吗?PHP都有什么样的功能,都能用来干嘛? PHP是什么? •PHP(PHP: Hypertext Preprocessor,超文本预处理器的缩写),是一 种被广泛应用的开放源代码的···

    PHP实现的汉字拼音转换和公历农历转换类及使用示例

    本文整理了PHP汉字拼音转换和公历农历转换两个功能类文件,非常实用。比如我们查找通讯录可以通过联系人姓名的拼音首字母来查询,可以通过首字母来导航大数据量,可以通过转换拼音来做网站优化等。公农历转化一般用在日历日程安排的项目中,方便农历的节日提醒等等。 1、PHP汉···

    Django+Vue跨域环境配置详解

    概述 在使用Django+Vue开发过程中,遇到了很多开发环境相关的问题,比如跨域,比如ajax请求参数等,本篇文章主要记录解决在开发过程中,遇到的一些问题。 跨域不带Cookie 在使用Vue脚手架开发的过程中,会使用Vue脚手架自带的Server进行项目调试,Vue自带的Server支持 hot rel···

    基于three.js编写的一个项目类示例代码

    WebVR 在开始之前,先对WebVR进行介绍,WebVR是一个实验性的Javascript API,允许HMD(head-mounted displays)连接到web apps,同时能够接受这些设备的位置和动作信息。这让使用Javascript开发VR应用成为可能(当然已经有很多接口API让Javascript作为开发语言了,不过这并不···

    PHP实现视频文件上传完整实例

    本文以一个完整实例的形式实现了视频文件上传的功能。虽然是比较基础的应用,仍有一定的参考价值。分享给大家供大家参考之用。具体方法如下: 首先,对PHP来说视频也属于文件,我们利用这个道理,可以通过与上传普通文件类似的方式实现上传PHP视频文件。不同的是视频文件一般较···

    HTML5里autofocus自动聚焦属性使用介绍

    HTML5给我们带来了一大堆神奇的东西。以前需要用JavaScript和Flash完成的任务,例如表单校验,INPUT placeholders , 客户端重命名下载文件和音频/视频,这些都可以使用基本的HTML完成。这里要讲的一个简单的HTML5功能是当页面加载完成时让输入焦点自动落到某个元素上;这个动作···

    详解Mysql导出数据的几种方式

    MySQL导出数据的目的有很多种,如数据库备份、表结构导出、表数据导出、分析数据采取等。 Part1 select into outfile 先说最短小精悍的select into outfile, 这是小型数据库分析数据最常用的采集数据方式,具体语法如下: 【select 语句】 into outfile 【导出文件名】 【导出···

    css模块化方案

    css的模块化方案可能和js的一样多,下面简单介绍几种主要的模块方案。 oocss 面对对象的规则,主要的原则是两种:分离结构和外观,分离容器和内容。 名词解释 分离结构和外观:增加可重复的设计单元,同时去推进产品和ui对这方面的思考,比如下面的css使用时对象模式的命名和···