flutter 用canvas绘制边框 并实现动画效果
Posted 刘文_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了flutter 用canvas绘制边框 并实现动画效果相关的知识,希望对你有一定的参考价值。
代码如下
利用 RRect绘制带radius的盒子和边际线 利用Path设置边界线轨迹
利用PathMetric和AnimationController达成轨迹动画效果
import 'dart:ui';
import 'package:flutter/material.dart';
class CutDownBorderButton extends StatefulWidget {
const CutDownBorderButton(
{Key? key,
this.radius,
required this.width,
required this.height,
required this.cutController,
this.initBorderColor,
this.activeBorderColor,
this.borderWidth})
: super(key: key);
@override
_CutWidgetState createState() => _CutWidgetState();
final double? radius;
final double width;
final double height;
final Color? initBorderColor;
final Color? activeBorderColor;
final double? borderWidth;
final CutController cutController;
}
class _CutWidgetState extends State<CutDownBorderButton> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return CustomSingleChildLayout(
delegate: _SingleChildLayoutDelegate(Size(widget.width, widget.height)),
child: CustomPaint(
painter: BorderPainter(
progress: widget.cutController.value /
widget.cutController.duration!.inSeconds,
radius: widget.radius,
width: widget.width,
height: widget.height,
borderWidth: widget.borderWidth,
initBorderColor: widget.initBorderColor,
activeBorderColor: widget.activeBorderColor,
boxColor: Colors.amber)));
}
}
class _SingleChildLayoutDelegate extends SingleChildLayoutDelegate {
final Size size;
_SingleChildLayoutDelegate(this.size);
@override
Size getSize(BoxConstraints constraints) {
return size;
}
@override
bool shouldRelayout(_SingleChildLayoutDelegate oldDelegate) {
return this.size != oldDelegate.size;
}
@override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
return BoxConstraints.tight(size);
}
@override
Offset getPositionForChild(Size size, Size childSize) {
return Offset((size.width - childSize.width) / 2,
(size.height - childSize.height) / 2);
}
}
class BorderPainter extends CustomPainter {
BorderPainter(
{required this.width,
required this.height,
required this.progress,
required this.boxColor,
this.initBorderColor,
this.activeBorderColor,
this.radius,
this.borderWidth});
final double? radius;
final double? borderWidth;
final double width;
final double height;
final double progress;
final Color boxColor;
final Color? initBorderColor;
final Color? activeBorderColor;
@override
void paint(Canvas canvas, Size size) {
Rect rect = Rect.fromCenter(
center: Offset(width / 2, height / 2), width: width, height: height);
RRect rRect = RRect.fromRectAndRadius(rect, Radius.circular(radius!));
Paint paintRect = Paint()
..color = boxColor
..style = PaintingStyle.fill;
canvas.drawRRect(rRect, paintRect);
canvas.save();
Paint initBorder = Paint()
..color = initBorderColor ?? Colors.red
..style = PaintingStyle.stroke
..strokeWidth = borderWidth ?? 5;
Paint activeBorder = Paint()
..color = activeBorderColor ?? Colors.grey
..style = PaintingStyle.stroke
..strokeWidth = borderWidth ?? 5;
Path path = Path()
..moveTo(width / 2, 0)
..relativeLineTo(width / 2 - radius!, 0)
..relativeArcToPoint(Offset(radius!, radius!),
radius: Radius.circular(radius!))
..relativeLineTo(0, height - radius! * 2)
..relativeArcToPoint(Offset(-radius!, radius!),
radius: Radius.circular(radius!))
..relativeLineTo(-width + radius! * 2, 0)
..relativeArcToPoint(Offset(-radius!, -radius!),
radius: Radius.circular(radius!))
..relativeLineTo(0, -height + radius! * 2)
..relativeArcToPoint(Offset(radius!, -radius!),
radius: Radius.circular(radius!))
..relativeLineTo(width / 2 - radius!, 0)
..close();
canvas.drawPath(path, initBorder);
path.addRRect(rRect);
PathMetrics pathMetrics = path.computeMetrics();
PathMetric pathMetric = pathMetrics.first;
Path extPath = pathMetric.extractPath(0, pathMetric.length * progress);
canvas.drawPath(extPath, activeBorder);
canvas.restore();
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
class CutController extends AnimationController {
CutController({required TickerProvider vsync, int durationInt = 30})
: super(
reverseDuration: Duration(milliseconds: durationInt * 1000),
duration: Duration(milliseconds: durationInt * 1000),
lowerBound: 0,
upperBound: durationInt.toDouble(),
vsync: vsync);
}
使用示范:
import 'package:flitter_okgo/cut_down_border_button.dart';
import 'package:flitter_okgo/hand_written_board.dart';
import 'package:flutter/material.dart';
class CutDownBorderPage extends StatefulWidget {
@override
_TestPageState createState() => _TestPageState();
}
class _TestPageState extends State<CutDownBorderPage>
with SingleTickerProviderStateMixin {
late CutController controller = CutController(vsync: this, durationInt: 3);
@override
void initState() {
super.initState();
controller.addListener(() {
setState(() {});
});
}
Widget get initFloatingActionButton {
return FloatingActionButton(
backgroundColor: Colors.grey,
elevation: 1,
focusElevation: 1,
onPressed: () {
controller.forward();
},
child: Icon(Icons.android),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
extendBody: true,
floatingActionButton: initFloatingActionButton,
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
body: Center(
child: Stack(
children: [
CutDownBorderButton(
cutController: controller,
radius: 25,
width: 100,
height: 100,
),
Container(
width: 100,
height: 100,
child: Center(
child: Text('${controller.value.round()}'),
),
),
],
)),
);
}
}
以上是关于flutter 用canvas绘制边框 并实现动画效果的主要内容,如果未能解决你的问题,请参考以下文章