Flare動畫進階——創建可互動的一拳超人動畫

本文來源:掘金,原作者:安卓小哥
鏈接:https://juejin.im/post/5d1dc60a6fb9a07eaa2299e4

開頭

這次和之前不一樣,我們直接來看最終的動畫實現效果:

(使用琦玉老師作爲例子,是因爲他的畫風比較簡單,非常適合新手操作!!!)

動畫演示完畢,接下來,就是實現過程啦。

聽,是引擎的聲音..

動畫部分

如果你對於Flare的一些基礎使用尚不熟悉,可以先去了解一下這篇文章: 打開Flutter動畫的另一種姿勢——Flare

繪製圖形

首先,我們需要將一拳超人畫出來,可以先在繪圖軟件上生成svg,再導入flare的項目中;或者也可以直接在flare項目中進行繪製。

這裏爲了方便起見,我是通過前者的方式去實現的

將琦玉老師創造成功後,我就可以準備下一步操作了

添加約束

上面的動畫裏面,我們可以看到琦玉的臉部是隨着手指移動的,所以我們需要將一起跟隨移動的部位添加同一個約束

創建一個Node節點,將其display屬性換成target

然後我們開始將多個臉部內容與這個節點約束在一起,下面以左眼眶爲例:

其中我們有對每個約束的 Strength 進行調整,參數爲1時,被約束的控件位置會和節點位置保持一致,所以這裏會根據控件與節點的距離來設置不同的大小

當我們所有約束都設置完成後,就可以看到如下效果:


同時,我們這裏將節點名字設置爲了 ctrl_eyes

之後我們再創建三個非常簡單的動畫,動畫名爲idel、fail、success,其中,fail和success的效果如下:


接下來,我們開始準備用代碼去控制這個動畫!

代碼部分

使用代碼去控制動畫纔是這篇文章的重中之重,在此之前,先確保項目中已經添加了flare的依賴

  flare_flutter: ^1.5.2

上一篇文章中,我們只是使用了flare提供的最基本的功能,現在要真正實現動畫與代碼的交互,就不得不介紹一下 FlareController

FlareController

一般情況下,我們要通過繼承的方式去使用 FlareController ,因爲它是一個抽象類,這個類中有三個需要重寫的方法:

  • **initialize(FlutterActorArtboard artboard)** :這個方法會在動畫初始時調用,在整個FlareController的生命週期中,只會調用一次。其中的 artboard 參數表示畫板對象,可以通過它獲取到所有節點,以及所有的動畫
  • **setViewTransform(Mat2D viewTransform)**: 這個方法用於進行矩陣座標的傳遞,其中的 viewTransform 參數表示flare畫板中的2d矩陣座標
  • **advance(FlutterActorArtboard artboard, double elapsed)** :這個方法會在每一幀都調用一次,操作動畫的主要邏輯就在這裏。其中 elapsed 參數表示消耗的時間

官方給我們提供了一個 FlareControls 類,這個類封裝好了一些基礎的方法,所以我們實現的Controller繼承這個類即可

接下來,我們來實現自定義的Controller,編寫一個 MyController 繼承 FlareControls

看一下其中的部分方法:

class MyController extends FlareControls{
    //用於獲取ctrl_eyes節點
    ActorNode _eyeControl;

    // 存儲"約束臉部節點"座標
    Vec2D _eyeOrigin = Vec2D();
    Vec2D _eyeOriginLocal = Vec2D();
    ...

    @override
    void initialize(FlutterActorArtboard artboard) {
        super.initialize(artboard);
        _eyeControl = artboard.getNode("ctrl_eyes");
        if (_eyeControl != null) {
            _eyeControl.getWorldTranslation(_eyeOrigin);
            Vec2D.copy(_eyeOriginLocal, _eyeControl.translation);
        }
        play("idle");
    }
    ...

}

initialize 中獲取到了之前拖拽的臉部約束節點,並且進行了存儲。

  // 用於存儲從flare轉換到flutter的矩陣
  Mat2D _globalToFlareWorld = Mat2D();

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

setViewTransform 方法中進行了矩陣座標的倒置。

其實關於矩陣座標的相關邏輯都是比較晦澀抽象的,這裏只要照搬即可,下面的 advance 方法同樣如此


  // 在flutter中當前焦點所在的座標
  Vec2D _caretGlobal = Vec2D();

  // 在flare中當前焦點所在的座標
  Vec2D _caretWorld = Vec2D();

  //判斷是否正在輸入
  bool _hasFocus = false;

  String _password = "";

  MyController({this.projectGaze = 100});

  //這個參數用於縮放從輸入焦點到約束節點之間的距離
  final double projectGaze;

  @override
  bool advance(FlutterActorArtboard artboard, double elapsed) {
    super.advance(artboard, elapsed);
    Vec2D targetTranslation;

    if(_hasFocus){
      // 獲取到flare中當前焦點所在的座標
      Vec2D.transformMat2D(_caretWorld, _caretGlobal, _globalToFlareWorld);

      //這裏是實現了動畫的"呼吸"效果,是爲了避免動畫靜止不動,讓動畫更加有趣
      _caretWorld[1] += sin(new DateTime.now().millisecondsSinceEpoch / 300.0) * 70.0;

      // 計算矢量方向
      Vec2D toCaret = Vec2D.subtract(Vec2D(), _caretWorld, _eyeOrigin);

      //獲取比例,再進行縮放
      Vec2D.normalize(toCaret, toCaret);
      Vec2D.scale(toCaret, toCaret, projectGaze);

      //用於計算"約束節點"到輸入焦點到距離
      Mat2D toFaceTransform = Mat2D();
      if (Mat2D.invert(toFaceTransform, _eyeControl.parent.worldTransform)) {
        Vec2D.transformMat2(toCaret, toCaret, toFaceTransform);
        targetTranslation = Vec2D.add(Vec2D(), toCaret, _eyeOriginLocal);
      }
    } else {
      targetTranslation = Vec2D.clone(_eyeOriginLocal);
    }

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

    _eyeControl.translation = frameTranslation;

    return true;
  }

advance 方法返回 true 表示每幀都進行刷新

實現完MyController之後,再搭配官方提供的 tracking_text_input.dartinput_helper.dart 就可以實現 琦玉眼睛跟隨輸入框的效果了

附錄

一、參考文章

Building an Interactive Login Screen with Flare & Flutter

二、Demo地址

【one_punch_demo】

三、Flare動畫地址

www.2dimensions.com/a/homeman/f…

四、環形列表控件

【circle_list】

環形列表是我寫的一個dart插件,後續也許會出一篇開發dart packages的踩坑記錄

喜歡的朋友們麻煩點個關注,你的支持是對我最大的動力!我會定期分享Android知識點及解析,還會不斷更新的BATJ面試專題,歡迎大家前來探討交流,如有好的文章也歡迎投稿。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章