react-native滑动吸顶效果的实现过程


Posted in Javascript onJune 03, 2019

前言

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

需求效果

react-native滑动吸顶效果的实现过程

翻车过程

第一种方案 失败

一开始的思路是这样的,大众思路,我们需要监听页面的滚动状态,当页面滚动到要吸顶元素所处的位置的时候,我们设置它为固定定位,不过很遗憾,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模拟器一跑就无语了?,效果很奇葩,手指滑动时不吸顶直接划上去隐藏掉大半,一松突然吸顶了。。。

见下图

react-native滑动吸顶效果的实现过程

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

总结

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

Javascript 相关文章推荐
鼠标经过的文本框textbox变色
May 21 Javascript
jQuery制作仿腾讯web qq用户体验桌面
Aug 20 Javascript
Jquery的hide及toggle方法让超链接慢慢消失
Sep 06 Javascript
一个不错的字符串转码解码函数(自写)
Jul 31 Javascript
JS函数的定义与调用方法推荐
May 12 Javascript
移动端点击图片放大特效PhotoSwipe.js插件实现
Aug 25 Javascript
jquery自定义表单验证插件
Oct 12 Javascript
利用jQuery实现一个简单的表格上下翻页效果
Mar 14 Javascript
如何选择jQuery版本 1.x? 2.x? 3.x?
Apr 01 jQuery
jQuery实现的简单前端搜索功能示例
Oct 28 jQuery
浅谈Vue组件及组件的注册方法
Aug 24 Javascript
vue页面切换过渡transition效果
Oct 08 Javascript
vue-cli 3 全局过滤器的实例代码详解
Jun 03 #Javascript
vue2之简易的pc端短信验证码的问题及处理方法
Jun 03 #Javascript
使用RxJS更优雅地进行定时请求详析
Jun 02 #Javascript
Vue CLI3基础学习之pages构建多页应用
Jun 02 #Javascript
Vue基础学习之项目整合及优化
Jun 02 #Javascript
JavaScript判断对象和数组的两种方法
May 31 #Javascript
vue中node_modules中第三方模块的修改使用详解
May 31 #Javascript
You might like
PHP 出现乱码和Sessions验证问题的解决方法!
2008/12/06 PHP
PHP substr 截取字符串出现乱码问题解决方法[utf8与gb2312]
2011/12/16 PHP
smarty 缓存控制前的页面静态化原理
2013/03/15 PHP
php实现httpclient类示例
2014/04/08 PHP
C/S和B/S两种架构区别与优缺点分析
2014/10/23 PHP
PHP+MySQL删除操作实例
2015/01/21 PHP
PHP strip_tags() 去字符串中的 HTML、XML 以及 PHP 标签的函数
2016/05/22 PHP
[原创]php简单隔行变色功能实现代码
2016/07/09 PHP
Alliance vs AM BO3 第二场2.13
2021/03/10 DOTA
js用图作提交按钮或超连接
2008/03/26 Javascript
JavaScript 基础问答三
2008/12/03 Javascript
Google排名中的10个最著名的 JavaScript库
2010/04/27 Javascript
js实现的折叠导航示例
2013/11/29 Javascript
JavaScript设计模式之观察者模式(发布者-订阅者模式)
2014/09/24 Javascript
javascript中HTMLDOM操作详解
2014/12/11 Javascript
使用JQuery实现Ctrl+Enter提交表单的方法
2015/10/22 Javascript
jQuery 中的 DOM 操作
2016/04/26 Javascript
实例讲解JavaScript中的this指向错误解决方法
2016/06/13 Javascript
AngularJS表单提交实例详解
2017/02/18 Javascript
JS实现手写parseInt的方法示例
2017/09/24 Javascript
JavaScript实现的弹出遮罩层特效经典示例【基于jQuery】
2019/07/10 jQuery
Antd的table组件表格的序号自增操作
2020/10/27 Javascript
可拖拽组件slider.js使用方法详解
2020/12/04 Javascript
[32:17]完美世界DOTA2联赛循环赛LBZS vs Forest第二场 10月30日
2020/10/31 DOTA
解决Python字典写入文件出行首行有空格的问题
2017/09/27 Python
目前最全的python的就业方向
2018/06/05 Python
python 以16进制打印输出的方法
2018/07/09 Python
使用python读取.text文件特定行的数据方法
2019/01/28 Python
pytorch中获取模型input/output shape实例
2019/12/30 Python
Python3.7安装PyQt5 运行配置Pycharm的详细教程
2020/10/15 Python
python tqdm库的使用
2020/11/30 Python
高级护理专业毕业生推荐信
2013/12/25 职场文书
年终晚会活动方案
2014/08/21 职场文书
2014大学生职业生涯规划书最新范文
2014/09/13 职场文书
如何拟写通知正文?
2019/04/02 职场文书
Python first-order-model实现让照片动起来
2022/06/25 Python