Flutter 33: 圖解自定義 View 之 Canvas (一)

??????小菜最近在學習自定義 View藤违,剛了解了一下 Paint 畫筆的神奇之處遏考,現在學習一下 Canvas 畫布的神秘之處逐纬。Flutter 提供了眾多的繪制方法,小菜接觸不深矢沿,盡量都嘗試一下滥搭。

Canvas 畫布

drawColor 繪制背景色

??????drawColor 需要傳入兩個參數,第一個為色值捣鲸,第二個為混合模式瑟匆,有眾多混合模式供選擇,但注意使用混合模式后會與繪制其上的其他 View 顏色混合像素栽惶。

canvas.drawColor(Colors.pinkAccent, BlendMode.srcIn);
drawPoints 繪制點/線

??????drawPoints 不僅可以繪制點愁溜,還可以繪制點與點的連線疾嗅;PointMode 包括 points 點 / lines 線 / polygon 多邊形;注意 lines 為每兩點之間的連線冕象,若為奇數個點代承,最后一個沒有與之相連的點。

// 繪制點
canvas.drawPoints(
  PointMode.points,
  [
    Offset(30.0, 30.0), Offset(60.0, 30.0),
    Offset(90.0, 30.0), Offset(90.0, 60.0),
    Offset(60.0, 60.0), Offset(30.0, 60.0)
  ],
  Paint()..strokeWidth = 4.0);
canvas.drawPoints(
  PointMode.points,
  [
    Offset(160.0, 30.0), Offset(190.0, 30.0),
    Offset(220.0, 30.0), Offset(220.0, 60.0),
    Offset(190.0, 60.0), Offset(160.0, 60.0)
  ],
  Paint()..strokeWidth = 4.0..strokeCap = StrokeCap.round);
// 繪制線
canvas.drawPoints(
    PointMode.lines,
    [
      Offset(30.0, 100.0), Offset(60.0, 100.0),
      Offset(90.0, 100.0), Offset(90.0, 130.0),
      Offset(60.0, 130.0), Offset(30.0, 130.0)
    ],
    Paint()..strokeWidth = 4.0..strokeCap = StrokeCap.round);
// 繪制多邊形
canvas.drawPoints(
    PointMode.polygon,
    [
      Offset(160.0, 100.0), Offset(190.0, 100.0),
      Offset(220.0, 100.0), Offset(220.0, 130.0),
      Offset(190.0, 130.0), Offset(160.0, 130.0)
    ],
    Paint()..strokeWidth = 4.0..strokeCap = StrokeCap.round);
drawLine 繪制線
canvas.drawLine(Offset(30.0, 90.0), Offset(Screen.width - 30.0, 90.0),
    Paint()..strokeWidth = 4.0);
canvas.drawLine(Offset(30.0, 120.0), Offset(Screen.width - 30.0, 120.0),
    Paint()..strokeWidth = 4.0..strokeCap = StrokeCap.round);
canvas.drawLine(Offset(30.0, 150.0), Offset(Screen.width - 30.0, 150.0),
    Paint()..strokeWidth = 4.0..strokeCap = StrokeCap.square);
drawArc 繪制弧/餅

??????drawArc 可以用來繪制圓弧甚至配合 Paint 繪制餅狀圖渐扮;drawArc 的第一個參數為矩形范圍论悴,即圓弧所在的圓的范圍,若非正方形則圓弧所在的圓會拉伸墓律;第二個參數為起始角度膀估,0.0 為坐標系 x 軸正向方形;第三個參數為終止角度只锻,若超過 2PI玖像,則為一個圓;第四個參數為是否由中心出發(fā)齐饮,false* 時只繪制圓弧捐寥,true 時繪制圓餅;第五個參數即 Paint 畫筆祖驱,可通過 PaintingStyle 屬性繪制是否填充等握恳;

const PI = 3.1415926;
canvas.drawArc(Rect.fromCircle(center: Offset(60.0, 60.0), radius: 80.0),
    0.0, PI / 2, false,
    Paint()..color = Colors.white..strokeCap = StrokeCap.round..strokeWidth = 4.0..style = PaintingStyle.stroke);
canvas.drawArc(Rect.fromCircle(center: Offset(200.0, 60.0), radius: 80.0),
    0.0, PI / 2, false,
    Paint()..color = Colors.white..strokeWidth = 4.0..style = PaintingStyle.fill);
canvas.drawArc(Rect.fromCircle(center: Offset(90.0, 160.0), radius: 80.0),
    0.0, PI * 2 / 3, true,
    Paint()..color = Colors.white..strokeWidth = 4.0..style = PaintingStyle.stroke);
