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的制做与使用

项目源码

相关文章
相关标签/搜索