flutter开发实战-Canvas绘图之Path路径动画
flutter提供一块2D画布Canvas,Canvas内部封装了一些基本绘制的API,开发者可以通过Canvas绘制各种自定义图形。canvas上绘图,有多种不同的方式,常用的就是使用 Path。这里是flutter实现Path路径动画。
实现小球根据Path来做动画效果。
一、效果图
运行后效果图如下
二、代码实现
实现小球根据Path来做动画效果。
代码使用的是Stack+position进行,通过动画计算Position的top、left更改位置。
Path.computeMetrics
computeMetrics是路径中一个非常实用的操作,可以更具这个方法获得很多有价值的信息。比如路径上某点在路径上的位置、角度、路径长度等。
获取路径某个位置Position
Offset calculate(value, path) {
PathMetrics pathMetrics = path.computeMetrics();
PathMetric pathMetric = pathMetrics.elementAt(0);
value = pathMetric.length * value;
Tangent pos = pathMetric.getTangentForOffset(value)!;
return pos.position;
}
创建Stack上的小球代码
class _MyHomePageState extends State<MyHomePage> {
// 弹珠的widgets
List<BallAnimation> _marbleWidgets = [];
void initState() {
// TODO: implement initState
super.initState();
}
void dispose() {
// TODO: implement dispose
super.dispose();
}
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
Size size = MediaQuery.of(context).size;
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Container(
width: size.width,
height: size.height,
alignment: Alignment.center,
child: Stack(
alignment: Alignment.center,
children: [
buildMarbleAnimation(context),
],
),
),
);
}
Widget buildMarbleAnimation(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Container(
width: size.width,
height: size.height,
alignment: Alignment.center,
child: Stack(
alignment: Alignment.center,
children: buildWidgets(context),
),
);
}
List<BallAnimation> buildWidgets(BuildContext context) {
Size size = MediaQuery.of(context).size;
_marbleWidgets.clear();
for(int index = 0; index < 10; index++) {
BallAnimation ballAnimation = BallAnimation(screenSize: size);
_marbleWidgets.add(ballAnimation);
}
return _marbleWidgets;
}
}
实现根据Path更改Position的top与left代码
class BallAnimation extends StatefulWidget {
const BallAnimation({super.key, required this.screenSize,});
final Size screenSize;
State<BallAnimation> createState() => _BallAnimationState();
}
class _BallAnimationState extends State<BallAnimation>
with TickerProviderStateMixin {
late AnimationController _animateController;
late Animation<double> _animation;
// 球的X
late Offset _ballOffset = Offset(0, 0);
// 球的X,Y
late double _ballX = 0;
late double _ballY = 0;
Path path = Path();
void initState() {
// TODO: implement initState]
super.initState();
startRunAnimation();
}
void startRunAnimation() {
runAnimation();
}
void runAnimation() {
double randomXPos1 = (Random().nextInt(100)/100)*widget.screenSize.width;
double randomYPos1 = (Random().nextInt(100)/100)*widget.screenSize.height;
// path的moveTo方法
path.moveTo(randomXPos1, randomYPos1);
for(int index = 0; index < 10; index++) {
double randomXPos = (Random().nextInt(100)/100)*widget.screenSize.width;
double randomYPos = (Random().nextInt(100)/100)*widget.screenSize.height;
// path的lineTo方法
path.lineTo(randomXPos, randomYPos);
}
Duration duration = Duration(seconds: 20);
Curve curve = Curves.linear;
_animateController = AnimationController(vsync: this, duration: duration);
//使用弹性曲线
_animation = CurvedAnimation(parent: _animateController, curve: curve);
_animation = Tween(begin: 0.0, end: 1.0).animate(_animation);
_animateController.addListener(() {
if (mounted) {
setState(() {
_ballX = calculate(_animation.value, path).dx;
_ballY = calculate(_animation.value, path).dy;
_ballOffset = Offset(_ballX, _ballY);
});
}
});
_animateController.addStatusListener((status) {
if (status == AnimationStatus.completed) {
_animateController.reset();
_animateController.forward();
}
});
_animateController.forward();
}
void dispose() {
// TODO: implement dispose
_animateController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Positioned(
top: _ballY,
left: _ballX,
child: Container(
width: 80,
height: 80,
decoration: BoxDecoration(
color: Colors.teal,
borderRadius: BorderRadius.all(Radius.circular(40))
),
),
);
}
Offset calculate(value, path) {
PathMetrics pathMetrics = path.computeMetrics();
PathMetric pathMetric = pathMetrics.elementAt(0);
value = pathMetric.length * value;
Tangent pos = pathMetric.getTangentForOffset(value)!;
return pos.position;
}
}
三、小结
flutter开发实战-Canvas绘图之Path路径动画
flutter提供一块2D画布Canvas,Canvas内部封装了一些基本绘制的API,开发者可以通过Canvas绘制各种自定义图形。canvas上绘图,有多种不同的方式,常用的就是使用 Path。这里是flutter实现Path路径动画。
学习记录,每天不停进步。