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> {// 弹珠的widgetsList<BallAnimation> _marbleWidgets = [];void initState() {// TODO: implement initStatesuper.initState();}void dispose() {// TODO: implement disposesuper.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;// 球的Xlate Offset _ballOffset = Offset(0, 0);// 球的X,Ylate 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路径动画。
学习记录,每天不停进步。