亲宝软件园·资讯

展开

Flutter绘制3.4边形渐变动画

十三点47 人气:0

正文

项目被优化了,人也跟着被优化了,正好趁这一个月整理整理关于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地址

加载全部内容

相关教程
猜你喜欢
用户评论