0
点赞
收藏
分享

微信扫一扫

Flutter篇 (二)绘制贝塞尔曲线 、折线 、柱状图,支持触摸

const CustomPaint({
    Key key,
    this.painter,
    this.foregroundPainter,
    this.size = Size.zero,
    this.isComplex = false,
    this.willChange = false,
    Widget child,
  }) :super(key: key, child: child);
double startX, endX, startY, endY;//定义绘制区域的边界
static const double basePadding = 16; //默认的边距
double fixedHeight, fixedWidth; //去除padding后曲线的真实宽高
bool isShowXyRuler; //是否显示xy刻度
List<ChatBean> chatBeans;//数据源

class ChatBean {
  String x;
  double y;
  int millisSeconds;
  Color color;

  ChatBean({@required this.x, @required this.y, this.millisSeconds, this.color});
}

///计算边界
  void initBorder(Size size) {
    print('size - - > $size');
    this.size = size;
    startX = yNum > 0 ? basePadding * 2.5 : basePadding * 2; //预留出y轴刻度值所占的空间
    endX = size.width - basePadding * 2;
    startY = size.height - (isShowXyRuler ? basePadding * 3 : basePadding);
    endY = basePadding * 2;
    fixedHeight = startY - endY;
    fixedWidth = endX - startX;
    maxMin = calculateMaxMin(chatBeans);
  }
///计算极值 最大值,最小值
  List<double> calculateMaxMin(List<ChatBean> chatBeans) {
    if (chatBeans == null || chatBeans.length == 0) return [0, 0];
    double max = 0.0, min = 0.0;
    for (ChatBean bean in chatBeans) {
      if (max < bean.y) {
        max = bean.y;
      }
      if (min > bean.y) {
        min = bean.y;
      }
    }
    return [max, min];
  }
var paint = Paint()
      ..isAntiAlias = true//抗锯齿
      ..strokeWidth = 2
      ..strokeCap = StrokeCap.round//折线连接处圆滑处理
      ..color = xyColor
      ..style = PaintingStyle.stroke;//描边
canvas.drawLine(Offset(startX, startY),Offset(endX + basePadding, startY), paint); //x轴
canvas.drawLine(Offset(startX, startY),Offset(startX, endY - basePadding), paint); //y轴
int length = chatBeans.length > 7 ? 7 : chatBeans.length; //最多绘制7个
double DW = fixedWidth / (length - 1); //两个点之间的x方向距离
double DH = fixedHeight / (length - 1); //两个点之间的y方向距离
for (int i = 0; i < length; i++) {
     ///绘制x轴文本
     TextPainter(
            textAlign: TextAlign.center,
            ellipsis: '.',
            text: TextSpan(
                text: chatBeans[i].x,
                style: TextStyle(color: fontColor, fontSize: fontSize)),
            textDirection: TextDirection.ltr)
          ..layout(minWidth: 40, maxWidth: 40)
          ..paint(canvas, Offset(startX + DW * i - 20, startY + basePadding));

      ///x轴刻度
      canvas.drawLine(Offset(startX + DW * i, startY),Offset(startX + DW * i, startY - rulerWidth), paint);
   }
      int yLength = yNum + 1; //包含原点,所以 +1
      double dValue = maxMin[0] / yNum; //一段对应的值
      double dV = fixedHeight / yNum; //一段对应的高度
      for (int i = 0; i < yLength; i++) {
        ///绘制y轴文本,保留1位小数
        var yValue = (dValue * i).toStringAsFixed(isShowFloat ? 1 : 0);
        TextPainter(
            textAlign: TextAlign.center,
            ellipsis: '.',
            maxLines: 1,
            text: TextSpan(  
                text: '$yValue',
                style: TextStyle(color: fontColor, fontSize: fontSize)),
            textDirection: TextDirection.rtl)
          ..layout(minWidth: 40, maxWidth: 40)
          ..paint(canvas, Offset(startX - 40, startY - dV * i - fontSize / 2));

        ///y轴刻度
        canvas.drawLine(Offset(startX, startY - dV * (i)),Offset(startX + rulerWidth, startY - dV * (i)), paint);
      }
path.cubicTo(double x1, double y1, double x2, double y2, double x3, double y3)
path = Path();
double preX, preY, currentX, currentY;
int length = chatBeans.length > 7 ? 7 : chatBeans.length;
double W = fixedWidth / (length - 1); //两个点之间的x方向距离
if (i == 0) {
     path.moveTo(startX, (startY - chatBeans[i].y / maxMin[0] * fixedHeight));
     continue;
   }
currentX = startX + W * i;
preX = startX + W * (i - 1);

preY = (startY - chatBeans[i - 1].y / maxMin[0] * fixedHeight);
currentY = (startY - chatBeans[i].y / maxMin[0] * fixedHeight);

path.cubicTo(
              (preX + currentX) / 2, preY,
              (preX + currentX) / 2, currentY,
              currentX, currentY
            );

如果是要画折线而非曲线,第一步还是path.moveTo ,折线不需要找辅助点,所以后续可以直接添加坐标,path.lineTo

最后将path绘制出来

canvas.drawPath(newPath, paint);
_controller = AnimationController(vsync: this, duration: widget.duration);
      Tween(begin: 0.0, end: widget.duration.inMilliseconds.toDouble())
          .animate(_controller)
            ..addStatusListener((status) {
              if (status == AnimationStatus.completed) {
                print('绘制完成');
              }
            })
            ..addListener(() {
              _value = _controller.value;//当前动画值
              setState(() {});
            });
      _controller.forward();
var pathMetrics = path.computeMetrics(forceClosed: false);
path.addPath(path, offset);
extractPath(double start, double end,{ bool startWithMoveTo:true }) → Path
给定起始和停止距离,返回中间段。
var pathMetrics = path.computeMetrics(forceClosed: true);
var list = pathMetrics.toList();
var length = value * list.length.toInt();
Path newPath = new Path();
for (int i = 0; i < length; i++) {
     var extractPath =list[i].extractPath(0, list[i].length * value, startWithMoveTo: true);
      newPath.addPath(extractPath, Offset(0, 0));
    }
 canvas.drawPath(newPath, paint);
var shader = LinearGradient(
              begin: Alignment.topCenter,
              end: Alignment.bottomCenter,
              tileMode: TileMode.clamp,
              colors: shaderColors)
          .createShader(Rect.fromLTRB(startX, endY, startX, startY));
Paint shadowPaint = new Paint();
      shadowPaint
        ..shader = shader
        ..isAntiAlias = true
        ..style = PaintingStyle.fill;
///从path的最后一个点连接起始点,形成一个闭环
      shadowPath
        ..lineTo(startX + fixedWidth * value, startY)
        ..lineTo(startX, startY)
        ..close();
      canvas..drawPath(shadowPath, shadowPaint);
举报

相关推荐

iOS 绘制贝塞尔曲线

0 条评论