V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
shuirong1997
V2EX  ›  分享创造

React Native: 用 Animted API 实现了个向上滚动时隐藏 Header 组件的动画

  •  
  •   shuirong1997 ·
    shuiRong · 2018-12-25 17:48:39 +08:00 · 1706 次点击
    这是一个创建于 2163 天前的主题,其中的信息可能已经有所发展或是发生改变。

    想先推荐一下近期在写的一个 React Native 项目,名字叫 Gakki :是一个Mastodon的第三方客户端

    预览

    rn.gif

    #写在前面


    本来我也不想造这个轮子的,奈何没找到合适的组件。只能自己上了~

    思路很清楚: 监听滚动事件,动态修改 Header 组件和 Content 组件的 top 值(当然,他们默认都是 position:relative )。

    接下来实现的时候遇到了问题,我第一个版本是通过动态设置 state 来实现,即:

    /**
     * 每次滚动时,重新设置 headerTop 的值
     */
    onScroll = event =>{
        const y = event.nativeEvent.contentOffset.y
        if (y >= 270) return
        // headerTop 即是 Header 和 Content 的 top 样式对应的值
        this.setState({
            headerTop: y
        })
    }
    

    这样虽然能实现,但是效果不好:明显可以看到在上滑的过程中,Header 组件一卡一卡地向上方移动(一点都不流畅)。

    因为就只能另寻他法了:动画

    React Native 提供了两个互补的动画系统:用于创建精细的交互控制的动画Animated和用于全局的布局动画LayoutAnimation (笔者注:这次没有用到它)

    #Animated 相关 API 介绍


    首先,这儿有一个简单“逐渐显示”动画的DEMO,需要你先看完(文档很简单明了且注释清楚,没必要 Copy 过来)。

    在看懂了 DEMO 的基础上,我们还需要了解两个关键的 API 才能实现完整的效果:

    1. interpolate

    插值函数。用来对不同类型的数值做映射处理。

    当然,这儿是文档说明,可能看了更不清楚:

    Each property can be run through an interpolation first. An interpolation maps input ranges to output ranges, typically using a linear interpolation but also supports easing functions. By default, it will extrapolate the curve beyond the ranges given, but you can also have it clamp the output value.

    翻译:

    每个属性可以先经过插值处理。插值对输入范围和输出范围之间做一个映射,通常使用线性插值,但也支持缓和函数。默认情况下,如果给定数据超出范围,他也可以自行推断出对于的曲线,但您也可以让它箝位输出值( P.S. 最后一句可能翻译错误,因为没搞懂 clamp value 指的是什么, sigh...)

    举个例子:

    在实现一个图片旋转动画时,输入值只能是这样的:

    this.state = {
      rotate: new Animated.Value(0) // 初始化用到的动画变量
    }
    
    ...
    
    // 这么映射是因为 style 样式需要的是 0deg 这样的值,你给它 0 这样的值,它可不能正常工作。因为必定需要一个映射处理。
    this.state.rotate.interpolate({ // 将 0 映射成 0deg,1 映射成 360deg。当然中间的数据也是如此映射。
      inputRange: [0, 1],
      outputRange: ['0deg', '360deg']
    })
    

    2. Animated.event

    一般动画的输入值都是默认设定好的,比如前面 DEMO 中的逐渐显示动画中的透明度:开始是 0,最后是 1。这是已经写死了的。

    但如果有些动画效果需要的不是写死的值,而是动态输入的呢,比如:手势(上滑、下滑,左滑,右滑...)、其它事件。

    那就用到了Animated.event

    直接看一个将滚动事件的 y 值(滚动条距离顶部高度)和我们的动画变量绑定起来的例子:

    // 这段代码表示:在滚动事件触发时,将 event.nativeEvent.contentOffset.y 的值动态绑定到 this.state.headerTop 上
    // 和最前面我通过 this.setState 动态设置的目的一样,但交给 Animated.event 做就不会造成视觉上的卡顿了。
    onScroll={Animated.event([
       {
          nativeEvent: {
            contentOffset: { y: this.state.headerTop }
          }
       }
    ])}
    

    <-- 完整代码移步博客 -->

    1 条回复    2018-12-26 10:34:38 +08:00
    jsq2627
        1
    jsq2627  
       2018-12-26 10:34:38 +08:00
    你这个实现在拉到底部要往上返回的话,header 会伸不出来的吧..
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5673 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 01:32 · PVG 09:32 · LAX 17:32 · JFK 20:32
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.