Flutter開發之Flare動畫

Flare動畫簡介

Flutter的動畫大致能夠分爲使用AnimationController 和Animation控制的基礎動畫、使用 Hero的轉場動畫和使用CustomPainter 的自定義動畫三大類。除此以外,Flutter還支持矢量動畫,是一種相似Android開發中的Lottie動畫。git

Flare是一家能夠快速製做矢量動畫的網站,提供專門的Flutter組件來承載網站導出的動畫文件,使用Flare建立的動畫不只能夠有效減小安裝包的體積,還能建立更加複雜絢麗的動畫體驗。Flare動畫最先出如今2019年12月舉行的Flutter技術大會上,一經發布立馬受到開發者的喜好和追捧。github

做爲一個專業製做矢量動畫的網站,Flare提供了很是豐富的免費矢量動畫。因爲Flare並無提供桌面版的開發工具,因此建立Flare動畫以前須要登陸Flare官網來製做Flare動畫文件,若是尚未Flare帳號能夠先註冊一個。segmentfault

Flare一般以工程形式來建立和管理動畫項目,目前Flare支持建立動畫項目有兩類,分別是Flare和Nima,它們的區別以下。windows

  • Flare:爲App和Web構建實時、快速的動畫,同時也支持構建遊戲應用動畫。
  • Nima:爲遊戲引擎和應用構建2D動畫。

因爲Nima主要用於構建2D遊戲動畫,因此若是是普通的應用開發只須要新建一個Flare項目便可。打開Flare官網,而後點擊【Your Files】菜單便可新建一個Flare項目,以下圖所示。bash

在這裏插入圖片描述

而後,系統會初始化一個空白的工做區用於開發者建立和製做動畫文件,以下圖所示。 app

在這裏插入圖片描述
在工做區的左上角有兩個切換按鈕,分別是SETUP和ANIMATE,表示兩種不一樣的工做模式。其中,SETUP模式用於導入和繪製矢量元素,而ANIMATE模式則用於處理矢量元素的動畫交互,動畫交互須要用到的動畫節點名稱位於工做區的左下角。

一般,製做Flare動畫文件是一項專業且複雜的工做,若是隻是爲了體驗Flare動畫的魅力,那麼可使用Flare提供的免費矢量動畫,以下圖所示。 ide

在這裏插入圖片描述

製做Flare動畫

若是咱們須要建立Flare動畫,那麼首先須要初始化一個動畫項目,以下圖所示。 工具

在這裏插入圖片描述
如上圖所示,在工做區的左上角有兩個切換按鈕,分別是SETUP和ANIMATE,表示兩種不一樣的工做模式。

  • SETUP:用於導入和繪製矢量元素
  • ANIMATE用於處理矢量元素的動畫交互

在SETUP模式下,咱們能夠經過Hierarchy樹狀圖來查看全部控件的層級結構關係,通常頂級結點是一個artboard,能夠定義scene的尺寸、背景顏色等屬性。一個Flare動畫能夠有多個artboard,而且控件均可以擁有本身的子控件,子控件會繼承父控件的全部變換。若是要添加矢量元素,能夠點擊 SETUP模式下工做區的「+」號按鈕,以下圖所示。 post

在這裏插入圖片描述
咱們以製做一個按鈕爲例。首先,咱們選擇菜單中的矩形,而後選中矩形,右側會出現屬性菜單欄,能夠修改位置、大小、顏色、線條等等屬性,以下圖所示。
在這裏插入圖片描述
固然,咱們也能夠按住鼠標右鍵(或者按住空格拖動鼠標)能夠拖動畫布,滾輪放大/縮小,上下左右鍵精確調整位置,Shift+上下左右鍵能夠大幅調整位置。接下來,咱們切換到ANIMATE模式添加動畫,底下會多出一行動畫控制面板,以下圖。
在這裏插入圖片描述
首先,打開動畫時長區間,將指針撥到00:01:00(mac可使用快捷鍵command+shift+左右,windows可使用快捷鍵ctrl+shift+左右,一次調整10幀),並在在00:01:00處更改矩形的屬性,以下圖所示。
在這裏插入圖片描述
而後,點擊左下角的播放鍵,效果以下圖。
在這裏插入圖片描述
最後,將製做好的Flare動畫文件導出便可。
在這裏插入圖片描述
關於如何建立Flare動畫,能夠參考官方開源的例子,以及 Flare動畫Flutter動畫之Flare的製做與使用

Flare動畫使用

製做Flare動畫文件是一項專業且複雜的工做,若是隻是爲了體驗Flare動畫的魅力,那麼可使用Flare提供的免費矢量動畫。首先,打開一個免費的矢量動畫,而後點擊面板中【OPEN IN RIVE】按鈕打開Flare動畫文件,以下圖所示。 開發工具

