Animated详解

匿名 (未验证) 提交于 2019-12-03 00:43:02

https://blog.csdn.net/aiynmimi/article/details/78866899

在APP的开发中,流畅合理的动画能大大提高用户体验,Android和iOS原生都有对应的动画系统,同样的在RN中也有用于创建动画的API,就是Animated。Animated库使得开发者可以非常容易地实现各种各样的动画和交互方式,并且具备极高的性能。

我们想要文本,图片等可以进行动画,就需要使用Animated进行封装
所以创建动画组件有以下5种:

  • Animated.View
  • Animated.Text
  • Animated.Image
  • Animated.ScrollView
  • 封装自定义动画组件:Animated.createAnimatedComponent()

其中前4个使用的时候,只需使用<Animated.xxx>封装即可,之后与普通组件没有区别!第5个封装自定义的动画组件,一般来说使用的不多!

Animated提供了3种动画类型,每种动画类型都提供了特定的函数曲线,用于控制动画值从初始值变化到最终值的变化过程:

  • Animated.decay():以指定的初始速度开始变化,然后变化速度越来越慢直至停下。
  • Animated.spring()
  • Animated.timing()easing函数随着时间的变化。这个类型我们平时用的最多!默认情况下,它采用了对称easeInOut 曲线传达一个对象的逐渐加速到全速,最后逐渐减速到停止。

Animated的值类型有两种:

  • AnimatedValue:单个值
  • AnimatedValueXY:向量值,二维平面

从字面上也能看出,AnimatedValue就是一个单一的值,而AnimatedValueXY则是XY两个方向的值!

举个例子,这里以timing()类型为例说一下动画的配置:

 Animated.timing(   this.state.xPosition,   {     toValue: 100,     easing: Easing.back,     duration: 2000,   }                               ).start();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这里提一下,有关Animated基本上所有相关的API都在AnimatedImplementation.js
node_modules/react-native/Libraries/Animated/src/AnimatedImplementation.js。我们可以看一下timing函数!

 const timing = function(   value: AnimatedValue | AnimatedValueXY,   config: TimingAnimationConfig, ): CompositeAnimation {   ...... }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6


比如例子中toValue到达的值,duration动画持续时间(毫秒),默认值为500。easing设置easing函数类型!关于easing函数类型下边再说!


它的源码在TimingAnimation.js

 export type TimingAnimationConfig = AnimationConfig & {   toValue: number | AnimatedValue | {x: number, y: number} | AnimatedValueXY,   easing?: (value: number) => number,   duration?: number,   delay?: number, };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

我们可以看到它是在AnimationConfig的基础上又添加了4个属性,例子中写了3个,还有一个delay
下边来看一下AnimationConfig,它在Animation.js中:

 export type AnimationConfig = {   isInteraction?: boolean,   useNativeDriver?: boolean,   onComplete?: ?EndCallback,   iterations?: number, };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

isInteraction:是否一起同步运行。这个属性可用于优化动画卡顿,将逻辑计算工作放在UI动画完成后执行,从而来保证动画的流畅度!这块我们放在后边UI优化的时候再具体深入的了解!可以参考InteractionManager
useNativeDriver
onComplete
iterations:设置动画运行迭代次数

最后的最后,别忘了调用start()方法来启动动画!!!!

上边配置中easing用来设置一个Easing函数类型,它的作用是来设置我们要动画改变的value要以什么状态来改变

