最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解利用Flutter中的Canvas繪制有趣的圖形

 更新時(shí)間:2022年03月22日 16:05:24   作者:島上碼農(nóng)  
本文將利用Flutter中的Canvas繪制三個(gè)有趣的圖形:使用等邊三角形組合成彩虹傘面、五角星和彩虹,快來(lái)跟隨小編一起動(dòng)手嘗試一下吧

簡(jiǎn)介

上一篇我們介紹了使用 Flutter 的 Canvas 繪制基本圖形的示例,簡(jiǎn)單的示例沒(méi)什么好玩的,今天這一篇我們來(lái)點(diǎn)有趣的,我們會(huì)完成如下圖形的繪制:

  • 發(fā)現(xiàn)數(shù)學(xué)重復(fù)之美:使用等邊三角形組合成彩虹傘面。
  • 繪制彩虹。
  • 繪制評(píng)分用的五角星。

通過(guò)這一篇,我們可以知道自定義形狀繪制的基本原理,然后可以在這個(gè)基礎(chǔ)上繪制你自己想要繪制的圖形。

等邊三角形構(gòu)建重復(fù)之美

首先我們來(lái)繪制等邊三角形,其實(shí)上一篇我們也有繪制等邊三角形,只是那是將三個(gè)頂點(diǎn)手動(dòng)計(jì)算出來(lái)的,這一篇我們封裝一個(gè)繪制等邊三角形的通用方法。老規(guī)矩,先定義方法的輸入?yún)?shù),如下所示:

  • canvasCanvas 畫(huà)布
  • color:繪制顏色
  • startVertex:三角形的第一個(gè)頂點(diǎn)位置,這里我們其他邊都是相對(duì)這個(gè)點(diǎn)旋轉(zhuǎn)的
  • length:邊長(zhǎng)
  • startAngle:第一條邊相對(duì)水平方向旋轉(zhuǎn)的夾角,這樣我們可以改變夾角來(lái)更改三角形的繪制位置。
  • clockwise:順時(shí)針繪制,如果是順時(shí)針,則繪制的偏移夾角往順時(shí)針?lè)较蜷_(kāi)始,否則逆時(shí)針。
  • filled:是否填充圖形。
void drawEquilateralTriangle(
    Canvas canvas, {
    required Color color,
    required Offset startVertex,
    required double length,
    double startAngle = 0,
    clockwise = true,
    filled = true,
  })

等邊三角形基于一個(gè)頂點(diǎn),一條邊和起始角度后就可以計(jì)算其他兩個(gè)頂點(diǎn)的位置,具體推到通過(guò)三角函數(shù)就可以了。

具體計(jì)算三角形的三個(gè)頂點(diǎn)的方法如下,這里逆時(shí)針?lè)较蚝晚槙r(shí)針?lè)较虻挠?jì)算方式有點(diǎn)不同,需要區(qū)分一下。

static List<Offset> getEquilateralTriangleVertexes(
      Offset startVertex, double length,
      {double startAngle = 0, bool clockwise = true}) {
  double point2X, point2Y, point3X, point3Y;
  point2X = startVertex.dx + length * cos(startAngle);
  point2Y = startVertex.dy - length * sin(startAngle);
  if (clockwise) {
    point3X = startVertex.dx + length * cos(pi / 3 + startAngle);
    point3Y = startVertex.dy - length * sin(pi / 3 + startAngle);
  } else {
    point3X = startVertex.dx + length * cos(pi / 3 - startAngle);
    point3Y = startVertex.dy + length * sin(pi / 3 - startAngle);
  }

  return [startVertex, Offset(point2X, point2Y), Offset(point3X, point3Y)];
}

有了頂點(diǎn)我們就可以使用 Path 將頂點(diǎn)連起來(lái)就完成等邊三角形的繪制了,繪制三角形的實(shí)現(xiàn)方法如下:

