本篇會作為一個系列,原文傳送門:
基於Flutter版本: 2.0.3
前言
上一篇講了Flutter CustomPaint
元件相關的基本概念,這篇圍繞canvas
基礎繪製能力展開。
繪製點-drawPoints
void drawPoints(PointMode pointMode, List points, Paint paint)
- pointMode: 設定點、線
- PointMode.points 設定點
- PointMode.lines 兩個兩個點之間連線,如果傳入的
points
是奇數,最後一個點將會被忽略 - PointMode.polygon 將所有點連線起來
- points: 一個
Offset
陣列,可以畫多個點
如果paint
設定了strokeCap = StrokeCap.round
,畫的點將是圓形(其它任何API畫的點、線都同理,都跟隨畫筆屬性)
import 'dart:ui' as ui;
//...
// 畫點
Paint paint = Paint()
..color = Colors.red
..strokeWidth = 20;
canvas.drawPoints(
ui.PointMode.points,
[
Offset(100, 100),
Offset(250, 180),
Offset(200, 300),
],
paint);
// 將端點設定為圓形
paint.strokeCap = StrokeCap.round;
canvas.drawPoints(ui.PointMode.points, [Offset(100, 200)], paint);
複製程式碼
如果將入參都設定為PointMode.polygon
繪製線段-drawLine
void drawLine(Offset p1, Offset p2, Paint paint)
- p1,p2表示兩個端點的位置
canvas.drawLine(Offset(100, 100), Offset(250,180), paint);
複製程式碼
drawLine
只能用於繪製一條線段,drawPoints
可以繪製同樣效果
canvas.drawPoints(
ui.PointMode.lines,
[
Offset(100, 100),
Offset(250, 180),
],
paint);
複製程式碼
繪製區域-drawRect
因為很多方法都會用到Rect
,所以這個我會把drawRect
方法放到最開頭講。
Rect
表示繪製一個矩形區域,它沒有建構函式,它只有一些靜態方法
fromLTRB
Rect Rect.fromLTRB(double left, double top, double right, double bottom)
此方法是所有該類方法的母本,其它方法都是使用此方法實現的
- left: 矩形左邊距離畫布左邊距離
- top: 矩形頂部距離畫布頂部距離
- right: 矩形右邊距離畫布左邊邊距離
- bottom: 矩形底部距離畫布頂部距離
下面是一個例子
canvas.drawRect(Rect.fromLTRB(50, 50, 350, 350), paint);
複製程式碼
fromCenter
Rect.fromCenter({Offset center, double width, double height})
畫一個長方形
- center: 長方形方形中心點位置
- width: 長方形的寬
- height: 長方形的高
canvas.drawRect(
Rect.fromCenter(center: Offset(200,300), width: 250, height: 350), paint);
複製程式碼
fromCircle
Rect.fromCircle({Offset center, double radius})
畫一個正方形
- center: 正方形中心點位置
- radius: 正方形四條邊距離中心點距離
canvas.drawRect(
Rect.fromCircle(center: Offset(200, 300), radius: 150), paint);
複製程式碼
fromPoints
Rect.fromPoints(Offset a, Offset b)
使用兩個點確定一個矩形
- a: 矩形左上角的位置
- b: 矩形右下角的位置
canvas.drawRect(Rect.fromPoints(Offset(100, 200), Offset(300, 400)), paint);
複製程式碼
繪製圓角矩形-RRect
void drawRRect(RRect rrect, Paint paint)
RRect用來繪製帶圓角的矩形,其繪製的位置原理同Rect.fromLTRB
一樣,只是多了一個設定圓角的引數。
RRect rRect = RRect.fromLTRBR(100, 100, 350, 350, Radius.circular(30));
canvas.drawRRect(rRect, paint);
複製程式碼
繪製圓-drawOval
void drawOval(Rect rect, Paint paint)
drawOval
用於繪製圓形
Rect pRect = Rect.fromLTRB(50, 150, 400, 350);
// 為了區別,先繪製一個矩形區域
canvas.drawRect(pRect, paint);
paint.color = Colors.yellow;
// 繪製橢圓
canvas.drawOval(pRect, paint);
複製程式碼
黃色區域就是繪製的圓,它就是在Rect中進行繪製的
繪製圓弧-drawArc
drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint)
繪製圓弧,useCenter表示是否繪製中心點到圓弧兩邊
Rect rect = Rect.fromCircle(
center: Offset(size.width / 2, size.height / 2), radius: 100);
// 為了方便檢視,把區域也繪製出來
canvas.drawRect(rect);
//繪製圓弧
canvas.drawArc(rect, 90 * (pi / 180), 90 * (pi / 180), false, paint);
複製程式碼
有關角度的介紹,請看我上篇文章
繪製路徑-drawPath
void drawPath(Path path, Paint paint)
這個厲害了,drawPath
是一個很強大的方法,它可以說是canvas
的精髓,幾乎所以的其它繪製都可以用它來繪製。其它方法可以簡略看看,這個一定要重點學習理解。下面來看看它的能力
繪製簡單的形狀
Path path = new Path();
path.moveTo(100, 100);
path.lineTo(200, 200);
path.lineTo(250, 180);
path.lineTo(200, 300);
path.lineTo(100, 200);
canvas.drawPath(path, paint);
複製程式碼
Path
有很多方法,下面介紹常用方法
moveTo
設定畫筆開始的位置
lineTo
繪製的下一個位置,傳入的是相對於座標系的具體位置,會按照程式碼順序進行移動繪製
relativeLineTo
與lineTo類似,不過傳入的是相對於上一個點為原點的位置,比如上一個點是在(100,100),傳入的是(150,150),如果用lineTo要達到同樣效果應該傳入(250,250)。它有很多方法,帶了relative
的都是同樣的原理,所以後續relative-
方法就不講解了。
arcTo
繪製一個弧線
void arcTo(Rect rect, double startAngle, double sweepAngle, bool forceMoveTo)
此方法同drawArc類似,前三個引數都一樣,最後一個參數列示是否跟path之前的繪製(如通過lineTo繪製的線段)相連,false表示連線,true表示不相連。
quadraticBezierTo
繪製二階貝塞爾曲線
void quadraticBezierTo(double x1, double y1, double x2, double y2)
什麼是塞爾曲線,維基百科這樣介紹它
在數學的數值分析領域中,貝塞爾曲線(英語:Bézier curve)是計算機圖形學中相當重要的引數曲線。更高維度的廣泛化貝塞爾曲線就稱作貝茲曲面,其中貝茲三角是一種特殊的例項。
它的繪製過程如下(來自維基百科):
二階貝塞爾曲線的確立,需要三個點,P0開始點、P1過程點、P2結束點
使用quadraticBezierTo
來繪製時,我們可以通過moveTo
來確定開始點P0,x1、y1確定P1,x2、y2確定P2
var path = Path();
path.moveTo(50, 500);
path.quadraticBezierTo(100, 300, 350, 300);
canvas.drawPath(path1, paint);
複製程式碼
其中藍色的座標系、黃色的點是我畫的輔助線,紅色即是上面程式碼執行結果
conicTo
void conicTo(double x1, double y1, double x2, double y2, double w
同樣也是繪製二階貝塞爾曲線,但是同quadraticBezierTo
相比,它多了一個引數w
,用於控制曲線的弧度。當 w < 1 時,曲線弧度更小;w = 1 時同quadraticBezierTo
效果一樣;w > 1 時,弧度更大
path.conicTo(100, 300, 350, 300, 3);
複製程式碼
cubicTo
繪製三階貝塞爾曲線
void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3)
三階貝塞爾曲線相比二階貝塞爾曲線只是多了一個點,原理類似
path.moveTo(50, 500);
path.cubicTo(50, 200, 300, 400, 350, 150);
複製程式碼
addRect
繪製一個矩形區域
path.addRect(Rect.fromLTRB(50, 50, 350, 350));
複製程式碼
此效果跟上面drawRect中第一個例子一樣
addRRect
繪製一個帶圓角的矩形
RRect rRect = RRect.fromLTRBR(100, 100, 350, 350, Radius.circular(30));
path.addRRect(rRect);
複製程式碼
上面程式碼效果跟drawRRect一樣
addArc
繪製一個圓弧
Path path = new Path();
// 畫一個矩形區域
Rect rect = Rect.fromCircle(
center: Offset(size.width / 2, size.height / 2), radius: 100);
canvas.drawRect(rect, paint);
// 在矩形區域畫圓弧
path.addArc(rect, 90 * (pi / 180), 90 * (pi / 180));
paint.color = Colors.red;
canvas.drawPath(path, paint);
複製程式碼
上面程式碼同drawArc效果一樣
canvas.drawArc
、path.addArc
、path.arcTo
(當forceMoveTo為true時),三種方式都能繪製同樣的圓弧效果
addOval
繪製一個橢圓
Rect pRect = Rect.fromLTRB(50, 150, 400, 350);
path.addOval(pRect);
canvas.drawPath(path, paint);
複製程式碼
程式碼效果同drawOval
addPolygon
通過點繪製線段
void addPolygon(List points, bool close)
- point: 傳入多個點的位置
- close: 為true時最後一個點會和第一個點相連
Path path = new Path();
path.addPolygon([
Offset(100, 100),
Offset(250, 180),
Offset(200, 300),
], false);
canvas.drawPath(path, paint);
複製程式碼
computeMetrics
PathMetrics computeMetrics({bool forceClosed = false})
computeMetrics
方法用於返回一個之前繪製的路徑的一份快照。當我們使用moveTo
、lineTo
、arcTo
、conicTo
等繪製路徑時,可以使用此來實現只繪製其中一部分。
比如:
var path = Path();
path.moveTo(50, 500);
path.cubicTo(50, 200, 300, 400, 350, 150);
// 將完整繪製圖形置為紅色
paint.color = Colors.red;
canvas.drawPath(path, paint);
ui.PathMetrics pathMetrics = path.computeMetrics();
// 繪製一半
var progress = 0.5;
// 將顏色更改為紫色用於區分
paint.color = Colors.deepPurple;
for (ui.PathMetric pathMetric in pathMetrics) {
Path extractPath = pathMetric.extractPath(
0.0,
pathMetric.length * progress,
);
canvas.drawPath(extractPath, paint);
}
複製程式碼
extendWithPath
void extendWithPath(Path path, Offset offset, {Float64List matrix4})
用於複製一份之前繪製的路徑並平移offset
的位置,原路徑會和新路徑連線。matrix4
是對新路徑進行一個4D矩陣處理。
var path = Path();
path.moveTo(50, 500);
// 繪製一個三階貝塞爾曲線
path.cubicTo(50, 200, 300, 400, 350, 150);
// 處理
path.extendWithPath(path, Offset(50, 30),
matrix4: Float64List.fromList(
[1, 0, 0, 0, .1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2]));
canvas.drawPath(path, paint);
複製程式碼
圖中藍色圈起來的部分就是方法的效果
shift
Path shift(Offset offset)
可用於複製之前繪製的路徑並平移offset
位置,返回Path
。與extendWithPath
不同的是,此方法僅僅是複製路徑,不會跟原路徑相連。
var path = Path();
path.moveTo(50, 500);
path.cubicTo(50, 200, 300, 400, 350, 150);
// 原始圖形為紅色
paint.color = Colors.red;
canvas.drawPath(path, paint);
// 複製原路徑並平移
var path2 = path.shift(Offset(50,20));
paint.color = Colors.yellow;
canvas.drawPath(path2, paint);
複製程式碼
close
void close()
用於將路徑起點與終點連線起來
總結
經過上面學習,我們知道了其實多種繪製方式能達到一樣的效果
繪製點
- canvas.drawPoints
- path.addPolygon
繪製直線
- drawPoints傳參
PointMode.polygon
或PointMode.lines
- drawLine
- path.lineTo
繪製圓弧
- canvas.drawArc
- path.addArc
下一篇將介紹Canvas
圖形變換相關如旋轉、平移、縮放等,如有興趣,請關注