Flutter學習:使用CustomPaint繪制圖形


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

Canvas

canvas繪制的所有內容都是以屏幕左上角為原點,右邊和下邊為正方向延伸的直角坐標系。

image

drawCircle 繪制圓形

drawCircle需要傳遞3個參數:

  • Offset c:圓心的位置坐標
  • double radius:圓的半徑
  • Paint paint:繪制對象
// 以左上角為圓心繪制
canvas.drawCircle(const Offset(0, 0), 100, paint);
// 以中心點為圓心繪制
canvas.drawCircle(size.center(Offset.zero), 100, paint);
// 以右下角為圓心繪制
canvas.drawCircle(Offset(size.width, size.height), 100, paint);![CustomPaint_drawCircle]

image

drawRect 繪制矩形

drawCircle需要傳遞2個參數:

  • Rect rect:Rect對象
  • Paint paint:繪制對象

創建Rect對象的方法有7種:

Rect.zero

繪制一個左、上、右和下邊緣都為零的矩形

Rect rect = Rect.zero;
canvas.drawRect(rect, paint);

Rect.largest

繪制一個覆蓋整個坐標空間的矩形

Rect rect = Rect.largest;
canvas.drawRect(rect, paint);

image

Rect.fromCenter

確定一個矩形的中心點坐標來繪制。

Rect.fromCenter({ required Offset center, required double width, required double height})需要傳遞3個參數:

  • center用來設置矩形的中心點坐標
  • width用來設置矩形的寬
  • height用來確定矩形的高
Offset center = size.center(Offset.zero);
Rect rect = Rect.fromCenter(center: center, width: 250, height: 250);
canvas.drawRect(rect, paint);

image

Rect.fromLTRB

從左、上、右和下邊緣構造一個矩形

Rect.fromLTRB( double left, double top, double right, double bottom)需要傳遞4個參數:

  • left代表左上角頂點的x坐標
  • top代表左上角頂點的y坐標
  • right代表右下角頂點的x坐標
  • bottom代表右下角頂點的y坐標
Rect rect = const Rect.fromLTRB(50, 300, 350, 500);
canvas.drawRect(rect, paint);

image

Rect.fromCircle

構造一個以給定圓為邊界的矩形

Rect.fromCircle({required Offset center, required double radius})需要傳遞2個對象:

  • center用來設置圓的圓心左標
  • radius用來設置圓的半徑
Offset center = size.center(Offset.zero);
Rect rect = Rect.fromCircle(center: center, radius: size.width / 3);
canvas.drawRect(rect, paint);

image

Rect.fromLTWH

通過左上角點的坐標和寬度、高度來構造一個矩形

Rect.fromLTWH( double left, double top, double width, double height)需要傳遞4個參數:

  • left用來設置左上角的x坐標
  • top用來設置左上角的y坐標
  • width用來設置矩形的寬
  • height用來設置矩形的高
Rect rect = const Rect.fromLTWH(50, 220, 300, 300);
canvas.drawRect(rect, paint);

image

Rect.fromPoints

通過兩個坐標點來繪制矩形。

Rect.fromPoints(Offset a, Offset b)需要傳遞兩個Offset對象:

  • a代表左上角點的坐標
  • b代表右下角的點的坐標
Rect rect = Rect.fromPoints(const Offset(60, 200), const Offset(320, 500));
canvas.drawRect(rect, paint);

image

drawRRect 繪制圓角矩形

RRect.zero

繪制一個左、上、右和下邊緣都為零的圓角矩形,和Rect.zero效果一樣

RRect rrect = RRect.zero;

RRect.fromRectAndRadius

通過繪制一個矩形再設置圓角半徑來繪制圓角矩形

RRect RRect.fromRectAndRadius(Rect rect, Radius radius)需要傳遞2個參數:

  • rect是一個矩形對象
  • radius為矩形設置圓角
Rect rect = Rect.fromPoints(const Offset(50, 200), const Offset(320, 600));
RRect rrect = RRect.fromRectAndRadius(rect, const Radius.circular(40.0));
canvas.drawRRect(rrect, paint);

image

RRect.fromLTRBR

RRect.fromLTRBR(double left, double top, double right, double bottom, Radius radius)需要傳遞5個參數,前4個參數和Rect.fromLTRB一樣:

  • left代表左上角頂點的x坐標
  • top代表左上角頂點的y坐標
  • right代表右下角頂點的x坐標
  • bottom代表右下角頂點的y坐標
  • radius用來設置矩形的圓角半徑