在這裏插入圖片描述
而後,點擊工做區右上角的導出圖標便可導出Flare動畫文件,該文件是一個flr 格式的文件,Flare動畫組件操做的就是該文件。

在Flutter中開發Flare動畫須要使用到flare_flutter或者smart_flare庫。其中,smart_flare庫是對flare_flutter庫的高度封裝,開發者只須要使用少許代碼便可實現與Flare動畫的交互。打開Flutter工程,並在pubspec.yaml文件中添加以下依賴配置。

dependencies:
  flare_flutter: ^2.0.5
  smart_flare: ^0.2.9+1
複製代碼

而後,使用flutter packages get命令將依賴的插件拉取到本地。而後,將以前導出的flr動畫文件拷貝到assets資源目錄下,並在pubspec.yaml配置文件中註冊該動畫文件,以下所示。

assets:
   - assets/button-animation.flr

複製代碼

若是隻是單純的加載動畫文件,而不須要處理與動畫交互,那麼可使用flare_flutter庫提供的FlareActor組件來加載動畫文件,以下所示。

FlareActor(
    "assets/Shake.flr",
    animation: "idle",
    alignment: Alignment.center,
    fit: BoxFit.contain
)

複製代碼

其中,Shake.flr表示Flare動畫文件的名稱,animation表示動畫的初始節點。一般,flr文件會有多個動畫節點,可使用artboard.getNode(String name)方法獲取動畫的節點,而後經過節點來對動畫進行精確地控制。

flare_flutter庫使用

使用flare_flutter庫執行動畫交互操做時,須要咱們繼承FlareControls類,並對initialize()、advance()和setViewTransform()三個方法進行重寫,以下所示。

  • initialize():通常用於動畫的初始化,因爲FlareActor控件已經構建完成,因此能夠在此方法中獲取動畫節點。
  • setViewTransform():每執行一幀動畫都會調用此方法。
  • advance():在每一次動畫即將被刷新的時候調用。

例如,下面是使用flare_flutter庫實現登陸的動畫,在此登陸交互動畫中,效果以下圖。

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

在上面的動畫交互中,主要包含以下6種動畫交互事件,分別是:

  • idle:無任何操做時的狀態(熊的身體會上下浮動和眨眼睛)
  • test:當咱們在 email 輸入框中輸入時的狀態(熊會看向輸入框,且隨着你輸入的長度旋轉頭部)
  • hands_up:當咱們在 password 輸入框中輸入時的狀態 (熊會用手蒙上眼睛)
  • hands_down:當咱們在 password 輸入框輸入完成時的狀態 (熊會放下雙手)
  • fail:當咱們登陸失敗時的狀態(熊會作出難過的表情)
  • success:當咱們登陸成功時的狀態(熊會作出高興的表情)

那麼若是要對用戶的行爲進行精準的響應,那麼就須要咱們繼承FlareControls,而後經過ActorNode的artboard.getNode(String name)獲取節點後執行對於的事件,以下所示。

class FlareSignInController extends FlareControls {

  ActorNode _faceControl;
  Mat2D _globalToFlareWorld = Mat2D();
  Vec2D _caretGlobal = Vec2D();
  Vec2D _caretWorld = Vec2D();
  Vec2D _faceOrigin = Vec2D();
  Vec2D _faceOriginLocal = Vec2D();
  bool _hasFocus = false;
  String _password;
  static const double _projectGaze = 60.0;

  @override
  bool advance(FlutterActorArtboard artboard, double elapsed) {
    super.advance(artboard, elapsed);
    Vec2D targetTranslation;
    if (_hasFocus) {
      Vec2D.transformMat2(_caretWorld, _caretGlobal, _globalToFlareWorld);
      _caretWorld[1] +=
          sin(new DateTime.now().millisecondsSinceEpoch / 300.0) * 70.0;

      Vec2D toCaret = Vec2D.subtract(Vec2D(), _caretWorld, _faceOrigin);
      Vec2D.normalize(toCaret, toCaret);
      Vec2D.scale(toCaret, toCaret, _projectGaze);

      Mat2D toFaceTransform = Mat2D();
      if (Mat2D.invert(toFaceTransform, _faceControl.parent.worldTransform)) {
        Vec2D.transformMat2(toCaret, toCaret, toFaceTransform);
        targetTranslation = Vec2D.add(Vec2D(), toCaret, _faceOriginLocal);
      }
    } else {
      targetTranslation = Vec2D.clone(_faceOriginLocal);
    }

    Vec2D diff =
        Vec2D.subtract(Vec2D(), targetTranslation, _faceControl.translation);
    Vec2D frameTranslation = Vec2D.add(Vec2D(), _faceControl.translation,
        Vec2D.scale(diff, diff, min(1.0, elapsed * 5.0)));

    _faceControl.translation = frameTranslation;

    return true;
  }

  @override
  void initialize(FlutterActorArtboard artboard) {
    super.initialize(artboard);
    _faceControl = artboard.getNode("ctrl_face");
    if (_faceControl != null) {
      _faceControl.getWorldTranslation(_faceOrigin);
      Vec2D.copy(_faceOriginLocal, _faceControl.translation);
    }
    play("idle");
  }

  @override
  void onCompleted(String name) {
    play("idle");
  }

  @override
  void setViewTransform(Mat2D viewTransform) {
    Mat2D.invert(_globalToFlareWorld, viewTransform);
  }

  void lookAt(Offset caret) {
    if (caret == null) {
      _hasFocus = false;
      return;
    }
    _caretGlobal[0] = caret.dx;
    _caretGlobal[1] = caret.dy;
    _hasFocus = true;
  }

  void setPassword(String value) {
    _password = value;
  }

  bool _isCoveringEyes = false;
  coverEyes(cover) {
    if (_isCoveringEyes == cover) {
      return;
    }
    _isCoveringEyes = cover;
    if (cover) {
      play("hands_up");
    } else {
      play("hands_down");
    }
  }

  void submitPassword() {
    if (_password == "bears") {
      play("success");
    } else {
      play("fail");
    }
  }
}

複製代碼

因爲使用flare_flutter庫實現登陸動畫比較複雜,因此詳細的代碼就很少講解,有興趣的能夠看看源碼:登陸動畫源碼

smart_flare庫使用

使用flare_flutter實現的Flare動畫,須要開發者編寫FlareControls來控制動畫交互,須要開發者具有較好的數學和物理基礎,實現起來也比較複雜。相比flare_flutter插件庫來講,使用smart_flare插件庫實現Flare動畫要簡單許多,只須要調用smart_flare插件庫提供的組件,而後傳入對應的參數便可。

ActiveArea(
    debugArea: true,
    area: Rect.fromLTWH(thirdOfWidth*2, 0, thirdOfWidth, animationHeight / 2),
animationName: 'image_tapped',
onAreaTapped: () {
   print('image_tapped…');
}
),

複製代碼

其中,area表示須要顯示的元素在屏幕的位置,animationName表示執行動畫交互時動畫節點的名稱,debugArea表示是否開啓調試模式,若是開啓調試模式會看到該元素區域有一個陰影,onAreaTapped用於響應用戶的點擊,以下圖所示。

例如,下面是使用smart_flare庫提供的SmartFlareActor和ActiveArea組件實現的菜單動畫的例子,效果以下。

在這裏插入圖片描述
下面是smart_flare庫實現按鈕彈出菜單的示例,源碼以下。

class FlareAnimPage extends StatefulWidget {

  @override
  _FlareAnimPageState createState() => _FlareAnimPageState();
}

class _FlareAnimPageState extends State<FlareAnimPage> {

  @override
  Widget build(BuildContext context) {

    var animW = 295.0;
    var animH = 251.0;
    var animWThirds = animW / 3;
    var halfAnimHeight = animH / 2;

    var activeAreas = [
      ActiveArea(
        area: Rect.fromLTWH(0, 0, animWThirds, halfAnimHeight),
        debugArea: false,
        guardComingFrom: ['deactivate'],
        animationName: 'camera_tapped',
      ),

      ActiveArea(
          area: Rect.fromLTWH(animWThirds, 0, animWThirds, halfAnimHeight),
          debugArea: false,
          guardComingFrom: ['deactivate'],
          animationName: 'pulse_tapped'),

      ActiveArea(
          area: Rect.fromLTWH(animWThirds * 2, 0, animWThirds, halfAnimHeight),
          debugArea: false,
          guardComingFrom: ['deactivate'],
          animationName: 'image_tapped',
          onAreaTapped: () {
            print('image_tapped!');
          }
      ),

      ActiveArea(
          area: Rect.fromLTWH(0, animH / 2, animW, animH / 2),
          debugArea: true,
          animationsToCycle: ['activate', 'deactivate'],
          onAreaTapped: () {
            print('Button tapped!');
          })

    ];

    return Scaffold(
      appBar: AppBar(
        title: Text('Flare Anim'),
      ),
      body: Container(
        color: Color(0xffcccccc),
        child: Align(
          alignment: Alignment.bottomCenter,
          child: SmartFlareActor(
            width: animW,
            height: animH,
            filename: 'assets/button-animation.flr',
            startingAnimation: 'deactivate',
            activeAreas: activeAreas,
          ),
        ),
      ),
    );
  }
}
複製代碼

參考:

Flare動畫實例教程

Flutter動畫之Flare的製做與使用

項目源碼

相關文章
相關標籤/搜索