Flutter新动画组件 - AnimatedPath

1,331 阅读4分钟

这个组件参考了很多大佬的代码和思路,感谢他们的无私分享

先来一些示意图吧:

DrawBoardCase.gif LissajousCase.gif EffectCase.gif

介绍

这是一个能够将一个或多个Path动画化展示的组件。接下来看看入参吧:

final List<Path> Function(Size size) paths;
final Widget? header;
final Color color;
final double strokeWidth;
final StrokeCap strokeCap;
final StrokeJoin strokeJoin;
final Animation<double> animation;
final int? fps;
final EdgeInsetsGeometry padding;

其中,pathscoloranimation必传。

paths

paths是一个需要返回List<Path>的函数,sizeAnimatedPath的大小,由LayoutBuilder获取,目的是为了方便使用者根据Size的不同来创建响应式的Path

比如你想创建一条在正方形内从左上到右下的路径,在10x10的正方形里就是:

Path path = Path();
path.moveTo(0, 0);
path.moveTo(10, 10);

如果是100x100的正方形那就是

Path path = Path();
path.moveTo(0, 0);
path.moveTo(100, 100);

这种情况下这个Size就很有用了。当然,不使用Size也是可以的。

animation

AnimatedPath是一个专注于解析Path的库,不会自己创建AnimatedController然后暴露播放和暂停的函数,而是需要使用者自己创建AnimatedController并将其传入组件中。你也可以使用CurvedAnimation等来对AnimatedController进行装饰。以下是一个示例:

final AnimationController controller = AnimationController(
  duration: const Duration(milliseconds: 2000),
  vsync: this,
);
final CurvedAnimation curve = CurvedAnimation(
  curve: Curves.easeInOut,
  parent: controller,
);
final Tween<double> tween = Tween(begin: 0, end: 1);

// 使用时:
AnimatedPath(
  paths: (size) => createPath(size),
  animation: tween.animate(curve),
)

UI相关

  1. color:必传,线条的颜色
  2. strokWidth:选传,线条的粗细,默认为1
  3. strokCap:选传,线条的结束的样式,默认为StrokCap.square
  4. strokJoin:选传,线条连接的样式,默认为StrokJoin.miter

其中StorkCapStrokJoin最好去参阅一下源码,官方写得更清楚且有一些样例可供参考。

原理

这个库的原理很简单,就是将传入的Path根据Animation的值来创建一个新的PathPath中有一个extractPath函数,传入startend,返回该Path的对应片段。那么这里只需要start传0,end传入进度即可。参考了张风捷特烈大佬的绘制番外中的代码,十分感谢。

扩展功能

扩展功能之fps

在写上面那个利萨茹曲线的示例时,发现一旦将那几个参数设置地大一点,到了动画的后期总是会出现卡顿。于是参考豆豆大佬的flutter_tilt库也实现了一个fps限制器。一般来说无需传入,除非实在很卡那就看情况传个60或者144吧。

扩展功能之头部特效

在做第三个示例时突发奇想想在动画上添加一个火花特效来跟随进度条。于是就加上了headerpadding两个参数。其中header传入一个任意Widget,它将跟随动画的进度而移动。padding则是用于处理一些可能导致header显示不完全的特殊情况。

要实现这个需求要做两件事:1是获取动画进度当前的位置,2是将它展示出来。

在Path的那堆方法里找到一个叫getTangentForOffset的函数,它用于计算当前Path上某个点在当前画面中的位置。那么我只需把前面算出来的新Path的总长度放进去就能完成第一点。

至于第二点的展示我最开始想的是如何将Widget在Painter中画出来。想了很久都没能成功,最后决定还是使用Stack。大致方案是在Stack中以Painter为底然后加上一个或多个Position。因此需要计算出Position的位置。但是如果为直接使用第一点返回的坐标,那header实际上会有偏移。

false.giftrue.gif

左图为直接使用坐标,右图为计算后得出的正确坐标

为了消除偏移,在计算坐标时需要减去header本身的大小,那么该如何获取header的大小呢?我这里选择的是杜文老师的AfterLayout

最后

这个库已经开源并上传到pub.dev,有兴趣的可以试着用一下,能顺手点个赞给个like就更好了,谢谢~

写完了才发现pub上已经有叫animated_path的同名库,只能改成animated_path_builder了..

pub: pub.dev/packages/an…

GitHub: github.com/Dabbit-Chan…