Flutter 画笔(Paint) 根据坐标点 绘制曲线图

丈夫志四海,万里犹比邻。——曹植

  

  

绘制范围(x、y轴最大或最小偏移量)

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo Chart',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Chart Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: Container(
        color: Colors.white.withOpacity(0.4),
        child: CustomPaint(
          size: Size(MediaQuery.of(context).size.width,
              MediaQuery.of(context).size.height / 2),
          painter: CharLinePainter(),
        ),
      ),
    );
  }
}

 初始化边界

class CharLinePainter extends CustomPainter {
  static const double basePadding = 16;//基础边界

  double startX, endX;//相对于原点x轴方向最小和最大偏移量(相对于原点的偏移量)
  double startY, endY;//相对于原点y轴方向最大和最小偏移量(相对于原点的偏移量)
  double _fixedWidth;//x轴方向:最大偏移量-最小偏移量(相对于原点的偏移量)
  double _fixedHeight;//y轴方向:最大偏移量-最小偏移量(相对于原点的偏移量)

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

  /// 初始化边界
  void _initBorder(Size size) {
    startX = basePadding * 2;
    endX = size.width - basePadding * 2;
    startY = size.height - basePadding * 2;
    endY = basePadding * 2;
    _fixedWidth = endX - startX;
    _fixedHeight = startY - endY;
  }

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

绘制xy轴

class CharLinePainter extends CustomPainter {
  static const double basePadding = 16;//基础边界

  double startX, endX;//相对于原点x轴方向最小和最大偏移量(相对于原点的偏移量)
  double startY, endY;//相对于原点y轴方向最大和最小偏移量(相对于原点的偏移量)
  double _fixedWidth;//x轴方向:最大偏移量-最小偏移量(相对于原点的偏移量)
  double _fixedHeight;//y轴方向:最大偏移量-最小偏移量(相对于原点的偏移量)

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

  /// 初始化边界
  void _initBorder(Size size) {
    startX = basePadding * 2;
    endX = size.width - basePadding * 2;
    startY = size.height - basePadding * 2;
    endY = basePadding * 2;
    _fixedWidth = endX - startX;
    _fixedHeight = startY - endY;
  }

  ///绘制xy轴
  ///绘制x轴-y轴偏移量不变(y轴坐标点不变)
  ///绘制y轴-x轴偏移量不变(x轴坐标点不变)
  void _drawXy(Canvas canvas){
    var paint = Paint()
      ..isAntiAlias = true
      ..strokeWidth = 1.0
      ..strokeCap = StrokeCap.square
      ..color = Colors.white
      ..style = PaintingStyle.stroke;
      canvas.drawLine(Offset(startX, startY), Offset(endX , startY), paint); //x轴
      canvas.drawLine(Offset(startX, startY), Offset(startX, endY), paint); //y轴
  }

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

绘制xy刻度

class CharLinePainter extends CustomPainter {
  static const double basePadding = 16;//基础边界

  double startX, endX;//相对于原点x轴方向最小和最大偏移量(相对于原点的偏移量)
  double startY, endY;//相对于原点y轴方向最大和最小偏移量(相对于原点的偏移量)
  double _fixedWidth;//x轴方向:最大偏移量-最小偏移量(相对于原点的偏移量)
  double _fixedHeight;//y轴方向:最大偏移量-最小偏移量(相对于原点的偏移量)

  @override
  void paint(Canvas canvas, Size size) {
    _initBorder(size);
    _drawXy(canvas);
    _drawXRuler(canvas);
  }

  /// 初始化边界
  void _initBorder(Size size) {
    startX = basePadding * 2;
    endX = size.width - basePadding * 2;
    startY = size.height - basePadding * 2;
    endY = basePadding * 2;
    _fixedWidth = endX - startX;
    _fixedHeight = startY - endY;
  }

