Flare 动画简介
Flutter 的动画大体能够分为应用 AnimationController 和 Animation 管制的根底动画、应用 Hero 的转场动画和应用 CustomPainter 的自定义动画三大类。除此之外,Flutter 还反对矢量动画,是一种相似 Android 开发中的 Lottie 动画。
Flare 是一家能够疾速制作矢量动画的网站,提供专门的 Flutter 组件来承载网站导出的动画文件,应用 Flare 创立的动画不仅能够无效缩小安装包的体积,还能创立更加简单壮丽的动画体验。Flare 动画最早呈现在 2019 年 12 月举办的 Flutter 技术大会上,一经公布立马受到开发者的青睐和追捧。
作为一个业余制作矢量动画的网站,Flare 提供了十分丰盛的收费矢量动画。因为 Flare 并没有提供桌面版的开发工具,所以创立 Flare 动画之前须要登录 Flare 官网来制作 Flare 动画文件,如果还没有 Flare 账号能够先注册一个。
Flare 通常以工程模式来创立和治理动画我的项目,目前 Flare 反对创立动画我的项目有两类,别离是 Flare 和 Nima,它们的区别如下。
- Flare:为 App 和 Web 构建实时、疾速的动画,同时也反对构建游戏利用动画。
- Nima:为游戏引擎和利用构建 2D 动画。
因为 Nima 次要用于构建 2D 游戏动画,所以如果是一般的利用开发只须要新建一个 Flare 我的项目即可。关上 Flare 官网,而后点击【Your Files】菜单即可新建一个 Flare 我的项目,如下图所示。
而后,零碎会初始化一个空白的工作区用于开发者创立和制作动画文件,如下图所示。
在工作区的左上角有两个切换按钮,别离是 SETUP 和 ANIMATE,示意两种不同的工作模式。其中,SETUP 模式用于导入和绘制矢量元素,而 ANIMATE 模式则用于解决矢量元素的动画交互,动画交互须要用到的动画节点名称位于工作区的左下角。
通常,制作 Flare 动画文件是一项业余且简单的工作,如果只是为了体验 Flare 动画的魅力,那么能够应用 Flare 提供的收费矢量动画,如下图所示。
制作 Flare 动画
如果咱们须要创立 Flare 动画,那么首先须要初始化一个动画我的项目,如下图所示。
如上图所示,在工作区的左上角有两个切换按钮,别离是 SETUP 和 ANIMATE,示意两种不同的工作模式。
- SETUP:用于导入和绘制矢量元素
- ANIMATE 用于解决矢量元素的动画交互
在 SETUP 模式下,咱们能够通过 Hierarchy 树状图来查看所有控件的层级构造关系,个别顶级结点是一个 artboard,能够定义 scene 的尺寸、背景色彩等属性。一个 Flare 动画能够有多个 artboard,并且控件都能够领有本人的子控件,子控件会继承父控件的所有变换。如果要增加矢量元素,能够点击 SETUP 模式下工作区的“+”号按钮,如下图所示。
咱们以制作一个按钮为例。首先,咱们抉择菜单中的矩形,而后选中矩形,右侧会呈现属性菜单栏,能够批改地位、大小、色彩、线条等等属性,如下图所示。
当然,咱们也能够按住鼠标右键(或者按住空格拖动鼠标)能够拖动画布,滚轮放大 / 放大,上下左右键准确调整地位,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 的制作与应用
我的项目源码