RRect rrect = RRect.fromLTRBR(80, 120, 320, 420, const Radius.circular(48));
canvas.drawRRect(rrect, paint);

image

RRect.fromLTRBXY

RRect.fromLTRBXY( double left, double top, double right, double bottom, double radiusX, double radiusY)需要傳遞6個參數,前4個參數和Rect.fromLTRB一樣:

  • left代表左上角頂點的x坐標
  • top代表左上角頂點的y坐標
  • right代表右下角頂點的x坐標
  • bottom代表右下角頂點的y坐標
  • radiusX用來設置x方向的半徑長度
  • radiusY用來設置y方向的半徑長度
RRect rrect = const RRect.fromLTRBXY(50, 150, 320, 530, 90, 60);
canvas.drawRRect(rrect, paint);

image

RRect.fromLTRBAndCorners

從其左、上、右和下邊緣以及 topLeft、topRight、bottomRight 和 bottomLeft 半徑構造一個圓角矩形

RRect.fromLTRBAndCorners(
  double left, double top, double right, double bottom, {
    Radius topLeft = Radius.zero,
    Radius topRight = Radius.zero, 
    Radius bottomRight = Radius.zero,
    Radius bottomLeft = Radius.zero
})

可以傳遞8個參數,其中4個為必選的參數:

  • left代表左上角頂點的x坐標
  • top代表左上角頂點的y坐標
  • right代表右下角頂點的x坐標
  • bottom代表右下角頂點的y坐標
  • topLeft用來設置左上角的圓角半徑
  • topRight用來設置右上角的圓角半徑
  • bottomRight用來設置右下角角的圓角半徑
  • bottomLeft用來設置左下角的圓角半徑
RRect rrect = RRect.fromLTRBAndCorners(50, 120, 320, 420,
  topLeft: const Radius.circular(20),
  topRight: const Radius.circular(40),
  bottomLeft: const Radius.circular(60),
  bottomRight: const Radius.circular(80));
canvas.drawRRect(rrect, paint);

image

RRect.fromRectXY

根據方法名可以看出,需要依據一個矩形來繪制

RRect.fromRectXY( Rect rect, double radiusX, double radiusY)需要傳遞3個參數:

  • rect用來繪制一個矩形

  • radiusX用來設置x方向的半徑長度

  • radiusY用來設置y方向的半徑長度

Rect rect = Rect.fromPoints(const Offset(50, 180), const Offset(300, 420));
RRect rrect = RRect.fromRectXY(rect, 60.0, 100.0);
canvas.drawRRect(rrect, paint);

image

RRect.fromRectAndCorners

RRect.fromRectAndCorners( Rect rect,{ Radius topLeft = Radius.zero, Radius topRight = Radius.zero, Radius bottomRight = Radius.zero, Radius bottomLeft = Radius.zero})可以傳遞5個參數,1個必要的,4個可選的:

  • rect用來繪制一個矩形
  • bottom代表右下角頂點的y坐標
  • topLeft用來設置左上角的圓角半徑
  • topRight用來設置右上角的圓角半徑
  • bottomRight用來設置右下角角的圓角半徑
  • bottomLeft用來設置左下角的圓角半徑
Rect rect = Rect.fromPoints(const Offset(40, 125), const Offset(330, 520));
RRect rrect = RRect.fromRectAndCorners(rect,
  topLeft: const Radius.circular(60),
  topRight: const Radius.circular(40),
  bottomLeft: const Radius.circular(80),
  bottomRight: const Radius.circular(20));
canvas.drawRRect(rrect, paint);

image

drawArc 繪制圓弧

drawArc繪制一個以矩形為參照物的圓弧,需要傳遞5個屬性:

  • Rect rect:矩形的位置和大小
  • double startAngle:圓弧開始的角度
  • double sweepAngle:圓弧開始到結束的角度大小
  • bool useCenter:是否向中心閉合
  • Paint paint:繪制對象
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 200, height: 200);
// 閉合
canvas.drawArc(rect, 0, 3.14, true, paint);
// 不閉合
canvas.drawArc(rect, 0, 3.14, false, paint);

image

drawColor 繪制顏色

drawColor繪制的顏色會沾滿子整個屏幕。需要傳遞2個參數:

  • Color color:要繪制的顏色
  • BlendMode blendMode:顏色的混合模式
canvas.drawColor(Colors.blue, BlendMode.darken);

drawShadow繪制陰影

繪制陰影

drawShadow(Path path, Color color, double elevation, bool transparentOccluder)需要傳遞4個參數:

  • path是需要繪制陰影的路徑
  • color是陰影的顏色
  • elevation是陰影的范圍
  • transparentOccluder表示如果遮擋對象是透明的,應該為true,否則為false
Path path = Path();
path.moveTo(80, 200);
path.lineTo(320, 400);
path.lineTo(200, 340);
path.lineTo(100, 460);
path.close();
canvas.drawShadow(path, Colors.black, 8.0, false);
canvas.drawPath(path, paint);

image

drawVertices

繪制頂點。需要傳遞3個參數:

  • Vertices vertices:需要繪制的頂點對象
  • BlendMode blendMode:混合模式
  • Paint paint:繪制對象

該方法的重點在於Vertices 對象,它有5個參數:

  • VertexMode mode:頂點模式
  • List positions:設置所有頂點的坐標
  • List<Offset>? textureCoordinates:用於裁剪圖像着色器中設置的圖像。切割部分應用於三角形網格。請注意, textureCoordinates是圖像上的坐標
  • List<Color>? colors:設置每個頂點處的顏色,數量必須和positions一樣
  • List<int>? indices:如果提供了indices參數,則列表中的所有值都必須是positions的有效索引值

先來繪制一個最簡單的:

Paint paint = Paint()..color = Colors.blue;
Vertices vertices = Vertices(
  VertexMode.triangles,
  const [ Offset.zero, Offset(-100, 120), Offset(60, 60)],
);
BlendMode blendMode = BlendMode.color;
canvas.drawVertices(vertices, blendMode, paint);

image

因為我的CustomPaint是包裹在Center組件中,所以它以中心為原點。如果沒有Center組件,會以左上角為原點。

VertexMode

我們來研究一下VertexMode這個枚舉類:

  • VertexMode.triangles:將三個點的每個序列繪制為三角形的頂點
  • VertexMode.triangleFan:繪制第一個點和兩個點的每個滑動窗口作為三角形的頂點
  • VertexMode.triangleStrip:將三個點的每個滑動窗口繪制為三角形的頂點

這幾句是官方文檔寫的,看起來很難理解,所以我引用【Flutter 專題】35 圖解自定義 View 之 Canvas (三)這篇文章關於這幾個值的描述,借用 A B C D E F G H I 來代替頂點:

  • VertexMode.triangles:每三個分割頂點相連,即 [A B C] [D E F] [G H I] 共 3
  • VertexMode.triangleStrip:每相鄰的三個頂點相連,即 [A B C] [B C D] [C D E] [D E F] [E F G] [F G H] [G H I] 共 7
  • VertexMode.triangleFan:每相鄰的兩個頂點與首點相連,即 [A B C] [A C D] [A D E] [A E F] [A F G] [A G H] [A H I] 共 7

試用一下VertexMode.triangleStrip

Paint paint = Paint()..color = Colors.blue;
Vertices vertices = Vertices(
  VertexMode.triangleStrip,
  const [Offset(-200, -80), Offset.zero, Offset(-100, 120), Offset(60, 60)],
);
BlendMode blendMode = BlendMode.color;
canvas.drawVertices(vertices, blendMode, paint);

image

試用一下VertexMode.triangleFan

Paint paint = Paint()..color = Colors.blue;
Vertices vertices = Vertices(
  VertexMode.triangleFan,
  const [Offset(-200, -80), Offset.zero, Offset(-100, 120), Offset(60, 60), Offset(160, -120)],
);
BlendMode blendMode = BlendMode.color;
canvas.drawVertices(vertices, blendMode, paint);

image

colors

設置每個頂點處的顏色,數量必須和positions一樣

Paint paint = Paint();
Vertices vertices = Vertices(
  VertexMode.triangleFan,
  const [Offset(-200, -80), Offset.zero, Offset(-100, 120), Offset(60, 60), Offset(160, -120)],
  colors: [Colors.red, Colors.orange, Colors.blue, Colors.yellow, Colors.green],
);
BlendMode blendMode = BlendMode.color;
canvas.drawVertices(vertices, blendMode, paint);

image

indices

用來過濾positions的索引

Paint paint = Paint()..color = Colors.blue;
Vertices vertices = Vertices(
  VertexMode.triangleFan,
  const [Offset(-200, -80), Offset.zero, Offset(-100, 120), Offset(60, 60), Offset(160, -120)],
  indices: [0, 1, 4],
);
BlendMode blendMode = BlendMode.color;
canvas.drawVertices(vertices, blendMode, paint);

