animation - 在颤振中创建非常自定义的动画
问题描述
我遵循了 flutter.io 上的各种动画教程(补间、交错、过渡等),它们都很棒。
我想探索的是如何根据 UI 对象的组合实际制作自定义动画。
让我们举一个简单的例子,暂停 -> 播放动画。
首先,我们有一个暂停图标,两个竖线。
假设我想
- 通过在最右侧垂直边的中心添加一个额外的角并将其向右移动,将右侧的条变成一个三角形。
- 之后,将该三角形从步骤 1 稍微向左移动,所以它现在粘在最左边的垂直条上,变成一个更大的“三角形”(实际上是一个五边形)。
这看起来像一个播放按钮,而不是一个暂停按钮了。
我将如何实现这种自定义动画?我假设我不能使用图标类。而且我很确定我不应该对小部件这样做,而只是移动它们。
我该从哪里开始探索动画中的这种精确度?
解决方案
@Alaric 的答案为您指出了几个软件包,但并没有真正说明您为什么要使用它们的任何理由。
手头的问题是,您正在谈论的动画在实际工作方式方面相当复杂。有多个项目会随着时间而变化,甚至可能变成一个更大的项目。
您可以采用两种方法来解决此问题。第一种是使用外部动画工具来创建此动画,使用动画工具必须进行项目更改和合并的任何功能。然后,一旦您的动画运行得令您满意,您就必须以某种方式将其导入您的项目中。这就是fluttie 和flare_flutter 插件的用武之地——如果您使用Aftereffects,您可以使用Lottie 导出文件,然后使用fluttie 插件来显示它。Flare 稍微简单一些,因为它适用于颤动,但您仍然在外部创建动画,然后将文件添加到要渲染的资产中。
另一种方法是自己制作动画。这需要三件事:
- 创建一个包含动画的小部件。
- 创建一个 CustomPainter 来实际绘制结果。
- 可选地,另一个类作为控制器来启动/停止/等动画。
如果您使用 GlobalKey 访问它并公开启动/停止方法,则包含动画的小部件也可能是控制器,但这有点混乱。最好有一个外部对象作为控制器 - 你甚至可以按原样使用 AnimationController,尽管它会不那么“干净”。
如果您不传递它,您的小部件中可能会有一个 AnimationController,您可以从控制器或类启动和停止它。它基本上只是从 0 到 1 并返回,并负责重建 CustomPainter(可能使用 AnimatedBuilder)。
这是一个非常基本的示例,不需要外部控制器,因为手势检测发生在小部件中。请注意,每次设置“started”成员时我都不会调用 setState,因为我实际上并不希望它在更改时重建。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: StartStop(),
),
),
);
}
}
class StartStop extends StatefulWidget {
@override
StartStopState createState() {
return new StartStopState();
}
}
class StartStopState extends State<StartStop> with TickerProviderStateMixin<StartStop> {
bool started = false;
AnimationController animationController;
@override
void initState() {
animationController = AnimationController(vsync: this, duration: Duration(milliseconds: 300));
super.initState();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
started ? animationController.forward() : animationController.reverse();
started = !started;
},
child: SizedBox(
width: 100,
height: 100,
child: AnimatedBuilder(
animation: animationController,
builder: (context, child) {
return CustomPaint(
painter: StartStopPainter(animationController.value),
size: Size.infinite,
child: child,
);
},
),
),
);
}
}
class StartStopPainter extends CustomPainter {
final double percentAnimated;
StartStopPainter(this.percentAnimated) : assert(percentAnimated >= 0 && percentAnimated <= 1);
@override
void paint(Canvas canvas, Size size) {
var pausePaint = Paint()..color = Colors.black.withOpacity(1 - percentAnimated);
canvas.drawRect(Rect.fromLTRB(20, 10, 40, 90), pausePaint);
canvas.drawRect(Rect.fromLTRB(60, 10, 80, 90), pausePaint);
var playPaint = Paint()..color = Colors.black.withOpacity(percentAnimated);
canvas.drawPath(Path()..addPolygon([Offset(20, 10), Offset(20, 90), Offset(80, 50)], true), playPaint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
我会将动画的实际自定义部分(将矩形更改为三角形等)留给您。您只需使用输入percentAnimated
来决定要绘制的路径或多边形,而不是使用不透明度和一些不同的绘制调用。
推荐阅读
- python - 如何在python中杀死一个子进程
- html - 阴影与盒子阴影?
- javascript - Jquery 单击不适用于自定义光标(鼠标后的 div)
- html - 溢出隐藏在弹性孩子的孩子身上
- javascript - 黑客是否可以从 iframe 外部检查保存到“window”的 javascript 对象?
- repast-simphony - Repast:如何根据多个条件获取特定的代理集?
- rest - 我可以使用 REST 进行除 CRUD 之外的任何其他操作,例如 BI 功能吗?
- javascript - Android youtube 播放器无法正常工作
- ios - 在自定义 UITableViewCell 中有多个按钮
- java - 将字符串值解析为 Int 数组