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:
parent
453cab71e4
commit
900ba4a555
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue