Flutter 中實現繪製的主要是CustomPainter類、html
咱們通常繼承這個類,來使用它;git
class MyPainter extends CustomPainter{
@override
void paint(Canvas canvas, Size size) {
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return null;
}
}複製代碼
而後放在父控件的child裏用CustomPaint包裹github
child: new CustomPaint(
size: new Size(200,200),
painter: new MyPainter())複製代碼
自定義繪製很是簡單,建立好 Paint 對象,重寫 paint(),繪製代碼放在paint()裏面,大概就是這樣:canvas
Paint _paint = Paint()
..color = Colors.amber //畫筆顏色
..strokeCap = StrokeCap.round //畫筆筆觸類型
..isAntiAlias = true //是否啓動抗鋸齒
..strokeWidth = 15.0; //畫筆的寬度
@override
void paint(Canvas canvas, Size size) {
// 畫個實心圓
canvas.drawCircle(new Offset(200, 200), 100, _paint);
}複製代碼
Canvas.drawXXX() 系列方法和 Paint 的基礎掌握了,就可以進行簡單的繪製需求。bash
Paint _paint = Paint()
..color = Colors.deepOrange//畫筆顏色
..strokeCap = StrokeCap.round //畫筆筆頭類型
..isAntiAlias = true //是否開啓抗鋸齒
..blendMode = BlendMode.src//顏色混合模式
..style = PaintingStyle.fill //畫筆樣式,默認爲填充
..colorFilter = ColorFilter.mode(Colors.blueAccent,
BlendMode.src) //顏色渲染模式
..maskFilter = MaskFilter.blur(BlurStyle.inner, 3.0) //模糊遮罩效果
..filterQuality = FilterQuality.high //顏色渲染模式的質量
..strokeWidth = 5.0; //畫筆的寬度複製代碼
好了,基礎介紹完了,你們能夠直接點擊下方,打開Flutter官方文檔查看原汁原味的資料哦async
這篇文章到這裏就能夠關閉了,嘻嘻。ide
如下結合示例說說主要方法的使用動畫
這個方法通常用於畫板底色填充及蒙版(使用蒙版時要注意圖片混合模式設爲BlendMode.srcOver)ui
canvas.drawColor(Color.fromARGB(80, 255, 0, 0), BlendMode.srcOver);複製代碼
第一個參數 c 是圓心點的座標,直接new一個Offset出來填入x,y座標就完事了,第二個參 radius 是圓的半徑,paint沒必要多說;spa
canvas.drawCircle(new Offset(200, 200), 100, _paint);複製代碼
座標系是以控件左上角開始的,跟初中學的座標系可不同;
canvas.drawRect(new Rect.fromLTWH(10, 50, 50, 50),_paint);
_paint.style = PaintingStyle.stroke; // 中途將畫筆風格設爲環形
canvas.drawRect(new Rect.fromLTWH(120, 50, 50, 50),_paint);複製代碼
效果以下:
第一個參數是點的模式,分三種
第二個參數是一個點的集合,第三個參數...emmm,好了話很少說,上圖
PointMode.points - 點模式
List<Offset> points = new List();
points.add(new Offset(100, 100));
points.add(new Offset(125, 200));
points.add(new Offset(50, 150));
points.add(new Offset(150, 150));
points.add(new Offset(75, 200));
canvas.drawPoints(PointMode.points, points, _paint);複製代碼
PointMode.lines - 情侶模式
canvas.drawPoints(PointMode.points, points, _paint);複製代碼
咦,我左下角的點呢?哦,原來這個模式沒有配對的小點點會被刪除,嗚嗚嗚,單身狗沒人權的嘛?
PointMode.polygon - 連線模式
canvas.drawPoints(PointMode.polygon, points, _paint);複製代碼
這個方法和drawRect()是一毛同樣的,只不過一個畫的是矩形,一個是橢圓
canvas.drawOval(new Rect.fromLTWH(50, 50, 100, 50), _paint);
_paint.style = PaintingStyle.stroke;
canvas.drawOval(new Rect.fromLTWH(170, 50, 100, 50), _paint);複製代碼
p1線的起點,p2線的終點
canvas.drawLine(new Offset(100, 100), new Offset(200, 200), _paint);複製代碼
Flutter並無提供畫多條線的方法,只能多寫幾回 drawLine() 或者使用 drawPoints() 的情侶模式
canvas.drawRRect(new RRect.fromLTRBR(50, 50, 200, 100, new Radius.circular(10.0)), _paint);
canvas.drawRRect(new RRect.fromLTRBR(50, 150, 200, 250, new Radius.elliptical(10.0, 30.0)), _paint);複製代碼
下面這個矩形看似縱向拉伸過了,其實沒有,只是圓角模式是 elliptical (橢圓)
第一個參數的區域必須包括第二個參數的區域,不然沒法顯示;
canvas.drawDRRect(new RRect.fromLTRBR(50, 50, 200, 100, new Radius.circular(10.0)),
new RRect.fromLTRBR(60, 60, 190, 90, new Radius.circular(10.0)), _paint);複製代碼
第一個參數仍然是一塊矩形區域,第二個參數 startAngle 與第三個參數 sweepAngle 須要注意是弧度制,要轉化一下 乘以個 (pi / 180.0) 就好了度(下圖中紅線是 0 度的位置;順時針爲正角度,逆時針爲負角度),第四個參數 useCenter 表明是否與圓心鏈接,
canvas.drawArc(new Rect.fromLTWH(50, 50, 100, 50), 0.0 * (pi / 180.0), 90 * (pi / 180.0), false, _paint);
canvas.drawArc(new Rect.fromLTWH(50, 50, 100, 50), 200.0* (pi / 180.0), 90 * (pi / 180.0), true, _paint);
_paint.style = PaintingStyle.stroke; // 畫線模式
canvas.drawArc(new Rect.fromLTWH(50, 50, 100, 50), 100.0* (pi / 180.0), 90 * (pi / 180.0), false, _paint);複製代碼
以上就是 Canvas 全部的簡單圖形的繪製。除了簡單圖形的繪製, Canvas 還可使用drawPath(Path path, Paint paint)來繪製自定義圖形。
這個方法經過描述路徑的方式來繪製圖形,用法大概是這樣:
Path _path = Path();
@override
void paint(Canvas canvas, Size size) {
_paint.style = PaintingStyle.stroke; // 畫線模式
_path.addArc(new Rect.fromLTWH(50, 50, 50, 50), 135.0 * (pi / 180.0), 225.0 * (pi / 180.0));
_path.addArc(new Rect.fromLTWH(100, 50, 50, 50), 180.0 * (pi / 180.0), 225.0 * (pi / 180.0));
_path.lineTo(100, 140);
_path.lineTo(58, 93);
canvas.drawPath(_path, _paint);
}複製代碼
Path 主要有方法以下:
直接描述路徑的方法還能夠細分爲兩組:添加子圖形和畫線(直線或曲線)
從當前位置向目標位置畫一條直線, x 和 y 是目標位置的座標。這兩個方法的區 別是, lineTo(x, y) 的參數是絕對座標,而 relativeLineTo(x, y) 的參數是相對當前位置的相對座標 ;
_paint.style = PaintingStyle.stroke; // 畫線模式
_path.lineTo(100, 100); // 由當前位置 (0, 0) 向 (100, 100) 畫一條直線
_path.relativeLineTo(100, 0); // 由當前位置 (100, 100) 向正右方畫100像素的位置
canvas.drawPath(_path, _paint);複製代碼
x1,y1是控制點的座標;x2,y2是結束點的座標;relativeQuadraticBezierTo()同上面相對直線方法
_paint.style = PaintingStyle.stroke; // 畫線模式
List<Offset> points = new List();
points.add(new Offset(100, 50)); // 畫出控制點位置,方便理解
canvas.drawPoints(PointMode.points, points, _paint);
_path.moveTo(0, 100); // 移動起點到(0,100)
_path.quadraticBezierTo(100, 50, 200, 100);
canvas.drawPath(_path, _paint);複製代碼
和上面這個 quadraticBezierTo()和relativeQuadraticBezierTo() 的二階貝塞爾曲線同理,就很少說了。
不管是直線仍是貝塞爾曲線,都是以當前位置做爲起點,而不能指定起點。但能夠經過 moveTo(x, y) 或 relativeMoveTo(x, y) 來改變當前位置,從而間接地設置這些方法的起點。
_paint.style = PaintingStyle.stroke; // 畫線模式
_path.moveTo(20, 40); // 移動起點到(20,40)
_path.lineTo(80, 100); // 畫條斜線
_path.moveTo(100, 40); // 移動起點到(100,20)
_path.lineTo(100, 100); // 畫條直線
canvas.drawPath(_path, _paint);複製代碼
但凡事都有例外 arcTo() 這個方法並不從當前位置開始繪製
前三個參數,咱們已經很熟悉了,最後一個參數的意思是,畫這個弧的時候是拖着筆到起點仍是擡下筆到起點
_paint.style = PaintingStyle.stroke; // 畫線模式
_path.moveTo(20, 40); // 移動起點到(20,40)
_path.lineTo(80, 100); // 畫條斜線
_path.arcTo(new Rect.fromLTWH(60, 60, 100, 100), 0.0 * (pi / 180.0), 90.0 * (pi / 180.0), false);
canvas.drawPath(_path, _paint);複製代碼
拖着筆:
擡下筆:
_paint.style = PaintingStyle.stroke; // 畫線模式
_path.moveTo(20, 40); // 移動起點到(20,20)
_path.lineTo(80, 100); // 畫條斜線
_path.arcTo(new Rect.fromLTWH(60, 60, 100, 100), 0.0 * (pi / 180.0), 90.0 * (pi / 180.0), false);
_path.close(); // 封閉當前路徑
canvas.drawPath(_path, _paint);複製代碼
到這裏Canvas圖形的繪製就講的差很少了,圖形簡單時,使用 drawCircle() drawRect() 等方法來直接繪製;圖形複雜時,使用 drawPath() 來繪製自定義圖形。 除此以外, Canvas 還能夠繪製圖片和文字。
drawImage() 從指定點開始將圖片寬高按像素繪製,因爲沒法控制圖片的大小,並不經常使用;
第一個參數是ui包下的Image,並非 Image Widget;
Image 能夠經過如下代碼獲取
ui.Image image;
/**
* 初始化圖片
*
Future<VoidCallback> initImage() async {
image = await _loadImage("./assets/images/img.jpg");
return null;
}
/**
* 經過assets路徑,獲取資源圖片
*/
Future<Image> _loadImage(String assets) async {
final ByteData data = await rootBundle.load(assets);
if (data == null) throw 'Unable to read data';
Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
FrameInfo frame = await codec.getNextFrame();
return frame.image;
}複製代碼
而後在initState() 方法中初始化( shouldRepaint() 方法必定要記得返回 true ,不然沒法重繪)
void initState() {
super.initState();
painter.initImage().then((val) {
setState(() {
});
});
}複製代碼
最後在 paint() 方法中填入如下代碼:
canvas.drawImage(image, new Offset(0, 0), _paint);複製代碼
drawImageRect() 這個方法常用;主要了解第二個參數與第三個參數:
canvas.drawImageRect(image, Offset(0.0, 0.0) & Size(image.width.toDouble(), image.height.toDouble()), Offset(0.0, 0.0) & Size(200, 200), _paint);複製代碼
正常比例:
拉伸:
代碼註釋的很清楚,這裏循環畫了5段文字
for (int i = 0; i<5 ;i++){
// 新建一個段落建造器,而後將文字基本信息填入;
ParagraphBuilder pb = ParagraphBuilder(ParagraphStyle(
textAlign: TextAlign.left,
fontWeight: FontWeight.w300,
fontStyle: FontStyle.normal,
fontSize: 15.0+i,
));
pb.pushStyle(ui.TextStyle(color: Colors.black87));
pb.addText('Flutter一統移動端');
// 設置文本的寬度約束
ParagraphConstraints pc = ParagraphConstraints(width: 300);
// 這裏須要先layout,將寬度約束填入,不然沒法繪製
Paragraph paragraph = pb.build()..layout(pc);
// 文字左上角起始點
Offset offset = Offset(50, 50+i*40.0);
canvas.drawParagraph(paragraph, offset);
}複製代碼
Canvas及paint的使用基礎部分大概就是這樣了,下期文章見