Flutter 34: 圖解自定義 View 之 Canvas (二)

??????小菜前幾天整理了以下 Canvas 的部分方法,今天小菜繼續(xù)學(xué)習(xí) Canvas 第二部分。

drawXXX

drawShadow 繪制陰影

??????drawShadow 用于繪制陰影,第一個(gè)參數(shù)時(shí)繪制一個(gè)圖形 Path,第二個(gè)是設(shè)置陰影顏色,第三個(gè)為陰影范圍,最后一個(gè)陰影范圍是否填充滿;

canvas.drawShadow(
    Path()
      ..moveTo(30.0, 30.0)..lineTo(120.0, 30.0)
      ..lineTo(120.0, 60.0)..lineTo(30.0, 60.0)
      ..close(),
    Colors.blue, 3, false);
canvas.drawShadow(
    Path()
      ..moveTo(30.0, 90.0)..lineTo(120.0, 90.0)
      ..lineTo(120.0, 120.0)..lineTo(30.0, 120.0),
    Colors.blue, 10, false);
canvas.drawShadow(
    Path()
      ..moveTo(30.0, 150.0)..lineTo(120.0, 150.0)
      ..lineTo(120.0, 180.0)..lineTo(30.0, 180.0)
      ..close(),
    Colors.blue, 3, true);
canvas.drawPath(
    Path()
      ..moveTo(30.0, 210.0)..lineTo(120.0, 210.0)
      ..lineTo(120.0, 240.0)..lineTo(30.0, 240.0),
    Paint()..color = Colors.blue..strokeWidth = 2..style = PaintingStyle.stroke);
drawImage 繪制圖片

??????drawImage 用于繪制圖片,繪制圖片是重點(diǎn),此時(shí)的 Image 并非日常所用的圖片加載,而是用的 dart.ui 類中的 ui.Image 并轉(zhuǎn)換成字節(jié)流 ImageStream 方式傳遞,包括本地圖片或網(wǎng)絡(luò)圖片

// 獲取圖片 本地為false 網(wǎng)絡(luò)為true
Future<ui.Image> _loadImage(var path, bool isUrl) async {
  ImageStream stream;
  if (isUrl) {
    stream = NetworkImage(path).resolve(ImageConfiguration.empty);
  } else {
    stream =
        AssetImage(path, bundle: rootBundle).resolve(ImageConfiguration.empty);
  }
  Completer<ui.Image> completer = Completer<ui.Image>();
  void listener(ImageInfo frame, bool synchronousCall) {
    final ui.Image image = frame.image;
    completer.complete(image);
    stream.removeListener(listener);
  }

  stream.addListener(listener);
  return completer.future;
}

// 加載圖片
_prepareImg() {
  _loadImage('images/icon_hzw02.jpg', false).then((image1) {
    _image1 = image1;
  }).whenComplete(() {
    _loadImage('https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=703702342,1604162245&fm=26&gp=0.jpg', true)
        .then((image2) {
      _image2 = image2;
    }).whenComplete(() {
      _prepDone = true;
      if (this.mounted) {
        setState(() {});
      }
    });
  });
}

canvas.drawImage(this.image, ui.Offset(120.0, 540.0), Paint());
canvas.drawImage(this.image2, ui.Offset(60.0, 60.0), Paint());

Tips:

??????小菜在嘗試過程中總是加載失敗,后來理解為繪制過程需要時(shí)間,可以通過 setState(() {});whenComplete 判斷狀態(tài)后進(jìn)行刷新,這種方式并非最佳,希望有更好方式的朋友多指導(dǎo)。

drawImageRect 繪制矩形圖片

??????drawImageRect 除了可以正常繪制圖片之外,還可以繪制圖片部分內(nèi)容,如下:第一個(gè)參數(shù)為 ui.Image,第二個(gè)參數(shù)為需要原圖繪制矩形范圍,第三個(gè)參數(shù)為本次繪制矩形范圍,最后一個(gè)為畫筆;

??????小菜繪制原圖與部分圖進(jìn)行對(duì)比,drawImageRect 繪制的原圖粉色圈出的范圍;且 drawImageRect 效率更好,推薦使用;

canvas.drawImage(this.image, ui.Offset(60.0, 540.0), Paint());
canvas.drawImageRect(
    this.image,
    Rect.fromLTWH(0, 0, 60, 60),
    Rect.fromLTWH(60, 60, image.width.toDouble(), image.height.toDouble()),
    Paint());
drawImageNine 繪制九圖

??????drawImageNine 同樣用來繪制圖片,與原圖繪制方式不同在于,drawImageNine 中第二個(gè)參數(shù)矩形變長延伸將原圖分割為九部分,劃為中心區(qū)域,第三個(gè)參數(shù)矩形即繪制整體矩形范圍,包括四個(gè)頂點(diǎn)位置;小菜繪制原圖與部分圖進(jìn)行對(duì)比,drawImageNine 繪制的原圖綠色圈出的范圍,小菜感覺類似于裁剪了原圖;

