flutter spider组件

组件效果:

 

由于需求上需要显示值和文字,该组件在https://www.jianshu.com/p/54aa09ce67a2 基础上进行修改。缺陷:文字显示坐标没有精确计算(有精确度误差),绘制文字内容超过父widget,可通过父widget Container加padding或者加Padding组件进行屏蔽。修改后对应组件代码如下

import 'dart:math';
import 'dart:ui' as ui;

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';


class SpiderWidget extends CustomPainter {
  //多边形边数
  int mEdgeSize;

  final double CIRCLE_ANGLE = 360;

  //中心坐标
  double mCenterX;
  double mCenterY;

  //线画笔
  Paint mPaint;

  //覆盖物画笔
  Paint mCoverPaint;

  //文本画笔
  Paint mTextPaint;
  Path mPath;

  List<String> texts = ["测试", "测试", "测试", "测试"];
  List<int> values = [10, 8, 9, 9];

  SpiderWidget() {
    // 初始化画笔
    mPaint = new Paint();
    mPaint.color = Colors.grey;
    // 设置抗锯齿
    mPaint.isAntiAlias = true;
    // 样式为描边
    mPaint.style = PaintingStyle.stroke;

    mPath = new Path();

    mCoverPaint = new Paint();
    mCoverPaint.isAntiAlias = true;
    mCoverPaint.style = PaintingStyle.fill;
    mCoverPaint.color = Colors.yellow;

    mTextPaint = new Paint();
    mTextPaint.isAntiAlias = true;
    mTextPaint.style = PaintingStyle.fill;
    mTextPaint.color = Colors.blue;
  }

  @override
  void paint(Canvas canvas, Size size) {
    init(canvas, size);
  }

  void init(Canvas canvas, Size size) {
    mCenterX = size.width / 2;
    mCenterY = size.height / 2;
    //设置边数
    mEdgeSize = texts.length;

    // 图层 防止刷新属性结构
    canvas.save();
    drawSpiderEdge(canvas);
    drawCover(canvas);
    drawText(canvas);
    drawValues(canvas);
    canvas.restore();
  }

