针对猴急一些的同窗,能够先在这个 Expo网站在线运行下demo看看效果 。javascript
完整的代码,在 Github仓库 。html
下拉刷新,是一个很常见的交互方式。React-Native(如下简称RN)内置的 FlatList
是支持下拉刷新组件的,经过设置 refreshControl 属性便可。一般咱们不只仅须要定制下拉组件,还须要在下拉过程当中,下拉组件执行一些动画,好比在咱们场景下,公司logo会随着下拉的幅度,不一样的笔画仍是显现出颜色。这就须要咱们的下拉组件,知道当前下拉的幅度,以此来计算咱们动画执行的进度。显然,RN官方的 refreshControl
并不能知足咱们的需求。前端
看到有两个已经存在的开源包 react-native-pull-refresh 和 react-native-ptr-control ,基本都有2年左右历史了,并且我也确实没看懂,为何要用到 两个 ScrollView
嵌套来实现。java
直观上来看,我应该只须要有一个 ScrollView
就能够了,我监听下拉距离,从新render自定义的下拉组件。嗯,按照这个思路,尝试撸一个试试。node
首先,咱们提供的是一个容器(命名为 PullToRefresh
吧 ),内部是用户经过 children
传进来的 FlatList
,这样也方便用户修改,在须要自定义下拉刷新的场景下,用咱们这个容器把已经存在的 FlatList
包起来就能够了,改动也挺小。固然,由于是自定义下拉刷新header,确定还须要用户把自定义的下拉刷新header组件传进来,就命名为 props.HeaderComponent
吧,到这一步,咱们容器内render出来的DOM结构,大概是这样的:react
<View>
<Animated.View><HeaderComponent /></Animated.View> <FlatList /> </View>
复制代码
最外层的 View
的展现区域,和用户本身的 FlatList
彻底同样。那么问题来了,咱们的下拉刷新 HeaderComponent 在默认状况下,应该是不可见的,是在用户下拉过程当中,逐渐的从上到下进入容器的可视区域。那就默认把 HeaderComponent 绝对定位到容器可视区域的外边吧,但是往上移动多大呢,这就须要用户告诉咱们容器一个下拉组件的高度了,props.headerHeight
,到这一步,容器渲染出来的样式大概以下:git
<View>
<Animated.View style={{position: 'absolute', top: - this.props.headerHeight}}> <HeaderComponent /> </Animated.View> <FlatList /> </View>
复制代码
完成了初始的DOM结构样式,接下来容器下拉时机的问题。github
首先,何时用户下拉,是触发咱们容器的下拉操做,而不是内部的 FlatList 的默认下拉呢?这个好像比较直接,当内部的 FlatList 已经下拉到顶部,不能再继续下拉时,用户的下拉动做,就应该触发容器的下拉。那么,咱们就须要知道内部的 FlatList 的当前下拉位置,这能够经过 FlatList 的 onScroll
属性来获取当前 FlatList 的滚动距离。小程序
什么时机触发容器的下拉肯定了,那在容器下拉过程当中,咱们须要更新哪些组件呢?1) 自定义header组件确定要更新,将最新的下拉距离传给header组件。2) 若是只是将header组件往下移动,咱们的 FlatList 不动,那么自定义header会遮挡住 FlatList 的内容,这不是咱们想要的;所以,在容器下拉过程当中,内部的 FlatList 位置也须要响应的往下移动。react-native
若是咱们用容器的 state.containerTop
这个 Animated.Value
来保存当前容器下拉的距离,那么目前咱们容器render的DOM结果大概以下:
const headerStyle = {
position: 'absolute',
left: 0,
width: '100%',
top: -this.props.headerHeight,
transform: [{ translateY: this.state.containerTop }],
};
<View>
<Animated.View style={[{ flex: 1, transform: [{ translateY: this.state.containerTop }] }]}>
<FlatList />
</Animated.View>
<Animated.View style={headerStyle}>
<HeaderComponent />
</Animated.View>
</View>
复制代码
这样,基本就完成了容器下拉过程当中,自定义header和内部的FlatList同步下拉了。
下拉动做实现了,那下拉到什么位置,能够触发刷新呢?这就须要用户再传递一个触发刷新的下拉距离,就叫 props.refreshTriggerHeight
吧,当用户松开时,若是当前下拉距离 >= props.refreshTriggerHeight
,就会调用用户传入的刷新函数 props.onRefresh
。一般,用户若是下拉的距离比较大,松开手指时触发了刷新动做,这时候会整个组件会先回跳到一个刷新中的位置,这个位置,用户能够经过 props.refreshingHoldHeight
来指定。props.refreshTriggerHeight
和 props.refreshingHoldHeight
都是可选的,若是用户不传,默认为 props.headerHeight
。
上面其实还省略了一些工做,最重要的,就是在容器下拉过程当中,怎么把下拉距离(下拉进度)传给用户的自定义 HeaderComponent ?上面容器上的 state.containerTop
其实就是当前容器下拉距离,只不过这是一个 Animated.Value
,咱们 不能 读取到它当前的值。所以,我在容器上添加了一个 实例属性 this.containerTranslateY
来保存当前容器下拉的距离,咱们会监听 state.containerTop
值的变化,在回调函数里,修改 this.containerTranslateY
。
等等!!containerTranslateY
为何没有放到容器的 state
上呢?不该该是 this.state.containerTranslateY
么??嗯,刚开始我确实是放在 state
上的,而后在用户下拉容器过程当中,经过在容器上 setState
,触发容器从新render,而后把 containerTranslateY
传递过header。可是,这样经过容器上 setState
触发header更新的方式,在我测试中,发现页面会比较卡顿。所以,在用户下拉容器过程当中,并没有去修改容器的 state
,而是经过 方法调用 的命令方式,将用户当前下拉距离传给了header组件。这里可能还能够怎么优化一下吧。I'm not sure.
所以,用户的自定义header组件,必须 暴露一个实例方法 setProgress
来接收容器下拉过程当中的一些参数,目前这个方法的签名是这样的:
// pullDistance 表示容器下拉的距离;percent 表明下拉的进度,[0, 1]
setProgress({pullDistance, percent}){}
复制代码
完整的 header 组件demo,请参考 expo上的运行demo 。
最后,据说,微交互动画,使用 lottie 和 RN 更配哦。
原本想尝试用 AE 作一个公司logo的 lottie
动画的,奈何没hold住……
完整的代码在github上:github.com/sophister/r… 。
人人贷大端技术博客中心
最后广而告之。 欢迎访问 人人贷大前端技术博客中心
里面有关 nodejs
react
react native
小程序 前端工程化等相关的技术文章陆续更新中,欢迎访问和吐槽~