Flutter中FlareActor动画时长获取设置及手写数字动画同步问题
嘿,我来帮你搞定这个Flare手写动画同步的问题!硬编码延迟确实会让动画序列显得生硬,咱们可以通过获取Flare动画实际时长或者自定义播放速度来解决,同时也能实现多组件间的有序同步。
一、获取Flare动画的实际时长
FlareActor本身支持通过自定义FlareController来访问动画的原始时长数据,这样就能精准控制后续动画的启动时机,完全不用硬编码延迟。
步骤1:实现自定义Flare控制器
这个控制器会帮我们加载动画并获取其时长,同时提供播放动画的方法:
class HandwrittenFlareController extends FlareController { late FlareAnimationLayer _animationLayer; double? animationDuration; // 存储动画时长(单位:秒) @override void initialize(FlutterActorArtboard artboard) { // 找到我们定义的"write"动画 final ActorAnimation? writeAnimation = artboard.getAnimation("write"); if (writeAnimation != null) { animationDuration = writeAnimation.duration; _animationLayer = FlareAnimationLayer() ..animation = writeAnimation ..name = "write"; } } @override bool advance(FlutterActorArtboard artboard, double elapsed) { _animationLayer.advance(elapsed); _animationLayer.apply(artboard); return true; } @override void setViewTransform(Mat2D viewTransform) {} // 暴露方法:重置并播放动画 void triggerAnimation() { _animationLayer.time = 0.0; } }
步骤2:在Widget中使用控制器并联动动画
通过自定义控制器拿到第一个动画的时长后,就可以精准触发第二个动画:
class HandwrittenNumberRow extends StatefulWidget { const HandwrittenNumberRow({super.key}); @override State<HandwrittenNumberRow> createState() => _HandwrittenNumberRowState(); } class _HandwrittenNumberRowState extends State<HandwrittenNumberRow> { final HandwrittenFlareController _num1Controller = HandwrittenFlareController(); final HandwrittenFlareController _num5Controller = HandwrittenFlareController(); @override void initState() { super.initState(); // 等待第一个控制器初始化完成,获取动画时长 WidgetsBinding.instance.addPostFrameCallback((_) { if (_num1Controller.animationDuration != null) { // 启动第一个动画 _num1Controller.triggerAnimation(); setState(() {}); // 计算第一个动画的结束时间,精准触发第二个动画 final int durationMs = (_num1Controller.animationDuration! * 1000).round(); Timer(Duration(milliseconds: durationMs), () { _num5Controller.triggerAnimation(); setState(() {}); }); } }); } @override Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ FlareActor( "assets/animations/hw_1.flr", controller: _num1Controller, alignment: Alignment.center, fit: BoxFit.fitHeight, ), const SizedBox(width: 20), FlareActor( "assets/animations/hw_5.flr", controller: _num5Controller, alignment: Alignment.center, fit: BoxFit.fitHeight, ), ], ); } }
二、自定义动画播放时长(调整速度)
如果想统一不同数字的动画时长,或者手动调整某个数字的书写速度,可以在控制器里添加速度参数,通过修改播放速率来改变实际时长:
class HandwrittenFlareController extends FlareController { late FlareAnimationLayer _animationLayer; double? animationDuration; double playSpeed = 1.0; // 1.0为原速,大于1加快,小于1变慢 @override bool advance(FlutterActorArtboard artboard, double elapsed) { // 用playSpeed调整播放速率 _animationLayer.advance(elapsed * playSpeed); _animationLayer.apply(artboard); return true; } // ... 其他方法和之前一致 }
比如原动画时长是200ms,想让它变成300ms,就设置playSpeed = 200/300 ≈ 0.666即可。
三、多TextView组件间的动画同步
如果要多个独立的TextView组件按顺序播放动画,可以用状态管理工具(比如Provider、Riverpod)来统一管理动画序列:
1. 创建动画管理器
class AnimationSequenceManager extends ChangeNotifier { int _currentPlayingIndex = -1; final List<double> _animationDurations; // 存储每个组件对应动画的时长 AnimationSequenceManager(this._animationDurations); void startSequence() { _playNextAnimation(0); } void _playNextAnimation(int index) { if (index >= _animationDurations.length) return; _currentPlayingIndex = index; notifyListeners(); // 计算当前动画时长,到点自动触发下一个 final int durationMs = (_animationDurations[index] * 1000).round(); Timer(Duration(milliseconds: durationMs), () { _playNextAnimation(index + 1); }); } bool shouldPlay(int index) => _currentPlayingIndex == index; }
2. 在TextView组件中监听状态
class HandwrittenTextView extends StatelessWidget { final String flareAsset; final int sequenceIndex; const HandwrittenTextView({ super.key, required this.flareAsset, required this.sequenceIndex, }); @override Widget build(BuildContext context) { final manager = Provider.of<AnimationSequenceManager>(context); final controller = HandwrittenFlareController(); if (manager.shouldPlay(sequenceIndex)) { controller.triggerAnimation(); } return FlareActor( flareAsset, controller: controller, alignment: Alignment.center, fit: BoxFit.fitHeight, ); } }
3. 启动整个序列
// 在父组件中初始化管理器并启动序列 final manager = AnimationSequenceManager([0.2, 0.5, 0.3]); // 对应三个动画的时长(秒) manager.startSequence();
这样所有TextView组件就会严格按照动画的实际时长依次播放,完全不用硬编码延迟,序列会自然流畅很多。
内容的提问来源于stack exchange,提问作者WieFel
相关产品推荐
相关产品推荐