void drawEquilateralTriangle(
    Canvas canvas, {
    required Color color,
    required Offset startVertex,
    required double length,
    double startAngle = 0,
    clockwise = true,
    filled = true,
  }) {
    assert(length > 0);
    Path trianglePath = Path();
    List<Offset> vertexes = ShapesUtil.getEquilateralTriangleVertexes(
      startVertex,
      length,
      clockwise: clockwise,
      startAngle: startAngle,
    );
    trianglePath.moveTo(vertexes[0].dx, vertexes[0].dy);
    for (int i = 1; i < vertexes.length; i++) {
      trianglePath.lineTo(vertexes[i].dx, vertexes[i].dy);
    }
    trianglePath.close();
    Paint paint = Paint();
    paint.color = color;
    if (!filled) {
      paint.style = PaintingStyle.stroke;
    }
    canvas.drawPath(trianglePath, paint);
  }
}

單獨(dú)一個(gè)三角形沒(méi)啥意思,我們通過(guò)畫(huà)6個(gè)等邊三角形,每個(gè)三角形旋轉(zhuǎn)60度,空心繪制看看怎么樣?

一個(gè) 完美的六邊形出來(lái)了,再試試12個(gè)怎么樣。

形狀越多,會(huì)越接近圓形,你會(huì)充分發(fā)現(xiàn)對(duì)稱(chēng)之美。下面是我們用24個(gè)三角形,填充不同顏色后的效果。有點(diǎn)像一把彩虹傘的傘面了,感覺(jué)是不是很美?

上面圖形的實(shí)現(xiàn)代碼如下,其中顏色是通過(guò)一個(gè)顏色數(shù)組完成的。

int number = 24;
for (int i = 0; i < number; ++i) {
  drawEquilateralTriangle(
    canvas,
    color: colors[i],
    startVertex: Offset(center.width, center.height),
    length: 120,
    startAngle: i * 2 * pi / number,
    clockwise: true,
    filled: true,
  );
}

繪制彩虹

有了上面的彩虹傘一樣的啟發(fā),我們決定來(lái)繪制彩虹。彩虹其實(shí)比較簡(jiǎn)單,繪制7條不同顏色的弧線即可。這里講一下弧線的繪制約束。如下圖所示,實(shí)際上弧線是通過(guò)矩形的內(nèi)接橢圓限制的(這里用正方形,內(nèi)接為圓形示例)。外面的矩形限制了橢圓位置和尺寸,而通過(guò) startAngle (起始角度)和 sweepAngle (弧線覆蓋的角度范圍)就能夠確定弧線的起點(diǎn)和終點(diǎn),從而得到一段弧線。注意的是,數(shù)學(xué)里我們是逆時(shí)針角度為正,但是在 Flutter 默認(rèn)是順時(shí)針為正,因此如果你要從逆時(shí)針?lè)较蜷_(kāi)始角度就要設(shè)置為負(fù)數(shù)。

下面是弧線繪制的示例代碼:

Path path1 = Path();
Rect rect1 = Rect.fromLTWH(startPoint.dx + (width - innerWidth) / 2,
    startPoint.dy + (width - innerWidth) / 2, innerWidth, innerWidth);
path1.arcTo(rect1, -pi / 6, -2 * pi / 3, true);
paint.color = colors[i];
canvas.drawPath(path1, paint);

有了這個(gè)基礎(chǔ),我們通過(guò)循環(huán) ,繪制7條弧線,保證每條弧線挨著就行。而弧線的線條粗細(xì)可以用畫(huà)筆的寬度來(lái)搞定,代碼如下。我們這里每條弧線的中心、起始角度和覆蓋角度是一樣的,通過(guò)改變不同弧線的正方形邊長(zhǎng)實(shí)現(xiàn)彩虹弧線的位置不同,然后畫(huà)筆粗細(xì)保持為每條彩虹的高度的一半就可以保證每條彩虹是挨著的了。

void drawRainbow(
    Canvas canvas, {
    required Offset startPoint,
    required double width,
  }) {
  assert(width > 0);
  var paint = Paint();
  double rowHeight = 12;
  paint.strokeWidth = rowHeight / 2;
  List<Color> colors = [
    Color(0xFFE05100),
    Color(0xFFF0A060),
    Color(0xFFE0E000),
    Color(0xFF10F020),
    Color(0xFF2080F5),
    Color(0xFF104FF0),
    Color(0xFFA040E5),
  ];
  paint.style = PaintingStyle.stroke;
  for (var i = 0; i < 7; i++) {
    double innerWidth = width - i * rowHeight;
    Path path1 = Path();
    Rect rect1 = Rect.fromLTWH(startPoint.dx + (width - innerWidth) / 2,
        startPoint.dy + (width - innerWidth) / 2, innerWidth, innerWidth);
    path1.arcTo(rect1, -pi / 6, -2 * pi / 3, true);
    paint.color = colors[i];
    canvas.drawPath(path1, paint);
  }
}

最終效果如下圖所示。

繪制五角星

五角星相對(duì)來(lái)說(shuō)會(huì)復(fù)雜一些,主要是要知道通過(guò)中心點(diǎn)確定10個(gè)頂點(diǎn)的坐標(biāo),這里就需要利用二維坐標(biāo)的旋轉(zhuǎn)公式了,具體可以查閱相關(guān)資料,結(jié)論是一個(gè)點(diǎn)(x2, y2)圍繞另一個(gè)點(diǎn)(x1, y1)旋轉(zhuǎn)某個(gè)角度(α)后得到的新坐標(biāo)(x, y)計(jì)算方式如下:

x=x1+(x2-x1)*cos(α)-(y2-y1)*sin(α)

y=y1+(y2-y1)*cos(α)+(x2-x1)*sin(α)

有了這個(gè)基礎(chǔ),我們就可以基于五角星的中心點(diǎn),第一個(gè)頂點(diǎn),邊長(zhǎng)(間隔一個(gè)點(diǎn)連線的線段長(zhǎng)度)來(lái)通過(guò)旋轉(zhuǎn)計(jì)算其他頂點(diǎn)了。其中外面5頂點(diǎn)一組計(jì)算,內(nèi)部5個(gè)頂點(diǎn)一組計(jì)算。最終獲取5個(gè)頂點(diǎn)的代碼如下:

static List<Offset> getStarVertexes(Offset center, double length) {
  assert(length > 0);
  // 外接圓半徑計(jì)算(五角星銳角為36度)
  double radius = length / 2 / cos(18 / 180 * pi);
  // 內(nèi)部頂點(diǎn)的半徑
  double innerRadius =
      radius / (cos(36 / 180 * pi) + sin(36 / 180 * pi) / sin(18 / 180 * pi));
  List<Offset> vertexes = [];
  Offset outerStartVertex = Offset(center.dx, center.dy - radius);
  Offset innerStartVertex = Offset(
    center.dx - innerRadius * sin(36 / 180 * pi),
    center.dy - innerRadius * cos(36 / 180 * pi),
  );
  vertexes.add(outerStartVertex);
  vertexes.add(innerStartVertex);
  // 計(jì)算方式為以第一個(gè)頂點(diǎn)圍繞五角星中心點(diǎn)坐標(biāo)旋轉(zhuǎn)得到
  const double rotateAngle = 72 / 180 * pi;
  for (int i = 1; i < 5; ++i) {
    vertexes.add(Offset(
      center.dx +
          (outerStartVertex.dx - center.dx) * cos(-i * rotateAngle) -
          (outerStartVertex.dy - center.dy) * sin(-i * rotateAngle),
      center.dy +
          (outerStartVertex.dy - center.dy) * cos(-i * rotateAngle) +
          (outerStartVertex.dx - center.dx) * sin(-i * rotateAngle),
    ));
    vertexes.add(Offset(
      center.dx +
          (innerStartVertex.dx - center.dx) * cos(-i * rotateAngle) -
          (innerStartVertex.dy - center.dy) * sin(-i * rotateAngle),
      center.dy +
          (innerStartVertex.dy - center.dy) * cos(-i * rotateAngle) +
          (innerStartVertex.dx - center.dx) * sin(-i * rotateAngle),
    ));
  }

  return vertexes;
}

有了頂點(diǎn),繪制方式就和三角形一樣了,將頂點(diǎn)連起來(lái)就好了。下面是我們繪制了一個(gè)常見(jiàn)的五星評(píng)分的圖形。

