(一)前言
之前我们已经学习过LayoutAnimation布局动画了,在移动客户端开发中流畅并且非常有意思的动画对我们提升用户体验是非常有用。所以官方在开发React Native的时候也已经做了相关动画的API:LayoutAnimation和Animated。其中我们已经讲解过用于全局的布局动画LayoutAnimcation了,今天我们主要进行讲解可以创建非常精细化的动画Animated。力求通过该专题就可以把Animated基础介绍、API以及相关实例实践可以讲解清晰。
本Animated专题会分为:基础篇,实例篇,深入分析篇共三篇。今天是讲解第一篇:基础篇。
(二)Animated内容介绍
Animated库可以让开发者非常容易并且非常高效的性能实现各种的动画以及交互的方式。使用Animated的时候,我们只需要关注设置动画的实现和结束即可,然后在里边设置一个动画可配置的函数。间接着通过start/stop的方法来控制动画按照顺序执行。例如下面就是一个在加载的时候带有比较简单的弹跳动画的效果实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
class
Playground
extends
React.Component {
constructor(props: any) {
super
(props);
this
.state = {
bounceValue:
new
Animated.Value(
0
),
};
}
render(): ReactElement {
return
(
<Animated.Image
// 基础组件: Image, Text, View
style={{
flex:
1
,
transform: [
// `transform` 有顺序的数组
{scale:
this
.state.bounceValue},
// Map `bounceValue` to `scale`
]
}}
/>
);
}
componentDidMount() {
this
.state.bounceValue.setValue(
1.5
);
// 动画开始的时候 设置一个比较大的值
Animated.spring(
// 动画可选类型: spring, decay, timing
this
.state.bounceValue,
// Animate `bounceValue`
{
toValue:
0.8
,
// Animate to smaller size
friction:
1
,
// Bouncier spring
}
).start();
// 开始执行动画
}
}
|
上面代码,其实bounceValue作为state的一部分在构造函数中进行初始化,然后复制给图片的缩放比例。在动画不断执行的时候,该数值会发生改变然后重新设置缩放大小。当组件被挂载的时候,该缩放值设置到1.5,然后在bounceValue值基础上面运行弹跳动画(spring),在动画运行过程中会重新刷新缩放值,同时图片的大小缩放比例也会发生变化了。这种方法比起调用setState方法并且重新刷新组件效率来的高。因为这一切配置都是直接声明配置的,当然我们还可以进一步进行优化动画效果。例如可以配置顺序执行的动画,然后整体动画效果会跟着我们的配置进行相应的执行。
2.0.根据官方的文档整理梳理了一下Animated库需要了解并且掌握的相关内容图如下:
上面是Animated库的相关知识点,接下来我们会一块块的进行详细讲解分析,同时在本专题第二篇会用大量的实例进行演示使用
2.1.核心API(Core API)
我在使用动画的时候大部分需要使用的模块方法都来自Animated模块,该包括两个值类型:①.Value代表单一值,ValueXY代表向量值。②.三个动画类型:spring,decay和timing。③.三个组件类型:View,Text和Image,并且另外通过Animated.createAnimatedComponent自定义创建的动画组件。
通过这三种动画类型我们几乎可以创建任何需要的动画效果:
2.1.1.spring:该为弹跳类型的动画
①.friction: bounciness值,摩擦力值 默认为7
②.tension: 弹跳的速度值 默认为40
2.1.2.decay:带有加速度值的动画,类似于正弦值
①.velocity:初始速度,必须要填写
②.deceleration:速度减小的比例,加速度。默认为0.997
2.1.3.timing:带有时间的渐变动画
①.duration:设置动画持续的时间(单位为毫秒),默认为500ms
②.easing:动画效果的函数,大家可以查看Easing模块查询更多的预定义的函数。iOS平台的默认动画为Easing.inOut(Easing.ease)
③.delay: 动画执行延迟时间(单位:毫秒).默认为0ms
动画通过调用start方法开始执行,start方法可以设置一个回调方法,当动画执行完毕会进行调用该方法。如果动画是正常播放完毕的,那么该回调方法会被执行并且传入参数{finished:true}。但是如果动画是在正常播放完毕之前调用了stop来进行停止的话,那么该会回调传入{finished:false}。
2.2.组合动画(Composing Animations)
如果我们有多个动画,那么可以进行使用单个动画组合起来进行执行。具体可以通过parallel(同时执行),sequence(顺序执行),以及stagger和delay来进行组合使用动画。这个几个类型都可以传入一个需要执行的动画的数组,然后自动start/stop来进行开始动画或者停止动画。具体可以看一下官方的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
Animated.sequence([
// 首先执行decay动画,然后通知执行spring和twirl动画
Animated.decay(position, {
// coast to a stop
velocity: {x: gestureState.vx, y: gestureState.vy},
//通过手势设置相关速度
deceleration:
0.997
,
}),
Animated.parallel([
// 在decay动画执行完成以后,同时执行如下动画
Animated.spring(position, {
toValue: {x:
0
, y:
0
}
// return to start
}),
Animated.timing(twirl, {
// and twirl
toValue:
360
,
}),
]),
]).start();
//开启执行这顺序动画
|
默认情况下,如果一个动画被停止或者中断了,那么在该组合动画中的其他动画也停止了。不过Parallel同时执行动画中有一个stopTogether可选属性,如果设置这个属性为false的话,那么就可以禁止自动停止啦。
2.3.插值动画(interpolate)
Animated库API还提供一个非常强大的功能是interpolate插值动画效果。该允许一个输入的区间范围映射到另外一个输入的区间范围。例如:一个简单的实例0-1的区间映射到0-100的区间范围:
1
2
3
4
|
value.interpolate({
inputRange: [
0
,
1
],
outputRange: [
0
,
100
],
});
|
interpolate还支持多段区间,该用来定义一些静态区间。例如:当我们输入为-300的时候取值相反值。然后在输入到-100的时候变成0,当输入接着0 的时候变成1,接着输入一直到100的时候又回到0。最终形成 的静态区间就为0。大于任何大于100的输入都返回0.具体事例如下:
1
2
3
4
|
value.interpolate({
inputRange: [-
300
, -
100
,
0
,
100
,
101
],
outputRange: [
300
,
0
,
1
,
0
,
0
],
});
|
类似的映射例子如下:
1
2
3
4
|
value.interpolate({
inputRange: [-
400
, -
300
, -
200
, -
100
, -
50
,
0
,
50
,
100
,
101
,
200
],
outputRange: [
450
,
300
,
150
,
0
,
0.5
,
1
,
0.5
,
0
,
0
,
0
],
});
|
interpolation还支持任意形式的渐变方法,很多类型已经Easing类中定义了,例如:二次函数,指数函数,贝塞尔曲线等step以及bounce。interpolation还可以设置区间outputRnage的输出值边界。可以设置extrapolate,extrapolateLeft或者extraplateRight可选属性。默认值为extend(允许超出),但是我们可以设置clamp属性来避免值超出outputRange的最大边界值。
[注意].这一插值函数小节大家可能也有小抽象,其实如果没有用过的人会感觉到非常的抽象,如果大家以前学过离散数学的话,可能理解起来会稍微好一点,不过也不用太担心,我会在后面的博客中专门讲解一下插值函数相关的知识点以及实例的。
2.4.追踪动态值(Tracking Dynamic Values)
在设置的动画中所设定的值可以通过追踪别的值进行获取到。我们只需要不设置toValue为一个普通固定的数组就可以了。例如我们想实现聊天头像的闪动的效果。又例如我们需要timing动画快速的在后面实现。
1
2
3
4
5
6
7
|
Animated.spring(follower, {toValue: leader}).start();
Animated.timing(opacity, {
toValue: pan.x.interpolate({
inputRange: [
0
,
300
],
outputRange: [
1
,
0
],
}),
}).start();
|
ValueXY是可以非常方便的处理2D交互,例如旋转或者拖拽动画。该ValueXY是简单的封装了两个Animated.Value实例以及一些可以去调用的方法。在很多情况下,我们可以使用ValueXY替换Value进行使用。上面的例子中,leader和follower都可以同时为ValueXY类型,这样x和y的值我们都可以获取到了。
2.5.输入事件(Input Events)
Animated.event在Animated API中和手势输入模块相关。该允许手势事件以及其他事件操作。该通过一个结构化得数组进行拆分显示复杂的事件对象。该方法参数是一个数组可以映射输入多个事件的对象。我们来看下面的一个例子:我们把event.nativeEvent.contentOffset.x的值赋值到scrollX变量中(event一般为回调方法的第一个参数)。同时取出手势状态的gestureState.dx和gestureState.dy的值赋值到pan.x何pan.y变量中(gestureState通常为PanResponder回调方法中的第二个参数)。来我们看代码示例:
1
2
3
4
5
6
7
8
9
10
|
onScroll={Animated.event(
// 设置scrollX = e.nativeEvent.contentOffset.x
[{nativeEvent: {contentOffset: {x: scrollX}}}]
)}
onPanResponderMove={Animated.event([
null
,
// 忽略原生事件
// 从gestureState中取出dx和dy的值
// like 'pan.x = gestureState.dx, pan.y = gestureState.dy'
{dx: pan.x, dy: pan.y}
]);
|
2.6.动画运行动态值
经过查看文档,也没有发现特定的方法让我们可以主动去获取在动画执行过程中的动态值信息。但是如果我们需要在JavaScript中可以监听到当前动画的响应值,可以有以下两种方法获取到:
- spring.stopAnimation(callback) 该方法当在停止动画的时候会把最终的响应值通过参数中的回调方法进行回调出来。该在处理手势的时候非常有用。
- spring.addListener(callback) 该当动画在运行过程中会异步回调callback方法,返回一个最近的动画响应值。该方法在触发状态改变的时候非常有用。不过该方法不会特别的灵敏。
(三)最后总结
今天我们主要了解一下React Native中的Animated库,主要进行相关内容,APi模块等等做了初步的介绍。当然我们接下来会继续更新动画模块的实例篇以及深入分析篇,会通过大量的实例来演示具体的使用方法以及深入分析一探动画的奥秘。