canvas.drawImage(this.image, ui.Offset(60.0, 540.0), Paint());
canvas.drawImageNine(
    this.image,
    Rect.fromLTWH(0, 0, 120, 60),
    Rect.fromLTWH(
        60, 1020, image.width.toDouble() - 120, image.height.toDouble()),
    Paint());
drawParagraph 繪制文字段落

??????小菜以前認(rèn)為 Canvas 不支持繪制文字,現(xiàn)在學(xué)習(xí)了 drawParagraph 完全可以繪制文字效果與 TextPainter 效果相同;

??????文字段落 Paragraphdart.ui 中的類,用構(gòu)造器方式進(jìn)行內(nèi)容綁定;ParagraphStyle 用來設(shè)置文字的樣式屬性,包括文字位置/方向/字體粗細(xì)/文字樣式/行數(shù)等;其中 ellipsis 用來設(shè)置內(nèi)容超出范圍截取時(shí)最后展示內(nèi)容,可隨意編輯;

ParagraphBuilder pb = ParagraphBuilder(ParagraphStyle(
  textAlign: TextAlign.center,
  fontWeight: FontWeight.w600,
  fontStyle: FontStyle.normal,
  fontSize: 18,
))
  ..pushStyle(ui.TextStyle(color: Colors.blue))
  ..addText(
      'Flutter是谷歌的移動(dòng)UI框架,可以快速在iOS和Android上構(gòu)建高質(zhì)量的原生用戶界面。 Flutter可以與現(xiàn)有的代碼一起工作。在全世界,F(xiàn)lutter正在被越來越多的開發(fā)者和組織使用,并且Flutter是完全免費(fèi)、開源的。');
ParagraphConstraints pc = ParagraphConstraints(width: Screen.width - 60);
Paragraph paragraph = pb.build()..layout(pc);
canvas.drawParagraph(paragraph, Offset(30, 30));

pb = ParagraphBuilder(ParagraphStyle(
  fontStyle: FontStyle.normal,
  fontWeight: FontWeight.w300,
  fontSize: 18,
))
  ..pushStyle(ui.TextStyle(color: Colors.red))
  ..addText(
      'Flutter是谷歌的移動(dòng)UI框架,可以快速在iOS和Android上構(gòu)建高質(zhì)量的原生用戶界面。 Flutter可以與現(xiàn)有的代碼一起工作。在全世界,F(xiàn)lutter正在被越來越多的開發(fā)者和組織使用,并且Flutter是完全免費(fèi)、開源的。');
pc = ParagraphConstraints(width: Screen.width - 60);
paragraph = pb.build()..layout(pc);
canvas.drawParagraph(paragraph, Offset(30, 180));

pb = ParagraphBuilder(ParagraphStyle(
  fontStyle: FontStyle.normal,
  fontSize: 18,
  maxLines: 3,
  ellipsis: '...',
))
  ..pushStyle(ui.TextStyle(color: Colors.green))
  ..addText(
      'Flutter是谷歌的移動(dòng)UI框架,可以快速在iOS和Android上構(gòu)建高質(zhì)量的原生用戶界面。 Flutter可以與現(xiàn)有的代碼一起工作。在全世界,F(xiàn)lutter正在被越來越多的開發(fā)者和組織使用,并且Flutter是完全免費(fèi)、開源的。');
pc = ParagraphConstraints(width: Screen.width - 60);
paragraph = pb.build()..layout(pc);
canvas.drawParagraph(paragraph, Offset(30, 340));

clipXXX

??????以上介紹的都是繪制方法,接下來小菜簡單介紹幾種裁剪方法。

clipRect 裁剪矩形

??????clipRect 可以在規(guī)定的矩形內(nèi)進(jìn)行繪制,超出范圍不繪制;

canvas.clipRect(Rect.fromLTWH(30, 1200, Screen.width / 0.3 - 60, 300),
    doAntiAlias: false);
canvas.drawImage(this.image, ui.Offset(260, 1200), Paint());
clipRRect 裁剪圓角矩形

??????clipRRect 可以在規(guī)定的圓角矩形內(nèi)進(jìn)行繪制,超出范圍不繪制;

canvas.clipRRect(
    RRect.fromRectXY(
        Rect.fromLTWH(300, 1600, image.width - 60.0, 300), 20, 20),
    doAntiAlias: false);
canvas.drawImage(this.image, ui.Offset(260, 1200), Paint());
clipPath 裁剪由線圍成區(qū)域

??????clipPath 可以在規(guī)定的點(diǎn)連線范圍內(nèi)進(jìn)行繪制,默認(rèn)終點(diǎn)與始點(diǎn)連接,當(dāng)然可以繪制圓或貝塞爾曲線等,超出范圍不繪制;

canvas.clipPath(Path()
  ..moveTo(300, 100)..lineTo(900, 100)
  ..lineTo(800, 600)..lineTo(400, 600));
canvas.drawImage(this.image, ui.Offset(260, 90), Paint());

??????Canvas 真的非常強(qiáng)大,還有很多研究不透徹的地方,小菜還在不斷學(xué)習(xí),有錯(cuò)誤的地方煩請(qǐng)多多指點(diǎn)!

來源:阿策小和尚

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容