總結(jié)

本篇介紹了基于 Flutter 的 CustomPaint 繪制定制化圖形的示例,可以看到,其實(shí)只要 UI 小姐姐給出的圖形能夠用數(shù)學(xué)表達(dá)式表示出來(lái),都可以用 CustomPaintCanvas 來(lái)實(shí)現(xiàn)。

到此這篇關(guān)于詳解利用Flutter中的Canvas繪制有趣的圖形的文章就介紹到這了,更多相關(guān)Flutter Canvas圖形內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Android sd卡讀取數(shù)據(jù)庫(kù)實(shí)例代碼

    Android sd卡讀取數(shù)據(jù)庫(kù)實(shí)例代碼

    這篇文章主要介紹了Android sd卡讀取數(shù)據(jù)庫(kù)實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • Android Animation之TranslateAnimation(平移動(dòng)畫(huà))

    Android Animation之TranslateAnimation(平移動(dòng)畫(huà))

    這篇文章主要為大家詳細(xì)介紹了Animation之TranslateAnimation平移動(dòng)畫(huà),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-09-09
  • 配置Android SDK

    配置Android SDK

    今天小編就為大家分享一篇關(guān)于配置Android SDK的文章,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2018-10-10
  • 使用VideoView播放App中的資源文件

    使用VideoView播放App中的資源文件

    這篇文章主要介紹了使用VideoView播放App中的資源文件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-06-06
  • Android APP之WebView校驗(yàn)SSL證書(shū)的方法

    Android APP之WebView校驗(yàn)SSL證書(shū)的方法

    這篇文章主要介紹了Android APP之WebView校驗(yàn)SSL證書(shū)的方法,需要的朋友可以參考下
    2017-09-09
  • Android如何動(dòng)態(tài)改變App桌面圖標(biāo)

    Android如何動(dòng)態(tài)改變App桌面圖標(biāo)

    這篇文章主要介紹了 Android動(dòng)態(tài)改變App桌面圖標(biāo)的方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下
    2017-01-01
  • Android序列化實(shí)現(xiàn)接口Serializable與Parcelable詳解

    Android序列化實(shí)現(xiàn)接口Serializable與Parcelable詳解

    我們使用 Intent 傳遞數(shù)據(jù)的時(shí)候,putExtra() 所支持的數(shù)據(jù)類(lèi)型事有限的,當(dāng)需要傳遞自定義對(duì)象的時(shí)候就需要序列化。Serializable更簡(jiǎn)單但是會(huì)把整個(gè)對(duì)象進(jìn)行序列化因此效率比Parcelable低一些
    2022-12-12
  • Android中Activity的生命周期探討

    Android中Activity的生命周期探討

    這篇文章主要介紹了Android中Activity的生命周期探討,本文同時(shí)講解了銷(xiāo)毀Activity、暫停與恢復(fù)、停止與重啟Activity等內(nèi)容,需要的朋友可以參考下
    2014-10-10
  • Android ListView實(shí)現(xiàn)下拉加載功能

    Android ListView實(shí)現(xiàn)下拉加載功能

    這篇文章主要為大家詳細(xì)介紹了Android ListView實(shí)現(xiàn)下拉加載功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • Android自定義控件如何在XML文件中使用自定義屬性

    Android自定義控件如何在XML文件中使用自定義屬性

    這篇文章主要為大家介紹了Android自定義控件之如何在XML文件中使用自定義屬性示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04

最新評(píng)論

鄂州市| 翼城县| 双城市| 湄潭县| 彰化县| 朝阳市| 金寨县| 定襄县| 连南| 江油市| 玉环县| 长子县| 巧家县| 同江市| 咸阳市| 哈尔滨市| 岳阳市| 牙克石市| 屏东县| 武夷山市| 敦煌市| 汉寿县| 门头沟区| 张家界市| 祁连县| 务川| 上栗县| 东乡县| 莱阳市| 漳浦县| 洮南市| 牙克石市| 肃宁县| 潞城市| 台南市| 桃园县| 西贡区| 旬阳县| 丰宁| 余庆县| 乐平市|