highlight: a11y-dark
0.前言
有很多人问我如何绘制虚线
,一直没有这方面需求,没有太在意。现在想一下,通过路径测量
实现虚线绘制应该是非常简单的。就抽了点空,顺手写个好用的虚线路径绘制工具
,不然平时画个辅助线
啥的确实挺费劲。
该绘制工具 dash_painter 已经上传到 pub
:
- pub 地址: https://pub.dev/packages/dash_painter
- github 地址: https://github.com/toly1994328/dash_painter
| 圆角矩形 | 圆形 | | ------------------------------------------------------------ | ------------------------------------------------------------ | |
|
|
1. 实现的绘制
如下画板,通过路径绘制出一条直线,这应该是绘制最基础
的东西了,不多介绍。下面来看一下如何实现将它变成一条虚线
。
```dart 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
的虚线。
```dart 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
,然后根据份数遍历绘制截取的路径即可。使用时也非常简单,只要一句即可化实为虚
:
dart const DashPainter(span: 10, step: 20).paint(canvas, path, paint);
通过控制 step
和 span
参数,可以控制虚线的显示效果。
| step:6, span: 6 | step:6, span: 4 | | :----------------------------------------------------------- | ------------------------------------------------------------ | |
|
|
其实到这里,就可以让 任意路径
虚线化,如下的圆角矩形和圆形:
dart 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
进行加长,如下图所示:
```dart 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);
});
} } ```
这样就可以完成一下很棒的东西,比如点画线圆
:
dart 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
的装饰,方便使用。这里只是一个简单的使用,可以基于此封装一下配置属性。
```dart 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();
} } ```
这属性配置些在库中已经封装,可以直接使用,如下实现一个渐变的单点画线圆角虚线框
。
dart 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, ), ),
本文就到这里,这个工具还有很多优化
和拓展
的空间,后面有时间或灵感时会持续维护
,希望能对你有所帮助。