  ///绘制xy轴
  ///绘制x轴-y轴偏移量不变(y轴坐标点不变)
  ///绘制y轴-x轴偏移量不变(x轴坐标点不变)
  void _drawXy(Canvas canvas){
    var paint = Paint()
      ..isAntiAlias = true
      ..strokeWidth = 1.0
      ..strokeCap = StrokeCap.square
      ..color = Colors.white
      ..style = PaintingStyle.stroke;
      canvas.drawLine(Offset(startX, startY), Offset(endX , startY), paint); //x轴
      canvas.drawLine(Offset(startX, startY), Offset(startX, endY), paint); //y轴
  }

  ///绘制x轴刻度
  void _drawXRuler(Canvas canvas){

    var paint = Paint()
      ..isAntiAlias = true
      ..strokeWidth = 1.0
      ..strokeCap = StrokeCap.square
      ..color = Colors.white
      ..style = PaintingStyle.stroke;

    ///临时刻度数量
    int rulerCount=16;
    ///x、y轴方向每个刻度的间距
    double xRulerW = _fixedWidth /rulerCount; //x方向两个点之间的距离(刻度长)
    double yRulerH=_fixedHeight/rulerCount;//y轴方向亮点之间的距离(刻度高)
    for (int i = 1; i <=rulerCount; i++) {

      canvas.drawLine(Offset(startX + xRulerW * i, startY), Offset(startX + xRulerW * i, startY - yRulerH), paint);

      canvas.drawLine(Offset(startX, startY-i*yRulerH), Offset(startX + xRulerW, startY-i*yRulerH), paint);
    }
  }

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

调整刻度线高度或长度

  ///绘制x轴刻度
  void _drawXRuler(Canvas canvas) {
    var paint = Paint()
      ..isAntiAlias = true
      ..strokeWidth = 1.0
      ..strokeCap = StrokeCap.square
      ..color = Colors.white
      ..style = PaintingStyle.stroke;

    ///临时刻度数量
    int rulerCount = 16;
    ///x、y轴方向每个刻度的间距
    double xRulerW = _fixedWidth / rulerCount; //x方向两个点之间的距离(刻度长)
    double yRulerH = _fixedHeight / rulerCount; //y轴方向亮点之间的距离(刻度高)
    ///减小刻度线高度或长度
    double reduceRuler=10.0;
    for (int i = 1; i <= rulerCount; i++) {
      canvas.drawLine(Offset(startX + xRulerW * i, startY), Offset(startX + xRulerW * i, startY - yRulerH+reduceRuler), paint);

     canvas.drawLine(Offset(startX, startY - i * yRulerH), Offset(startX + xRulerW-reduceRuler, startY - i * yRulerH), paint);
    }
  }

绘制xy刻度文本

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

class CharLinePainter extends CustomPainter {
  static const double basePadding = 16; //基础边界

  double startX, endX; //相对于原点x轴方向最小和最大偏移量(相对于原点的偏移量)
  double startY, endY; //相对于原点y轴方向最大和最小偏移量(相对于原点的偏移量)
  double _fixedWidth; //x轴方向:最大偏移量-最小偏移量(相对于原点的偏移量)
  double _fixedHeight; //y轴方向:最大偏移量-最小偏移量(相对于原点的偏移量)

  @override
  void paint(Canvas canvas, Size size) {
    _initBorder(size);
    _drawXy(canvas);
    _drawXYRulerText(canvas);
  }

  /// 初始化边界
  void _initBorder(Size size) {
    startX = basePadding * 2;
    endX = size.width - basePadding * 2;
    startY = size.height - basePadding * 2;
    endY = basePadding * 2;
    _fixedWidth = endX - startX;
    _fixedHeight = startY - endY;
  }

