feat: Add text support for horizontal and vertical lines
- Add text-related default settings (text, textColor, fontSize, bold, italic, alignVert, alignHorz) - Support text rendering on horizontal/vertical lines with alignment options - Add label position storage and hit detection for draggable text labels
This commit is contained in:
@ -35,13 +35,27 @@ export class DrawingManager {
|
||||
color: '#2962ff',
|
||||
width: 2,
|
||||
style: 0,
|
||||
opacity: 100
|
||||
opacity: 100,
|
||||
text: '',
|
||||
textColor: '#2962ff',
|
||||
fontSize: 14,
|
||||
bold: false,
|
||||
italic: false,
|
||||
alignVert: 'top',
|
||||
alignHorz: 'left'
|
||||
},
|
||||
vertical_line: {
|
||||
color: '#2962ff',
|
||||
width: 2,
|
||||
style: 0,
|
||||
opacity: 100
|
||||
opacity: 100,
|
||||
text: '',
|
||||
textColor: '#2962ff',
|
||||
fontSize: 14,
|
||||
bold: false,
|
||||
italic: false,
|
||||
alignVert: 'top',
|
||||
alignHorz: 'left'
|
||||
}
|
||||
};
|
||||
|
||||
@ -454,6 +468,16 @@ export class DrawingManager {
|
||||
return; // Critical: exit here so we don't move measurement points
|
||||
}
|
||||
|
||||
if (this.dragMode === 'label') {
|
||||
const dx = pos.x - this.dragStartPos.x;
|
||||
const dy = pos.y - this.dragStartPos.y;
|
||||
if (!d.labelOffset) d.labelOffset = { x: 0, y: 0 };
|
||||
d.labelOffset.x += dx;
|
||||
d.labelOffset.y += dy;
|
||||
this.dragStartPos = pos;
|
||||
this.update();
|
||||
return;
|
||||
}
|
||||
if (this.dragMode === 'p1') {
|
||||
d.p1 = { time: pos.time, price: pos.price };
|
||||
} else if (this.dragMode === 'p2') {
|
||||
@ -554,10 +578,26 @@ export class DrawingManager {
|
||||
}
|
||||
} else if (d.price !== undefined && d.type === 'horizontal_line') {
|
||||
const dy = this.series.priceToCoordinate(d.price);
|
||||
if (dy !== null && Math.abs(y - dy) < threshold) return { drawing: d, part: 'all' };
|
||||
if (dy !== null && Math.abs(y - dy) < threshold) {
|
||||
if (d.text && d.labelPos) {
|
||||
if (x >= d.labelPos.x && x <= d.labelPos.x + d.labelPos.width &&
|
||||
y >= d.labelPos.y && y <= d.labelPos.y + d.labelPos.height) {
|
||||
return { drawing: d, part: 'label' };
|
||||
}
|
||||
}
|
||||
return { drawing: d, part: 'all' };
|
||||
}
|
||||
} else if (d.time !== undefined && d.type === 'vertical_line') {
|
||||
const dx = this.timeToX(d.time);
|
||||
if (dx !== null && Math.abs(x - dx) < threshold) return { drawing: d, part: 'all' };
|
||||
if (dx !== null && Math.abs(x - dx) < threshold) {
|
||||
if (d.text && d.labelPos) {
|
||||
if (x >= d.labelPos.x && x <= d.labelPos.x + d.labelPos.width &&
|
||||
y >= d.labelPos.y && y <= d.labelPos.y + d.labelPos.height) {
|
||||
return { drawing: d, part: 'label' };
|
||||
}
|
||||
}
|
||||
return { drawing: d, part: 'all' };
|
||||
}
|
||||
} else if (d.time !== undefined && d.price !== undefined) {
|
||||
const dx = this.timeToX(d.time);
|
||||
const dy = this.series.priceToCoordinate(d.price);
|
||||
@ -796,6 +836,47 @@ export class DrawingManager {
|
||||
ctx.fillStyle = '#ffffff';
|
||||
ctx.beginPath(); ctx.arc(0, y, 5, 0, Math.PI * 2); ctx.fill(); ctx.stroke();
|
||||
}
|
||||
|
||||
// Render Text if present
|
||||
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 metrics = ctx.measureText(d.text);
|
||||
const labelWidth = metrics.width + 16;
|
||||
const labelHeight = 24;
|
||||
const defaultLabelX = scope.mediaSize.width / 2 - labelWidth / 2;
|
||||
const defaultLabelY = d.alignVert === 'top' ? y - 40 : (d.alignVert === 'bottom' ? y + 20 : y - 12);
|
||||
d.labelPos = {
|
||||
x: defaultLabelX + (d.labelOffset?.x || 0),
|
||||
y: defaultLabelY + (d.labelOffset?.y || 0),
|
||||
width: labelWidth,
|
||||
height: labelHeight
|
||||
};
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
} else if (d.type === 'vertical_line') {
|
||||
@ -817,6 +898,47 @@ export class DrawingManager {
|
||||
ctx.fillStyle = '#ffffff';
|
||||
ctx.beginPath(); ctx.arc(x, 0, 5, 0, Math.PI * 2); ctx.fill(); ctx.stroke();
|
||||
}
|
||||
|
||||
// Render Text if present
|
||||
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 metrics = ctx.measureText(d.text);
|
||||
const labelWidth = metrics.width + 16;
|
||||
const labelHeight = 24;
|
||||
const defaultLabelX = x - labelWidth / 2;
|
||||
const defaultLabelY = d.alignVert === 'top' ? -50 : (d.alignVert === 'bottom' ? 20 : -12);
|
||||
d.labelPos = {
|
||||
x: defaultLabelX + (d.labelOffset?.x || 0),
|
||||
y: defaultLabelY + (d.labelOffset?.y || 0),
|
||||
width: labelWidth,
|
||||
height: labelHeight
|
||||
};
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
} else if (d.type === 'fib_retracement') {
|
||||
|
||||
Reference in New Issue
Block a user