路径在`node_modules/react-native/Libraries/Animated/src/Easing.js


easing?: (value: number) => number,可以看出接收的是一个函数,将一个value传入,并返回一个改变后的value!而Easing这个类就是干这个的,我们可以随便找一个看一下,比如例子中Easing.back

 static back(s: number): (t: number) => number {     if (s === undefined) {       s = 1.70158;     }     return (t) => t * t * ((s + 1) * t - s);   }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

我们可以看出Easing这个类中的每一个函数都是(value: number) => number类型的,所以我们才可以easing : Easing.back
也就是说官方给我们写好了一系列的easing 供我们选择使用,而不需要我们自己再写了!当然,如果我们想自定义value的变化形式的话,那就要自己写了!

那么我们想要使用哪一种变化类型,就去Easing中选择就可以了,这里就不再一一列举了!

我们可以将多个动画效果组合在一起进行动画,RN提供了四种组合的动画的API:

  • Animated.delay(time: number)
    starts an animation after a given delay.设置动画延迟执行。(相当于timing动画类型中的delay属性)。

  • starts a number of animations at the same time.同时执行一组动画。

    ParallelConfig里边只有一个属性stopTogether,是否一起停止默认为true,如果任何一个动画被停止或中断了,组内所有其它的动画也会被停止。如果想要禁止自动停止,设置为false!

  • Animated.sequence(animations: Array<CompositeAnimation>)
    starts the animations in order, waiting for each to complete before starting the next.按照顺序执行动画,等到上一个动画完成之后才能启动下一个动画。如果当前的动画被中止,后面的动画则不会继续执行。

  • starts animations in order and in parallel, but with successive delays.顺序且并行启动动画,但随着连续的延误。

我们可以使用加乘除以及取余等运算来把两个动画值合成为一个新的动画值。

  • Animated.add():将两个动画值相加计算,得出一个新的动画值。
  • Animated.divide():将两个动画值相除计算,得出一个新的动画值。
  • Animated.modulo():将两个动画值做取模(取余数)计算,得出一个新的动画值。
  • Animated.multiply();将两个动画值相乘计算,得出一个新的动画值。

举个官网的例子,转换缩放比例(2X―>0.5X):

 const a = new Animated.Value(1); const b = Animated.divide(1, a);  Animated.spring(a, {   toValue: 2, }).start();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Animated.event
我们先来看一下event()方法的样子,源码同样在AnimatedImplementation.js

 const event = function(argMapping: Array<?Mapping>, config?: EventConfig): any {   const animatedEvent = new AnimatedEvent(argMapping, config);   if (animatedEvent.__isNative) {     return animatedEvent;   } else {     return animatedEvent.__getHandler();   } };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

可以看到event()函数接收两个参数,第一个参数接受一个映射的数组,对应的解开每个值,然后调用所有对应的输出的setValue方法。第二个参数是可选的,接收一个EventConfig,我们看一下具体可以设置什么?源码在AnimatedEvent.js中:

 export type EventConfig = {   listener?: ?Function,   useNativeDriver?: boolean, };
  • 1
  • 2
  • 3
  • 4

可以看到能够设置两个属性,第一个设置一个监听函数,第二个设置是否使用原生驱动。

举个例子,列表滑动:

 onScroll={Animated.event(   [{nativeEvent: {contentOffset: {x: scrollX}}}],   // scrollX = e.nativeEvent.contentOffset.x   {listener}// 可选的异步监听函数 )}
  • 1
  • 2
  • 3
  • 4
  • 5

上边scrollX被映射到了event.nativeEvent.contentOffset.x(event通常是PanResponder回调函数的第一个参数),而且设置了一个异步的监听函数!

关于PanResponder手势系统,我们在之后再做深入了解!本篇文章只了解动画相关的内容!

AnimatedValue

用于驱动动画的标准值。一个Animated.Value可以用一种同步的方式驱动多个属性,但同时只能被一个行为所驱动。启用一个新的行为(譬如开始一个新的动画,或者运行setValue
下边我只列表几个比较常用的方法:

  • constructor(value)
  • setValue(value)
  • setOffset(offset)
  • flattenOffset()
  • addListener(callback)
  • stopAnimation(callback?)终止动画,并在动画结束后执行callback,参数是动画结束后的最终值,这个值可能会用于同步更新状态与动画位置。
  • interpolate(config)

我们先来看一下interpolate函数:

 interpolate(config: InterpolationConfigType): AnimatedInterpolation {     return new AnimatedInterpolation(this, config);   }
  • 1
  • 2
  • 3

可以看出它的全部实现都是在AnimatedInterpolation.js
函数接收一个InterpolationConfigType类型参数:

 export type InterpolationConfigType = {   inputRange: Array<number>,   /* $FlowFixMe(>=0.38.0 site=react_native_fb,react_native_oss) - Flow error    * detected during the deployment of v0.38.0. To see the error, remove this    * comment and run flow    */   outputRange: Array<number> | Array<string>,   easing?: (input: number) => number,   extrapolate?: ExtrapolateType,   extrapolateLeft?: ExtrapolateType,   extrapolateRight?: ExtrapolateType, };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

interpolate
比如一个最简单的例子:

 value.interpolate({   inputRange: [0, 1],   outputRange: [0, 100], });
  • 1
  • 2
  • 3
  • 4

outputRange参数还可以接收字符串数组,常用于角度旋转时的转换

 value.interpolate({    inputRange: [0, 360],    outputRange: ['0deg', '360deg']  })
  • 1
  • 2
  • 3
  • 4

easing参数应该不陌生了吧,在上边已经提到过了,就是来设置变化的类型!

剩下的3个参数extrapolateextrapolateLeftextrapolateRight属性可以用来限制输出区间outputRange。我们在上边可以看到接收的值类型是ExtrapolateType
ExtrapolateType的取值有3个:

 type ExtrapolateType = 'extend' | 'identity' | 'clamp';
  • 1

默认值是extend,表示允许超出。如果不允许超出输出区间,可以设置clamp


你可能会注意到这里没有一个明显的方法来在动画的过程中读取当前的值――这是出于优化的角度考虑,有些值只有在原生代码运行阶段中才知道。如果你需要在JavaScript中响应当前的值,有两种可能的办法:

  • spring.stopAnimation(callback)会停止动画并且把最终的值作为参数传递给回调函数callback――这在处理手势动画的时候非常有用。
  • spring.addListener(callback)

AnimatedValueXY

用来驱动2D动画的2D值,譬如滑动操作等。API和普通的Animated.Value几乎一样,只不过是个复合结构。它实际上包含两个普通的Animated.Value。

相关的API这里就不再详细说了,需要了解的可以去查看官方文档!

是否开启使用原生驱动,useNativeDriver这个属性,我们在上边也已经提到过,但是它具体是干嘛的呢?



所以需要开启使用原生驱动的话,很简单,配置useNativeDriver:true即可!

需要注意的问题


(翻译)本地驱动程序目前不支持Animated所能做的一切。 主要限制是您只能对非布局属性进行动画处理:像transform和opacity这样的东西可以工作,但是flexbox和position属性不会。 使用Animated.event时,只能使用直接事件而不冒泡事件。 这意味着它不适用于PanResponder,但可以处理像ScrollView#onScroll这样的东西。


好了,先这样,我们下篇再见!

参考文章
[React Native]动画-Animated
React Native开发之动画(Animations)
ReactNative Animated动画详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/aiynmimi/article/details/78866899

文章来源: Animated详解
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!