chart.dart 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. import 'dart:math';
  2. import 'dart:ui' as ui;
  3. import 'package:flutter/material.dart';
  4. import 'package:sport/pages/run/run_page.dart';
  5. class RunChart extends StatelessWidget {
  6. final List<double> values;
  7. final bool gradient;
  8. const RunChart({Key? key, required this.values, required this.gradient}) : super(key: key);
  9. @override
  10. Widget build(BuildContext context) {
  11. return Padding(
  12. padding: const EdgeInsets.only(top: 24.0),
  13. child: CustomPaint(
  14. painter: _Chart(values: this.values, gradient: this.gradient),
  15. child: Container(
  16. height: 150.0,
  17. ),
  18. ),
  19. );
  20. }
  21. }
  22. class _Chart extends CustomPainter {
  23. final List<double> values;
  24. final bool gradient;
  25. _Chart({
  26. required this.values,
  27. this.gradient = false,
  28. });
  29. final Paint _paint = Paint()
  30. ..color = const Color(0xffDCDCDC)
  31. ..strokeWidth = 0.5
  32. ..isAntiAlias = true;
  33. final Paint _dataPaint = Paint()
  34. ..color = const Color(0xffFFC400)
  35. ..strokeWidth = 2
  36. ..style = PaintingStyle.stroke
  37. ..isAntiAlias = true;
  38. final ui.ParagraphStyle _valueStyle = ui.ParagraphStyle(
  39. textAlign: TextAlign.right,
  40. fontSize: 8,
  41. );
  42. final ui.ParagraphStyle _labelStyle = ui.ParagraphStyle(
  43. textAlign: TextAlign.left,
  44. fontSize: 8,
  45. );
  46. final Path path = Path();
  47. double _zero = 0;
  48. double _max = 0;
  49. double _min = 0;
  50. double _paddingLeft = 30;
  51. double _paddingRight = 10;
  52. double _labelHeight = 30;
  53. double _total = 0;
  54. @override
  55. void paint(Canvas canvas, Size size) {
  56. double zero = _zero = size.height - _labelHeight;
  57. double zeroX = _paddingLeft;
  58. if (values.isEmpty == true) {
  59. _min = 0;
  60. _max = 100;
  61. } else {
  62. _max = 0;
  63. values.forEach((element) {
  64. _max = max(_max, element);
  65. _min = min(_min, element);
  66. });
  67. _max = _min.abs() + _max.abs();
  68. }
  69. int count = 4;
  70. double split = _max / count;
  71. int length = split.round().toString().length;
  72. int one = int.parse("1" + List.filled(max(0, length - 2), 0).join(""));
  73. int p = one;
  74. while (p < split) {
  75. p += one;
  76. }
  77. // print("max $max $p $split");
  78. List<double> yAxis = List.generate(count + 1, (index) => _min + index * p.toDouble());
  79. // print("1111111111111111 values = $values $_max $split $length $p $yAxis");
  80. // print("1111111111111111 max = $_max $split $length $p $yAxis");
  81. _total = _min < 0 ? yAxis.last * 2 : yAxis.last;
  82. // draw 值 行数
  83. int valuePadding = 10;
  84. // ui.ParagraphBuilder pb = ui.ParagraphBuilder(_valueStyle)
  85. // ..pushStyle(ui.TextStyle(color: Color(0xff999999)))
  86. // ..addText("0");
  87. // ui.ParagraphConstraints constraints = ui.ParagraphConstraints(width: _paddingLeft - valuePadding);
  88. // ui.Paragraph paragraph = pb.build()..layout(constraints);
  89. // paragraph.computeLineMetrics().forEach((element) {
  90. // canvas.drawParagraph(paragraph, Offset(0, zero - element.baseline / 2));
  91. // });
  92. // canvas.drawLine(Offset(zeroX, zero), Offset(size.width, zero), _paint);
  93. canvas.drawLine(Offset(zeroX, zero), Offset(zeroX, 0), _paint);
  94. double rowHeight = zero / (yAxis.length - 1);
  95. // double valueSpace = (size.height - _labelHeight) / valueSize;
  96. // print("11111111111111 height ${size.height} $_labelHeight ${size.height - _labelHeight}");
  97. for (var i = 0; i < yAxis.length; i++) {
  98. // canvas.drawLine(Offset(_paddingLeft, valueSpace * i), Offset(size.width, valueSpace * i), _paint);
  99. double dy = zero - i * rowHeight;
  100. var value = yAxis[i];
  101. var label = value.toInt();
  102. if (i == 0) canvas.drawLine(Offset(zeroX, dy), Offset(size.width, dy), _paint);
  103. // print("11111111111111 value ${(_total) ~/ valueSize} ${(valueSize - i)} $value i $i y $y ${i * (_total) ~/ valueSize}");
  104. ui.ParagraphBuilder pb = ui.ParagraphBuilder(_valueStyle)
  105. ..pushStyle(ui.TextStyle(color: Color(0xff999999)))
  106. ..addText(label.abs() < 1000 ? "$label" : "${formatNum(label / 1000, 1)}k");
  107. ui.ParagraphConstraints constraints = ui.ParagraphConstraints(width: _paddingLeft - valuePadding);
  108. ui.Paragraph paragraph = pb.build()..layout(constraints);
  109. paragraph.computeLineMetrics().forEach((element) {
  110. canvas.drawParagraph(paragraph, Offset(0, dy - element.baseline / 2));
  111. });
  112. }
  113. if (values.length > 0) {
  114. int orgLength = values.length;
  115. int difflength = orgLength % 10;
  116. int length = max(orgLength, orgLength - difflength + (difflength == 0 ? 0 : 10));
  117. // print("222222222222222222 $orgLength $length $difflength");
  118. int maxGrid = length ~/ 10;
  119. final int grid = max(maxGrid, length ~/ maxGrid);
  120. double preY = calValue(values[0] + (_min < 0 ? _max : 0));
  121. double _x = zeroX;
  122. double preX = _x;
  123. path.reset();
  124. if (this.gradient == true) {
  125. path.moveTo(_x, zero);
  126. path.lineTo(_x, preY);
  127. } else {
  128. path.moveTo(_x, preY);
  129. }
  130. // path.moveTo(preX, preY);
  131. double space = (size.width - _x) / length;
  132. for (int i = 1; i < values.length; i++) {
  133. int index = i + 1;
  134. double x = _x + index * space;
  135. // if (index % grid == 0 || index == values.length) {
  136. double y = calValue(values[i] + (_min < 0 ? _max : 0));
  137. // print("11111111111111 y $y $preY ${values[i]} zero: $zero");
  138. double controlX = (preX + x) / 2;
  139. path.cubicTo(controlX, preY, controlX, y, x, y);
  140. // path.lineTo(x, y);
  141. //
  142. // path.lineTo(x, y);
  143. preX = x;
  144. preY = y;
  145. // }
  146. if (index % grid == 0) {
  147. ui.ParagraphBuilder pb = ui.ParagraphBuilder(_labelStyle)
  148. ..pushStyle(ui.TextStyle(color: const Color(0xff999999)))
  149. ..addText("$index");
  150. ui.ParagraphConstraints constraints = ui.ParagraphConstraints(width: 20.0);
  151. ui.Paragraph paragraph = pb.build()..layout(constraints);
  152. paragraph.computeLineMetrics().forEach((element) {
  153. var lx = x - element.width / 2;
  154. var y = zero + 5;
  155. canvas.drawParagraph(paragraph, Offset(lx, y));
  156. // canvas.drawLine(Offset(x, zero), Offset(x, 0), _paint);
  157. });
  158. }
  159. }
  160. if (values.length % grid != 0) {
  161. ui.ParagraphBuilder pb = ui.ParagraphBuilder(_labelStyle)
  162. ..pushStyle(ui.TextStyle(color: const Color(0xff999999)))
  163. ..addText("${values.length + grid - values.length % grid}");
  164. ui.ParagraphConstraints constraints = ui.ParagraphConstraints(width: 20.0);
  165. ui.Paragraph paragraph = pb.build()..layout(constraints);
  166. paragraph.computeLineMetrics().forEach((element) {
  167. var x = size.width - element.width / 2;
  168. var y = zero + 5;
  169. canvas.drawParagraph(paragraph, Offset(x, y));
  170. // canvas.drawLine(Offset(size.width, zero), Offset(size.width, 0), _paint);
  171. });
  172. }
  173. if (this.gradient == true) {
  174. double x = preX;
  175. double y = zero;
  176. double controlX = (preX + x) / 2;
  177. path.cubicTo(controlX, preY, controlX, y, x, y);
  178. Paint valuePaint = Paint()
  179. ..shader = LinearGradient(
  180. begin: Alignment.bottomCenter,
  181. end: Alignment.topCenter,
  182. colors: <Color>[Color(0x30FFC400), Color(0xccFFC400)],
  183. ).createShader(path.getBounds());
  184. canvas.drawPath(path, valuePaint);
  185. } else {
  186. canvas.drawPath(path, _dataPaint);
  187. }
  188. }
  189. }
  190. @override
  191. bool shouldRepaint(_Chart oldDelegate) => false;
  192. double calValue(num value) {
  193. if (value == null) return _zero;
  194. if (_total == 0) return _zero;
  195. return max(0, _zero - _zero * value / _total);
  196. }
  197. }