今天继续更新RN相关的博客。上篇博客详细的聊了RN中关于Flex布局的相关东西,具体请参见《ReactNative之参照具体示例来看RN中的FlexBox布局》。本篇博客继续更新RN的动画部分,博客中的内容依然是依托于具体的示例来进行的。
下方是官网对RN动画的的一个综述,意思就是说在RN的组件中View、Text、Image 和ScrollView是支持动画的,不过你可以使用Animated.createAnimatedComponent()这个方法来创建一个支持动画的组件稍后会介绍到。这些支持动画的组件在使用动画是都差不多,本篇博客中的示例主要以View为主,也会有Text、Image的部分动画。
Animated
exports four animatable component types:View
,Text
,Image
, andScrollView
, but you can also create your own usingAnimated.createAnimatedComponent()
.
第一部分我们先从一个简单的Moving动画来入手。这个Moving动画是非常简单的,就是一个View,然后点击这个View,会从一个地方移动到另一个地方。然后再次点击会回归到原位。下方是效果实现的分析:
下方代码段是上述动画效果的部分代码。代码比较简单:
上面这段代码是动画设置的相关代码,下方这块代码是动画使用的相关代码片段。下方是对这段代码的解析:
完整代码片段:
1 type States = {
2 moveValue: Animated.Value // 存储动画的值
3 }
4
5 class MoveViewTestComponent extends Component<null, States> {
6 toValue = 0
7 constructor (props) {
8 super(props)
9 this.state = {
10 moveValue: new Animated.Value(0)
11 }
12 }
13
14 pressView = () => {
15 this.toValue = this.toValue === 0 ? 1 : 0
16 Animated.timing(
17 this.state.moveValue, // 初始化从0开始
18 {
19 toValue: this.toValue, // 目标值
20 duration: 1000, // 时间间隔
21 easing: Easing.bounce // 缓动函数
22 }
23 ).start()
24 }
25
26 render () {
27 let { moveValue } = this.state
28
29 let toValue = moveValue.interpolate({
30 inputRange: [0,1],
31 outputRange: ['10%', '60%']
32 })
33 return (
34 <TouchableOpacity onPress={this.pressView}>
35 <Animated.View // 使用专门的可动画化的View组件
36 style={{
37 width: 100,
38 height: 50,
39 backgroundColor: 'red',
40 left: toValue,
41 }}
42 >
43 <Text style={{ color: '#fff' }}> Tap Me Move </Text>
44 </Animated.View>
45 </TouchableOpacity>
46 )
47 }
48 }
上面设设置的left属性,我们还可以对上述代码进行修改,使用动画来改变每个角的弧度,具体动画效果如下所示:
代码就比较简单了,就是把动画的值直接赋值给我们的 borderRadius 属性即可。
在上面的示例中我们指定的移动动画的Bounce效果,下方我们将通过一个示例来看一下Easing中所有的效果,具体动画效果如下所示。从下方的示例中我们不难看出,每种效果的动画运动轨迹都不同,我们在平时开发中可以根据自己的需要来选择相关的效果。当然我们还可以通过矩阵来定义动画的变换路径,在此就不做过多赘述了。
接下来我们来看一下上述动画实现的一个Demo的设计与实现。
首先我们定义了一个MoveView组件,也就是对应着上述Demo中的每个Button,上面可点击可移动的每个View就是一个MoveView。该View会从外部接收一个easing参数,该参数会被设置为该View的动画效果。具体代码如下所示:
然后我们创建了一个 TestMoveView 的一个组件,这个组件就是上述演示的内容。在 TestMoveView 中我们定义了两个数组,第一个数组用来存放每个按钮的Title, 第二个数组则用来存放相关按钮的动画类型。稍后会用到下方的这两个数组。
下方就是两个比较核心的方法了, 下方是对这两个方法的介绍
最后部分我们就来看一下 点击Tap Me 按钮所执行的相关方法了,下方的Click就是该方法。 在Click方法中主要做的就是调用 this.refs 方法获取所有可移动的MoveView的对象,然后调用该对象的pressView方法执行相关的动画。所有点击 Tap Me 按钮,下方所有的按钮都会运动。
完整代码:
1 import React,{ Component } from 'react'
2 // import MoveView from './MoveView'
3 import { Animated, Easing, Text, TouchableOpacity, View } from 'react-native'
4
5 type EasingType = (value: number) => number
6
7 export default class TestMoveView extends Component {
8 animatedKey: string[] = [
9 'linear',
10 'quad',
11 'cubic',
12 'sin',
13 'exp',
14 'bounce',
15 'poly-5',
16 'elastic-2',
17 'back-2',
18 'bezier'
19 ]
20
21 animatedEasingType: EasingType[] = [
22 Easing.linear,
23 Easing.quad,
24 Easing.cubic,
25 Easing.sin,
26 Easing.exp,
27 Easing.bounce,
28 Easing.poly(5),
29 Easing.elastic(2),
30 Easing.back(2),
31 Easing.bezier(0,1.6, 1,-0.67)
32 ]
33
34 click = () => {
35 for (let i = 0; i < this.animatedKey.length; i++) {
36 this.refs[this.animatedKey[i]].pressView()
37 }
38 }
39
40 item = (title: string, easing: EasingType) => {
41 return (
42 <MoveView
43 easing= {easing}
44 ref = {title}>
45 <Text style={{ fontSize: 17, textAlign: 'center' }}>
46 {title}
47 </Text>
48 </MoveView>
49 )
50 }
51
52 createItems = () => {
53 let items = []
54 for (let i = 0; i < this.animatedKey.length; i++) {
55 items.push(this.item(this.animatedKey[i], this.animatedEasingType[i]))
56 }
57 return items
58 }
59
60 render () {
61 console.log('lizelu')
62 return (
63 <View style={{ flex: 1, justifyContent: 'center' }}>
64 <TouchableOpacity onPress={this.click}>
65 <Text>Tap Me</Text>
66 </TouchableOpacity>
67 { this.createItems() }
68 </View>
69 )
70 }
71 }
72
73 // MoveView
74
75 type MoveViewProps = {
76 easing?: (value: number) => number
77 }
78
79 class MoveView extends Component<MoveViewProps> {
80 toValue = 0
81 state = {
82 moveValue: new Animated.Value(0)
83 }
84
85 pressView = () => {
86 this.toValue = this.toValue === 0 ? 1 : 0
87 Animated.timing(
88 this.state.moveValue, // 初始化从0开始
89 {
90 toValue: this.toValue, // 目标值
91 duration: 1000, // 时间间隔
92 easing: this.props.easing // 动画效果
93 }
94 ).start()
95 }
96
97 render () {
98 let { moveValue } = this.state
99 let toValue = moveValue.interpolate({
100 inputRange: [0,1],
101 outputRange: ['10%', '70%']
102 })
103 return (
104 <TouchableOpacity onPress={this.pressView}>
105 <Animated.View // 使用专门的可动画化的View组件
106 style={[{
107 width: 80,
108 height: 30,
109 backgroundColor: 'powderblue',
110 margin: 2,
111 left: toValue // 动画的目标值
112 }]}
113 >
114 {this.props.children}
115 </Animated.View>
116 </TouchableOpacity>
117 )
118 }
119 }
接下来我们通过一个Loading中经常使用的旋转动画,来看一下RN动画中的插值函数。下方的Loading动画本质上就是一张图片在不停的转圈,不过在转圈的时候我们设置了一个Circle的动画效果。
需要实现的效果就是上面这个效果,然后我们看一下代码实现以及插值函数的使用。首先我们来看一下上述动画启动时的相关代码:
然后就是Render方法中获取动画值,给相关的组件设置动画了,具体代码如下所示:
经过上述步骤我们的图片就转起来了。插值函数在动画中还是比较常用的,上面是把 0 ~ 1映射成角度,我们还可以将该值映射成透明度、颜色等等,总之插值函数是RN动画中比较重要的角色。而且我们可以给一个RN元素设置多个插值动画,这样这个元素就会有多个动画效果。
下方这个动画就由多个插值函数结合着多种变换方式组合而成的,分别是移动、旋转、放大这三种变换同时作用到一个View上所展示的效果,具体效果如下所示:
上述效果是在第一个转圈的动画中丰富了一下而形成的,具体代码如下:
上面是缩放、移动、旋转的变换。下面我们来看一下斜切的变换。下方第一个是X方向上的斜切,第二个是Y轴方向上的斜切。
代码也比较简单,下方是设置斜切的相关代码:
天不早了,今天博客就先到这儿,下篇博客继续RN动画的相关内容。下篇博客我们会通过一系列的“拉皮条”操作来看一下RN中的Spring动画。下篇的“拉皮条”的示例还是比较有意思的。稍后会更新。