Flutter繪制3.4邊形及多邊形漸變動畫實現示例

正文

項目被優化瞭,人也跟著被優化瞭,正好趁這一個月整理整理關於flutter的一些東西。

繪制3.4邊形

先看一下效果圖:

起因是上上上上上個月瀏覽flutter的canvas相關內容時,點進去一個網站,看到一個讓我眼前一亮的動效:

作者用的代碼是swift的,我沒細看,不過他文章裡的一句話讓我醍醐灌頂:

That is, we want the shape be asked to draw multiple times, each time with a different value for the sides parameter: 3, 3.1, 3.15, 3.2, 3.25, all the way to 4.

大意就是說我們想搞一個從三邊形到四邊形的動畫,我們隻需要畫出3.1邊形,3.2邊形,3.3邊形,一直畫到4邊形。

這句話真的讓我醍醐灌頂,因為我看到這個動畫,第一反應是需要計算每個頂點位置,從而做出動畫;但是這句話讓這個問題脫離瞭具體細節,將問題抽象化,數學化。隻要我們定義出分數邊形的繪制方法,我們就可以很簡單的完成這個動畫。

現在我們隻要定義如何繪制分數邊形就可以瞭。

整數邊形的繪制

在定義繪制分數邊形的繪制方法之前,我們先來看整數邊形是如何繪制的:

繪制正三角形,我們會在圓上找出三等分點,然後依次連接這三個點,這就是正三角形的繪制方法。

而且繪制的時候我們通常會固定一個起點,然後從這個起點開始等分。

分數邊形的繪制

分數邊形的繪制也是一樣的道理,比如3.1邊形的繪制,我們需要找到四個點

我們先固定一個起點,然後從這個起點開始旋轉(2*pi/3.1)個弧度,這樣依次找到剩下三個點,(因為不是等分,所以可這樣找下去可以找到無數個點,但我們隻需要找四個點),而且當我們給到兩個很相近的分數,比如3.1和3.11時,3.1邊形對應的四個點和3.11對應的四個點,由於它們的起點是固定的,剩下各自的三個點對應的位置都是很接近的(因為3.1和3.11對應的弧度是很接近的),這樣一直畫到4.0邊形,就完成瞭從三邊形到四邊形的漸變動畫。

具體代碼

獲取多邊形頂點

List<Offset> points = [];
List<Offset> getPolygonPoints1(double sides) {
    for (int i = 0; i < sides.ceil(); i++) {
      double x, y;
      x = radius * sin(i * 2 * pi / sides);
      y = -radius * cos(i * 2 * pi / sides);
      points.add(Offset(x, y));
    }
    return points;
}

獲取到多邊形頂點之後我們就可以在Custompaint的paint函數中將其繪制出來:

@override
  void paint(Canvas canvas, Size size) {
      Paint paint = Paint()
          ..color = const Color(0xFF47484B)
          ..style = PaintingStyle.stroke
          ..strokeWidth = 1
          ..isAntiAlias = true;
      List<Offset> points = getPolygonPoints1(progress);
      for (int i = 0; i < points.length; i++) {
      canvas.drawLine(
          points[i % points.length], points[(i + 1) % points.length], paint);
      }
}

可以看到效果如下:

但是如果我想要下圖這種效果,當邊數為奇數時,頂點位於最上方,邊數由奇數變成偶數時,最上方的頂點分裂成兩個,類似下圖效果:

效果改進1

想要達到這種效果,我們隻需要將代碼改進一下,不再固定起始點,而是在邊數由奇數變為偶數時,將起始點的弧度由(pi / sides)漸變為0,由偶數變位奇數時,起始點弧度由0變為(pi / sides)。

代碼如下:

List<Offset> getPolygonPoints2(double sides) {
    for (int i = 0; i < sides.ceil(); i++) {
      double x, y;
      if (sides.ceil() % 2 == 0) {
        x = radius *
            sin(lerpDouble(0, (pi / sides), sides - sides.floor())! +
                i * 2 * pi / sides);
        y = -radius *
            cos(lerpDouble(0, (pi / sides), sides - sides.floor())! +
                i * 2 * pi / sides);
      } else {
        x = radius *
            sin(lerpDouble((pi / sides), 0, sides - sides.floor())! +
                i * 2 * pi / sides);
        y = -radius *
            cos(lerpDouble((pi / sides), 0, sides - sides.floor())! +
                i * 2 * pi / sides);
      }
      points.add(Offset(x, y));
    }
    return points;
}

此時效果如下:

但是還是有些不完美,我還想讓多邊形邊數為偶數時,起始點是從最上方的邊的中點一直漸變到最上方的點,就是下面這種效果:

效果改進2

此時我們隻需要將多邊形由偶數變為奇數時的起始點改為最上方邊線的中點即可。 此時代碼如下:

List<Offset> getPolygonPoints(double sides) {
    for (int i = 0; i < sides.ceil(); i++) {
      double x, y;
      if (sides.ceil() % 2 == 0) {
        if (sides.ceil() == sides) {
          x = radius * sin((pi / sides) + i * 2 * pi / sides);
          y = -radius * cos((pi / sides) + i * 2 * pi / sides);
        } else {
          x = radius *
              sin(lerpDouble(0, (pi / sides), sides - sides.floor())! +
                  i * 2 * pi / sides);
          y = -radius *
              cos(lerpDouble(0, (pi / sides), sides - sides.floor())! +
                  i * 2 * pi / sides);
        }
      } else {
        if (sides.ceil() == sides) {
          x = radius * sin(i * 2 * pi / sides);
          y = -radius * cos(i * 2 * pi / sides);
        } else {
          // 起始點位置單獨計算
          if (i == 0) {
            double startY = -radius * cos(pi / sides);
            double endY = -radius;
            x = 0;
            y = lerpDouble(startY, endY, sides - sides.floor())!;
          } else {
            x = radius *
                sin(lerpDouble((pi / sides), 0, sides - sides.floor())! +
                    (i - lerpDouble(1, 0, sides - sides.floor())!) *
                        2 *
                        pi /
                        sides);
            y = -radius *
                cos(lerpDouble((pi / sides), 0, sides - sides.floor())! +
                    (i - lerpDouble(1, 0, sides - sides.floor())!) *
                        2 *
                        pi /
                        sides);
          }
        }
      }
      points.add(Offset(x, y));
    }
    return points;
  }

在這個基礎上再畫出對角線,加上縮放,就能達到我們一開始看到的最終效果瞭。

一些canvas的其他小demo

關於flutter canvas的其他效果,我後面會陸續分享出來,大傢喜歡的話可以關註一下~

git地址

以上就是Flutter繪制3.4邊形之多邊形漸變動畫實現示例的詳細內容,更多關於Flutter繪制3.4邊形漸變動畫的資料請關註WalkonNet其它相關文章!

推薦閱讀: