flutter绘纸曲线图

Flutter绘制线,然后中间用空心圆连接,并且不穿过圆
效果如图:
在这里插入图片描述

import 'dart:math';

import 'package:flutter/material.dart';

class DataPoint {
  final double x;
  final double y;

  DataPoint(this.x, this.y);
}

class ChartPainter extends CustomPainter {
  final List<DataPoint> dataPoints;
  final List<int> pointY;
  final int numHorizontalLine;
  final double selectedRadius;

  ChartPainter(
    this.dataPoints,
    this.pointY,
    this.numHorizontalLine,
    this.selectedRadius,
  );

  double? startX, startY, endX, endY; //坐标的起始和结束点
  double? horizontalSpacing;
// 记录点击的坐标点索引
  int selectedPointIndex = -1;
  @override
  void paint(Canvas canvas, Size size) {
    initPoint(size);
    drawHorizontalLine(canvas);
    drawCurve(canvas);
  }

  initPoint(Size size) {
    startX = 40;
    endX = size.width - startX!;
    endY = 20;
    startY = size.height - endY!;
    horizontalSpacing = (startY! - endY!) / (numHorizontalLine - 1);
  }

  //绘制背景横线和坐标值
  drawHorizontalLine(Canvas canvas) {
    final Paint linePaint = Paint()..strokeWidth = 1.0;

    final TextPainter textPainter = TextPainter(
      textAlign: TextAlign.right,
      textDirection: TextDirection.rtl,
    );

    for (int i = 0; i < numHorizontalLine; i++) {
      final double y = startY! - i * horizontalSpacing!;
      if (i == 0) {
        linePaint.color = Colors.black;
      } else {
        linePaint.color = Color(0x28000000);
      }
      canvas.drawLine(
          Offset(startX!, y), Offset(endX! + startX!, y), linePaint);
      final String label = pointY[i].toString();
      textPainter.text = TextSpan(
        text: label,
        style: TextStyle(color: Colors.black),
      );
      textPainter.layout();
      textPainter.paint(
        canvas,
        Offset(0, y - textPainter.height / 2),
      );
    }
  }

  // 绘制曲线图
  void drawCurve(Canvas canvas) {
    final Paint curvePaint = Paint()
      ..color = Colors.blue
      ..strokeWidth = 2.0
      ..style = PaintingStyle.stroke;
    final double circleRadius = 4.0; // 圆的半径
    final double xScale = endX! / (dataPoints.length - 1);
    final double yScale = (startY! - endY!) / (pointY.last - pointY.first);

    for (int i = 0; i < dataPoints.length; i++) {
      final double x = startX! + i * xScale;
      final double y = startY! - (dataPoints[i].y - pointY.first) * yScale;

      final double prevX = i > 0 ? startX! + (i - 1) * xScale : x;
      final double prevY = i > 0 ? startY! - (dataPoints[i - 1].y - pointY.first) * yScale : y;

      final double angle = atan2(y - prevY, x - prevX); // 计算线段的角度
      final double offsetX = cos(angle) * circleRadius; // 计算连接到圆边界的偏移量
      final double offsetY = sin(angle) * circleRadius;

      // 计算线段起点和终点,使其连接到圆的边界
      final double lineStartX = prevX + offsetX;
      final double lineStartY = prevY + offsetY;
      final double lineEndX = x - offsetX;
      final double lineEndY = y - offsetY;

      if (i == 0) {
        canvas.drawCircle(Offset(x, y), circleRadius, curvePaint); // 绘制圆
      } else {
        canvas.drawLine(Offset(lineStartX, lineStartY), Offset(lineEndX, lineEndY), curvePaint); // 绘制线段
        canvas.drawCircle(Offset(x, y), circleRadius, curvePaint); // 绘制圆
      }
    }
  }



  //检测点击事件
  void onTapDown(Offset offset) {
    for (int i = 0; i < dataPoints.length; i++) {
      final double x = startX! + i * ((endX!) / (dataPoints.length - 1));
      final double y = startY! -
          (dataPoints[i].y - pointY.first) *
              ((startY! - endY!) / (pointY.last - pointY.first));
      print(
          "==>${offset.dx}->${offset.dy}==>$x==>$y==>${(offset.dx - x).abs()}=>$selectedRadius=>${(offset.dy - y).abs()}");

      if ((offset.dx - x).abs() <= selectedRadius &&
          (offset.dy - y).abs() <= selectedRadius) {
        selectedPointIndex = i;
        break;
      } else {
        selectedPointIndex = -1;
      }
    }
    print("==$selectedPointIndex");
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }
}

外部调用

GestureDetector(
              onTapDown: (TapDownDetails detail) {
                chartController.onTapDown(detail);
              },
              child: CustomPaint(
                  size: Size(Get.width - 20.w, 280.h),
                  painter:ChartPainter([
                    DataPoint(0, 100),
                    DataPoint(1, 50),
                    DataPoint(2, 200),
                    DataPoint(3, 250),
                    DataPoint(4, 100),
                    DataPoint(5, 150),
                    DataPoint(6, 50),
                    DataPoint(7, 20),
                  ], [
                    0,
                    50,
                    100,
                    150,
                    200,
                    250,
                    300
                  ], 7,5),
              ),
            )
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值