  ///绘制xy轴
  ///绘制x轴-y轴偏移量不变(y轴坐标点不变)
  ///绘制y轴-x轴偏移量不变(x轴坐标点不变)
  void _drawXy(Canvas canvas) {
    var paint = Paint()
      ..isAntiAlias = true
      ..strokeWidth = 1.0
      ..strokeCap = StrokeCap.square
      ..color = Colors.white
      ..style = PaintingStyle.stroke;
    canvas.drawLine(Offset(startX, startY), Offset(endX, startY), paint); //x轴
    canvas.drawLine(Offset(startX, startY), Offset(startX, endY), paint); //y轴
  }

  ///绘制xy轴刻度+文本
  void _drawXYRulerText(Canvas canvas) {
    var paint = Paint()
      ..isAntiAlias = true
      ..strokeWidth = 1.0
      ..strokeCap = StrokeCap.square
      ..color = Colors.white
      ..style = PaintingStyle.stroke;

    ///临时刻度数量
    int rulerCount = 16;

    ///x、y轴方向每个刻度的间距
    double xRulerW = _fixedWidth / rulerCount; //x方向两个点之间的距离(刻度长)
    double yRulerH = _fixedHeight / rulerCount; //y轴方向亮点之间的距离(刻度高)
    ///减小刻度线高度或长度
    double reduceRuler = 10.0;
    for (int i = 1; i <= rulerCount; i++) {
      _drawXRuler(canvas, xRulerW, yRulerH, reduceRuler, i, paint);
      _drawYRuler(canvas, xRulerW, yRulerH, reduceRuler, i, paint);
      _drawXText(canvas, xRulerW, i);
      _drawYText(canvas, yRulerH, i);
    }
  }
  ///绘制x轴刻度
  void _drawXRuler(Canvas canvas, double xRulerW, double yRulerH,
      double reduceRuler, int i, var paint) {
    double drawRulerSx = startX + xRulerW * i;
    double drawRulerSy = startY;
    double drawRulerEx = startX + xRulerW * i;
    double drawRulerEy = startY - yRulerH + reduceRuler;
    canvas.drawLine(Offset(drawRulerSx, drawRulerSy),
        Offset(drawRulerEx, drawRulerEy), paint);
  }

  ///绘制y轴刻度
  void _drawYRuler(Canvas canvas, double xRulerW, double yRulerH,
      double reduceRuler, int i, var paint) {
    double drawRulerSx = startX;
    double drawRulerSy = startY - i * yRulerH;
    double drawRulerEx = startX + xRulerW - reduceRuler;
    double drawRulerEy = startY - i * yRulerH;
    canvas.drawLine(Offset(drawRulerSx, drawRulerSy),
        Offset(drawRulerEx, drawRulerEy), paint);
  }

  ///绘制x轴文本
  void _drawXText(Canvas canvas,double xRulerW,int i) {
    TextPainter(
        textAlign: TextAlign.center,
        ellipsis: '.',
        text: TextSpan(
            text: (i).toString(),
            style: TextStyle(color: Colors.white, fontSize: 12.0)),
        textDirection: TextDirection.ltr)
      ..layout(minWidth: xRulerW, maxWidth: xRulerW)
      ..paint(canvas, Offset(startX + xRulerW * (i - 1) - xRulerW / 2, startY+basePadding/2));
  }

  ///绘制y轴文本
  void _drawYText(Canvas canvas,double yRulerH,int i) {
    TextPainter(
        textAlign: TextAlign.center,
        ellipsis: '.',
        text: TextSpan(
            text: (i-1).toString(),
            style: TextStyle(color: Colors.white, fontSize: 12.0)),
        textDirection: TextDirection.ltr)
      ..layout(minWidth: yRulerH, maxWidth: yRulerH)
      ..paint(canvas, Offset(startX-basePadding/2*3,startY - yRulerH * (i - 1) - yRulerH / 2));
  }

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

绘制折线图

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

class CharLinePainter extends CustomPainter {
  static const double basePadding = 24; //基础边界

  double startX, endX; //相对于原点x轴方向最小和最大偏移量(相对于原点的偏移量)
  double startY, endY; //相对于原点y轴方向最大和最小偏移量(相对于原点的偏移量)
  double _fixedWidth; //x轴方向:最大偏移量-最小偏移量(相对于原点的偏移量)
  double _fixedHeight; //y轴方向:最大偏移量-最小偏移量(相对于原点的偏移量)
  Path _path = new Path();

