【我的 Flutter 开源库 】 - 虚线绘制库 dash_painter
0.前言
有很多人问我如何绘制虚线
,一直没有这方面需求,没有太在意。现在想一下,通过路径测量
实现虚线绘制应该是非常简单的。就抽了点空,顺手写个好用的虚线路径绘制工具
,不然平时画个辅助线
啥的确实挺费劲。
该绘制工具 dash_painter 已经上传到 pub
:
- pub 地址: pub.dev/packages/da…
- github 地址: github.com/toly1994328…
圆角矩形 | 圆形 |
---|---|
![]() | ![]() |
1. 实现的绘制
如下画板,通过路径绘制出一条直线,这应该是绘制最基础
的东西了,不多介绍。下面来看一下如何实现将它变成一条虚线
。
class TolyPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
canvas.translate(size.width / 2, size.height / 2);
Paint paint = Paint()..style = PaintingStyle.stroke
..color=Colors.orangeAccent..strokeWidth = 2;
Path path = Path();
path.moveTo(-100, 0);
path.lineTo(100, 0);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant TolyPainter oldDelegate) => false;
}
2.绘制虚线 - level1
为了方便管理和拓展,可以将虚线对象分离出一个类 DashPainter
。既然要画虚线,自然要明确相关的虚线参数,这里先来个简单的。 虚线的 单线长
和 间距
分别使用 step
和 span
表示,如下是一个 step:20, span: 10
的虚线。
class DashPainter {
final double step;
final double span;
const DashPainter({this.step = 2, this.span = 2,});
double get partLength => step + span;
void paint(Canvas canvas, Path path, Paint paint) {
final PathMetrics pms = path.computeMetrics();
pms.forEach((PathMetric pm) {
final int count = pm.length ~/ partLength;
for (int i = 0; i < count; i++) {
canvas.drawPath(
pm.extractPath(partLength * i, partLength * i + step), paint);
}
final double tail = pm.length % partLength;
canvas.drawPath(pm.extractPath(pm.length-tail, pm.length), paint);
});
}
}
实现的逻辑也非常简单: 对路径进行 computeMetrics
,然后根据份数遍历绘制截取的路径即可。使用时也非常简单,只要一句即可化实为虚
:
const DashPainter(span: 10, step: 20).paint(canvas, path, paint);
通过控制 step
和 span
参数,可以控制虚线的显示效果。
step:6, span: 6 | step:6, span: 4 |
---|---|
![]() | ![]() |
其实到这里,就可以让 任意路径
虚线化,如下的圆角矩形和圆形:
final Path path = Path();
path.addRRect(RRect.fromRectAndRadius(
Rect.fromCircle(center: Offset.zero, radius: 100),
Radius.circular(20),
));
const DashPainter(span: 4, step: 9).paint(canvas, path, paint);
圆角矩形 | 圆形 |
---|---|
![]() | ![]() |
2.绘制虚线 - level2
除了虚线,有时还会有点划线
的需求,如下
单点划线
:
双点划线
:
三点划线
:
代码实现如下,增加了 pointCount
和 pointWidth
两个属性,分别表示点划线数
和点划线长
。其实整体思路是不变的, step
和 span
还是那个含义,只不过单体的长度 pointLineLength
需要根据 pointCount
和 pointWidth
进行加长,如下图所示:
class DashPainter {
const DashPainter({
this.step = 2,
this.span = 2,
this.pointCount = 0,
this.pointWidth,
});
final double step;
final double span;
final int pointCount;
final double pointWidth;
void paint(Canvas canvas, Path path, Paint paint) {
final PathMetrics pms = path.computeMetrics();
final double pointLineLength = pointWidth ?? paint.strokeWidth;
final double partLength =
step + span * (pointCount + 1) + pointCount * pointLineLength;
pms.forEach((PathMetric pm) {
final int count = pm.length ~/ partLength;
for (int i = 0; i < count; i++) {
canvas.drawPath(
pm.extractPath(partLength * i, partLength * i + step), paint,);
for (int j = 1; j <= pointCount; j++) {
final start =
partLength * i + step + span * j + pointLineLength * (j - 1);
canvas.drawPath(
pm.extractPath(start, start + pointLineLength),
paint,
);
}
}
final double tail = pm.length % partLength;
canvas.drawPath(pm.extractPath(pm.length - tail, pm.length), paint);
});
}
}
这样就可以完成一下很棒的东西,比如点画线圆
:
final Path path = Path();
path.moveTo(-200, 0);
path.lineTo(200, 0);
path.moveTo(0, -200);
path.lineTo(0, 200);
path.addOval(Rect.fromCircle(center: Offset.zero, radius: 100));
const DashPainter(
span: 4, // 空格长
step: 10, // 实线长
pointCount: 2, // 点划线个数
pointWidth: 2 // 点划线长
).paint(canvas, path, paint);
3.装饰绘制
可能很多人不会自定义画板自己绘制,或只想简单地使用。其实除了 CustomPainter
还有其他地方有 canvas
。比如 Decoration
。我们可以自定义 DashDecoration
的装饰,方便使用。这里只是一个简单的使用,可以基于此封装一下配置属性。
class DashDecoration extends Decoration {
@override
BoxPainter createBoxPainter([onChanged]) => const DashBoxPainter();
}
class DashBoxPainter extends BoxPainter {
final Color color;
const DashBoxPainter({this.color});
@override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
canvas.save();
final Paint paint = Paint()
..style = PaintingStyle.stroke
..color = Colors.orangeAccent
..strokeWidth = 1;
final Path path = Path();
canvas.translate(
offset.dx + configuration.size.width / 2,
offset.dy + configuration.size.height / 2,
);
final Rect zone = Rect.fromCenter(
center: Offset.zero,
width: configuration.size.width,
height: configuration.size.height,
);
path.addRRect(RRect.fromRectAndRadius(
zone,
Radius.circular(20),
));
const DashPainter(span: 4, step: 9).paint(canvas, path, paint);
canvas.restore();
}
}
这属性配置些在库中已经封装,可以直接使用,如下实现一个渐变的单点画线圆角虚线框
。
Container(
width: 100,
height: 100,
decoration: DashDecoration(
pointWidth: 2,
step: 5,
pointCount: 1,
radius: Radius.circular(15),
gradient: SweepGradient(colors: [
Colors.blue,
Colors.red,
Colors.yellow,
Colors.green
])),
child: Icon(
Icons.add,
color: Colors.orangeAccent,
size: 40,
),
),
本文就到这里,这个工具还有很多优化
和拓展
的空间,后面有时间或灵感时会持续维护
,希望能对你有所帮助。
转载自:https://juejin.cn/post/6960468833073102885