ReactNative之結合具體示例來看RN中的的Timing動畫

今天繼續更新RN相關的博客。上篇博客詳細的聊了RN中關於Flex佈局的相關東西,具體請參見《ReactNative之參照具體示例來看RN中的FlexBox佈局》。本篇博客繼續更新RN的動畫部分,博客中的內容依然是依託於具體的示例來進行的。html

下方是官網對RN動畫的的一個綜述,意思就是說在RN的組件中View、Text、Image 和ScrollView是支持動畫的,不過你可使用Animated.createAnimatedComponent()這個方法來建立一個支持動畫的組件稍後會介紹到。這些支持動畫的組件在使用動畫是都差很少,本篇博客中的示例主要以View爲主,也會有Text、Image的部分動畫。react

Animated exports four animatable component types: ViewTextImage, and ScrollView, but you can also create your own using Animated.createAnimatedComponent().react-native

 

1、一個簡單的Moving動畫

第一部分咱們先從一個簡單的Moving動畫來入手。這個Moving動畫是很是簡單的,就是一個View,而後點擊這個View,會從一個地方移動到另外一個地方。而後再次點擊會迴歸到原位。下方是效果實現的分析:數組

  • 首先咱們會爲View添加一個點擊事件,點擊View時,從一個位置移到另外一個位置。
  • 再次點擊時,會回到上次的一個位置。
  • 在移動時咱們加了一個Bounce的一個動畫效果。

  

 

下方代碼段是上述動畫效果的部分代碼。代碼比較簡單:ide

  • 首先在State中定義了一個類型爲 Animated.Value 的動畫值,該值就負責來記錄動畫路徑的值。該值在組件的構造器中進行了初始化,其初始值爲零。
  • 而後就是 pressView 方法了,該方法就是上述紅色View點擊時所執行的方法。爲了點擊反覆移動,咱們使用了 toValue來記錄下次要運動的重點值。這個toValue值 0 和 1稍後會詳細介紹。
  • 而後就是關鍵了,調用了Animated 的timing 方法,該方法是用來配置一些動畫效果的,好比設置動畫執行時間的duration(單位爲ms)、設置目標值的 toValue屬性,以及指定緩動效果的熟悉 easing。關於這個easing下方會有詳細的Demo介紹到。
  • 設置完動畫所執行的參數後,就調用了start() 方法來執行這個動畫了。

  

 

上面這段代碼是動畫設置的相關代碼,下方這塊代碼是動畫使用的相關代碼片斷。下方是對這段代碼的解析:函數

  • 首先是從state中取出了動畫值,咱們將該值付給了moveValue。
  • 而後咱們是根據這個 moveValue 經過差值函數建立了一個 toValue的值,這個值就是咱們動畫的目標值,也是咱們真正要使用的值。
  • 這個 interpolate() 差值函數負責用來指定 toValue的生成規則, 該函數能夠把這個

  

 完整代碼片斷:oop

 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 }
View Code

 

上面設設置的left屬性,咱們還能夠對上述代碼進行修改,使用動畫來改變每一個角的弧度,具體動畫效果以下所示:佈局

 

代碼就比較簡單了,就是把動畫的值直接賦值給咱們的 borderRadius 屬性便可。flex

  

 

 

2、使用Easing函數指定相關的動畫效果

在上面的示例中咱們指定的移動動畫的Bounce效果,下方咱們將經過一個示例來看一下Easing中全部的效果,具體動畫效果以下所示。從下方的示例中咱們不難看出,每種效果的動畫運動軌跡都不一樣,咱們在平時開發中能夠根據本身的須要來選擇相關的效果。固然咱們還能夠經過矩陣來定義動畫的變換路徑,在此就不作過多贅述了。動畫

  

 

接下來咱們來看一下上述動畫實現的一個Demo的設計與實現。

首先咱們定義了一個MoveView組件,也就是對應着上述Demo中的每一個Button,上面可點擊可移動的每一個View就是一個MoveView。該View會從外部接收一個easing參數,該參數會被設置爲該View的動畫效果。具體代碼以下所示:

  

 

 而後咱們建立了一個 TestMoveView 的一個組件,這個組件就是上述演示的內容。在 TestMoveView 中咱們定義了兩個數組,第一個數組用來存放每一個按鈕的Title, 第二個數組則用來存放相關按鈕的動畫類型。稍後會用到下方的這兩個數組。

  

 

