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