canvas.drawArc(Rect.fromCircle(center: Offset(250.0, 160.0), radius: 80.0),
    0.0, PI * 2 / 3, true,
    Paint()..color = Colors.white..strokeWidth = 4.0..style = PaintingStyle.fill);
canvas.drawArc(Rect.fromLTWH(30.0, 300.0, 200.0, 100.0),
    0.0, 5.0, true,
    Paint()..color = Colors.white..style = PaintingStyle.fill);
canvas.drawArc(Rect.fromPoints(Offset(260.0, 260.0), Offset(320.0, 420.0)),
    0.0, 5.0, true,
    Paint()..color = Colors.white..style = PaintingStyle.fill);
drawRect 繪制矩形

??????drawRect 用來繪制矩形,Flutter 提供了多種繪制矩形方法:

  1. Rect.fromPoints 根據兩個點(左上角點/右下角點)來繪制捺僻;
  2. Rect.fromLTRB 根據以屏幕左上角為坐標系圓點乡洼,分別設置上下左右四個方向距離;
  3. Rect.fromLTWH 根據設置左上角的點與矩形寬高來繪制匕坯;
  4. Rect.fromCircle 最特殊束昵,根據圓形繪制正方形;
canvas.drawRect(Rect.fromPoints(Offset(30.0, 30.0), Offset(150.0, 100.0)),
    Paint()..color = Colors.white..strokeWidth = 4.0..style = PaintingStyle.stroke);
canvas.drawRect(Rect.fromPoints(Offset(210.0, 30.0), Offset(330.0, 100.0)),
    Paint()..color = Colors.white..style = PaintingStyle.fill);
canvas.drawRect(Rect.fromLTRB(30.0, 140.0, 150.0, 210.0),
    Paint()..color = Colors.white);
canvas.drawRect(Rect.fromLTWH(210.0, 140.0, 120.0, 70.0),
    Paint()..color = Colors.white);
canvas.drawRect(Rect.fromCircle(center: Offset(90.0, 300.0), radius: 60.0),
    Paint()..color = Colors.white..strokeWidth = 4.0..style = PaintingStyle.stroke);
drawRRect 繪制圓角矩形

??????drawRRect 繪制圓角矩形葛峻,Flutter 提供了多種繪制方法:

  1. RRect.fromLTRBXY 前四個參數用來繪制矩形位置锹雏,剩余兩個參數繪制固定 x/y 弧度;
  2. RRect.fromLTRBR 前四個參數用來繪制矩形位置术奖,最后一個參數繪制 Radius 弧度礁遵;
  3. RRect.fromLTRBAndCorners 前四個參數用來繪制矩形位置,剩余四個可選擇參數采记,根據需求設置四個角 Radius 弧度佣耐,可不同;
  4. RRect.fromRectXY 第一個參數繪制矩形唧龄,可以用上面介紹的多種矩形繪制方式兼砖,剩余兩個參數繪制固定 x/y 弧度;
  5. RRect.fromRectAndRadius 第一個參數繪制矩形,可以用上面介紹的多種矩形繪制方式掖鱼,最后一個參數繪制 Radius 弧度然走;
  6. RRect.fromRectAndCorners第一個參數繪制矩形,可以用上面介紹的多種矩形繪制方式戏挡,剩余四個可選擇參數,根據需求設置四個角 Radius 弧度晨仑,最為靈活褐墅。
// RRect.fromLTRBXY 方式
canvas.drawRRect(
    RRect.fromLTRBXY(30.0, 30.0, 150.0, 100.0, 8.0, 8.0),
    Paint()..color = Colors.white..strokeWidth = 4.0..style = PaintingStyle.stroke);
canvas.drawRRect(
    RRect.fromLTRBXY(210.0, 30.0, 330.0, 100.0, 8.0, 18.0),
    Paint()..color = Colors.white..style = PaintingStyle.fill);
// RRect.fromLTRBR 方式
canvas.drawRRect(
    RRect.fromLTRBR(30.0, 140.0, 150.0, 210.0, Radius.circular(8.0)),
    Paint()..color = Colors.white..strokeWidth = 4.0..style = PaintingStyle.stroke);
// RRect.fromLTRBAndCorners 方式
canvas.drawRRect(
    RRect.fromLTRBAndCorners(210.0, 140.0, 330.0, 210.0,
        topLeft: Radius.circular(5.0),
        topRight: Radius.circular(20.0),
        bottomRight: Radius.circular(5.0),
        bottomLeft: Radius.circular(20.0)),
    Paint()..color = Colors.white..strokeWidth = 4.0..style = PaintingStyle.stroke);
