123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469 |
- import 'dart:math';
- import 'dart:ui' as ui;
- import 'dart:ui';
- import 'package:flutter/cupertino.dart';
- import 'package:flutter/material.dart';
- import 'package:sport/services/Converter.dart';
- const WEEK = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
- class ChartItem {
- final String createdAt;
- final num value;
- ChartItem(this.createdAt, this.value);
- }
- class Chart extends CustomPainter {
- final Paint _paint = Paint()
- ..color = const Color(0xffDCDCDC)
- ..strokeWidth = 0.5
- ..isAntiAlias = true;
- final Paint _paintCurrent = Paint()
- ..color = const Color(0xffFFC400)
- ..strokeWidth = 0.5
- ..isAntiAlias = true;
- static const Color _maxColor = Color(0xffFF5B1D);
- final Paint _maxPaint = Paint()
- ..color = _maxColor
- ..strokeWidth = 0.5
- ..isAntiAlias = true;
- static const double fontSize = 12;
- final ParagraphStyle _labelStyle = ParagraphStyle(
- textAlign: TextAlign.left,
- fontSize: fontSize,
- );
- final ParagraphStyle _valueStyle = ParagraphStyle(
- textAlign: TextAlign.right,
- fontSize: fontSize,
- );
- final Paint columnPaint = Paint()
- ..color = const Color(0xffFFC400)
- ..isAntiAlias = true;
- double _zero = 0;
- double _paddingLeft = 40;
- double _paddingRight = 30;
- double _labelHeight = 30;
- int _valueSize = 7;
- int _valueSplit = 200;
- int type;
- int decimal;
- List<ChartItem>? records;
- DateTime dateTime;
- late Map<String, num> values;
- bool drawMax = false;
- bool drawCurrent = false;
- String unit = "kal";
- double _max = 750;
- int _currentIndex = 0;
- double dx, dy;
- Chart(
- {this.type = 0,
- this.records,
- required this.dateTime,
- this.drawMax = false,
- this.drawCurrent = true,
- this.unit = "",
- this.decimal = 0,
- this.dx = 0,
- this.dy = 0});
- void initData(
- {double maxValue = 750, int valueSize = 5, int valueSplit = 200}) {
- _valueSize = valueSize;
- _valueSplit = valueSplit;
- values = {};
- var records = this.records ?? [];
- var now = DateTime.now();
- _currentIndex = -1;
- // print("$values --- $records");
- if (this.type == 0) {
- records.forEach((element) {
- var t = DateTime.parse(element.createdAt);
- values.update("${t.hour}", (value) => value + element.value,
- ifAbsent: () => Converter.toDouble(element.value));
- });
- for (int i = 0; i <= 24; i++) {
- values.putIfAbsent("$i", () => 0.0);
- }
- _currentIndex = -1;
- } else if (this.type == 1) {
- records.forEach((element) {
- var t = DateTime.parse(element.createdAt);
- values.update("${t.weekday == 0 ? 7 : t.weekday}",
- (value) => value + element.value,
- ifAbsent: () => Converter.toDouble(element.value));
- });
- for (int i = 1; i <= 7; i++) {
- values.putIfAbsent("$i", () => 0.0);
- }
- if (now.difference(dateTime).inDays < 7)
- _currentIndex = (now.weekday == 0 ? 7 : now.weekday) - 1;
- } else if (this.type == 2) {
- records.forEach((element) {
- var t = DateTime.parse(element.createdAt);
- values.update("${t.day}", (value) => value + element.value,
- ifAbsent: () => Converter.toDouble(element.value));
- });
- for (int i = 1; i <= 31; i++) {
- values.putIfAbsent("$i", () => 0.0);
- }
- if (now.difference(dateTime).inDays < 28) _currentIndex = now.day - 1;
- } else if (this.type == 3) {
- records.forEach((element) {
- values.update("${element.createdAt}", (value) => value + element.value,
- ifAbsent: () => Converter.toDouble(element.value));
- });
- for (int i = 1; i <= 12; i++) {
- values.putIfAbsent("$i", () => 0.0);
- }
- if (now.difference(dateTime).inDays < 360) _currentIndex = now.month - 1;
- } else if (this.type == 4) {
- records.forEach((element) {
- values.update("${element.createdAt}", (value) => value + element.value,
- ifAbsent: () => Converter.toDouble(element.value));
- });
- for (int i = 1; i <= 10; i++) {
- values.putIfAbsent("${now.year - i + 1}", () => 0.0);
- }
- _currentIndex = -1;
- }
- _max = 0;
- values.values.forEach((element) {
- _max = max(_max, Converter.toDouble(element));
- });
- double diff = _max % _valueSplit;
- double round = _max - diff;
- print("--- $_max $round $diff");
- _max = max(_valueSplit.toDouble(), _max - diff + _valueSplit);
- print("$values --- $_max $maxValue $diff");
- }
- @override
- void paint(Canvas canvas, Size size) {
- double zero = _zero = size.height - _labelHeight;
- double right = size.width - _paddingRight;
- // draw 值 行数
- int valuePadding = 10;
- canvas.drawLine(Offset(_paddingLeft, zero), Offset(right, zero), _paint);
- ParagraphBuilder pb = ParagraphBuilder(_valueStyle)
- ..pushStyle(ui.TextStyle(color: Color(0xff999999)))
- ..addText("0");
- ParagraphConstraints constraints =
- ParagraphConstraints(width: _paddingLeft - valuePadding);
- Paragraph paragraph = pb.build()..layout(constraints);
- paragraph.computeLineMetrics().forEach((element) {
- canvas.drawParagraph(paragraph, Offset(0, zero - element.baseline / 2));
- });
- canvas.drawLine(
- Offset(_paddingLeft, zero), Offset(_paddingLeft, 0), _paint);
- canvas.drawLine(Offset(right, zero), Offset(right, 0), _paint);
- double valueSpace = (size.height - _labelHeight) / _valueSize;
- for (var i = 0; i < _valueSize; i++) {
- canvas.drawLine(Offset(_paddingLeft, valueSpace * i),
- Offset(right, valueSpace * i), _paint);
- double value = (_valueSize - i) * _max / _valueSize;
- String label = "";
- if (value < 1000) {
- label = value == value.toInt()
- ? value.toInt().toString()
- : value.toStringAsFixed(1);
- } else {
- value /= 1000;
- label =
- "${value == value.toInt() ? value.toInt().toString() : value.toStringAsFixed(1)}k";
- }
- ParagraphBuilder pb = ParagraphBuilder(_valueStyle)
- ..pushStyle(ui.TextStyle(color: Color(0xff999999)))
- ..addText(label);
- ParagraphConstraints constraints =
- ParagraphConstraints(width: _paddingLeft - valuePadding);
- Paragraph paragraph = pb.build()..layout(constraints);
- paragraph.computeLineMetrics().forEach((element) {
- canvas.drawParagraph(
- paragraph, Offset(0, valueSpace * i - element.baseline / 2));
- });
- // print("${value / 1000} ${_max} ${(_valueSize - i) * _max ~/ _valueSize}");
- }
- // draw 数据列
- double width = right - _paddingRight; // 柱子的有效空间
- double valueStroke = 8; // 柱子宽度
- List<String> valueLabel = []; // 柱子数量
- int scaleCount = 0; // 刻度数量
- int scaleWeight = 3;
- if (this.type == 0) {
- // 日,按时间
- scaleWeight = 6;
- valueLabel = List.generate(4, (index) => "${index * scaleWeight}:00");
- // valueStroke = width / valueLabel.length / scaleWeight - 5;
- scaleCount = 24;
- } else if (this.type == 1) {
- width -= 40;
- scaleWeight = 1;
- valueLabel = List.generate(7, (index) => WEEK[index]);
- // valueStroke = 12;
- scaleCount = 7;
- } else if (this.type == 2) {
- DateTime now = this.dateTime;
- // DateTime now = DateTime(2019,2);
- DateTime startTime = DateTime.parse(
- '${now.year}-${now.month < 10 ? '0${now.month}' : now.month}-01');
- DateTime endTime = DateTime.parse(
- '${now.year}-${now.month + 1 < 10 ? '0${now.month + 1}' : now.month + 1}-01');
- int diffDay = endTime.difference(startTime).inDays;
- // print("11111111111111111111111111111 $startTime $endTime $diffDay");
- // List<int> days = [1, 8, 15, 22, diffDay == 28 ? 28:29];
- scaleCount = diffDay;
- scaleWeight = 5;
- // valueLabel = days.map((e) => '$e/${now.month}').toList();
- // valueLabel = List.generate(diffDay ~/ scaleWeight + (scaleCount % scaleWeight == 0 ? 1 : 0), (index) => "${index * 2 + 1}");
- valueLabel = List.generate(
- scaleCount ~/ scaleWeight + 1, (index) => '${index * scaleWeight}');
- valueStroke = 5;
- } else if (this.type == 3) {
- valueLabel = List.generate(12, (index) => '${index + 1}');
- // valueStroke = 12;
- scaleCount = 12;
- scaleWeight = 1;
- } else if (this.type == 4) {
- int len = 6;
- var now = DateTime.now();
- int start = now.year - len;
- valueLabel = List.generate(len, (index) => '${start + index + 1}');
- // valueStroke = 12;
- scaleCount = len;
- scaleWeight = 1;
- }
- int labelSize = valueLabel.length;
- double labelSpace = (width) / labelSize;
- double scaleWidth = (right - _paddingLeft) / (scaleCount + 1);
- // print("${size.width} $_paddingLeft $right $width $scaleWidth $scaleCount");
- double left = scaleWidth + _paddingLeft;
- if (type == 2) {
- left = _paddingLeft;
- }
- // if (this.type < 2) {
- //draw 刻度
- for (var i = 0; i < scaleCount; i++) {
- var index = scaleWidth * (i + 1);
- canvas.drawLine(Offset(_paddingLeft + index, zero),
- Offset(_paddingLeft + index, zero - 5), _paint);
- }
- // }
- for (var i = 0; i < labelSize; i++) {
- int index = i;
- String label = valueLabel[i];
- bool offset = false;
- if (label == "0") {
- label = "1";
- offset = true;
- }
- // if (type == 2 && i % 2 == 1) {
- // if (i != _currentIndex) continue;
- // }
- // print("1111111111111111111 $i $_currentIndex $label");
- bool curr = i == _currentIndex && drawCurrent == true;
- ParagraphBuilder pb = ParagraphBuilder(_labelStyle)
- ..pushStyle(ui.TextStyle(
- color: curr ? const Color(0xffFFC400) : const Color(0xff999999)))
- ..addText(label);
- ParagraphConstraints constraints =
- ParagraphConstraints(width: labelSpace);
- Paragraph paragraph = pb.build()..layout(constraints);
- List<LineMetrics> lines = paragraph.computeLineMetrics();
- for (var line = 0; line < lines.length; line++) {
- LineMetrics element = lines[line];
- var x = left + scaleWidth * scaleWeight * index - element.width / 2;
- if (offset) {
- x += scaleWidth;
- }
- var y = zero + 5;
- canvas.drawParagraph(paragraph, Offset(x, y));
- // if (curr) {
- // x = x + element.width / 2;
- // canvas.drawPath(
- // Path()
- // ..moveTo(x, y + fontSize + 5)
- // ..lineTo(x - 3, y + fontSize + 8)
- // ..lineTo(x + 3, y + fontSize + 8)
- // ..close(),
- // _paintCurrent);
- // }
- break;
- }
- if (type == 2) {}
- //
- // double l = left + scaleWidth * scaleWeight * i - valueStroke / 2;
- // Rect rect = Rect.fromLTRB(l, zero, l + valueStroke, 0);
- // print("value draw $rect");
- // Paint valuePaint = Paint()
- // ..shader = LinearGradient(
- // begin: Alignment.bottomCenter,
- // end: Alignment.topCenter,
- // colors: <Color>[Color(0xffFF9100), Color(0xffFFE600)],
- // ).createShader(rect);
- // canvas.drawRRect(RRect.fromRectAndRadius(rect, Radius.circular(100)), valuePaint);
- }
- num maxVal = values.values
- .reduce((value, element) => max(value, element).toDouble());
- bool _drawMax = this.drawMax;
- // if (this.type < 2) {
- for (var layer = 0; layer < 3; layer++) {
- for (var i = 0; i < scaleCount; i++) {
- String key = type == 4
- ? valueLabel[i]
- : (type == 0)
- ? '$i'
- : '${i + 1}';
- if (!values.containsKey(key)) continue;
- num value = values[key]!;
- double l = left + scaleWidth * i - valueStroke / 2 + ((type==2)?scaleWidth:0);
- // print("$key ${values[key]} 11111111111111 $scaleCount");
- Rect rect = Rect.fromLTRB(l, calValue(value), l + valueStroke, zero);
- if(layer == 0) {
- Paint valuePaint = Paint()
- ..shader = LinearGradient(
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
- colors: <Color>[Color(0xffFF9100), Color(0xffFFE600)],
- ).createShader(rect);
- canvas.drawRRect(
- RRect.fromRectAndRadius(rect, Radius.circular(100)), valuePaint);
- }
- // canvas.drawRRect(RRect.fromRectAndRadius(rect, Radius.circular(100)), columnPaint);
- String _label =
- "${this.decimal == 0 ? value.round() : value.toStringAsFixed(this.decimal)}${this.unit}";
- if (layer == 1) {
- double _dx = dx;
- if (rect.contains((ui.Offset(_dx, dy)))) {
- _drawMax = false;
- double width = 140;
- ParagraphBuilder unit = ParagraphBuilder(_labelStyle)
- ..pushStyle(ui.TextStyle(color: Colors.white))
- ..addText("$_label");
- ParagraphConstraints pc = ParagraphConstraints(width: width);
- paragraph = unit.build()..layout(pc);
- paragraph.computeLineMetrics().forEach((element) {
- Rect tips = Rect.fromCenter(
- center: Offset(rect.center.dx, rect.top - element.height * 1.5),
- width: element.width + 12,
- height: element.height * 2);
- Paint bgPaint = Paint()..color = Colors.black87;
- Path path = Path();
- path.addRRect(RRect.fromRectAndRadius(tips, Radius.circular(6)));
- path.moveTo(rect.center.dx, tips.bottom + 5);
- path.lineTo(rect.center.dx - 5, tips.bottom);
- path.lineTo(rect.center.dx + 5, tips.bottom);
- canvas.drawPath(path, bgPaint);
- var x = l + valueStroke / 2 - element.width / 2;
- canvas.drawParagraph(paragraph,
- Offset(x, tips.center.dy - element.height / 2 + 1));
- });
- }
- }
- if (layer == 2 && _drawMax) {
- if (maxVal <= value) {
- if (maxVal > 0) {
- double offsetY = calValue(maxVal);
- double offsetX = _paddingLeft;
- while (offsetX < right) {
- canvas.drawLine(Offset(offsetX, offsetY),
- Offset(min(offsetX + 3, right), offsetY), _maxPaint);
- offsetX += 5;
- }
- ParagraphBuilder unit = ParagraphBuilder(_labelStyle)
- ..pushStyle(ui.TextStyle(color: _maxColor))
- ..addText("$_label");
- ParagraphConstraints pc = ParagraphConstraints(width: size.width);
- paragraph = unit.build()..layout(pc);
- paragraph.computeLineMetrics().forEach((element) {
- var x = l + valueStroke / 2 - element.width / 2;
- canvas.drawParagraph(
- paragraph, Offset(x, offsetY - element.baseline - 10));
- x = l + valueStroke / 2;
- canvas.drawPath(
- Path()
- ..moveTo(x, offsetY - 4)
- ..lineTo(x - 3, offsetY - 7)
- ..lineTo(x + 3, offsetY - 7)
- ..close(),
- _maxPaint);
- });
- }
- }
- }
- }
- }
- // } else {
- // Path path = Path();
- // path.moveTo(_paddingLeft, calValue(values['1']));
- // Path area = Path()
- // ..moveTo(_paddingLeft, zero)
- // ..lineTo(_paddingLeft, calValue(values['1']));
- // for (var i = 1; i <= scaleCount; i++) {
- // path.lineTo(left + scaleWidth * i, calValue(values['$i']));
- // area.lineTo(left + scaleWidth * i, calValue(values['$i']));
- // }
- // area.lineTo(size.width, zero);
- // area.close();
- //
- // canvas.save();
- // canvas.clipPath(area);
- // Rect rect = Rect.fromLTRB(_paddingLeft, zero, size.width, calValue(_max));
- // Paint valuePaint = Paint()
- // ..shader = LinearGradient(
- // begin: Alignment.bottomCenter,
- // end: Alignment.topCenter,
- // colors: <Color>[Color(0xffFFC400), Color(0x50FFFFFF)],
- // ).createShader(rect);
- // canvas.drawRect(rect, valuePaint);
- // canvas.restore();
- //
- // canvas.drawPath(path, _linePaint);
- // }
- }
- double calValue(num value) {
- if (value == null) return _zero;
- return max(0, _zero - _zero * value / _max);
- }
- @override
- bool shouldRepaint(CustomPainter oldDelegate) {
- return false;
- }
- }
|