簡單使用react-native的Animated動畫

在項目中簡單使用了 react-native 的 Animated 動畫辆脸,這里介紹項目中使用到的兩種場景。
場景一:點擊關閉一個彈窗時,彈窗會慢慢變小到消失事镣,同時運動軌跡是慢慢從中心到標題欄的右側。該場景是在用戶關閉 APP 的功能介紹彈窗時揪胃,能夠讓用戶下次需要了解該功能時知道從哪里獲取信息璃哟。
場景二:點擊切換標簽頁時,被激活的標簽底部的橫線滑動的動畫效果喊递。這個功能很多插件都自帶這個功能随闪,不需要額外定義,我也是在使用 react-native-scrollable-tab-view 庫時添加了一些額外的標簽欄樣式時骚勘,需要自行添加改動畫铐伴。

Animated 動畫

Animated 動畫組件

Animated 中默認導出了以下這些可以直接使用的動畫組件:

  • Animated.Image
  • Animated.ScrollView
  • Animated.Text
  • Animated.View

我們還可以使用 createAnimatedComponent() 方法自定義動畫組件,下面示例中演示了創(chuàng)建一個可點擊的動畫組件:

import { TouchableWithoutFeedback, Animated } from 'react-native';
const AnimatedTouchableWithoutFeedback = Animated.createAnimatedComponent(TouchableWithoutFeedback);

// ...
<AnimatedTouchableWithoutFeedback onPress={() => {...}}>
  <Animated.View style={{ opacity: this.state.fadeInOpacity }} />
</AnimatedTouchableWithoutFeedback>

兩種類型的值

Animated 提供了兩種類型的值:

  • Animated.Value():用于單個值
  • Animated.ValueXY():用于矢量值

配置動畫

Animated 提供了三種動畫類型俏讹。每種動畫類型都提供了特定的函數(shù)曲線当宴,用于控制動畫值從初始值變化到最終值的變化過程:

  • Animated.timing():線性變化,使用 easing 函數(shù)讓數(shù)值隨時間動起來泽疆。
  • Animated.decay():衰變效果户矢,以一個初始的速度和一個衰減系數(shù)逐漸減慢變?yōu)?。殉疼。
  • Animated.spring():彈簧效果梯浪,提供了一個簡單的彈簧物理模型.

大多數(shù)情況下使用 timing()就可以了。默認情況下株依,它使用對稱的 easeInOut 曲線驱证,將對象逐漸加速到全速,然后通過逐漸減速停止結束恋腕。

組合動畫

動畫還可以使用組合函數(shù)以復雜的方式進行組合:

  • Animated.delay():動畫延遲抹锄,在給定延遲后開始動畫。
  • Animated.parallel():同時啟動多個動畫。默認情況下伙单,如果有任何一個動畫停止了获高,其余的也會被停止∥怯可以通過stopTogether 選項設置為 false 來取消這種關聯(lián)念秧。
  • Animated.sequence():按順序啟動動畫,等待每一個動畫完成后再開始下一個動畫布疼。如果當前的動畫被中止摊趾,后面的動畫則不會繼續(xù)執(zhí)行。
  • Animated.stagger():按照給定的延時間隔游两,順序并行的啟動動畫砾层。即在前一個動畫開始之后,隔一段指定時間開始執(zhí)行下一個動畫贱案,并不關心前一個動畫是否已經(jīng)完成肛炮,所以有可能會出現(xiàn)多個動畫同時執(zhí)行的情況。

使用示例

創(chuàng)建動畫最簡單的工作流程是創(chuàng)建一個 Animated.Value 宝踪,將它連接到動畫組件的一個或多個樣式屬性侨糟,然后使用 Animated.timing() 等動畫效果展示數(shù)據(jù)的變化,tart/stop 方法來控制基于時間的動畫執(zhí)行瘩燥。示例中透明度 new Animated.Value(0) 秕重,首先設為 0,后面 timing 動畫中 toValue: 1颤芬,即最終變?yōu)橥耆煌该鞅N覀円部梢远x一個漸隱的效果,從 1 變?yōu)?0站蝠,組件就會慢慢消失汰具。