// RRect.fromRectAndCorners 方式
canvas.drawRRect(
    RRect.fromRectAndCorners(Rect.fromLTWH(30.0, 260.0, 120.0, 70.0),
        topLeft: Radius.circular(5.0),
        topRight: Radius.circular(20.0),
        bottomRight: Radius.circular(5.0),
        bottomLeft: Radius.circular(20.0)),
    Paint()..color = Colors.white..strokeWidth = 4.0..style = PaintingStyle.stroke);
// RRect.fromRectAndRadius 方式
canvas.drawRRect(
    RRect.fromRectAndRadius(Rect.fromLTWH(210.0, 260.0, 120.0, 70.0),
        Radius.elliptical(8.0, 18.0)),
    Paint()..color = Colors.white..strokeWidth = 4.0..style = PaintingStyle.stroke);
// RRect.fromRectXY 方式
canvas.drawRRect(
    RRect.fromRectXY(
        Rect.fromCircle(center: Offset(90.0, 420.0), radius: 60.0),
        8.0, 8.0),
    Paint()..color = Colors.white..strokeWidth = 4.0..style = PaintingStyle.stroke);
drawDRRect 繪制嵌套矩形

??????drawDRRect 繪制嵌套矩形,第一個參數為外部矩形洪己,第二個參數為內部矩形妥凳,可用上述多種設置圓角矩形方式;最后一個參數為 Paint 畫筆答捕,且 PaintingStylefill 時填充的是兩個矩形之間的范圍逝钥。

canvas.drawDRRect(
    RRect.fromRectXY(
        Rect.fromCircle(center: Offset(90.0, 420.0), radius: 60.0), 8.0, 8.0),
    RRect.fromRectXY(
        Rect.fromCircle(center: Offset(90.0, 420.0), radius: 54.0), 8.0, 8.0),
    Paint()..color = Colors.whit..strokeWidth = 3.0
      ..style = PaintingStyle.stroke);
canvas.drawDRRect(
    RRect.fromRectXY(
        Rect.fromCircle(center: Offset(270.0, 420.0), radius: 60.0), 8.0, 8.0),
    RRect.fromRectXY(
        Rect.fromCircle(center: Offset(270.0, 420.0), radius: 54.0), 8.0, 8.0),
    Paint()..color = Colors.white..strokeWidth = 3.0
      ..style = PaintingStyle.fill);
drawCircle 繪制圓形

??????drawCircle 繪制圓形,僅需設置原點及半徑即可拱镐;

canvas.drawCircle(Offset(90.0, 420.0), 60.0,
    Paint()..color = Colors.white..strokeWidth = 3.0
      ..style = PaintingStyle.stroke);
canvas.drawCircle(Offset(270.0, 420.0), 60.0,
    Paint()..color = Colors.white..strokeWidth = 3.0
      ..style = PaintingStyle.fill);
drawOval 繪制橢圓

??????drawOval 繪制橢圓方式很簡單艘款,主要繪制一個矩形即可;

canvas.drawOval(Rect.fromLTRB(30.0, 30.0, 150.0, 100.0),
    Paint()..color = Colors.white..strokeWidth = 3.0
      ..style = PaintingStyle.stroke);
canvas.drawOval(Rect.fromLTRB(210.0, 30.0, 330.0, 100.0),
    Paint()..color = Colors.white..strokeWidth = 3.0
      ..style = PaintingStyle.fill);
drawPath 繪制路徑

??????drawPath 用來繪制路徑沃琅,Flutter 提供了眾多路徑方法哗咆,小菜嘗試幾種常用的方法:

  1. moveTo() 即從當前坐標點開始,不設置時默認為屏幕左上角位置益眉;
  2. lineTo() 即從起點繪制到設置的新的點位晌柬;
  3. close() 即最后的點到起始點連接,但對于中間繪制矩形/弧等時最后不會相連郭脂;
  4. reset() 即清空連線年碘;
  5. addRect() 添加矩形連線;
  6. addOval() 添加弧線展鸡,即貝塞爾(二階)曲線屿衅;
  7. cubicTo() 添加弧線,即貝塞爾(三階)曲線娱颊;
  8. relativeMoveTo() 相對于移動到當前點位傲诵,小菜認為與 moveTo 相比整個坐標系移動;
  9. relativeLineTo() 相對連接到當前點位箱硕,并將坐標系移動到當前點位拴竹;
