feat(kline): add dynamic X-axis time labels

- Added _drawTimeAxis method to render time labels
- Labels dynamically adjust spacing based on candleWidth
- Shows HH:MM format, or M/D for midnight
- Labels follow pan/zoom movements
- Increased bottomPadding to 20px for label space

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-19 21:51:23 -08:00
parent 453cab71e4
commit 900ba4a555
1 changed files with 75 additions and 1 deletions

View File

@ -54,7 +54,7 @@ class KlinePainter extends CustomPainter {
const leftPadding = 8.0;
const rightPadding = 50.0;
const topPadding = 20.0;
const bottomPadding = 8.0;
const bottomPadding = 20.0; //
final chartWidth = size.width - leftPadding - rightPadding;
final chartHeight = size.height - topPadding - bottomPadding;
@ -235,6 +235,9 @@ class KlinePainter extends CustomPainter {
_drawCrossLine(canvas, size, crossLineIndex, leftPadding, actualCandleWidth, priceToY, scrollOffset);
}
//
_drawTimeAxis(canvas, size, leftPadding, rightPadding, bottomPadding, actualCandleWidth, scrollOffset);
// MA图例
_drawLegend(canvas, size, leftPadding, topPadding);
}
@ -280,6 +283,77 @@ class KlinePainter extends CustomPainter {
}
}
void _drawTimeAxis(
Canvas canvas,
Size size,
double leftPadding,
double rightPadding,
double bottomPadding,
double candleWidth,
double scrollOffset,
) {
if (klines.isEmpty) return;
final textPainter = TextPainter(textDirection: ui.TextDirection.ltr);
final drawableWidth = size.width - leftPadding - rightPadding;
// K线宽度动态计算时间标签间隔
// 60
final minLabelSpacing = 60.0;
final klinePerLabel = (minLabelSpacing / candleWidth).ceil();
// 5, 10, 15, 20, 30, 60
final intervals = [5, 10, 15, 20, 30, 60, 120, 240];
int labelInterval = intervals.firstWhere(
(i) => i >= klinePerLabel,
orElse: () => klinePerLabel,
);
//
final startIndex = (scrollOffset / candleWidth).floor();
final endIndex = ((scrollOffset + drawableWidth) / candleWidth).ceil();
//
final firstLabelIndex = ((startIndex / labelInterval).ceil()) * labelInterval;
final y = size.height - bottomPadding + 4;
for (int i = firstLabelIndex; i <= endIndex && i < klines.length; i += labelInterval) {
if (i < 0) continue;
final x = leftPadding + i * candleWidth + candleWidth / 2 - scrollOffset;
//
if (x < leftPadding - 20 || x > size.width - rightPadding + 20) continue;
final time = klines[i].time;
final timeStr = _formatTimeLabel(time);
textPainter.text = TextSpan(
text: timeStr,
style: TextStyle(color: _textColor, fontSize: 9),
);
textPainter.layout();
//
final textX = x - textPainter.width / 2;
//
final clampedX = textX.clamp(leftPadding, size.width - rightPadding - textPainter.width);
textPainter.paint(canvas, Offset(clampedX, y));
}
}
String _formatTimeLabel(DateTime time) {
//
//
final hour = time.hour.toString().padLeft(2, '0');
final minute = time.minute.toString().padLeft(2, '0');
if (time.hour == 0 && time.minute == 0) {
// /
return '${time.month}/${time.day}';
}
return '$hour:$minute';
}
void _drawLine(
Canvas canvas,
List<double?> data,