如何在Flutter中实现沿矩形路径的匀速Widget动画?
矩形路径匀速等间距容器动画解决方案
核心问题分析
直接用AnimationController的线性进度驱动矩形路径动画时,因为矩形四条边长度不同,相同进度增量对应的实际移动距离不一致,导致速度不均;同时如果直接按进度等分位置,会因边的长度差异造成容器间距不统一。
实现步骤
- 计算矩形路径参数:先确定矩形的宽
rectWidth、高rectHeight,算出总周长totalLength = 2 * (rectWidth + rectHeight),再明确每条边的长度及占周长的比例:- 上边长度:
rectWidth,占比rectWidth / totalLength - 右边长度:
rectHeight,占比rectHeight / totalLength - 下边长度:
rectWidth,占比rectWidth / totalLength - 左边长度:
rectHeight,占比rectHeight / totalLength
- 上边长度:
- 进度转路径距离:将
AnimationController的线性进度value(0-1区间)转换为实际路径上的移动距离distance = value * totalLength,确保进度变化对应的移动距离均匀,实现匀速效果。 - 坐标计算逻辑:根据
distance所在区间,判断容器处于哪条边,再计算对应坐标:- 上边:
distance <= rectWidth→ x从0到rectWidth,y固定为0 - 右边:
rectWidth < distance <= rectWidth + rectHeight→ x固定为rectWidth,y从0到rectHeight - 下边:
rectWidth+rectHeight < distance <= 2*rectWidth + rectHeight→ x从rectWidth到0,y固定为rectHeight - 左边:
2*rectWidth+rectHeight < distance <= totalLength→ x固定为0,y从rectHeight到0
- 上边:
- 多容器等间距处理:给每个容器的进度加上
1 / 容器总数的偏移量(取模1保证循环),让每个容器在路径上的距离差为totalLength / 容器总数,实现均匀间距。
代码示例
import 'package:flutter/material.dart'; class RectPathAnimation extends StatefulWidget { const RectPathAnimation({super.key}); @override State<RectPathAnimation> createState() => _RectPathAnimationState(); } class _RectPathAnimationState extends State<RectPathAnimation> with SingleTickerProviderStateMixin { late AnimationController _controller; final int _containerCount = 4; final double _rectWidth = 300; final double _rectHeight = 200; late double _totalLength; @override void initState() { super.initState(); _totalLength = 2 * (_rectWidth + _rectHeight); _controller = AnimationController( duration: const Duration(seconds: 4), vsync: this, )..repeat(); } @override void dispose() { _controller.dispose(); super.dispose(); } Offset _getContainerOffset(double progressOffset) { final double adjustedProgress = (_controller.value + progressOffset) % 1; final double distance = adjustedProgress * _totalLength; if (distance <= _rectWidth) { // 上边路径 return Offset(distance, 0); } else if (distance <= _rectWidth + _rectHeight) { // 右边路径 final double y = distance - _rectWidth; return Offset(_rectWidth, y); } else if (distance <= 2 * _rectWidth + _rectHeight) { // 下边路径 final double x = _rectWidth - (distance - _rectWidth - _rectHeight); return Offset(x, _rectHeight); } else { // 左边路径 final double y = _rectHeight - (distance - 2 * _rectWidth - _rectHeight); return Offset(0, y); } } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Stack( alignment: Alignment.center, children: [ // 矩形路径参考框 Container( width: _rectWidth, height: _rectHeight, decoration: BoxDecoration( border: Border.all(color: Colors.grey, width: 2), ), ), // 动画容器组 AnimatedBuilder( animation: _controller, builder: (context, child) { return Stack( children: List.generate(_containerCount, (index) { final progressOffset = index / _containerCount; final offset = _getContainerOffset(progressOffset); return Positioned( left: offset.dx, top: offset.dy, child: Container( width: 20, height: 20, decoration: const BoxDecoration( color: Colors.blue, shape: BoxShape.circle, ), ), ); }), ); }, ), ], ), ), ); } }
关键说明
- 通过
adjustedProgress = (_controller.value + progressOffset) % 1实现容器的循环偏移,确保间距始终均匀。 - 进度转路径距离的逻辑,保证容器在矩形各边上的移动速度一致。
- 可通过修改
_controller的duration调整整体动画速度,修改_containerCount、_rectWidth、_rectHeight适配不同场景需求。
内容的提问来源于stack exchange,提问作者Ahm2025
相关产品推荐
相关产品推荐

