Flutter學習:使用CustomPaint繪制路徑


Flutter學習:認識CustomPaint組件和Paint對象
Flutter學習:使用CustomPaint繪制路徑
Flutter學習:使用CustomPaint繪制圖形
Flutter學習:使用CustomPaint繪制文字
Flutter學習:使用CustomPaint繪制圖片

drawPath 繪制路徑

drawPath需要傳遞2個參數:

  • Path path 路徑對象
  • Paint paint 繪制對象
Path path = Path()..moveTo(100, 100);
path.lineTo(250, 250);
path.lineTo(350, 180);
path.lineTo(200, 500);
// 控制路徑是否閉合,可不寫
path.close();
canvas.drawPath(path, paint);

image

path.moveTo和path.lineTo

path.moveTopath.lineTo一樣,都需要傳遞2個參數:

  • double x用來設置點的橫坐標(x軸)
  • double y用來設置點的縱坐標(y軸)
path.moveTo(80, 200);
path.lineTo(320, 400);
canvas.drawPath(path, paint);

其中p0path.moveTo設置的坐標,p1path.lineTo設置的坐標

image

path.relativeMoveTo和path.relativeLineTo

path.relativeMoveTopath.relativeLineTofiType的使用方法和path.moveTopath.lineTo一樣,只是起始點不再是左上角,而是前一個點的坐標

path.close

用來設置路徑是否自己閉合:

path.close();

image

path.reset

清除之前所有的Path對象。並重置原點為左上角。

path.reset();

path.fillType

關於fillType的資料可以查看以下文章:

fillType有兩個枚舉值:

  • PathFillType.nonZero:內部由有符號邊緣交叉的非零和定義。對於給定點,如果從該點到無窮遠的線與繞該點順時針方向的線交叉的次數與與繞該點逆時針方向的線交叉的次數不同,則認為該點位於路徑的內側。
  • PathFillType.evenOdd:內部由奇數個邊緣交叉定義。對於給定點,如果從該點到無窮遠的線穿過奇數條線,則認為該點位於路徑的內側
Path path = Path()..moveTo(size.width / 2, 200);
path.lineTo(size.width / 4, 500);
path.lineTo(size.width / 7 * 6, 320);
path.lineTo(size.width / 7, 320);
path.lineTo(size.width / 4 * 3, 500);
path.close();
// 默認值
path.fillType = PathFillType.nonZero;
path.fillType = PathFillType.evenOdd;
canvas.drawPath(path, paint);

image

path.addArc

通過路徑繪制圓弧

addArc(Rect oval, double startAngle, double sweepAngle)需要傳遞3個參數:

  • oval繪制一個矩形
  • startAngle圓弧開始處
  • sweepAngle圓弧開始到結束的角度大小
// 只有path.addArc方法,path.moveTo方法將會無效
Path path = Path();
// path.moveTo(size.width / 2, 200); 無效
Rect oval = Rect.fromPoints(const Offset(80, 80), const Offset(300, 180));
path.addArc(oval, 0, 4);
canvas.drawPath(path, paint);
// 有path.moveTo、path.lineTo、path.addArc這3個方法
// path.lineTo在誰的后面就跟誰連接,默認path.moveTo為(0,0)
Path path = Path();
path.moveTo(size.width / 2, 200);
Rect oval = Rect.fromPoints(const Offset(80, 80), const Offset(300, 180));
path.addArc(oval, 0, 4);
path.lineTo(250, 400);
canvas.drawPath(path, paint);

image

path.addOval、path.addRect、path.addRRect用法和path.addArc一樣

path.addPath

該方法將復制一遍已繪制的路徑,並進行偏移

addPath(Path path, Offset offset, {Float64List? matrix4})可以傳遞3個參數,2個必要的,一個可選的:

  • path已繪制的路徑對象
  • offsetpath對象進行的偏移量
  • matrix4將矩陣平移給定偏移量

沒有使用matrix4屬性:

Path path = Path()..moveTo(100, 100);
path.lineTo(210, 240);
path.lineTo(80, 380);
path.addPath(path, const Offset(120, 120));
canvas.drawPath(path, paint);

使用了matrix4屬性:

Path path = Path()..moveTo(100, 100);
path.lineTo(210, 240);
path.lineTo(80, 380);
// 變形(傾斜)路徑
path.addPath(path, const Offset(120, 120), matrix4: Matrix4.skew(.1, .1).storage);
canvas.drawPath(path, paint);

image

path.extendWithPath

效果和path.addPath一樣,屬性也一樣,只是該方法會將這兩條線段連接起來:

Path path = Path()..moveTo(100, 100);
path.lineTo(210, 240);
path.lineTo(80, 380);
path.extendWithPath(path, const Offset(120, 120), matrix4: Matrix4.skew(.2, .4).storage);
canvas.drawPath(path, paint);

image

path.addPolygon

添加一條新路徑

addPolygon(List points, bool close)需要傳遞2個參數:

  • points一系列點的坐標
  • close是否首位相連
path.moveTo(size.width / 2, 200);
path.lineTo(200, 380);
path.lineTo(80, 460);
List<Offset> points = const [
  Offset(100, 40),
  Offset(350, 240),
  Offset(200, 500),
];
path.addPolygon(points, true);
canvas.drawPath(path, paint);

image

path.arcTo

繪制圓弧路徑

arcTo( Rect rect, double startAngle, double sweepAngle, bool forceMoveTo, )需要傳遞4個參數:

  • rect繪制一個矩形用來確定圓弧的位置
  • startAngle圓弧開始的角度
  • sweepAngle圓弧開始到結束的角度大小
  • forceMoveTo圓弧路徑為新路徑還是與原路徑相連
path.moveTo(size.width / 2, 200);
path.lineTo(80, 460);
Rect rect = Rect.fromPoints(const Offset(80, 340), const Offset(280, 420));
// forceMoveTo為true
path.arcTo(rect, 0, 5, true);
// forceMoveTo為false
path.arcTo(rect, 0, 5, false);
canvas.drawPath(path, paint);

image

path.arcToPoint

繪制一個兩點之間線段距離的直徑圓弧

arcToPoint(Offset arcEnd, {Radius radius = Radius.zero, double rotation = 0.0, bool largeArc = false, bool clockwise = true})可以傳遞5個參數,1個必要的,4個可選的:

  • arcEnd確定圓弧的結束點坐標
  • radius為0時將繪制一條直線
  • rotation感覺沒啥用
  • largeArc是否是大圓弧
  • clockwise決定圓弧的繪制是從左邊還是右邊
path.moveTo(100, 200);
// p0
path.arcToPoint(
  const Offset(320, 500),
  radius: const Radius.circular(.5),
  rotation: 2,
  argeArc: true,
  clockwise: false,
);
// p1
path.arcToPoint(
  onst Offset(160, 250),
  adius: const Radius.circular(1),
  otation: 0,
  argeArc: true,
  lockwise: false,
);
// p2
path.arcToPoint(
  const Offset(240, 375),
  radius: const Radius.circular(0),
  rotation: 5,
  largeArc: false,
  clockwise: true,
);
canvas.drawPath(path, paint);

image

path.relativeArcToPoint

效果和path.arcToPoint一樣,只是起始點為前一個點

path.conicTo

繪制圓錐路徑

conicTo(double x1, double y1, double x2, double y2, double w)需要傳遞5個參數:

  • x1是第一個點x軸的坐標
  • y1是第一個點y軸的坐標
  • x2是第二個點x軸的坐標
  • y2是第二個點y軸的坐標
  • w是權重值。如果權重大於1,則曲線為雙曲線;如果權重等於 1,則為拋物線;如果小於 1,則為橢圓
path.moveTo(size.width / 2, 200);
// 權重=0
path.conicTo(80, 280, 300, 380, 0);
// 權重=1
path.conicTo(80, 280, 300, 380, 1);
// 權重=2
path.conicTo(80, 280, 300, 380, 2);
canvas.drawPath(path, paint);

image

path.relativeConicTo

效果和path.conicTo一樣,只是起始點為前一個點

path.quadraticBezierTo

使用控制點 (x1,y1) 添加一個從當前點彎曲到給定點 (x2,y2) 的二次貝塞爾曲線段

quadraticBezierTo( double x1, double y1, double x2, double y2)需要傳遞4個參數:

  • x1是第一個點x軸的坐標
  • y1是第一個點y軸的坐標
  • x2是第二個點x軸的坐標
  • y2是第二個點y軸的坐標
path.moveTo(20, 200);
path.quadraticBezierTo(200, 80, size.width - 20, size.width);
canvas.drawPath(path, paint);

其中p0代表起始點位置(path.moveTo(20, 200)),p1的坐標是(x1, y1)p2的坐標是(x2, y2)

image

path.relativeQuadraticBezierTo

效果和path.quadraticBezierTo一樣,只是起始點為前一個點

path.cubicTo

使用控制點 (x1,y1) 和 (x2,y2) 添加從當前點彎曲到給定點 (x3,y3) 的三次貝塞爾曲線段

