微信交流群

# 渐变进度圆环

自定义动画分为两种:组合动画自绘动画

组合动画就是多个组件或者多个动画控制器组成一个新的动画,就是将基础动画组件进行拼接的过程。

自绘动画就是使用 Canvas 进行绘制,并与动画控制器联动进行联动。

下面一步一步绘制渐变进度圆环,先根据进度绘制一个静态的圆环

class _CircleProgressPainter extends CustomPainter {
  final double progress;

  _CircleProgressPainter(this.progress);

  Paint _paint = Paint()
    ..style = PaintingStyle.stroke
    ..strokeWidth = 10
    ..color = Colors.blue;

  
  void paint(Canvas canvas, Size size) {
    double radius = min(size.width, size.height) / 2;
    canvas.drawArc(Rect.fromLTWH(0, 0, radius * 2, radius * 2), -pi / 2,
        pi * 2 * progress, false, _paint);
  }

  
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

使用:

Container(
  height: 150,
  width: 150,
  child: CustomPaint(
    painter: _CircleProgressPainter(.3),
    child: Center(child: Text('30%')),
  ),
)
1
2
3
4
5
6
7
8

将蓝色修改为渐变色,_CircleProgressPainter修改如下:


void paint(Canvas canvas, Size size) {
  double radius = min(size.width, size.height) / 2;

  Gradient gradient = SweepGradient(
    endAngle: pi * 2 * progress,
    colors: [
      Color(0xFFD32D2F),
      Color(0xFFEA4886),
    ],
  );
  var rect = Rect.fromLTWH(0, 0, radius * 2, radius * 2);

  _paint.shader = gradient.createShader(rect);

  canvas.drawArc(Rect.fromLTWH(0, 0, radius * 2, radius * 2), -pi / 2,
      pi * 2 * progress, false, _paint);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

发现渐变色是从水平方向开始的,而圆形进度是从垂直方向顶部开始,修改渐变色从垂直方向顶部开始:

Gradient gradient = SweepGradient(
  startAngle: -pi / 2,
  endAngle: pi * 2 * progress,
  colors: [
    Color(0xFFD32D2F),
    Color(0xFFEA4886),
  ],
);
1
2
3
4
5
6
7
8

渐变色设置 startAngle 参数,但却无效,效果没有发生变化,不知道是否是用法不对?因此只能换一个思路实现,旋转 canvas


void paint(Canvas canvas, Size size) {
  double radius = min(size.width, size.height) / 2;

  Gradient gradient = SweepGradient(
    startAngle: -pi / 2,
    endAngle: pi * 2 * progress,
    colors: [
      Color(0xFFD32D2F),
      Color(0xFFEA4886),
    ],
  );
  var rect = Rect.fromLTWH(0, 0, radius * 2, radius * 2);

  _paint.shader = gradient.createShader(rect);

  canvas.save();
  canvas.rotate(-pi / 2);

  canvas.drawArc(Rect.fromLTWH(0, 0, radius * 2, radius * 2), -pi / 2,
      pi * 2 * progress, false, _paint);

  canvas.restore();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

画布的旋转是以左上角为原点,所以先向下平移,再旋转:


  void paint(Canvas canvas, Size size) {
    double radius = min(size.width, size.height) / 2;

    Gradient gradient = SweepGradient(
      startAngle: -pi / 2,
      endAngle: pi * 2 * progress,
      colors: [
        Color(0xFFD32D2F),
        Color(0xFFEA4886),
      ],
    );
    var rect = Rect.fromLTWH(0, 0, radius * 2, radius * 2);

    _paint.shader = gradient.createShader(rect);

    canvas.save();
    canvas.translate(0.0, size.height);
    canvas.rotate(-pi / 2);

    canvas.drawArc(rect, 0, pi * 2 * progress, false, _paint);

    canvas.restore();
  }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

静态圆环绘制完成了,只需给其加上动画控制器,改变进度即可:


Widget build(BuildContext context) {
  return Scaffold(
    body: Center(
      child: Container(
        width: 150,
        height: 150,
        child: TweenAnimationBuilder(
          tween: Tween(begin: 0.0, end: 1.0),
          duration: Duration(seconds: 3),
          builder: (BuildContext context, double value, Widget child) {
            return CustomPaint(
              painter: _CircleProgressPainter(value),
              child: Center(child: Text('${(value * 100).floor()}%')),
            );
          },
        ),
      ),
    ),
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

进度圆环的两端是矩形,修改为圆形:

Paint _paint = Paint()
  ..style = PaintingStyle.stroke
  ..strokeWidth = 10
  ..strokeCap = StrokeCap.round;
1
2
3
4