image

textureCoordinates

裁剪圖像着色器繪制的圖片,數量必須和positions一樣

canvas.drawImage(image, Offset.zero, Paint());
Paint paint = Paint()
  ..color = Colors.blue
  ..shader = ImageShader(image, TileMode.decal, TileMode.decal, Matrix4.skewX(0).storage);
Vertices vertices = Vertices(
  VertexMode.triangleFan,
  [
    Offset.zero,
    Offset(size.width, 0),
    Offset(0, size.height),
    Offset(size.width, size.height),
    Offset(size.width, 0),
  ],
  textureCoordinates: [
    Offset.zero,
    Offset(size.width - 100, 0),
    Offset(0, size.height - 100),
    Offset(size.width - 80, size.height - 80),
    Offset(size.width - 40, 0),
  ],
);
BlendMode blendMode = BlendMode.color;
canvas.drawVertices(vertices, blendMode, paint);

image

drawDRRect 繪制嵌套圓角矩形

drawDRRect繪制嵌套的兩個矩形,outer圓角矩形的寬高必須大於等於inner圓角矩形的寬高。需要傳遞3個參數:

  • RRect outer:繪制外圍圓角矩形
  • RRect inner:繪制內部圓角矩形
  • Paint paint:繪制對象
// 外圍圓角矩形
Rect rectOuter = Rect.fromCenter(center: size.center(Offset.zero), width: 300, height: 300);
RRect outer = RRect.fromRectAndRadius(rectOuter, const Radius.circular(80));
// 內部圓角矩形
Rect rectInner = Rect.fromCenter(center: size.center(Offset.zero), width: 200, height: 100);
RRect inner = RRect.fromRectAndRadius(rectInner, const Radius.circular(60));
canvas.drawDRRect(outer, inner, paint);

image

drawLine 繪制線段

drawLine需要傳遞3個參數:

  • Offset p1:第一個點的位置
  • Offset p2:第二個點的位置
  • Paint paint:繪制對象
Offset p1 = const Offset(200, 50);
Offset p2 = const Offset(600, 200);
canvas.drawLine(p1, p2, paint);

image

drawOval 繪制橢圓

drawOval繪制一個軸對齊的橢圓。需要傳遞2個參數:

  • Rect rect:獲取橢圓的原點和寬高
  • Paint paint:繪制對象
// 寬高不相等為橢圓
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 300, height: 200);
canvas.drawOval(rect, paint);
// 寬高相等為正圓
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 300, height: 300);
canvas.drawOval(rect, paint);

image

drawPoints 繪制點

drawPoints根據給定的PointMode繪制一系列點。需要傳遞3個參數:

  • PointMode pointMode:點的繪制模式
  • List points:點的坐標
  • Paint paint:繪制對象
List<Offset> points = const [
  Offset(100, 100), Offset(200, 200),
  Offset(100, 300), Offset(300, 400),
  Offset(200, 500), Offset(300, 400),
];
// 繪制多條線段,兩個點一組
canvas.drawPoints(PointMode.lines, points, paint);
// 繪制點,由strokeCap控制點的樣式
canvas.drawPoints(PointMode.points, points, paint);
// 將每一個點連接起來
canvas.drawPoints(PointMode.polygon, points, paint);

image

drawRawPoints 繪制點

繪制一系列點,需要傳入3個參數:

  • PointMode pointMode:點的繪制模式
  • Float32List points:點的坐標
  • Paint paint:繪制對象
Paint paint = Paint()
  ..color = Colors.blue
  ..strokeWidth = 12;
Float32List points = Float32List.fromList([100, 120, 500, 250, 150, 300]);
canvas.drawRawPoints(PointMode.points, points, paint);

image

其他用法和drawPoints一樣。

canvas.clipPath 裁剪路徑

繪制一條路徑,用來裁剪其他形狀。有2個參數傳遞:

  • Path path:繪制裁剪的形狀
  • bool doAntiAlias:是否抗鋸齒
// 裁剪的形狀、路徑
Path path = Path();
path.lineTo(-100, -100);
path.lineTo(-100, 200);
path.close();
canvas.clipPath(path);
// 裁剪前的原圖形
Paint paint = Paint()..color = Colors.blue;
canvas.drawCircle(size.center(Offset.zero), 120, paint);

image

canvas.clipRect 裁剪矩形

