利用flutter寫了個類似于充電的動畫,比如光伏板給電池充電,模仿電力輸送的過程壶运,效果如下圖所示
下面直接貼出代碼,屬性都標(biāo)注了注釋浪秘,使用的時候在外面加個盒子固定寬高蒋情,然后給出每個點的坐標(biāo),內(nèi)部會根據(jù)坐標(biāo)的點數(shù)來判斷有幾根線耸携,然后畫出線的路徑棵癣,進(jìn)行動畫模擬,可以根據(jù)項目需求來自定義點的顏色夺衍,每條路徑上點的個數(shù)以及點的大小等等狈谊,
import 'package:flutter/material.dart';
class ElectricAnimated extends StatelessWidget {
final int duration; //動畫時長
final List<Offset> path; //路徑的點坐標(biāo)
const ElectricAnimated({
super.key,
this.duration = 5,
required this.path,
});
@override
Widget build(BuildContext context) {
return Stack(
children: List.generate(
path.length - 1,
(index) {
return AnimatedDots(
path: [
path[index],
path[index + 1],
],
dotCount: 3,
duration: Duration(
seconds: duration,
),
);
},
),
);
}
}
class AnimatedDots extends StatefulWidget {
final List<Offset> path; //路徑的點坐標(biāo)
final int dotCount; //每條路徑上的點的個數(shù)
final double dotSize; //點的大小
final Duration duration;
final Color dotColor; //點的顏色
const AnimatedDots({
Key? key,
required this.path,
this.dotCount = 5,
this.dotSize = 2,
this.duration = const Duration(seconds: 2),
this.dotColor = Colors.white,
}) : super(key: key);
@override
State<AnimatedDots> createState() => _AnimatedDotsState();
}
class _AnimatedDotsState extends State<AnimatedDots>
with SingleTickerProviderStateMixin {
late AnimationController _animationController;
late Animation<double> _animation;
int _currentIndex = 0;
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: widget.duration,
);
_animation = Tween<double>(
begin: 0,
end: 1,
).animate(_animationController);
_animationController.addStatusListener((status) {
if (status == AnimationStatus.completed) {
_currentIndex++;
if (_currentIndex >= widget.path.length - 1) {
_currentIndex = 0;
}
_animationController.reset();
_animationController.forward();
}
});
_animationController.forward();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animation,
builder: (context, child) {
final value = _animation.value;
final current = widget.path[_currentIndex];
final next = widget.path[(_currentIndex + 1) % widget.path.length];
final dots = List.generate(widget.dotCount, (index) {
final dotValue = (value + index / widget.dotCount) % 1;
final x = current.dx + (next.dx - current.dx) * dotValue;
final y = current.dy + (next.dy - current.dy) * dotValue;
return Positioned(
left: x - widget.dotSize / 2,
top: y - widget.dotSize / 2,
child: Container(
width: widget.dotSize,
height: widget.dotSize,
decoration: BoxDecoration(
color: widget.dotColor,
shape: BoxShape.circle,
),
),
);
});
return Stack(
children: dots,
);
},
);
}
}
使用
body: Center(
child: Container(
color: Colors.black,
width: 150,
height: 150,
child: const ElectricAnimated(
duration: 3,
path: [
Offset(30, 0),
Offset(30, 67),
Offset(90, 67),
Offset(90, 137),
],
),
),
),
CSDN地址