更新打鼾图显示

This commit is contained in:
wyf
2025-12-20 17:41:31 +08:00
parent 8e7411cf92
commit ee9965e3df
9 changed files with 529 additions and 235 deletions

View File

@@ -1,5 +1,6 @@
import 'dart:ui' as ui;
//柱形图显示
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:vbvs_app/common/color/appConstants.dart';
@@ -81,7 +82,7 @@ class _BarChartWidgetState extends State<BarChartWidget> {
onTapDown: (details) =>
_handleTapOrDrag(details.localPosition, constraints.biggest),
child: CustomPaint(
size: Size(double.infinity, 500.rpx),
size: Size(double.infinity, 250.rpx),
painter: BarChartPainter(
widget.data,
widget.startTime,
@@ -282,3 +283,330 @@ class BarChartPainter extends CustomPainter {
}
}
}
//横线显示
// import 'dart:math';
// import 'dart:ui' as ui;
// import 'package:flutter/material.dart';
// import 'package:intl/intl.dart';
// import 'package:vbvs_app/common/util/FitTool.dart';
// import 'package:vbvs_app/common/util/MyUtils.dart';
// class BarData {
// final int st; // 起始时间(毫秒)
// final int et; // 结束时间(毫秒)
// final double value; // 横线的高度值
// final int id;
// final String name;
// final Color color;
// BarData({
// required this.st,
// required this.et,
// required this.value,
// required this.id,
// required this.name,
// required this.color,
// });
// }
// class BarChartWidget extends StatefulWidget {
// final List<BarData> data;
// final int startTime; // 毫秒时间戳
// final int endTime; // 毫秒时间戳
// final double maxYValue; // Y轴最大值
// final int yStepCount; // Y轴分段数
// const BarChartWidget({
// super.key,
// required this.data,
// required this.startTime,
// required this.endTime,
// required this.maxYValue,
// this.yStepCount = 5,
// });
// @override
// State<BarChartWidget> createState() => _BarChartWidgetState();
// }
// class _BarChartWidgetState extends State<BarChartWidget> {
// BarData? selectedBar;
// void _handleTapOrDrag(Offset localPosition, Size size) {
// print('点击位置: $localPosition, 画布大小: $size');
// final chartWidth = size.width - 30.rpx;
// final totalDuration = widget.endTime - widget.startTime;
// // 使用与绘制相同的chartHeight计算方式
// final double topPadding = 0;
// final double bottomPadding = 0;
// final double leftPadding = 30.rpx;
// final double chartHeight = size.height - topPadding - bottomPadding;
// for (final d in widget.data) {
// final left = ((d.st - widget.startTime) / totalDuration) * chartWidth +
// leftPadding;
// final right = ((d.et - widget.startTime) / totalDuration) * chartWidth +
// leftPadding;
// // 使用与绘制相同的Y坐标计算方式
// final y = topPadding + chartHeight * (1 - d.value / widget.maxYValue);
// // 判断点击是否在横线附近(增加容差范围)
// if (localPosition.dx >= left - 5.rpx &&
// localPosition.dx <= right + 5.rpx &&
// (localPosition.dy - y).abs() < 15.rpx) {
// setState(() {
// selectedBar = d;
// });
// return;
// }
// }
// setState(() {
// selectedBar = null;
// });
// }
// @override
// Widget build(BuildContext context) {
// return LayoutBuilder(builder: (context, constraints) {
// return GestureDetector(
// behavior: HitTestBehavior.opaque,
// onPanDown: (details) =>
// _handleTapOrDrag(details.localPosition, constraints.biggest),
// onPanUpdate: (details) =>
// _handleTapOrDrag(details.localPosition, constraints.biggest),
// onTapDown: (details) =>
// _handleTapOrDrag(details.localPosition, constraints.biggest),
// child: CustomPaint(
// // size: Size(double.infinity, 500.rpx),
// size: Size(constraints.maxWidth, 500.rpx), // 使用约束的最大宽度
// painter: BarChartPainter(
// widget.data,
// widget.startTime,
// widget.endTime,
// maxYValue: widget.maxYValue,
// yStepCount: widget.yStepCount,
// selectedBar: selectedBar,
// ),
// ),
// );
// });
// }
// }
// class BarChartPainter extends CustomPainter {
// final List<BarData> data;
// final int startTime;
// final int endTime;
// final double maxYValue;
// final int yStepCount;
// final BarData? selectedBar;
// final double topPadding = 0;
// final double bottomPadding = 0;
// final double leftPadding = 30.rpx;
// BarChartPainter(
// this.data,
// this.startTime,
// this.endTime, {
// required this.maxYValue,
// this.yStepCount = 5,
// this.selectedBar,
// });
// @override
// void paint(Canvas canvas, Size size) {
// final chartWidth = size.width - leftPadding;
// final chartHeight = size.height - topPadding - bottomPadding;
// final totalDuration = endTime - startTime;
// final textPainter = TextPainter(textDirection: ui.TextDirection.ltr);
// final stepValue = maxYValue / yStepCount;
// // Y轴刻度 - 从大到小显示(与参考代码一致)
// for (int i = 0; i <= yStepCount; i++) {
// final value = maxYValue - (stepValue * i); // 从最大值开始递减
// final y = topPadding + chartHeight * i / yStepCount;
// final dashPaint = Paint()
// ..color = Colors.grey.withOpacity(0.4)
// ..strokeWidth = 1.rpx;
// // 最上面的线i == ySegments是实线
// if (i == yStepCount) {
// canvas.drawLine(
// Offset(leftPadding, y), Offset(size.width, y), dashPaint);
// } else {
// // 其他线都是虚线
// drawDashedLine(
// canvas, Offset(leftPadding, y), Offset(size.width, y), dashPaint);
// }
// textPainter.text = TextSpan(
// text: value.toStringAsFixed(0),
// style: TextStyle(
// fontSize: 18.rpx,
// color: themeController.currentColor.sc4,
// ),
// );
// textPainter.layout();
// textPainter.paint(
// canvas,
// Offset(leftPadding - textPainter.width - 4, y - textPainter.height / 2),
// );
// }
// // X轴刻度 - 24小时制
// final startDate = DateTime.fromMillisecondsSinceEpoch(startTime);
// final endDate = DateTime.fromMillisecondsSinceEpoch(endTime);
// final xAxisY = topPadding + chartHeight; // 这是最底部的Y坐标
// // 计算总小时数
// final totalHours = endDate.difference(startDate).inHours + 1;
// final startHour = startDate.hour;
// // 绘制X轴主线实线
// final xAxisPaint = Paint()
// ..color = Colors.grey.withOpacity(0.4)
// ..strokeWidth = 1.rpx;
// canvas.drawLine(
// Offset(leftPadding, xAxisY),
// Offset(size.width, xAxisY),
// xAxisPaint,
// );
// // 绘制左右两侧时间标签
// final leftLabel = DateFormat('HH:mm').format(startDate);
// textPainter.text = TextSpan(
// text: leftLabel,
// style: TextStyle(
// fontSize: 18.rpx,
// color: themeController.currentColor.sc4,
// ),
// );
// textPainter.layout();
// textPainter.paint(
// canvas, Offset(leftPadding - textPainter.width / 2, xAxisY + 8.rpx));
// final rightLabel = DateFormat('HH:mm').format(endDate);
// textPainter.text = TextSpan(
// text: rightLabel,
// style: TextStyle(
// fontSize: 18.rpx,
// color: themeController.currentColor.sc4,
// ),
// );
// textPainter.layout();
// textPainter.paint(
// canvas, Offset(size.width - textPainter.width / 2, xAxisY + 8.rpx));
// // 绘制中间小时刻度
// for (int i = 1; i < totalHours; i++) {
// final double x = leftPadding + chartWidth * i / totalHours;
// int hourLabelNum = (startHour + i) % 24;
// final hourLabel = '$hourLabelNum';
// textPainter.text = TextSpan(
// text: hourLabel,
// style: TextStyle(
// fontSize: 18.rpx,
// color: themeController.currentColor.sc4,
// ),
// );
// textPainter.layout();
// textPainter.paint(
// canvas, Offset(x - textPainter.width / 2, xAxisY + 8.rpx));
// }
// // 绘制数据横线(根据数据值绘制水平线段)
// for (final d in data) {
// final left =
// ((d.st - startTime) / totalDuration) * chartWidth + leftPadding;
// final right =
// ((d.et - startTime) / totalDuration) * chartWidth + leftPadding;
// // 根据value计算Y位置0在底部maxYValue在顶部
// final y = topPadding + chartHeight * (1 - d.value / maxYValue);
// final linePaint = Paint()
// ..style = PaintingStyle.stroke
// ..strokeWidth = 3.rpx
// ..color = d.color
// ..strokeCap = StrokeCap.round;
// // 画水平线段
// canvas.drawLine(Offset(left, y), Offset(right, y), linePaint);
// }
// // 如果选中了某条横线,显示提示信息
// // 如果选中了某条横线,显示提示信息
// if (selectedBar != null) {
// final d = selectedBar!;
// final left =
// ((d.st - startTime) / totalDuration) * chartWidth + leftPadding;
// final right =
// ((d.et - startTime) / totalDuration) * chartWidth + leftPadding;
// final y = topPadding + chartHeight * (1 - d.value / maxYValue);
// final tipText =
// '${d.name}\n${d.value.toStringAsFixed(1)}次\n${MyUtils.formatToHHmm(d.st)}';
// final tp = TextPainter(
// text: TextSpan(
// text: tipText,
// style: TextStyle(fontSize: 16.rpx, color: Colors.white),
// ),
// textAlign: TextAlign.center,
// textDirection: ui.TextDirection.ltr,
// );
// tp.layout();
// final tipWidth = tp.width + 20.rpx;
// final tipHeight = tp.height + 10.rpx;
// final tipLeft = left + (right - left) / 2 - tipWidth / 2;
// final tipTop = y - tipHeight - 10.rpx;
// // 确保tip不会超出画布顶部 - 明确转换为double
// final double adjustedTipTop = tipTop < 0 ? 0.0 : tipTop;
// // 绘制tip背景
// final rect = RRect.fromRectAndRadius(
// Rect.fromLTWH(tipLeft, adjustedTipTop, tipWidth, tipHeight),
// Radius.circular(8.rpx),
// );
// final tipBgPaint = Paint()..color = Colors.black.withOpacity(0.8);
// canvas.drawRRect(rect, tipBgPaint);
// // 绘制tip文字
// tp.paint(
// canvas,
// Offset(tipLeft + 10.rpx, adjustedTipTop + 5.rpx),
// );
// }
// }
// @override
// bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
// void drawDashedLine(Canvas canvas, Offset start, Offset end, Paint paint,
// {double dashWidth = 4, double dashSpace = 3}) {
// final dx = end.dx - start.dx;
// final dy = end.dy - start.dy;
// final distance = sqrt(dx * dx + dy * dy);
// final direction = Offset(dx / distance, dy / distance);
// double drawn = 0;
// while (drawn < distance) {
// final from = start + direction * drawn;
// final to = start + direction * (drawn + dashWidth).clamp(0, distance);
// canvas.drawLine(from, to, paint);
// drawn += dashWidth + dashSpace;
// }
// }
// }