import React from 'react';
import { Animated, Text, View } from 'react-native';

class FadeInView extends React.Component {
  state = {
    fadeInOpacity: new Animated.Value(0),  // 透明度初始值設為0
  }
  componentDidMount() {
    Animated.timing(                       // 隨時間變化而執(zhí)行動畫
      this.state.fadeInOpacity,            // 動畫中的變量值
      {
        toValue: 1,                        // 透明度最終變?yōu)?,即完全不透明
        duration: 10000,                   // 讓動畫持續(xù)一段時間
      }
    ).start();                             // 開始執(zhí)行動畫
  }
  render() {
    const { fadeInOpacity } = this.state;

    return (
      <Animated.View                       // 使用專門的可動畫化的View組件
        style={{
          ...this.props.style,
          opacity: fadeInOpacity,          // 將透明度指定為動畫變量值
        }}
      >
        {this.props.children}
      </Animated.View>
    );
  }
}

場景一示例

class Comp1 extends React.Component<IProps, {}> {
  constructor(props: IProps) {
    super(props);
    this.state = {
      isModalVisible: false,                      // 彈窗是否可見
      modalWidth: new Animated.Value(1),          // 彈窗初始寬度
      modalHeight: new Animated.Value(1),         // 彈窗初始高度
    };
  }

  startAnimated = () => {
    // 同步執(zhí)行的動畫
    Animated.parallel([
      Animated.timing(this.state.modalHeight, {
        toValue: 0,
        duration: 500,
        easing: Easing.linear,
      }),
      Animated.timing(this.state.modalWidth, {
        toValue: 0,
        duration: 500,
        easing: Easing.linear,
      }),
      // 可以添加其他動畫
    ]).start(() => {
      // 這里可以添加動畫之后要執(zhí)行的函數(shù)
      setTimeout(() => {
        this.setState({ isModalVisible: false });
      }, 100);
    });
  };
  
