美文网首页FlutterFlutter学习
Flutter-绘制自定义仪表盘

Flutter-绘制自定义仪表盘

作者: Cosecant | 来源:发表于2021-03-20 14:02 被阅读0次
    • 仪表盘绘制,效果图如下:


      仪表盘效果图
    • 仪表盘基本参数
      min - 最小值
      max - 最大值
      progress - 进度值
      仪表盘总共300度,那么每个点的刻度为:300 / (max-min)

    • 效果图主要掌握如下,
      Canvas: 1. ARC绘制,2.Paragraph文本绘制,3.图形旋转;

    • 绘制方法:
      - _drawProgressArc 绘制扇形⭕️
      - _drawArcPointLine 绘制仪表盘刻度线
      - _drawArcProgressPoint 绘制仪表盘刻度文字

    • 用到的基本公式:
      1.角度转弧度:num _toRadius(num degree) => degree * Math.pi / 180;
      2.圆上的点坐标(x, y)求值:
      已知圆心坐标(ox, oy),半径为r,则圆上点坐标可如下求解,
      x = ox + r * cos(rad);
      y = oy + r * sin(rad);

    • 注意点:
      绘制文本,需要PragraphBuilder 执行 layout 方法。

    • 基本代码如下:

    import 'dart:math' as Math;
    import 'dart:ui' as UI;
    import 'package:flutter/material.dart';
    
    class ArcProgressBar extends StatelessWidget {
      final double width;
      final double height;
      final double min;
      final double max;
      final double progress;
    
      ArcProgressBar({
        Key key,
        this.width = 180,
        this.height = 180,
        this.min = 0,
        this.max = 100,
        this.progress = 0,
      }) : super(key: key);
    
      @override
      Widget build(BuildContext context) => AspectRatio(
          aspectRatio: 1,
          child: CustomPaint(
            size: Size(width, height),
            painter: _ArcProgressBarPainter(8, progress, min: min, max: max),
          ));
    }
    
    class _ArcProgressBarPainter extends CustomPainter {
      Paint _paint = Paint();
    
      double _strokeSize;
    
      double get _margin => _strokeSize / 2;
    
      num progress = 0;
    
      num min;
    
      num max;
    
      _ArcProgressBarPainter(double strokeSize, this.progress,
          {this.min = 0, this.max = 100}) {
        this._strokeSize = strokeSize;
        if (progress == null || progress < min) progress = 0;
        if (progress > max) progress = max;
        if (min == null || min <= 0) min = 0;
        if (max == null || max <= min) max = 100;
      }
    
      @override
      void paint(Canvas canvas, Size size) {
        num radius = size.width / 2;
        num cx = radius;
        num cy = radius;
        _drawProgressArc(canvas, size);
        _drawArcProgressPoint(canvas, cx, cy, radius);
        _drawArcPointLine(canvas, cx, cy, radius);
      }
    
      void _drawProgressArc(Canvas canvas, Size size) {
        _paint
          ..isAntiAlias = true
          ..color = Colors.grey[400]
          ..style = PaintingStyle.stroke
          ..strokeCap = StrokeCap.round
          ..strokeWidth = _strokeSize;
        canvas.drawArc(
            Rect.fromLTWH(_margin, _margin, size.width - _strokeSize,
                size.height - _strokeSize),
            _toRadius(120),
            _toRadius(300),
            false,
            _paint);
    
        _paint
          ..color = Colors.cyan
          ..strokeWidth = _strokeSize - 1;
        canvas.drawArc(
            Rect.fromLTWH(_margin + 1, _margin + 1, size.width - _strokeSize - 2,
                size.height - _strokeSize - 2),
            _toRadius(120),
            progress * _toRadius(300 / (max - min)),
            false,
            _paint);
      }
    
      void _drawArcProgressPoint(Canvas canvas, num cx, num cy, num radius) {
        _paint.strokeWidth = 1;
        canvas.save();
        canvas.translate(cx, cy);
        canvas.rotate(_toRadius(120));
        canvas.translate(-cx, -cy);
        for (int i = 0; i <= (max - min); i++) {
          num evaDegree = i * _toRadius(300 / (max - min));
          num b = i % 10 == 0 ? -5 : 0;
          num x = cx + (radius - 20 + b) * Math.cos(evaDegree);
          num y = cy + (radius - 20 + b) * Math.sin(evaDegree);
          num x1 = cx + (radius - 12) * Math.cos(evaDegree);
          num y1 = cx + (radius - 12) * Math.sin(evaDegree);
          canvas.drawLine(Offset(x, y), Offset(x1, y1), _paint);
        }
        canvas.translate(cx, cy);
        canvas.rotate(_toRadius(-120));
        canvas.translate(-cx, -cy);
        for (int i = min; i <= max; i += 10) {
          var pb = UI.ParagraphBuilder(
              UI.ParagraphStyle(fontSize: 15, textAlign: TextAlign.start))
            ..pushStyle(UI.TextStyle(color: Colors.black))
            ..addText(i.toString());
          UI.Paragraph p = pb.build()..layout(UI.ParagraphConstraints(width: 30));
          num evaDegree = _toRadius(120) + i * _toRadius(300 / (max - min));
          num x = cx + (radius - 40) * Math.cos(evaDegree);
          num y = cy + (radius - 40) * Math.sin(evaDegree);
          canvas.drawParagraph(p, Offset(x - 8, y - 10));
        }
        canvas.restore();
      }
    
      void _drawArcPointLine(UI.Canvas canvas, num cx, num cy, num radius) {
        canvas.save();
        canvas.translate(cx, cy);
        canvas.rotate(_toRadius(120));
        canvas.translate(-cx, -cy);
        _paint
          ..color = Colors.amber[800]
          ..style = PaintingStyle.fill
          ..strokeWidth = 3;
        num degree = _toRadius(300 / (max - min)) * progress;
        num x = cx + radius * 3 / 5 * Math.cos(degree);
        num y = cy + radius * 3 / 5 * Math.sin(degree);
        canvas.drawLine(Offset(cx, cy), Offset(x, y), _paint);
        _paint.color = Colors.amber[900];
        canvas.drawCircle(Offset(cx, cy), 12, _paint);
        canvas.restore();
      }
    
      num _toRadius(num degree) => degree * Math.pi / 180;
    
      @override
      bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
    }
    
    

    相关文章

      网友评论

        本文标题:Flutter-绘制自定义仪表盘

        本文链接:https://www.haomeiwen.com/subject/vmhwcltx.html