繪制一個矩形,用來裁剪其他形狀。有3個參數傳遞:

  • Rect rect:繪制裁剪的形狀
  • ClipOp clipOp:裁剪的類型
  • bool doAntiAlias:是否抗鋸齒
// 裁剪的形狀
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 200, height: 200);
canvas.clipRect(rect, clipOp: ClipOp.intersect);
// 裁剪前的原圖形
Paint paint = Paint()..color = Colors.blue;
canvas.drawCircle(size.center(Offset.zero), 120, paint);

image

canvas.clipRRect 裁剪圓角矩形

繪制一個圓角矩形,用來裁剪其他形狀。有2個參數傳遞:

  • RRect rrect:繪制裁剪的形狀
  • bool doAntiAlias:是否抗鋸齒
// 裁剪的形狀
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 200, height: 200);
RRect rrect = RRect.fromRectAndRadius(rect, const Radius.circular(80));
canvas.clipRRect(rrect);
// 裁剪前的原圖形
Paint paint = Paint()..color = Colors.blue;
canvas.drawCircle(size.center(Offset.zero), 120, paint);

image

canvas.transform

對形狀進行變形。只需要傳遞一個對象:

  • Float64List matrix4:將當前變換乘以指定的 4⨉4 變換矩陣,該矩陣指定為以列優先順序排列的值列表
Paint paint = Paint()..color = Colors.blue;
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 200, height: 200);
canvas.transform(Matrix4.rotationZ(1).storage);
canvas.drawRect(rect, paint);

image

canvas.rotate

旋轉圖形。只有一個傳遞的屬性:

  • double radians:旋轉的角度,以3.14為標准
Paint paint = Paint()..color = Colors.blue;
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 200, height: 200);
canvas.rotate(1);
canvas.drawRect(rect, paint);

視圖和canvas.transform的一樣

canvas.scale

縮放圖形。有2個參數可以傳遞:

  • double sx:水平方向縮放
  • double? sy:垂直方向縮放

只寫一個參數,第二個參數默認一樣

Paint paint = Paint()..color = Colors.blue;
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 200, height: 200);
canvas.scale(1.3);
canvas.drawRect(rect, paint);

image

canvas.skew

傾斜圖形。需要傳遞2個參數:

  • double sx:圍繞原點順時針在運行單位上的水平傾斜
  • double sy:原點周圍順時針在運行單位上的垂直傾斜

兩個參數的取值最好在 0-1 之間

Paint paint = Paint()..color = Colors.blue;
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 200, height: 200);
canvas.skew(.4, .2);
canvas.drawRect(rect, paint);

image

canvas.translate

偏移圖形。需要傳遞2個參數:

  • double dx:水平移動距離
  • double dy:垂直移動距離
Paint paint = Paint()..color = Colors.blue;
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 200, height: 200);
canvas.translate(40, 40);
canvas.drawRect(rect, paint);

image

canvas.save和canvas.restore

canvas可以把之前的屬性存儲起來,canvas.restore在把存儲的屬性釋放出來

Paint paint = Paint()..color = Colors.blue;
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 150, height: 150);
canvas.save();
canvas.scale(.8, .8);
canvas.translate(150, 150);
canvas.drawRect(rect, paint);
canvas.restore();
canvas.drawRect(rect, paint);

save方法是將它之前的屬性拷貝了一份存起來。restore是將存起來的那部分代碼釋放出來,就是把save之前的內容復制一份,放在restore的位置。

image

canvas.getSaveCount

返回保存堆棧上的項目數,包括初始狀態。這意味着它為干凈的畫布返回 1,並且每次調用save和saveLayer都會增加它,並且每次匹配的restore調用都會減少它。這個數字不能低於 1。

canvas.saveLayer

相當於clipRect + save 兩個方法。傳遞2個參數:

  • Rect? bounds:繪制一個區域
  • Paint paint:畫筆對象,主要是為了使用Paint.colorFilter和Paint.blendMode這兩個屬性

設置一個范圍,圖形只能在該范圍顯示,超出部分裁剪掉:

Paint paint = Paint()..color = Colors.blue;
Rect rect = Rect.fromCenter(center: size.center(Offset.zero), width: 150, height: 150);
canvas.drawRect(rect, paint);
Rect rect1 = const Rect.fromLTWH(100, 100, 200, 200);
canvas.drawRect(rect1, Paint()..color = Colors.red);
canvas.saveLayer(rect1, paint);
canvas.scale(.8, .8);
canvas.translate(150, 150);
canvas.drawRect(rect, paint);

image


免責聲明!

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



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