  render() {
    const modalWidth = this.state.modalWidth.interpolate({
      inputRange: [0, 1],
      outputRange: [1, 300],
    });
    const modalHeight = this.state.modalHeight.interpolate({
      inputRange: [0, 1],
      outputRange: [1, 200],
    });

    return (
      <View>
        {/* 其他組件... */}

        <Animated.View
          style={[
            styles.modalContent,
            {
              width: modalWidth,
              height: modalHeight,
            },
          ]}
        >
          <View style={{ // ... }}>
            <Animated.Text style={[styles.title, { fontSize: titleFontSize }]}>標題</Animated.Text>
            <ScrollView>
              <Animated.Text style={[styles.content, { fontSize: contentFontSize }]}>這是提示文本菱魔,這是提示文本留荔,這是提示文本。</Animated.Text>
            </ScrollView>
            <Button onClick={() => {}}>
              <Animated.Text style={{ fontSize: titleFontSize }}>跳轉(zhuǎn)</Animated.Text>
            </Button>
          </View>
          <TouchableOpacity
            style={{ marginTop: 20, padding: 10, alignItems: 'center' }}
            onPress={this.startAnimated}
          >
            {/* 關閉按鈕 */}
          </TouchableOpacity>
        </Animated.View>
      </View>
    )
  }
}

場景二示例

class Comp2 extends React.Component<IProps, {}> {
  constructor(props: IProps) {
    super(props);
    this.state = {
      lineWidth: new Animated.Value(0),
      lineLeft: new Animated.Value(0),
      prevWidth: 0,
      prevLeft: 0,
      nextWidth: 0,
      nextLeft: 0,
    };
  }

  componentDidUpdate() {
    if (// ...) {
      this.startAnimated();
      this.setState((prevState: any, props: IProps) => ({
        prevLeft: prevState.nextLeft,
        prevWidth: prevState.nextWidth,
        nextLeft: this.tabbarInfos[activeTab].left,
        nextWidth: this.tabbarInfos[activeTab].width,
      }));
    }
  }

  startAnimated = () => {
    this.state.lineWidth.setValue(0);
    this.state.lineLeft.setValue(0);
    Animated.parallel([
      Animated.timing(this.state.lineWidth, {
        toValue: 1,
        duration: 200,
        easing: Easing.linear,
      }),
      Animated.timing(this.state.lineLeft, {
        toValue: 1,
        duration: 200,
        easing: Easing.linear,
      }),
    ]).start();
  };
  
  render() {
    const lineLeft = this.state.lineLeft.interpolate({
      inputRange: [0, 1],
      outputRange: [this.state.prevLeft, this.state.nextLeft],
    });
    const lineWidth = this.state.lineWidth.interpolate({
      inputRange: [0, 1],
      outputRange: [this.state.prevWidth, this.state.nextWidth],
    });

    const tabUnderlineStyle = {
      position: 'absolute',
      height: 1,
      backgroundColor: '#666',
      bottom: 0,
      width: lineWidth,
      left: lineLeft,
    };

    return (
      <View>
        {/* 其他組件... */}

        <Animated.View style={[tabUnderlineStyle, { //... }]} />
      </View>
    )
  }
}

參考文章

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末澜倦,一起剝皮案震驚了整個濱河市聚蝶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌藻治,老刑警劉巖碘勉,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異桩卵,居然都是意外死亡验靡,警方通過查閱死者的電腦和手機倍宾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胜嗓,“玉大人高职,你說我怎么就攤上這事〈侵荩” “怎么了怔锌?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長变过。 經(jīng)常有香客問我埃元,道長,這世上最難降的妖魔是什么媚狰? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任亚情,我火速辦了婚禮,結果婚禮上哈雏,老公的妹妹穿的比我還像新娘。我一直安慰自己衫生,他們只是感情好裳瘪,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著罪针,像睡著了一般彭羹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上泪酱,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天派殷,我揣著相機與錄音,去河邊找鬼墓阀。 笑死毡惜,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的斯撮。 我是一名探鬼主播经伙,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼勿锅!你這毒婦竟也來了帕膜?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤溢十,失蹤者是張志新(化名)和其女友劉穎垮刹,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體张弛,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡荒典,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年酪劫,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片种蝶。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡契耿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出螃征,到底是詐尸還是另有隱情搪桂,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布盯滚,位于F島的核電站踢械,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏魄藕。R本人自食惡果不足惜内列,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望背率。 院中可真熱鬧话瞧,春花似錦、人聲如沸寝姿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饵筑。三九已至埃篓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間根资,已是汗流浹背架专。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留玄帕,地道東北人部脚。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像桨仿,于是被迫代替她去往敵國和親睛低。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1服傍、通過CocoaPods安裝項目名稱項目信息 AFNetworking網(wǎng)絡請求組件 FMDB本地數(shù)據(jù)庫組件 SD...
    陽明先生_X自主閱讀 15,979評論 3 119
  • 塵事空勞如一夢钱雷,幾多瑣屑誤年華。 人生何處尋詩意吹零?微雨空庭數(shù)落花罩抗。
    過雨采蘋閱讀 553評論 1 5
  • 文/寶木笑 上高中的時候套蒂,學習壓力非常大钞支,從高一開始就像進入高考倒計時了,各種考試和排名次整的同窗之間仿佛江湖仇敵...
    寶木笑閱讀 890評論 6 12
  • 他說:螢火是夏夜的光 她抬頭指著月亮操刀,調(diào)皮地說:那它呢 他和她牽手走在安靜的操場 她說:這里沒有很多 夜亦或是你 ...
    原小尚閱讀 258評論 4 1
  • 總是在想你的時候喝一杯紅酒 假裝是個高雅的嗜好 假裝在那喧嘩的世界里過得很好 假裝自己并不孤獨 假裝一切與你無關 ...
    Spancer_Wu閱讀 382評論 1 0