前有大佬分享了用CustomPaint畫一個自定義的CircleProgressBar
的文章, 今天我分享一波自定義View(CustomPaint)的一些基礎知識git
畫布是一個矩形區域,咱們能夠控制其每一像素來繪製咱們想要的內容github
canvas 擁有多種繪製點、線、路徑、矩形、圓形、以及添加圖像的方法,結合這些方法咱們能夠繪製出變幻無窮的畫面。canvas
雖然,畫布能夠畫這些東西,可是決定這些圖形顏色、粗細表現的仍是畫筆。markdown
Paint很是好理解,就是咱們用來畫圖形的工具,咱們能夠設置畫筆的顏色、粗細、是否抗鋸齒、筆觸形狀以及做畫風格。工具
經過這些屬性咱們能夠很方便的來定製本身的UI效果,固然咱們在「做畫」的過程當中能夠定義多個畫筆,這樣更方便咱們對圖形的繪製oop
canvas中有多個與繪製相關的方法,如drawLine()、drawRect()、drawOval()、drawOval()、等方法。spa
可是,僅僅使用canvas這個畫布還不夠,咱們還須要一個畫筆paint,咱們可使用以下代碼來構建paint3d
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; //畫筆的寬度
複製代碼
固然,在正常的開發中通常不會使用這麼多的屬性,你們能夠根據須要去具體的瞭解和使用。code
如下內容基於此畫筆繪製:orm
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)
複製代碼
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看下效果。
而後咱們把PointMode改成lines
PointMode爲lines時,兩個點相互鏈接,也就是說第一個和第二個點鏈接,第三個跟第四個鏈接,若是最後只有一個點就捨棄不鏈接了,在咱們的例子中有7個點,因此圖中只有三條連線。
而後咱們把PointMode改成lines
對,你看的沒有錯跟上面繪製線段的效果是同樣的,相鄰點互相鏈接。
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
);
複製代碼
在這裏我將畫筆Paint的style改爲了stroke 而後咱們將畫筆style改爲fill (填充) ,fill也是畫筆的style的默認值. 填充以後,這個圓就變成實心的了.
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);
複製代碼
在前面咱們已經講過了使用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座標矩形的寬高來肯定矩形的大小和位置
複製代碼
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);
複製代碼
繪製個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 將useCenter改爲true 試試: 發現圓弧向中心點閉合了.
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);
複製代碼
將圓角的半徑設置爲邊長(從20改爲50)試一下: 就變成了圓.
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
能夠看到兩個圓角矩形,固然咱們也能夠嘗試調整角度的度數大小。
//繪製兩個矩形
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);
複製代碼
你甚至能夠調整角度的大小使兩個矩形都變成圓來造成一個圓環.
void drawPath(Path path, Paint paint)
繪製路徑,首先須要一個要繪製的路徑path,而後就是這個paint了。
方法名 | 做用 |
---|---|
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)的點.
咱們也能夠繪製多個路徑:
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);
複製代碼
void arcTo(Rect rect, double startAngle, double sweepAngle, bool forceMoveTo)
rect
咱們都知道了,是一個矩形,startAngle
是開始的弧度,sweepAngle
是結束的弧度 重點介紹一下forceMoveTo
. forceMoveTo:
例如:
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);
複製代碼
這裏,咱們利用貝塞爾曲線繪製了一個半圓,由於起始點的座標是(100,100),而咱們繪製貝塞爾曲線的時候,曲線的原點是(200,200)半徑,60,因此咱們移動到(200,260)的位置再畫這個曲線.
由於forceMoveTo此時爲false,因此從起始點到曲線的起始點畫出了直線路徑, 改成true能夠看到,由於啓動了一個新的子路徑,因此那條線段沒有了:
固然,你甚至能夠用貝塞爾曲線直接畫一個圓:
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);
複製代碼
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);
複製代碼
看一下效果:
而後咱們改變paint的樣式:
canvas.drawPath(path, _paint);
替換爲:
canvas.drawPath(
path,
_paint
..style = PaintingStyle.fill
..color = Colors.red);
複製代碼
咱們將畫筆的顏色改爲紅色,樣式改成填充:
void drawColor(Color color, BlendMode blendMode)
咱們先繪製一個圓:
canvas.drawCircle(Offset(100.0, 100.0), 50.0, _paint);
複製代碼
而後咱們添加一行代碼:
canvas.drawCircle(Offset(100.0, 100.0), 50.0, _paint);
canvas.drawColor(Colors.red, BlendMode.color); // 添加這行
複製代碼
能夠看到,圓的顏色變成了紅色, 咱們還能夠改變BlendMode, 例如: BlendMode.colorDodge
更多效果能夠查詢BlendMode源碼.
void drawImage(Image image, Offset p, Paint paint)
將給定的[image]以其左上角的[偏移量]繪製到畫布中 首先咱們須要獲取本地圖片文件,而後繪製圖片便可
全文相關代碼已提交到 github