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,
),
),
複製程式碼
本文就到這裡,這個工具還有很多優化
和擴充
的空間,後面有時間或靈感時會持續維護
,希望能對你有所幫助。