下方就是兩個比較核心的方法了, 下方是對這兩個方法的介紹

  • 第一個 item 方法用來建立一個 MoveView,該View對應的就是上述一個按鈕。第一個參數Title就是按鈕上的title, 二後邊的easing則是動畫效果。
  • 在Item方法中咱們給 MoveView 設置了一個ref的屬性,該屬性的Value值咱們用的是按鈕上的Title。設置完這個ref值後,咱們可使用 this.refs 來獲取相關key值的對象。稍後咱們會用到。 
  • 而後就是 createItem 方法了,該方法負責調用 上面咱們事先建立好的數組,從數組中取出相關的值,而後調用 item 方法建立一系列的 MoveView 放到相關的數組裏並返回。
  • 在 Render 方法中咱們就能夠調用下方的這個 createItem 方法來建立相關的按鈕了。上的圖片中能動的按鈕都是經過這個 CreateItem 方法來建立的。

  

 

最後部分咱們就來看一下 點擊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 }
Easing

 

 

3、動畫的插值函數及transform

一、插值函數

接下來咱們經過一個Loading中常常使用的旋轉動畫,來看一下RN動畫中的插值函數。下方的Loading動畫本質上就是一張圖片在不停的轉圈,不過在轉圈的時候咱們設置了一個Circle的動畫效果。

  

須要實現的效果就是上面這個效果,而後咱們看一下代碼實現以及插值函數的使用。首先咱們來看一下上述動畫啓動時的相關代碼:

  • 首先在 ComponentDidMount 方法中調用了啓動方法的函數 startAnimation
  • startAnimation 函數中,咱們經過 Animation的 loop 方法來執行循環動畫動畫的值從 0 到 1
  • 而且咱們設置了動畫效果爲 circle
  • 最後就是調用start方法啓動動畫了。

  

 

而後就是Render方法中獲取動畫值,給相關的組件設置動畫了,具體代碼以下所示:

  • 首先咱們從state中獲取到相關的動畫值 animationValue
  • 而後調用該動畫值的插值函數 interpolate,將動畫值中的 0~1的範圍映射成角度 0deg ~ 360deg
  • 最後就是將這個插值函數生成的值 rotateZValue設置給 Image的transform便可。

  

 

二、Transform

通過上述步驟咱們的圖片就轉起來了。插值函數在動畫中仍是比較經常使用的,上面是把 0 ~ 1映射成角度,咱們還能夠將該值映射成透明度、顏色等等,總之插值函數是RN動畫中比較重要的角色。並且咱們能夠給一個RN元素設置多個插值動畫,這樣這個元素就會有多個動畫效果。

下方這個動畫就由多個插值函數結合着多種變換方式組合而成的,分別是移動、旋轉、放大這三種變換同時做用到一個View上所展現的效果,具體效果以下所示:

  

上述效果是在第一個轉圈的動畫中豐富了一下而造成的,具體代碼以下:

  • 前兩個負責生成移動和縮放效果使用的值的插值函數和上面那個轉圈的比較一致,只不過映射的值不一樣。
  • 而後看第三個旋轉使用的插值函數就稍微有點不一樣了,該插值函數能夠將 0 ~ 1 不一樣的區間的值映射成不一樣範圍的值, 從這個旋轉的插值函數的映射關係不難看出,上述View的旋轉路徑是先快後慢的,這一點在插值函數中也是比較經常使用的。
  • 最後就是將這三個插值函數所生成的結果設置在View的 transform 的各個key中就能夠了。

  

 

上面是縮放、移動、旋轉的變換。下面咱們來看一下斜切的變換。下方第一個是X方向上的斜切,第二個是Y軸方向上的斜切。

 

    

代碼也比較簡單,下方是設置斜切的相關代碼:

  

  

天不早了,今天博客就先到這兒,下篇博客繼續RN動畫的相關內容。下篇博客咱們會經過一系列的「拉皮條」操做來看一下RN中的Spring動畫。下篇的「拉皮條」的示例仍是比較有意思的。稍後會更新。

相關文章
相關標籤/搜索