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',
|
color: '#2962ff',
|
||||||
width: 2,
|
width: 2,
|
||||||
style: 0,
|
style: 0,
|
||||||
opacity: 100
|
opacity: 100,
|
||||||
|
text: '',
|
||||||
|
textColor: '#2962ff',
|
||||||
|
fontSize: 14,
|
||||||
|
bold: false,
|
||||||
|
italic: false,
|
||||||
|
alignVert: 'top',
|
||||||
|
alignHorz: 'left'
|
||||||
},
|
},
|
||||||
vertical_line: {
|
vertical_line: {
|
||||||
color: '#2962ff',
|
color: '#2962ff',
|
||||||
width: 2,
|
width: 2,
|
||||||
style: 0,
|
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
|
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') {
|
if (this.dragMode === 'p1') {
|
||||||
d.p1 = { time: pos.time, price: pos.price };
|
d.p1 = { time: pos.time, price: pos.price };
|
||||||
} else if (this.dragMode === 'p2') {
|
} else if (this.dragMode === 'p2') {
|
||||||
@ -554,10 +578,26 @@ export class DrawingManager {
|
|||||||
}
|
}
|
||||||
} else if (d.price !== undefined && d.type === 'horizontal_line') {
|
} else if (d.price !== undefined && d.type === 'horizontal_line') {
|
||||||
const dy = this.series.priceToCoordinate(d.price);
|
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') {
|
} else if (d.time !== undefined && d.type === 'vertical_line') {
|
||||||
const dx = this.timeToX(d.time);
|
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) {
|
} else if (d.time !== undefined && d.price !== undefined) {
|
||||||
const dx = this.timeToX(d.time);
|
const dx = this.timeToX(d.time);
|
||||||
const dy = this.series.priceToCoordinate(d.price);
|
const dy = this.series.priceToCoordinate(d.price);
|
||||||
@ -796,6 +836,47 @@ export class DrawingManager {
|
|||||||
ctx.fillStyle = '#ffffff';
|
ctx.fillStyle = '#ffffff';
|
||||||
ctx.beginPath(); ctx.arc(0, y, 5, 0, Math.PI * 2); ctx.fill(); ctx.stroke();
|
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();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
} else if (d.type === 'vertical_line') {
|
} else if (d.type === 'vertical_line') {
|
||||||
@ -817,6 +898,47 @@ export class DrawingManager {
|
|||||||
ctx.fillStyle = '#ffffff';
|
ctx.fillStyle = '#ffffff';
|
||||||
ctx.beginPath(); ctx.arc(x, 0, 5, 0, Math.PI * 2); ctx.fill(); ctx.stroke();
|
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();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
} else if (d.type === 'fib_retracement') {
|
} else if (d.type === 'fib_retracement') {
|
||||||
|
|||||||
Reference in New Issue
Block a user