chart.dart 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. import 'dart:math';
  2. import 'dart:ui' as ui;
  3. import 'dart:ui';
  4. import 'package:flutter/cupertino.dart';
  5. import 'package:flutter/material.dart';
  6. import 'package:sport/bean/sport_detail.dart';
  7. import 'package:sport/services/Converter.dart';
  8. const WEEK = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
  9. class Chart extends CustomPainter {
  10. final Paint _paint = Paint()
  11. ..color = Color(0xffDCDCDC)
  12. ..strokeWidth = 0.5
  13. ..isAntiAlias = true;
  14. final Paint _linePaint = Paint()
  15. ..color = Color(0xffFFC400)
  16. ..strokeWidth = 2
  17. ..isAntiAlias = true
  18. ..style = PaintingStyle.stroke;
  19. final ParagraphStyle _labelStyle = ParagraphStyle(
  20. textAlign: TextAlign.left,
  21. fontSize: 8,
  22. );
  23. final ParagraphStyle _valueStyle = ParagraphStyle(
  24. textAlign: TextAlign.right,
  25. fontSize: 8,
  26. );
  27. double _zero = 0;
  28. double _paddingLeft = 25;
  29. double _paddingRight = 10;
  30. double _labelHeight = 30;
  31. int _valueSize = 5;
  32. int _valueSplit = 200;
  33. int _type;
  34. List<RecordsToday> _records;
  35. DateTime _dateTime;
  36. Map<String, num> values;
  37. double _max = 750;
  38. Chart(this._type, this._records, this._dateTime);
  39. void initData() {
  40. values = {};
  41. var records = _records ?? [];
  42. if (_type == 0) {
  43. records.forEach((element) {
  44. var t = DateTime.parse(element.createdAt);
  45. values.update("${max(6, t.hour) - 5}", (value) => value + element.consume, ifAbsent: () => Converter.toDouble(element.consume));
  46. });
  47. for (int i = 1; i <= 24; i++) {
  48. values.putIfAbsent("$i", () => 0.0);
  49. }
  50. } else if (_type == 1) {
  51. records.forEach((element) {
  52. var t = DateTime.parse(element.createdAt);
  53. values.update("${t.weekday == 0 ? 7 : t.weekday}", (value) => value + element.consume, ifAbsent: () => Converter.toDouble(element.consume));
  54. });
  55. for (int i = 1; i <= 7; i++) {
  56. values.putIfAbsent("$i", () => 0.0);
  57. }
  58. } else if (_type == 2) {
  59. records.forEach((element) {
  60. var t = DateTime.parse(element.createdAt);
  61. values.update("${t.day}", (value) => value + element.consume, ifAbsent: () => Converter.toDouble(element.consume));
  62. });
  63. for (int i = 1; i <= 31; i++) {
  64. values.putIfAbsent("$i", () => 0.0);
  65. }
  66. } else if (_type == 3) {
  67. records.forEach((element) {
  68. values.update("${element.month}", (value) => value + element.consume, ifAbsent: () => Converter.toDouble(element.consume));
  69. });
  70. for (int i = 1; i <= 12; i++) {
  71. values.putIfAbsent("$i", () => 0.0);
  72. }
  73. }
  74. values.values.forEach((element) {
  75. _max = max(_max, Converter.toDouble(element));
  76. });
  77. double diff = _max % _valueSplit;
  78. _max = max(750, _max + diff);
  79. print(values);
  80. }
  81. @override
  82. void paint(Canvas canvas, Size size) {
  83. double zero = _zero = size.height - _labelHeight;
  84. // draw 值 行数
  85. int valuePadding = 10;
  86. canvas.drawLine(Offset(_paddingLeft, zero), Offset(size.width, zero), _paint);
  87. ParagraphBuilder pb = ParagraphBuilder(_valueStyle)
  88. ..pushStyle(ui.TextStyle(color: Color(0xff999999)))
  89. ..addText("0");
  90. ParagraphConstraints constraints = ParagraphConstraints(width: _paddingLeft - valuePadding);
  91. Paragraph paragraph = pb.build()..layout(constraints);
  92. paragraph.computeLineMetrics().forEach((element) {
  93. canvas.drawParagraph(paragraph, Offset(0, zero - element.baseline / 2));
  94. });
  95. canvas.drawLine(Offset(_paddingLeft, zero), Offset(_paddingLeft, 0), _paint);
  96. double valueSpace = (size.height - _labelHeight) / _valueSize;
  97. for (var i = 0; i < _valueSize; i++) {
  98. canvas.drawLine(Offset(_paddingLeft, valueSpace * i), Offset(size.width, valueSpace * i), _paint);
  99. var value = (_valueSize - i) * _max ~/ _valueSize;
  100. ParagraphBuilder pb = ParagraphBuilder(_valueStyle)
  101. ..pushStyle(ui.TextStyle(color: Color(0xff999999)))
  102. ..addText(value < 1000 ? "${value}" : "${value ~/ 1000}k");
  103. ParagraphConstraints constraints = ParagraphConstraints(width: _paddingLeft - valuePadding);
  104. Paragraph paragraph = pb.build()..layout(constraints);
  105. paragraph.computeLineMetrics().forEach((element) {
  106. canvas.drawParagraph(paragraph, Offset(0, valueSpace * i - element.baseline / 2));
  107. });
  108. }
  109. // draw 数据列
  110. double left = _paddingLeft + 10;
  111. double width = size.width - _paddingRight - left; // 柱子的有效空间
  112. double valueStroke = 20; // 柱子宽度
  113. List<String> valueLabel = []; // 柱子数量
  114. int scaleCount = 0; // 刻度数量
  115. int scaleWeight = 3;
  116. if (_type == 0) {
  117. // 日,按时间
  118. scaleWeight = 2;
  119. valueLabel = List.generate(10, (index) => "${6 + index * scaleWeight}:00");
  120. valueStroke = width / valueLabel.length / scaleWeight - 5;
  121. scaleCount = 18;
  122. } else if (_type == 1) {
  123. scaleWeight = 1;
  124. valueLabel = List.generate(7, (index) => WEEK[index]);
  125. valueStroke = 12;
  126. scaleCount = 6;
  127. } else if (_type == 2) {
  128. DateTime now = _dateTime;
  129. // DateTime now = DateTime(2019,2);
  130. DateTime startTime = DateTime.parse('${now.year}-${now.month < 10 ? '0${now.month}' : now.month}-01');
  131. DateTime endTime = DateTime.parse('${now.year}-${now.month + 1 < 10 ? '0${now.month + 1}' : now.month + 1}-01');
  132. int diffDay = endTime.difference(startTime).inDays;
  133. // List<int> days = [1, 8, 15, 22, diffDay == 28 ? 28:29];
  134. scaleCount = diffDay -1;
  135. scaleWeight = 2;
  136. // valueLabel = days.map((e) => '$e/${now.month}').toList();
  137. valueLabel = List.generate(diffDay ~/ scaleWeight +(scaleCount % scaleWeight == 0?1:0), (index) => "${index*2+1}");
  138. valueStroke = 5;
  139. } else if (_type == 3) {
  140. valueLabel = List.generate(12, (index) => '${index + 1}');
  141. valueStroke = 12;
  142. scaleCount = 12 - 1;
  143. scaleWeight = 1;
  144. }
  145. int labelSize = valueLabel.length;
  146. double labelSpace = (width) / labelSize;
  147. double scaleWidth = (width) / scaleCount;
  148. // if (_type < 2) {
  149. //draw 刻度
  150. for (var i = 0; i <= scaleCount; i++) {
  151. canvas.drawLine(Offset(left + scaleWidth * i, zero), Offset(left + scaleWidth * i, zero - 3), _paint);
  152. }
  153. // }
  154. for (var i = 0; i < labelSize; i++) {
  155. String label = valueLabel[i];
  156. ParagraphBuilder pb = ParagraphBuilder(_labelStyle)
  157. ..pushStyle(ui.TextStyle(color: Color(0xff999999)))
  158. ..addText(label);
  159. ParagraphConstraints constraints = ParagraphConstraints(width: labelSpace);
  160. Paragraph paragraph = pb.build()..layout(constraints);
  161. paragraph.computeLineMetrics().forEach((element) {
  162. canvas.drawParagraph(paragraph, Offset(left + scaleWidth * scaleWeight * i - element.width / 2, zero + 5));
  163. });
  164. //
  165. // double l = left + scaleWidth * scaleWeight * i - valueStroke / 2;
  166. // Rect rect = Rect.fromLTRB(l, zero, l + valueStroke, 0);
  167. // print("value draw $rect");
  168. // Paint valuePaint = Paint()
  169. // ..shader = LinearGradient(
  170. // begin: Alignment.bottomCenter,
  171. // end: Alignment.topCenter,
  172. // colors: <Color>[Color(0xffFF9100), Color(0xffFFE600)],
  173. // ).createShader(rect);
  174. // canvas.drawRRect(RRect.fromRectAndRadius(rect, Radius.circular(100)), valuePaint);
  175. }
  176. // if (_type < 2) {
  177. for (var i = 0; i <= scaleCount; i++) {
  178. String key = '${i + 1}';
  179. if (!values.containsKey(key)) continue;
  180. num value = values[key];
  181. double l = left + scaleWidth * i - valueStroke / 2;
  182. Rect rect = Rect.fromLTRB(l, zero, l + valueStroke, calValue(value));
  183. Paint valuePaint = Paint()
  184. ..shader = LinearGradient(
  185. begin: Alignment.bottomCenter,
  186. end: Alignment.topCenter,
  187. colors: <Color>[Color(0xffFF9100), Color(0xffFFE600)],
  188. ).createShader(rect);
  189. canvas.drawRRect(RRect.fromRectAndRadius(rect, Radius.circular(100)), valuePaint);
  190. }
  191. // } else {
  192. // Path path = Path();
  193. // path.moveTo(_paddingLeft, calValue(values['1']));
  194. // Path area = Path()
  195. // ..moveTo(_paddingLeft, zero)
  196. // ..lineTo(_paddingLeft, calValue(values['1']));
  197. // for (var i = 1; i <= scaleCount; i++) {
  198. // path.lineTo(left + scaleWidth * i, calValue(values['$i']));
  199. // area.lineTo(left + scaleWidth * i, calValue(values['$i']));
  200. // }
  201. // area.lineTo(size.width, zero);
  202. // area.close();
  203. //
  204. // canvas.save();
  205. // canvas.clipPath(area);
  206. // Rect rect = Rect.fromLTRB(_paddingLeft, zero, size.width, calValue(_max));
  207. // Paint valuePaint = Paint()
  208. // ..shader = LinearGradient(
  209. // begin: Alignment.bottomCenter,
  210. // end: Alignment.topCenter,
  211. // colors: <Color>[Color(0xffFFC400), Color(0x50FFFFFF)],
  212. // ).createShader(rect);
  213. // canvas.drawRect(rect, valuePaint);
  214. // canvas.restore();
  215. //
  216. // canvas.drawPath(path, _linePaint);
  217. // }
  218. }
  219. double calValue(num value) {
  220. if (value == null) return _zero;
  221. return min(_zero, _zero - _zero * value / _max);
  222. }
  223. @override
  224. bool shouldRepaint(CustomPainter oldDelegate) {
  225. return false;
  226. }
  227. }