canvas.drawPath(
    Path()
      ..moveTo(30.0, 100.0)..lineTo(120.0, 100.0)
      ..lineTo(90.0, 130.0)..lineTo(180.0, 130.0)
      ..close(),
    Paint()
      ..color = Colors.white..strokeWidth = 3.0
      ..style = PaintingStyle.stroke);
canvas.drawPath(
    Path()
      ..moveTo(200.0, 100.0)..lineTo(290.0, 100.0)
      ..lineTo(260.0, 130.0)..lineTo(350.0, 130.0)
      ..close(),
    Paint()
      ..color = Colors.white..strokeWidth = 3.0
      ..style = PaintingStyle.fill);
canvas.drawPath(
    Path()
      ..moveTo(30.0, 170.0)..lineTo(120.0, 170.0)
      ..lineTo(90.0, 210.0)..lineTo(180.0, 210.0)
      ..addRect(Rect.fromLTWH(180.0, 210.0, 120.0, 70.0))
      ..addOval(Rect.fromLTWH(180.0, 210.0, 120.0, 70.0))
      ..moveTo(230.0, 170.0)..lineTo(320.0, 170.0)
      ..close(),
    Paint()
      ..color = Colors.white..strokeWidth = 3.0..style = PaintingStyle.stroke);
canvas.drawPath(
    Path()
      ..arcTo(Rect.fromCircle(center: Offset(60, 300), radius: 80), -PI / 6,
          PI * 2 / 3, false),
    Paint()
      ..color = Colors.white..strokeWidth = 3.0..style = PaintingStyle.stroke);
canvas.drawPath(
    Path()
      ..moveTo(210.0, 300.0)
      ..cubicTo(210.0, 390.0, 270.0, 330.0, 330.0, 300.0),
    Paint()
      ..color = Colors.black..strokeWidth = 3.0..style = PaintingStyle.stroke);

??????小菜繪制了一個基本的坐標系來比較一下 moveTo()/lineTo()relativeMoveTo()/relativeLineTo() 的區(qū)別:

canvas.drawPath(
    Path()
      ..relativeMoveTo(30.0, 30.0)..relativeLineTo(120.0, 30.0)
      ..relativeLineTo(90.0, 60.0)..relativeLineTo(180.0, 60.0),
    Paint()
      ..color = Colors.blue..strokeWidth = 6.0
      ..style = PaintingStyle.stroke);
canvas.drawPath(
    Path()
      ..moveTo(30.0, 30.0)..lineTo(120.0, 30.0)
      ..lineTo(90.0, 60.0)..lineTo(180.0, 60.0),
    Paint()
      ..color = Colors.orange..strokeWidth = 6.0
      ..style = PaintingStyle.stroke);

??????小菜對自定義 View 研究還不深入,有很多方法還沒有嘗試剧罩,有錯誤的地方希望多多指導栓拜!

來源:阿策小和尚

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子幕与,更是在濱河造成了極大的恐慌挑势,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件啦鸣,死亡現場離奇詭異潮饱,居然都是意外死亡,警方通過查閱死者的電腦和手機诫给,發(fā)現死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門香拉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人中狂,你說我怎么就攤上這事凫碌。” “怎么了胃榕?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵盛险,是天一觀的道長。 經常有香客問我勋又,道長苦掘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任赐写,我火速辦了婚禮鸟蜡,結果婚禮上,老公的妹妹穿的比我還像新娘挺邀。我一直安慰自己揉忘,他們只是感情好,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布端铛。 她就那樣靜靜地躺著泣矛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪禾蚕。 梳的紋絲不亂的頭發(fā)上您朽,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天,我揣著相機與錄音换淆,去河邊找鬼哗总。 笑死,一個胖子當著我的面吹牛倍试,可吹牛的內容都是我干的讯屈。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼县习,長吁一口氣:“原來是場噩夢啊……” “哼涮母!你這毒婦竟也來了谆趾?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤叛本,失蹤者是張志新(化名)和其女友劉穎沪蓬,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體来候,經...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡跷叉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了吠勘。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片性芬。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖剧防,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情辫樱,我是刑警寧澤峭拘,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站狮暑,受9級特大地震影響鸡挠,放射性物質發(fā)生泄漏。R本人自食惡果不足惜搬男,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一拣展、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧缔逛,春花似錦备埃、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至敦冬,卻和暖如春辅搬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背脖旱。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工堪遂, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人萌庆。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓溶褪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親踊兜。 傳聞我的和親對象是個殘疾皇子竿滨,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內容