  @override
  void paint(Canvas canvas, Size size) {
    _initBorder(size);
    _drawXy(canvas);
    _drawXYRulerText(canvas);
    _drawLine(canvas);
  }

  /// 初始化边界
  void _initBorder(Size size) {
    startX = basePadding * 2;
    endX = size.width - basePadding * 2;
    startY = size.height - basePadding * 2;
    endY = basePadding * 2;
    _fixedWidth = endX - startX;
    _fixedHeight = startY - endY;
  }

  ///绘制xy轴
  ///绘制x轴-y轴偏移量不变(y轴坐标点不变)
  ///绘制y轴-x轴偏移量不变(x轴坐标点不变)
  void _drawXy(Canvas canvas) {
    var paint = Paint()
      ..isAntiAlias = true
      ..strokeWidth = 1.0
      ..strokeCap = StrokeCap.square
      ..color = Colors.white
      ..style = PaintingStyle.stroke;
    canvas.drawLine(Offset(startX, startY), Offset(endX, startY), paint); //x轴
    canvas.drawLine(Offset(startX, startY), Offset(startX, endY), paint); //y轴
  }

  ///绘制xy轴刻度+文本
  void _drawXYRulerText(Canvas canvas) {
    var paint = Paint()
      ..isAntiAlias = true
      ..strokeWidth = 1.0
      ..strokeCap = StrokeCap.square
      ..color = Colors.white
      ..style = PaintingStyle.stroke;

    ///临时刻度数量
    int rulerCount = 10;

    ///x、y轴方向每个刻度的间距
    double xRulerW = _fixedWidth / rulerCount; //x方向两个点之间的距离(刻度长)
    double yRulerH = _fixedHeight / rulerCount; //y轴方向亮点之间的距离(刻度高)
    ///减小刻度线高度或长度
    double reduceRuler = 20.0;
    for (int i = 1; i <= rulerCount; i++) {
      _drawXRuler(canvas, xRulerW, yRulerH, reduceRuler, i, paint);
      _drawYRuler(canvas, xRulerW, yRulerH, reduceRuler, i, paint);
      _drawXText(canvas, xRulerW, i);
      _drawYText(canvas, yRulerH, i);
      _initPath(i-1, xRulerW, yRulerH);
    }
  }

  ///绘制x轴刻度
  void _drawXRuler(Canvas canvas, double xRulerW, double yRulerH,
      double reduceRuler, int i, var paint) {
    double drawRulerSx = startX + xRulerW * i;
    double drawRulerSy = startY;
    double drawRulerEx = startX + xRulerW * i;
    double drawRulerEy = startY - yRulerH + reduceRuler;
    canvas.drawLine(Offset(drawRulerSx, drawRulerSy),
        Offset(drawRulerEx, drawRulerEy), paint);
  }

  ///绘制y轴刻度
  void _drawYRuler(Canvas canvas, double xRulerW, double yRulerH,
      double reduceRuler, int i, var paint) {
    double drawRulerSx = startX;
    double drawRulerSy = startY - i * yRulerH;
    double drawRulerEx = startX + xRulerW - reduceRuler;
    double drawRulerEy = startY - i * yRulerH;
    canvas.drawLine(Offset(drawRulerSx, drawRulerSy),
        Offset(drawRulerEx, drawRulerEy), paint);
  }

  ///绘制x轴文本
  void _drawXText(Canvas canvas, double xRulerW, int i) {
    TextPainter(
        textAlign: TextAlign.center,
        ellipsis: '.',
        text: TextSpan(
            text: ((i - 1) * xRulerW).toString(),
            style: TextStyle(color: Colors.white, fontSize: 9.0)),
        textDirection: TextDirection.ltr)
      ..layout(minWidth: xRulerW, maxWidth: xRulerW)
      ..paint(
          canvas,
          Offset(startX + xRulerW * (i - 1) - xRulerW / 2,
              startY + basePadding / 2));
  }