  void drawText(Canvas canvas) {
    double radius = mCenterX > mCenterY ? mCenterX : mCenterY;
    double angle = CIRCLE_ANGLE / mEdgeSize;
    for (int j = 0; j < mEdgeSize; j++) {
      // 移动
      double x = mCenterX + radius * cos(degToRad(angle * j));
      double y = mCenterY + radius * sin(degToRad(angle * j));

      ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle(
        textAlign: TextAlign.center,
        fontStyle: FontStyle.normal,
        fontSize: 12,
      ))
        ..pushStyle(ui.TextStyle(color: Colors.green))
        ..addText(texts[j]);
      ui.ParagraphConstraints pc =
          ui.ParagraphConstraints(width: double.infinity);
      ui.Paragraph paragraph = pb.build()..layout(pc);
      Offset offset;
      if (x < mCenterX && y < mCenterY) {
        offset = Offset(x - paragraph.minIntrinsicWidth - 8, y - 12);
      } else if (x < mCenterX && y > mCenterY) {
        offset = Offset(x - paragraph.minIntrinsicWidth - 8, y - 12);
      } else if (x > mCenterX && y < mCenterY) {
        offset = Offset(x +8, y - 12);
      } else if (x > mCenterX && y > mCenterY) {
        offset = Offset(x - 8, y +8);
      } else if (x == mCenterX && y > mCenterY) {
        offset = Offset(x, y );
      } else if (x == mCenterX && y < mCenterY) {
        offset = Offset(x+paragraph.minIntrinsicWidth, y - 12);
      } else if (y == mCenterY && x > mCenterX) {
        offset = Offset(x +8, y -8);
      } else {
        offset = Offset(x - 12, y);
      }

      canvas.drawParagraph(paragraph, offset);
    }
  }

  /// 绘制边线
  void drawSpiderEdge(Canvas canvas) {
    double angle = CIRCLE_ANGLE / mEdgeSize;
    double radius = 0;
    double radiusMaxLimit = mCenterX > mCenterY ? mCenterX : mCenterY;
    for (int i = 0; i < mEdgeSize; i++) {
      mPath.reset();
      radius = radiusMaxLimit / mEdgeSize * (i + 1);
      for (int j = 0; j < mEdgeSize + 1; j++) {
        // 移动
        if (j == 0) {
          mPath.moveTo(mCenterX + radius, mCenterY);
        } else {
          double x = mCenterX + radius * cos(degToRad(angle * j));
          double y = mCenterY + radius * sin(degToRad(angle * j));
          mPath.lineTo(x, y);
        }
      }
      mPath.close();
      canvas.drawPath(mPath, mPaint);
    }
    drawSpiderAxis(canvas, radiusMaxLimit, angle);
  }

  /// 绘制轴线
  void drawSpiderAxis(Canvas canvas, double radius, double angle) {
    for (int i = 0; i < mEdgeSize; i++) {
      mPath.reset();
      mPath.moveTo(mCenterX, mCenterY);
      double x = mCenterX + radius * cos(degToRad(angle * i));
      double y = mCenterY + radius * sin(degToRad(angle * i));
      mPath.lineTo(x, y);
      canvas.drawPath(mPath, mPaint);
    }
  }

  /// 绘制覆盖区域
  void drawCover(Canvas canvas) {
    mPath.reset();
//    Random random = new Random();

    double angle = CIRCLE_ANGLE / mEdgeSize;
    double radiusMaxLimit = min(mCenterY, mCenterY);
    for (int i = 0; i < mEdgeSize; i++) {
//      double value = (random.nextInt(10) + 1) / 10;
      double value = values[i] / 10;
      double x = mCenterX + radiusMaxLimit * cos(degToRad(angle * i)) * value;
      double y = mCenterY + radiusMaxLimit * sin(degToRad(angle * i)) * value;
      if (i == 0) {
        mPath.moveTo(x, mCenterY);
      } else {
        mPath.lineTo(x, y);
      }
    }
    mPath.close();
    canvas.drawPath(mPath, mCoverPaint);
  }

  void drawValues(Canvas canvas) {
    double angle = CIRCLE_ANGLE / mEdgeSize;
    double radiusMaxLimit = min(mCenterY, mCenterY);
    for (int i = 0; i < mEdgeSize; i++) {
      double value = values[i] / 10;
      double x = mCenterX + radiusMaxLimit * cos(degToRad(angle * i)) * value;
      double y = mCenterY + radiusMaxLimit * sin(degToRad(angle * i)) * value;
      //画值
      ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle(
        textAlign: TextAlign.center,
        fontStyle: FontStyle.normal,
        fontSize: 12,
      ))
        ..pushStyle(ui.TextStyle(color: Colors.grey))
        ..addText("${values[i]}");
      ui.ParagraphConstraints pc =
          ui.ParagraphConstraints(width: double.infinity);
      ui.Paragraph paragraph = pb.build()..layout(pc);
      Offset offset;
      if (x < mCenterX && y < mCenterY) {
        offset = Offset(x, y + 8);
      } else if (x < mCenterX && y > mCenterY) {
        offset = Offset(x + paragraph.minIntrinsicWidth, y - 12);
      } else if (x > mCenterX && y < mCenterY) {
        offset = Offset(x - paragraph.minIntrinsicWidth, y + 6);
      } else if (x > mCenterX && y > mCenterY) {
        offset = Offset(x - paragraph.minIntrinsicWidth, y - 20);
      } else if (x == mCenterX && y > mCenterY) {
        offset = Offset(x, y - 12);
      } else if (x == mCenterX && y < mCenterY) {
        offset = Offset(x, y + 12);
      } else if (y == mCenterY && x > mCenterX) {
        offset = Offset(x - paragraph.minIntrinsicWidth - 12, y - 4);
      } else {
        offset = Offset(x + 12, y);
      }
      canvas.drawParagraph(paragraph, offset);
    }
  }

  num degToRad(num deg) => deg * (pi / 180.0);

  num radToDeg(num rad) => rad * (180.0 / pi);

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return oldDelegate != this;
  }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值