import 'dart:math'; import 'dart:ui' as ui; import 'dart:ui'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:sport/bean/sport_detail.dart'; import 'package:sport/services/Converter.dart'; const WEEK = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]; class Chart extends CustomPainter { final Paint _paint = Paint() ..color = Color(0xffDCDCDC) ..strokeWidth = 0.5 ..isAntiAlias = true; final Paint _linePaint = Paint() ..color = Color(0xffFFC400) ..strokeWidth = 2 ..isAntiAlias = true ..style = PaintingStyle.stroke; final ParagraphStyle _labelStyle = ParagraphStyle( textAlign: TextAlign.left, fontSize: 8, ); final ParagraphStyle _valueStyle = ParagraphStyle( textAlign: TextAlign.right, fontSize: 8, ); double _zero = 0; double _paddingLeft = 25; double _paddingRight = 10; double _labelHeight = 30; int _valueSize = 5; int _valueSplit = 200; int _type; List _records; DateTime _dateTime; Map values; double _max = 750; Chart(this._type, this._records, this._dateTime); void initData() { values = {}; var records = _records ?? []; if (_type == 0) { records.forEach((element) { var t = DateTime.parse(element.createdAt); values.update("${max(6, t.hour) - 5}", (value) => value + element.consume, ifAbsent: () => Converter.toDouble(element.consume)); }); for (int i = 1; i <= 24; i++) { values.putIfAbsent("$i", () => 0.0); } } else if (_type == 1) { records.forEach((element) { var t = DateTime.parse(element.createdAt); values.update("${t.weekday == 0 ? 7 : t.weekday}", (value) => value + element.consume, ifAbsent: () => Converter.toDouble(element.consume)); }); for (int i = 1; i <= 7; i++) { values.putIfAbsent("$i", () => 0.0); } } else if (_type == 2) { records.forEach((element) { var t = DateTime.parse(element.createdAt); values.update("${t.day}", (value) => value + element.consume, ifAbsent: () => Converter.toDouble(element.consume)); }); for (int i = 1; i <= 31; i++) { values.putIfAbsent("$i", () => 0.0); } } else if (_type == 3) { records.forEach((element) { values.update("${element.month}", (value) => value + element.consume, ifAbsent: () => Converter.toDouble(element.consume)); }); for (int i = 1; i <= 12; i++) { values.putIfAbsent("$i", () => 0.0); } } values.values.forEach((element) { _max = max(_max, Converter.toDouble(element)); }); double diff = _max % _valueSplit; _max = max(750, _max + diff); print(values); } @override void paint(Canvas canvas, Size size) { double zero = _zero = size.height - _labelHeight; // draw 值 行数 int valuePadding = 10; canvas.drawLine(Offset(_paddingLeft, zero), Offset(size.width, 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); double valueSpace = (size.height - _labelHeight) / _valueSize; for (var i = 0; i < _valueSize; i++) { canvas.drawLine(Offset(_paddingLeft, valueSpace * i), Offset(size.width, valueSpace * i), _paint); var value = (_valueSize - i) * _max ~/ _valueSize; ParagraphBuilder pb = ParagraphBuilder(_valueStyle) ..pushStyle(ui.TextStyle(color: Color(0xff999999))) ..addText(value < 1000 ? "${value}" : "${value ~/ 1000}k"); 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)); }); } // draw 数据列 double left = _paddingLeft + 10; double width = size.width - _paddingRight - left; // 柱子的有效空间 double valueStroke = 20; // 柱子宽度 List valueLabel = []; // 柱子数量 int scaleCount = 0; // 刻度数量 int scaleWeight = 3; if (_type == 0) { // 日,按时间 scaleWeight = 2; valueLabel = List.generate(10, (index) => "${6 + index * scaleWeight}:00"); valueStroke = width / valueLabel.length / scaleWeight - 5; scaleCount = 18; } else if (_type == 1) { scaleWeight = 1; valueLabel = List.generate(7, (index) => WEEK[index]); valueStroke = 12; scaleCount = 6; } else if (_type == 2) { DateTime now = _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; // List days = [1, 8, 15, 22, diffDay == 28 ? 28:29]; scaleCount = diffDay -1; scaleWeight = 2; // valueLabel = days.map((e) => '$e/${now.month}').toList(); valueLabel = List.generate(diffDay ~/ scaleWeight +(scaleCount % scaleWeight == 0?1:0), (index) => "${index*2+1}"); valueStroke = 5; } else if (_type == 3) { valueLabel = List.generate(12, (index) => '${index + 1}'); valueStroke = 12; scaleCount = 12 - 1; scaleWeight = 1; } int labelSize = valueLabel.length; double labelSpace = (width) / labelSize; double scaleWidth = (width) / scaleCount; // if (_type < 2) { //draw 刻度 for (var i = 0; i <= scaleCount; i++) { canvas.drawLine(Offset(left + scaleWidth * i, zero), Offset(left + scaleWidth * i, zero - 3), _paint); } // } for (var i = 0; i < labelSize; i++) { String label = valueLabel[i]; ParagraphBuilder pb = ParagraphBuilder(_labelStyle) ..pushStyle(ui.TextStyle(color: Color(0xff999999))) ..addText(label); ParagraphConstraints constraints = ParagraphConstraints(width: labelSpace); Paragraph paragraph = pb.build()..layout(constraints); paragraph.computeLineMetrics().forEach((element) { canvas.drawParagraph(paragraph, Offset(left + scaleWidth * scaleWeight * i - element.width / 2, zero + 5)); }); // // 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(0xffFF9100), Color(0xffFFE600)], // ).createShader(rect); // canvas.drawRRect(RRect.fromRectAndRadius(rect, Radius.circular(100)), valuePaint); } // if (_type < 2) { for (var i = 0; i <= scaleCount; i++) { String key = '${i + 1}'; if (!values.containsKey(key)) continue; num value = values[key]; double l = left + scaleWidth * i - valueStroke / 2; Rect rect = Rect.fromLTRB(l, zero, l + valueStroke, calValue(value)); Paint valuePaint = Paint() ..shader = LinearGradient( begin: Alignment.bottomCenter, end: Alignment.topCenter, colors: [Color(0xffFF9100), Color(0xffFFE600)], ).createShader(rect); canvas.drawRRect(RRect.fromRectAndRadius(rect, Radius.circular(100)), valuePaint); } // } 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(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 min(_zero, _zero - _zero * value / _max); } @override bool shouldRepaint(CustomPainter oldDelegate) { return false; } }