  ///绘制y轴文本
  void _drawYText(Canvas canvas, double yRulerH, int i) {
    TextPainter(
        textAlign: TextAlign.center,
        ellipsis: '.',
        text: TextSpan(
            text: ((i - 1) * yRulerH).toString(),
            style: TextStyle(color: Colors.white, fontSize: 9.0)),
        textDirection: TextDirection.ltr)
      ..layout(minWidth: yRulerH, maxWidth: yRulerH)
      ..paint(
          canvas,
          Offset(startX - basePadding / 2 * 3,
              startY - yRulerH * (i - 1) - yRulerH / 2));
  }

  ///计算path
  void _initPath(int i, double xRulerW, double yRulerH) {
    if (i == 0) {
      var key = startX;
      var value = startY;
      _path.moveTo(key, value);
    } else {
      double currentX = startX + xRulerW * i;
      double currentY = (startY  - (i%2==0?yRulerH:yRulerH*4));
      _path.lineTo(currentX, currentY);
    }
  }

  ///绘制直线
  void _drawLine(canvas) {
    var paint = Paint()
      ..isAntiAlias = true
      ..strokeWidth = 2.0
      ..strokeCap = StrokeCap.round
      ..color = Colors.red
      ..style = PaintingStyle.stroke;

    var pathMetrics = _path.computeMetrics(forceClosed: false);
    var list = pathMetrics.toList();
    var length = list.length.toInt();
    Path linePath = new Path();
    for (int i = 0; i < length; i++) {
      var extractPath = list[i].extractPath(0, list[i].length, startWithMoveTo: true);
      linePath.addPath(extractPath, Offset(0, 0));
    }
    canvas.drawPath(linePath, paint);
  }

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

绘制曲线图

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

class CharLinePainter extends CustomPainter {
  static const double basePadding = 24; //基础边界

  double startX, endX; //相对于原点x轴方向最小和最大偏移量(相对于原点的偏移量)
  double startY, endY; //相对于原点y轴方向最大和最小偏移量(相对于原点的偏移量)
  double _fixedWidth; //x轴方向:最大偏移量-最小偏移量(相对于原点的偏移量)
  double _fixedHeight; //y轴方向:最大偏移量-最小偏移量(相对于原点的偏移量)
  Path _path = new Path();

  @override
  void paint(Canvas canvas, Size size) {
    _initBorder(size);
    _drawXy(canvas);
    _drawXYRulerText(canvas);
    _drawLine(canvas);
  }

  /// 初始化边界
  void _initBorder(Size size) {
    startX = basePadding * 2;
    endX = size.width - basePadding * 2;
    startY = size.height - basePadding * 2;
    endY = basePadding * 2;
    _fixedWidth = endX - startX;
    _fixedHeight = startY - endY;
  }

  ///绘制xy轴
  ///绘制x轴-y轴偏移量不变(y轴坐标点不变)
  ///绘制y轴-x轴偏移量不变(x轴坐标点不变)
  void _drawXy(Canvas canvas) {
    var paint = Paint()
      ..isAntiAlias = true
      ..strokeWidth = 1.0
      ..strokeCap = StrokeCap.square
      ..color = Colors.white
      ..style = PaintingStyle.stroke;
    canvas.drawLine(Offset(startX, startY), Offset(endX, startY), paint); //x轴
    canvas.drawLine(Offset(startX, startY), Offset(startX, endY), paint); //y轴
  }

  ///绘制xy轴刻度+文本
  void _drawXYRulerText(Canvas canvas) {
    var paint = Paint()
      ..isAntiAlias = true
      ..strokeWidth = 1.0
      ..strokeCap = StrokeCap.square
      ..color = Colors.white
      ..style = PaintingStyle.stroke;

    ///临时刻度数量
    int rulerCount = 10;

    ///x、y轴方向每个刻度的间距
    double xRulerW = _fixedWidth / rulerCount; //x方向两个点之间的距离(刻度长)
    double yRulerH = _fixedHeight / rulerCount; //y轴方向亮点之间的距离(刻度高)
    ///减小刻度线高度或长度
    double reduceRuler = 20.0;
    for (int i = 1; i <= rulerCount; i++) {
      _drawXRuler(canvas, xRulerW, yRulerH, reduceRuler, i, paint);
      _drawYRuler(canvas, xRulerW, yRulerH, reduceRuler, i, paint);
      _drawXText(canvas, xRulerW, i);
      _drawYText(canvas, yRulerH, i);
      _initCurvePath(i-1, xRulerW, yRulerH);
    }
  }

  ///绘制x轴刻度
  void _drawXRuler(Canvas canvas, double xRulerW, double yRulerH,
      double reduceRuler, int i, var paint) {
    double drawRulerSx = startX + xRulerW * i;
    double drawRulerSy = startY;
    double drawRulerEx = startX + xRulerW * i;
    double drawRulerEy = startY - yRulerH + reduceRuler;
    canvas.drawLine(Offset(drawRulerSx, drawRulerSy),
        Offset(drawRulerEx, drawRulerEy), paint);
  }

  ///绘制y轴刻度
  void _drawYRuler(Canvas canvas, double xRulerW, double yRulerH,
      double reduceRuler, int i, var paint) {
    double drawRulerSx = startX;
    double drawRulerSy = startY - i * yRulerH;
    double drawRulerEx = startX + xRulerW - reduceRuler;
    double drawRulerEy = startY - i * yRulerH;
    canvas.drawLine(Offset(drawRulerSx, drawRulerSy),
        Offset(drawRulerEx, drawRulerEy), paint);
  }

  ///绘制x轴文本
  void _drawXText(Canvas canvas, double xRulerW, int i) {
    TextPainter(
        textAlign: TextAlign.center,
        ellipsis: '.',
        text: TextSpan(
            text: ((i - 1) * xRulerW).toString(),
            style: TextStyle(color: Colors.white, fontSize: 9.0)),
        textDirection: TextDirection.ltr)
      ..layout(minWidth: xRulerW, maxWidth: xRulerW)
      ..paint(
          canvas,
          Offset(startX + xRulerW * (i - 1) - xRulerW / 2,
              startY + basePadding / 2));
  }

  ///绘制y轴文本
  void _drawYText(Canvas canvas, double yRulerH, int i) {
    TextPainter(
        textAlign: TextAlign.center,
        ellipsis: '.',
        text: TextSpan(
            text: ((i - 1) * yRulerH).toString(),
            style: TextStyle(color: Colors.white, fontSize: 9.0)),
        textDirection: TextDirection.ltr)
      ..layout(minWidth: yRulerH, maxWidth: yRulerH)
      ..paint(
          canvas,
          Offset(startX - basePadding / 2 * 3,
              startY - yRulerH * (i - 1) - yRulerH / 2));
  }
  
  ///计算曲线path
  void _initCurvePath(int i, double xRulerW, double yRulerH) {
    if (i == 0) {
      var key = startX;
      var value = startY;
      _path.moveTo(key, value);
    } else {
      double preX = startX + xRulerW * (i-1);
      double preY = (startY  - (i%2!=0?yRulerH:yRulerH*6));
      double currentX = startX + xRulerW * i;
      double currentY = (startY  - (i%2==0?yRulerH:yRulerH*6));

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

  ///绘制直线或曲线
  void _drawLine(canvas) {
    var paint = Paint()
      ..isAntiAlias = true
      ..strokeWidth = 2.0
      ..strokeCap = StrokeCap.round
      ..color = Colors.red
      ..style = PaintingStyle.stroke;

    var pathMetrics = _path.computeMetrics(forceClosed: false);
    var list = pathMetrics.toList();
    var length = list.length.toInt();
    Path linePath = new Path();
    for (int i = 0; i < length; i++) {
      var extractPath = list[i].extractPath(0, list[i].length, startWithMoveTo: true);
      linePath.addPath(extractPath, Offset(0, 0));
    }
    canvas.drawPath(linePath, paint);
  }


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

绘制阴影

class CharLinePainter extends CustomPainter {
  static const double basePadding = 24; //基础边界

  double startX, endX; //相对于原点x轴方向最小和最大偏移量(相对于原点的偏移量)
  double startY, endY; //相对于原点y轴方向最大和最小偏移量(相对于原点的偏移量)
  double _fixedWidth; //x轴方向:最大偏移量-最小偏移量(相对于原点的偏移量)
  double _fixedHeight; //y轴方向:最大偏移量-最小偏移量(相对于原点的偏移量)
  Path _path = new Path();

  @override
  void paint(Canvas canvas, Size size) {
    _initBorder(size);
    _drawXy(canvas);
    _drawXYRulerText(canvas);
    _drawLine(canvas);
  }

  /// 初始化边界
  void _initBorder(Size size) {
    startX = basePadding * 2;
    endX = size.width - basePadding * 2;
    startY = size.height - basePadding * 2;
    endY = basePadding * 2;
    _fixedWidth = endX - startX;
    _fixedHeight = startY - endY;
  }

  ///绘制xy轴
  ///绘制x轴-y轴偏移量不变(y轴坐标点不变)
  ///绘制y轴-x轴偏移量不变(x轴坐标点不变)
  void _drawXy(Canvas canvas) {
    var paint = Paint()
      ..isAntiAlias = true
      ..strokeWidth = 1.0
      ..strokeCap = StrokeCap.square
      ..color = Colors.white
      ..style = PaintingStyle.stroke;
    canvas.drawLine(Offset(startX, startY), Offset(endX, startY), paint); //x轴
    canvas.drawLine(Offset(startX, startY), Offset(startX, endY), paint); //y轴
  }

  ///绘制xy轴刻度+文本
  void _drawXYRulerText(Canvas canvas) {
    var paint = Paint()
      ..isAntiAlias = true
      ..strokeWidth = 1.0
      ..strokeCap = StrokeCap.square
      ..color = Colors.white
      ..style = PaintingStyle.stroke;

    ///临时刻度数量
    int rulerCount = 10;

    ///x、y轴方向每个刻度的间距
    double xRulerW = _fixedWidth / rulerCount; //x方向两个点之间的距离(刻度长)
    double yRulerH = _fixedHeight / rulerCount; //y轴方向亮点之间的距离(刻度高)
    ///减小刻度线高度或长度
    double reduceRuler = 20.0;
    for (int i = 1; i <= rulerCount; i++) {
      _drawXRuler(canvas, xRulerW, yRulerH, reduceRuler, i, paint);
      _drawYRuler(canvas, xRulerW, yRulerH, reduceRuler, i, paint);
      _drawXText(canvas, xRulerW, i);
      _drawYText(canvas, yRulerH, i);
      _initCurvePath(i - 1, xRulerW, yRulerH);
    }
  }

  ///绘制x轴刻度
  void _drawXRuler(Canvas canvas, double xRulerW, double yRulerH,
      double reduceRuler, int i, var paint) {
    double drawRulerSx = startX + xRulerW * i;
    double drawRulerSy = startY;
    double drawRulerEx = startX + xRulerW * i;
    double drawRulerEy = startY - yRulerH + reduceRuler;
    canvas.drawLine(Offset(drawRulerSx, drawRulerSy),
        Offset(drawRulerEx, drawRulerEy), paint);
  }

  ///绘制y轴刻度
  void _drawYRuler(Canvas canvas, double xRulerW, double yRulerH,
      double reduceRuler, int i, var paint) {
    double drawRulerSx = startX;
    double drawRulerSy = startY - i * yRulerH;
    double drawRulerEx = startX + xRulerW - reduceRuler;
    double drawRulerEy = startY - i * yRulerH;
    canvas.drawLine(Offset(drawRulerSx, drawRulerSy),
        Offset(drawRulerEx, drawRulerEy), paint);
  }

  ///绘制x轴文本
  void _drawXText(Canvas canvas, double xRulerW, int i) {
    TextPainter(
        textAlign: TextAlign.center,
        ellipsis: '.',
        text: TextSpan(
            text: ((i - 1) * xRulerW).toString(),
            style: TextStyle(color: Colors.white, fontSize: 9.0)),
        textDirection: TextDirection.ltr)
      ..layout(minWidth: xRulerW, maxWidth: xRulerW)
      ..paint(
          canvas,
          Offset(startX + xRulerW * (i - 1) - xRulerW / 2,
              startY + basePadding / 2));
  }

  ///绘制y轴文本
  void _drawYText(Canvas canvas, double yRulerH, int i) {
    TextPainter(
        textAlign: TextAlign.center,
        ellipsis: '.',
        text: TextSpan(
            text: ((i - 1) * yRulerH).toString(),
            style: TextStyle(color: Colors.white, fontSize: 9.0)),
        textDirection: TextDirection.ltr)
      ..layout(minWidth: yRulerH, maxWidth: yRulerH)
      ..paint(
          canvas,
          Offset(startX - basePadding / 2 * 3,
              startY - yRulerH * (i - 1) - yRulerH / 2));
  }

  ///计算曲线path
  void _initCurvePath(int i, double xRulerW, double yRulerH) {
    if (i == 0) {
      var key = startX;
      var value = startY;
      _path.moveTo(key, value);
    } else {
      double preX = startX + xRulerW * i;
      double preY = (startY - (i % 2 != 0 ? yRulerH : yRulerH * 6));
      double currentX = startX + xRulerW * (i+1);
      double currentY = (startY - (i % 2 == 0 ? yRulerH : yRulerH * 6));

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

  ///绘制直线或曲线
  void _drawLine(canvas) {
    var paint = Paint()
      ..isAntiAlias = true
      ..strokeWidth = 2.0
      ..strokeCap = StrokeCap.round
      ..color = Colors.red
      ..style = PaintingStyle.stroke;

    var pathMetrics = _path.computeMetrics(forceClosed: false);
    var list = pathMetrics.toList();
    var length = list.length.toInt();
    Path linePath = new Path();
    Path shadowPath = new Path();
    for (int i = 0; i < length; i++) {
      var extractPath = list[i].extractPath(0, list[i].length, startWithMoveTo: true);
      shadowPath = extractPath;
      linePath.addPath(extractPath, Offset(0, 0));
    }
    _drawShader(canvas, shadowPath);
    canvas.drawPath(linePath, paint);
  }

  ///绘制阴影
  void _drawShader(canvas, shadowPath) {
    var shader = LinearGradient(
        begin: Alignment.topCenter,
        end: Alignment.bottomCenter,
        tileMode: TileMode.repeated,
        colors: [
          Colors.deepPurple.withOpacity(0.5),
          Colors.red.withOpacity(0.5),
          Colors.cyan.withOpacity(0.5),
          Colors.deepPurpleAccent.withOpacity(0.5),
        ]).createShader(Rect.fromLTRB(startX, endY, startX, startY));

    ///从path的最后一个点连接起始点,形成一个闭环
    shadowPath
      ..lineTo(startX + _fixedWidth, startY)
      ..lineTo(startX, startY)
      ..close();

    canvas
      ..drawPath(
          shadowPath,
          new Paint()
            ..shader = shader
            ..isAntiAlias = true
            ..style = PaintingStyle.fill);
  }

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

Flutter 曲线图案例下载

猜你喜欢

转载自blog.csdn.net/u013491829/article/details/108315927