Flutter學習:認識CustomPaint組件和Paint對象
Flutter學習:使用CustomPaint繪制路徑
Flutter學習:使用CustomPaint繪制圖形
Flutter學習:使用CustomPaint繪制文字
Flutter學習:使用CustomPaint繪制圖片
Canvas
canvas繪制的所有內容都是以屏幕左上角為原點,右邊和下邊為正方向延伸的直角坐標系。
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]
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
因為我的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);
試用一下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);
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);
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);
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);
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);
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);
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);
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);
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);
其他用法和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);
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);
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);
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);
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);
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);
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);
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的位置。
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);