cubicTo( double x1, double y1, double x2, double y2, double x3, double y3)需要傳遞6個參數:

  • x1是第一個點x軸的坐標
  • y1是第一個點y軸的坐標
  • x2是第二個點x軸的坐標
  • y2是第二個點y軸的坐標
  • x3是第三個點x軸的坐標
  • y3是第三個點y軸的坐標
path.moveTo(100, 200);
path.cubicTo(120, 120, 240, 360, 310, 360);
canvas.drawPath(path, paint);

其中p1代表p0點控制線的右邊坐標,p2代表p3點控制線的左邊坐標

image

path.relativeCubicTo

效果和path.cubicTo一樣,只是起始點為前一個點

path.transform

復制線段,並對其路徑進行變形。transform(Float64List matrix4)需要傳遞一個Float64List對象:

繪制第一條線段:

Paint paint = Paint()
  ..style = PaintingStyle.stroke
  ..strokeWidth = 8
  ..color = Colors.red;
Path path = Path();
path.moveTo(80, 100);
path.lineTo(140, 200);
path.lineTo(320, 280);
path.lineTo(100, 400);
canvas.drawPath(path, paint);

繪制第二條線段:

Paint paint1 = Paint()
  ..style = PaintingStyle.stroke
  ..strokeWidth = 8
  ..color = Colors.blue;
Path path1 = path.transform(Matrix4.skew(.1, .4).storage);
canvas.drawPath(path1, paint1);

image

path.getBounds

獲取路徑的邊界矩形

path.moveTo(50, 200);
path.lineTo(300, 280);
// path.getBounds()的結果為Rect.fromLTRB(50.0, 200.0, 300.0, 280.0)
print(path.getBounds());
canvas.drawPath(path, paint);

image

path.shift

功能和path.add一樣,只不過不會自動繪制一份路徑,會把路徑復制一份然后賦值給其他路徑。

  • Offset offset,復制一份路徑后設置偏移量

第一條路徑:

Paint paint = Paint()
  ..style = PaintingStyle.stroke
  ..strokeWidth = 8
  ..color = Colors.red;
Path path = Path();
path.moveTo(size.width / 2, 200);
path.lineTo(80, 450);
path.lineTo(size.width - 80, 450);
path.close();
canvas.drawPath(path, paint);

復制第一條路徑,並偏移:

Paint paint1 = Paint()
  ..style = PaintingStyle.stroke
  ..strokeWidth = 8
  ..color = Colors.blue;
Path path1 = path.shift(const Offset(50, 50));
canvas.drawPath(path1, paint1);

image

path.contains

測試給定點是否在路徑內。也就是說,如果路徑與Canvas.clipPath一起使用,該點是否位於路徑的可見部分。

  • Offset point,設置要檢測的點的位置
Offset point1 = const Offset(200, 320);
print(path.contains(point1));

Offset point2 = const Offset(200, 520);
print(path.contains(point2));

如果該點在路徑內,就返回 true,否則返回 false。

image

path.computeMetrics

path.computeMetrics可以獲取路徑的詳細信息。

computeMetrics({bool forceClosed = false})有一個可選參數:

  • forceClosed,如果沒有使用path.close();,該方法獲取的結果isClose將會為false。設置該值為trueisClose將會為true

我們先繪制一條普通路徑:

Paint paint = Paint()
  ..style = PaintingStyle.stroke
  ..strokeWidth = 8
  ..color = Colors.red;
Path path = Path();
path.moveTo(size.width / 2, 200);
path.lineTo(80, 450);
path.lineTo(size.width - 80, 450);
path.close();

如果直接對以上繪制的路徑使用path.computeMetrics,會返回一個PathMetrics對象:

PathMetrics pathMetrics = path.computeMetrics();

一般我們需要用到的是PathMetric對象,獲取該對象一共有以下幾種方法:

// 如果只有一個PathMetric對象推薦使用該方法
PathMetric pathMetric = pathMetrics.single;
// 如果有很多個PathMetric對象使用以下任意方法
PathMetric pathMetric = pathMetrics.elementAt(0);
PathMetric pathMetric = pathMetrics.first;
PathMetric pathMetric = pathMetrics.last;

獲取到PathMetric對象后,我們就可以沿着這條路徑繪制一條新的路徑,使用extractPath方法。

該方法可以傳遞3個參數:

  • double start:給定線段開始的距離
  • double end:給定線段結束的距離
  • bool startWithMoveTo:是否以 moveTo 點開始繪制線段
Path path1 = pathMetric.extractPath(50, 400, startWithMoveTo: true);
Path path1 = pathMetric.extractPath(50, 400, startWithMoveTo: false);

image

關於computeMetrics的方法和屬性還有很多沒講到,等以后有時間了再慢慢研究。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM