畫布canvas
畫布是一個矩形區域,我們可以控制其每一像素來繪制我們想要的內容
canvas 擁有多種繪制點、線、路徑、矩形、圓形、以及添加圖像的方法,結合這些方法我們可以繪制出千變萬化的畫面。
雖然,畫布可以畫這些東西,但是決定這些圖形顏色、粗細表現的還是畫筆。
畫筆Paint
Paint非常好理解,就是我們用來畫圖形的工具,我們可以設置畫筆的顏色、粗細、是否抗鋸齒、筆觸形狀以及作畫風格。
通過這些屬性我們可以很方便的來定制自己的UI效果,當然我們在“作畫”的過程中可以定義多個畫筆,這樣更方便我們對圖形的繪制
畫筆Paint的屬性
canvas中有多個與繪制相關的方法,如drawLine()、drawRect()、drawOval()、drawOval()、等方法。
但是,僅僅使用canvas這個畫布還不夠,我們還需要一個畫筆paint,我們可以使用如下代碼來構建paint
Paint _paint = Paint()
..color = Colors.blueAccent //畫筆顏色
..strokeCap = StrokeCap.round //畫筆筆觸類型
..isAntiAlias = true //是否啟動抗鋸齒
..blendMode = BlendMode.exclusion //顏色混合模式
..style = PaintingStyle.fill //繪畫風格,默認為填充
..colorFilter = ColorFilter.mode(Colors.blueAccent,
BlendMode.exclusion) //顏色渲染模式,一般是矩陣效果來改變的,但是flutter中只能使用顏色混合模式
..maskFilter = MaskFilter.blur(BlurStyle.inner, 3.0) //模糊遮罩效果,flutter中只有這個
..filterQuality = FilterQuality.high //顏色渲染模式的質量
..strokeWidth = 15.0; //畫筆的寬度
復制代碼
當然,在正常的開發中一般不會使用這么多的屬性,大家可以根據需要去具體的了解和使用。
畫布canvas的方法
以下內容基于此畫筆繪制:
Paint _paint = new Paint()
..color = Colors.blueAccent
..strokeCap = StrokeCap.round
..isAntiAlias = true
..strokeWidth = 5.0
..style = PaintingStyle.stroke;
復制代碼
繪制直線
void drawLine(Offset p1, Offset p2, Paint paint)
使用給定的涂料在給定點之間繪制一條線。 該行被描邊,此調用忽略[Paint.style]的值。p1
和p2
參數為兩個點的坐標 , 在這兩點之間繪制一條直線。
eg : canvas.drawLine(Offset(20.0, 20.0), Offset(100.0, 100.0), _paint)
復制代碼
[圖片上傳中...(image-e444c2-1563937266654-23)]
<figcaption></figcaption>
繪制點drawPoints
void drawPoints(PointMode pointMode, List points, Paint paint)
繪制點也是非常的簡單,3個參數分別為: PointMode枚舉,坐標 list 和 paint PointMode的枚舉類型有三個,points(點),lines(線,隔點連接),polygon(線,相鄰連接)
canvas.drawPoints(
///PointMode的枚舉類型有三個,points(點),lines(線,隔點連接),polygon(線,相鄰連接)
PointMode.points,
[
Offset(20.0, 130.0),
Offset(100.0, 210.0),
Offset(100.0, 310.0),
Offset(200.0, 310.0),
Offset(200.0, 210.0),
Offset(280.0, 130.0),
Offset(20.0, 130.0),
],
_paint..color = Colors.redAccent);
復制代碼
為了方便演示,我們在上面定義了7個點,第一個和最后一個點重合。
然后我們設置PointMode為points看下效果。
[圖片上傳中...(image-2e5035-1563937266654-22)]
<figcaption></figcaption>
然后我們把PointMode改為lines
[圖片上傳中...(image-c7b574-1563937266654-21)]
<figcaption></figcaption>
PointMode為lines時,兩個點相互連接,也就是說第一個和第二個點連接,第三個跟第四個連接,如果最后只有一個點就舍棄不連接了,在我們的例子中有7個點,所以圖中只有三條連線。
然后我們把PointMode改為lines
[圖片上傳中...(image-aa0ccb-1563937266654-20)]
<figcaption></figcaption>
對,你看的沒有錯跟上面繪制線段的效果是一樣的,相鄰點互相連接。
繪制圓rawCircle
void drawCircle(Offset c, double radius, Paint paint)
參數分別為:圓心的坐標、半徑和paint即可。 圓形是否填充或描邊(或兩者)由Paint.style
控制。
//繪制圓 參數(圓心,半徑,畫筆)
canvas.drawCircle(
Offset(100.0, 350.0),
30.0,
_paint
..color = Colors.greenAccent
..style = PaintingStyle.stroke //繪畫風格改為stroke
);
復制代碼
[圖片上傳中...(image-170b7-1563937266654-19)]
<figcaption></figcaption>
在這里我將畫筆Paint的style改成了stroke 然后我們將畫筆style改成fill (填充) ,fill也是畫筆的style的默認值.
[圖片上傳中...(image-8fbd17-1563937266654-18)]
<figcaption></figcaption>
填充之后,這個圓就變成實心的了.
繪制橢圓drawOval
void drawOval(Rect rect, Paint paint)
繪制一個軸對稱的橢圓形 參數為一個矩形和畫筆paint.
//使用左上和右下角坐標來確定矩形的大小和位置,橢圓是在這個矩形之中內切的
Rect rect1 = Rect.fromPoints(Offset(150.0, 200.0), Offset(300.0, 250.0));
canvas.drawOval(rect1, _paint);
復制代碼
[圖片上傳中...(image-4e7b03-1563937266653-17)]
<figcaption></figcaption>
在前面我們已經講過了使用Rect便可確認這個矩形的大小和位置。
其實,Rect也有多種構建方式:
fromPoints(Offset a, Offset b)
使用左上和右下角坐標來確定矩形的大小和位置
fromCircle({ Offset center, double radius })
使用圓的圓心點坐標和半徑和確定外切矩形的大小和位置
fromLTRB(double left, double top, double right, double bottom)
使用矩形左邊的X坐標、矩形頂部的Y坐標、矩形右邊的X坐標、矩形底部的Y坐標來確定矩形的大小和位置
fromLTWH(double left, double top, double width, double height)
使用矩形左邊的X坐標、矩形頂部的Y坐標矩形的寬高來確定矩形的大小和位置
復制代碼
繪制圓弧drawArc
void drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint)
首先還是需要Rect來確認圓弧的位置,還需要開始的弧度、結束的弧度、是否使用中心點繪制(圓弧是否向中心閉合)、以及paint.
弧度
根據定義,一周的弧度數為2πr/r=2π,360°角=2π弧度,因此,1弧度約為57.3°,即57°17’44.806’’,1°為π/180弧度,近似值為0.01745弧度,周角為2π弧度,平角(即180°角)為π弧度,直角為π/2弧度。
特殊的弧度:
度 | 弧度 |
---|---|
0° | 0 |
30° | π/6 |
45° | π/4 |
60° | π/3 |
90° | π/2 |
120° | 2π/3 |
180° | π |
270° | 3π/2 |
360° | 2π |
//繪制圓弧
// Rect來確認圓弧的位置,還需要開始的弧度、結束的弧度、是否使用中心點繪制、以及paint弧度
Rect rect2 = Rect.fromCircle(center: Offset(200.0, 50.0), radius: 80.0);
canvas.drawArc(rect2, 0.0, 0.8, false, _paint);
復制代碼
[圖片上傳中...(image-925ab1-1563937266653-16)]
<figcaption></figcaption>
繪制個90度的弧度
const PI = 3.1415926;
Rect rect2 = Rect.fromCircle(center: Offset(200.0, 50.0), radius: 80.0);
canvas.drawArc(rect2, 0.0, PI / 2, false, _paint);
復制代碼
定義π為3.1415926,定義開始的角度為0°掃過的角度為PI / 2(90°),設置userCenter為false
[圖片上傳中...(image-ab313c-1563937266653-15)]
<figcaption></figcaption>
將useCenter改成true 試試:[圖片上傳中...(image-6337dc-1563937266653-14)]
<figcaption></figcaption>
發現圓弧向中心點閉合了.
繪制圓角矩形drawDRRect
void drawRRect(RRect rrect, Paint paint)
使用RRect確定矩形大小及弧度,使用paint來完成繪制。
RRect構建起來也非常的方便,直接使用fromRectAndRadius即可
RRect.fromRectAndRadius(rect, radius)
rect依然用來表示矩形的位置和大小,radius用來表示圓角的大小。
//用Rect構建一個邊長50,中心點坐標為100,100的矩形
Rect rect = Rect.fromCircle(center: Offset(100.0, 150.0), radius: 50.0);
//根據上面的矩形,構建一個圓角矩形
RRect rrect = RRect.fromRectAndRadius(rect, Radius.circular(20.0));
canvas.drawRRect(rrect, _paint);
復制代碼
[圖片上傳中...(image-24ee2d-1563937266653-13)]
<figcaption></figcaption>
將圓角的半徑設置為邊長(從20改成50)試一下:
[圖片上傳中...(image-1e3090-1563937266653-12)]
<figcaption></figcaption>
就變成了圓.
繪制雙圓角矩形drawRRect
void drawDRRect(RRect outer, RRect inner, Paint paint)
和drawRRect類似,使用RRect確定內部、外部矩形大小及弧度,使用paint來完成繪制。
//繪制兩個矩形
Rect rect1 = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 60.0);
Rect rect2 = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 40.0);
//分別繪制外部圓角矩形和內部的圓角矩形
RRect outer = RRect.fromRectAndRadius(rect1, Radius.circular(10.0));
RRect inner = RRect.fromRectAndRadius(rect2, Radius.circular(10.0));
canvas.drawDRRect(outer, inner, _paint);
復制代碼
使用Rect.fromCircle來創建Rect,使用RRect.fromRectAndRadius來創建RRect
[圖片上傳中...(image-3dab2a-1563937266653-11)]
<figcaption></figcaption>
可以看到兩個圓角矩形,當然我們也可以嘗試調整角度的度數大小。
//繪制兩個矩形
Rect rect1 = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 60.0);
Rect rect2 = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 40.0);
//分別繪制外部圓角矩形和內部的圓角矩形
RRect outer = RRect.fromRectAndRadius(rect1, Radius.circular(30.0));
RRect inner = RRect.fromRectAndRadius(rect2, Radius.circular(5.0));
canvas.drawDRRect(outer, inner, _paint);
復制代碼
[圖片上傳中...(image-d42b23-1563937266653-10)]
<figcaption></figcaption>
你甚至可以調整角度的大小使兩個矩形都變成圓來形成一個圓環.
繪制路徑drawPath
void drawPath(Path path, Paint paint)
繪制路徑,首先需要一個要繪制的路徑path,然后就是這個paint了。
Path的常用方法:
方法名 | 作用 |
---|---|
moveTo | 將路徑起始點移動到指定的位置 |
relativeMoveTo | 相對于當前位置移動到 |
lineTo | 從當前位置連接指定點 |
relativeLineTo | 相對當前位置連接到 |
arcTo | 曲線 |
conicTo | 貝塞爾曲線 |
add** | 添加其他圖形,如addArc,在路徑是添加圓弧 |
contains | 路徑上是否包括某點 |
transfor | 給路徑做matrix4變換 |
combine | 結合兩個路徑 |
close | 關閉路徑,連接路徑的起始點 |
reset | 重置路徑,恢復到默認狀態 |
eg:
//新建了一個path,然后將路徑起始點移動到坐標(100,100)的位置
Path path = new Path()..moveTo(100.0, 100.0);
path.lineTo(200.0, 200.0);
canvas.drawPath(path, _paint);
復制代碼
首先新建了一個path,然后將路徑起始點移動到坐標(100,100)的位置, 然后從這個位置連接(200,200)的點.
[圖片上傳中...(image-97f172-1563937266652-9)]
<figcaption></figcaption>
我們也可以繪制多個路徑:
Path path = new Path()..moveTo(100.0, 100.0);
path.lineTo(200.0, 200.0);
path.lineTo(100.0, 300.0);
path.lineTo(150.0, 350.0);
path.lineTo(150.0, 500.0);
canvas.drawPath(path, _paint);
復制代碼
[圖片上傳中...(image-c5e81-1563937266652-8)]
<figcaption></figcaption>
使用二階貝塞爾曲線繪制弧線:
void arcTo(Rect rect, double startAngle, double sweepAngle, bool forceMoveTo)
rect
我們都知道了,是一個矩形,startAngle
是開始的弧度,sweepAngle
是結束的弧度 重點介紹一下forceMoveTo
.forceMoveTo:
- 如果“forceMoveTo”參數為false,則添加一條直線段和一條弧段。
- 如果“forceMoveTo”參數為true,則啟動一個新的子路徑,其中包含一個弧段。
例如:
Path path = new Path()..moveTo(100.0, 100.0);
Rect rect = Rect.fromCircle(center: Offset(200.0, 200.0), radius: 60.0);
path.arcTo(rect, 0.0, 3.14, false);
canvas.drawPath(path, _paint);
復制代碼
[圖片上傳中...(image-4225cb-1563937266651-7)]
<figcaption></figcaption>
這里,我們利用貝塞爾曲線繪制了一個半圓,因為起始點的坐標是(100,100),而我們繪制貝塞爾曲線的時候,曲線的原點是(200,200)半徑,60,所以我們移動到(200,260)的位置再畫這個曲線.
因為forceMoveTo此時為false,所以從起始點到曲線的起始點畫出了直線路徑, 改為true可以看到,因為啟動了一個新的子路徑,所以那條線段沒有了:
[圖片上傳中...(image-8b24f7-1563937266651-6)]
<figcaption></figcaption>
當然,你甚至可以用貝塞爾曲線直接畫一個圓:
Rect rect = Rect.fromCircle(center: Offset(200.0, 200.0), radius: 60.0);
path.arcTo(rect, 0.0, 3.14*2, false);
canvas.drawPath(path, _paint);
復制代碼
[圖片上傳中...(image-32635a-1563937266651-5)]
<figcaption></figcaption>
使用三階貝塞爾曲線繪制?:
void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3)
var width = 200;
var height = 300;
path.moveTo(width / 2, height / 4);
path.cubicTo((width * 6) / 7, height / 9, (width * 13) / 13,
(height * 2) / 5, width / 2, (height * 7) / 12);
canvas.drawPath(path, _paint);
Path path2 = new Path();
path2.moveTo(width / 2, height / 4);
path2.cubicTo(width / 7, height / 9, width / 21, (height * 2) / 5,
width / 2, (height * 7) / 12);
canvas.drawPath(path2, _paint);
復制代碼
看一下效果:
[圖片上傳中...(image-e35e61-1563937266651-4)]
<figcaption></figcaption>
然后我們改變paint的樣式:
canvas.drawPath(path, _paint);
替換為:
canvas.drawPath(
path,
_paint
..style = PaintingStyle.fill
..color = Colors.red);
復制代碼
我們將畫筆的顏色改成紅色,樣式改為填充:
[圖片上傳中...(image-c01de9-1563937266651-3)]
<figcaption></figcaption>
繪制顏色drawColor
void drawColor(Color color, BlendMode blendMode)
我們先繪制一個圓:
canvas.drawCircle(Offset(100.0, 100.0), 50.0, _paint);
復制代碼
[圖片上傳中...(image-b56f60-1563937266651-2)]
<figcaption></figcaption>
然后我們添加一行代碼:
canvas.drawCircle(Offset(100.0, 100.0), 50.0, _paint);
canvas.drawColor(Colors.red, BlendMode.color); // 添加這行
復制代碼
[圖片上傳中...(image-1a6320-1563937266650-1)]
<figcaption></figcaption>
可以看到,圓的顏色變成了紅色, 我們還可以改變BlendMode, 例如:BlendMode.colorDodge
[圖片上傳中...(image-32ac1c-1563937266650-0)]
<figcaption></figcaption>
更多效果可以查詢BlendMode源碼.
繪制圖片drawImage
void drawImage(Image image, Offset p, Paint paint)
將給定的[image]以其左上角的[偏移量]繪制到畫布中 首先我們需要獲取本地圖片文件,然后繪制圖片即可
全文相關代碼已提交到 github