fix: Ensure horizontal/vertical line text is visible on screen

- Add bounds checking to only render text when line is on screen
- Fix text positioning to be consistent with line position
This commit is contained in:
DiTus
2026-03-21 23:56:41 +01:00
parent c8d9cf9645
commit e3999e6506

View File

@ -819,7 +819,7 @@ export class DrawingManager {
}
} else if (d.type === 'horizontal_line') {
const y = series.priceToCoordinate(d.price);
if (y !== null) {
if (y !== null && y >= 0 && y <= scope.mediaSize.height) {
ctx.save();
ctx.strokeStyle = d.opacity !== undefined ? this.hexToRgba(d.color, d.opacity) : d.color;
ctx.lineWidth = d.width || 2;
@ -837,51 +837,72 @@ export class DrawingManager {
ctx.beginPath(); ctx.arc(0, y, 5, 0, Math.PI * 2); ctx.fill(); ctx.stroke();
}
// Render Text if present
// Store label position for hit detection (before rendering)
let textX = scope.mediaSize.width / 2;
let textY = y;
let labelPos = null;
if (d.text) {
ctx.save();
ctx.setLineDash([]);
const fontSize = d.fontSize || 14;
const fontStyle = (d.bold ? 'bold ' : '') + (d.italic ? 'italic ' : '');
ctx.font = `${fontStyle}${fontSize}px Inter`;
ctx.fillStyle = d.textColor || d.color;
const tx = scope.mediaSize.width / 2;
const ty = y;
const offset = 10;
const finalTy = d.alignVert === 'top' ? ty - offset : (d.alignVert === 'bottom' ? ty + offset : ty);
ctx.textAlign = d.alignHorz || 'center';
ctx.textBaseline = d.alignVert === 'middle' ? 'middle' : (d.alignVert === 'bottom' ? 'top' : 'bottom');
ctx.fillText(d.text, tx, finalTy);
ctx.restore();
}
// Store label position for hit detection
if (d.text) {
ctx.save();
ctx.setLineDash([]);
const fontSize = d.fontSize || 14;
const fontStyle = (d.bold ? 'bold ' : '') + (d.italic ? 'italic ' : '');
ctx.font = `${fontStyle}${fontSize}px Inter`;
const font = `${fontStyle}${fontSize}px Inter`;
ctx.font = font;
const metrics = ctx.measureText(d.text);
const labelWidth = metrics.width + 16;
const labelHeight = 24;
const defaultLabelX = scope.mediaSize.width / 2 - labelWidth / 2;
const offset = 10;
const defaultLabelY = d.alignVert === 'top' ? y - 40 : (d.alignVert === 'bottom' ? y + 20 : y - 12);
d.labelPos = {
labelPos = {
x: defaultLabelX + (d.labelOffset?.x || 0),
y: defaultLabelY + (d.labelOffset?.y || 0),
width: labelWidth,
height: labelHeight
};
textX = scope.mediaSize.width / 2;
textY = d.alignVert === 'top' ? y - offset : (d.alignVert === 'bottom' ? y + offset : y);
}
// Render Text if present and visible on screen
if (d.text && labelPos && labelPos.y >= 0 && labelPos.y <= scope.mediaSize.height) {
ctx.save();
ctx.setLineDash([]);
ctx.font = font;
ctx.fillStyle = d.textColor || d.color;
ctx.textAlign = d.alignHorz || 'center';
ctx.textBaseline = d.alignVert === 'middle' ? 'middle' : (d.alignVert === 'bottom' ? 'top' : 'bottom');
ctx.fillText(d.text, textX, textY);
ctx.restore();
}
// Store label position for hit detection
if (labelPos) {
d.labelPos = labelPos;
}
// Render Text if present
if (d.text) {
ctx.save();
ctx.setLineDash([]);
ctx.font = `${fontStyle}${fontSize}px Inter`;
ctx.fillStyle = d.textColor || d.color;
ctx.textAlign = d.alignHorz || 'center';
ctx.textBaseline = d.alignVert === 'middle' ? 'middle' : (d.alignVert === 'bottom' ? 'top' : 'bottom');
ctx.fillText(d.text, textX, textY);
ctx.restore();
}
// Store label position for hit detection
if (labelPos) {
d.labelPos = labelPos;
}
ctx.restore();
}
} else if (d.type === 'vertical_line') {
const x = this.timeToX(d.time);
if (x !== null) {
if (x !== null && x >= 0 && x <= scope.mediaSize.width) {
ctx.save();
ctx.strokeStyle = d.opacity !== undefined ? this.hexToRgba(d.color, d.opacity) : d.color;
ctx.lineWidth = d.width || 2;
@ -899,46 +920,47 @@ export class DrawingManager {
ctx.beginPath(); ctx.arc(x, 0, 5, 0, Math.PI * 2); ctx.fill(); ctx.stroke();
}
// Render Text if present
// Store label position for hit detection (before rendering)
let textX = x;
let textY = 0;
let labelPos = null;
if (d.text) {
ctx.save();
ctx.setLineDash([]);
const fontSize = d.fontSize || 14;
const fontStyle = (d.bold ? 'bold ' : '') + (d.italic ? 'italic ' : '');
ctx.font = `${fontStyle}${fontSize}px Inter`;
ctx.fillStyle = d.textColor || d.color;
const tx = x;
const ty = 0;
const offset = 10;
const finalTy = d.alignVert === 'top' ? ty - offset : (d.alignVert === 'bottom' ? ty + offset : ty);
ctx.textAlign = d.alignHorz || 'center';
ctx.textBaseline = d.alignVert === 'middle' ? 'middle' : (d.alignVert === 'bottom' ? 'top' : 'bottom');
ctx.fillText(d.text, tx, finalTy);
ctx.restore();
}
// Store label position for hit detection
if (d.text) {
ctx.save();
ctx.setLineDash([]);
const fontSize = d.fontSize || 14;
const fontStyle = (d.bold ? 'bold ' : '') + (d.italic ? 'italic ' : '');
ctx.font = `${fontStyle}${fontSize}px Inter`;
const font = `${fontStyle}${fontSize}px Inter`;
ctx.font = font;
const metrics = ctx.measureText(d.text);
const labelWidth = metrics.width + 16;
const labelHeight = 24;
const defaultLabelX = x - labelWidth / 2;
const offset = 10;
const defaultLabelY = d.alignVert === 'top' ? -50 : (d.alignVert === 'bottom' ? 20 : -12);
d.labelPos = {
x: defaultLabelX + (d.labelOffset?.x || 0),
labelPos = {
x: x - labelWidth / 2 + (d.labelOffset?.x || 0),
y: defaultLabelY + (d.labelOffset?.y || 0),
width: labelWidth,
height: labelHeight
};
textY = d.alignVert === 'top' ? -offset : (d.alignVert === 'bottom' ? offset : 0);
}
// Render Text if present and visible on screen
if (d.text && labelPos && labelPos.y >= 0 && labelPos.y <= scope.mediaSize.height) {
ctx.save();
ctx.setLineDash([]);
ctx.font = font;
ctx.fillStyle = d.textColor || d.color;
ctx.textAlign = d.alignHorz || 'center';
ctx.textBaseline = d.alignVert === 'middle' ? 'middle' : (d.alignVert === 'bottom' ? 'top' : 'bottom');
ctx.fillText(d.text, textX, textY);
ctx.restore();
}
// Store label position for hit detection
if (labelPos) {
d.labelPos = labelPos;
}
ctx.restore();
}
} else if (d.type === 'fib_retracement') {