Загрузка данных
import { getThemeStore } from '@src/theme';
import { SettingField, SettingsTab, SettingsValues } from '@src/types';
export interface TrendLineStyle {
lineColor: string;
}
export interface TrendLineTextStyle {
text: string;
fontSize: number;
isBold: boolean;
isItalic: boolean;
textColor: string;
}
export type TrendLineSettings = TrendLineStyle & TrendLineTextStyle & SettingsValues;
export function createDefaultSettings(): TrendLineSettings {
const { colors } = getThemeStore();
return {
lineColor: colors.chartLineColor,
text: '',
fontSize: 14,
isBold: false,
isItalic: false,
textColor: colors.chartPriceLineText,
};
}
export function getTrendLineSettingsTabs(settings: TrendLineSettings): SettingsTab[] {
const styleFields: SettingField[] = [
{
key: 'lineColor',
label: 'Цвет линии',
type: 'color',
defaultValue: settings.lineColor,
},
];
const textFields: SettingField[] = [
{
key: 'textColor',
label: 'Цвет текста',
type: 'color',
defaultValue: settings.textColor,
},
{
key: 'fontSize',
label: 'Размер текста',
type: 'number',
defaultValue: settings.fontSize,
min: 8,
max: 24,
},
{
key: 'isBold',
label: 'Жирный текст',
type: 'boolean',
defaultValue: settings.isBold,
},
{
key: 'isItalic',
label: 'Курсив',
type: 'boolean',
defaultValue: settings.isItalic,
},
{
key: 'text',
label: 'Текст',
type: 'textarea',
defaultValue: settings.text,
placeholder: 'Введите текст',
},
];
return [
{
key: 'style',
label: 'Стиль',
fields: styleFields,
},
{
key: 'text',
label: 'Текст',
fields: textFields,
},
];
}
import { CanvasRenderingTarget2D } from 'fancy-canvas';
import { IPrimitivePaneRenderer } from 'lightweight-charts';
import { getThemeStore } from '@src/theme';
import { TrendLine } from './trendLine';
const UI = {
lineWidth: 2,
handleRadius: 5,
handleBorderWidth: 2,
textOffset: 8,
textLineHeightMultiplier: 1.2,
};
export class TrendLinePaneRenderer implements IPrimitivePaneRenderer {
private readonly trendLine: TrendLine;
constructor(trendLine: TrendLine) {
this.trendLine = trendLine;
}
public draw(target: CanvasRenderingTarget2D): void {
const data = this.trendLine.getRenderData();
if (!data) {
return;
}
const { colors } = getThemeStore();
target.useBitmapCoordinateSpace(({ context, horizontalPixelRatio, verticalPixelRatio }) => {
const pixelRatio = Math.max(horizontalPixelRatio, verticalPixelRatio);
const startX = data.startPoint.x * horizontalPixelRatio;
const startY = data.startPoint.y * verticalPixelRatio;
const endX = data.endPoint.x * horizontalPixelRatio;
const endY = data.endPoint.y * verticalPixelRatio;
context.save();
context.lineWidth = UI.lineWidth * pixelRatio;
context.strokeStyle = data.lineColor;
context.beginPath();
context.moveTo(startX, startY);
context.lineTo(endX, endY);
context.stroke();
if (data.text.trim()) {
drawText(
context,
(startX + endX) / 2,
(startY + endY) / 2,
data.text,
data.fontSize,
data.isBold,
data.isItalic,
data.textColor,
verticalPixelRatio,
);
}
if (data.showHandles) {
context.fillStyle = colors.chartBackground;
context.strokeStyle = colors.chartLineColor;
context.lineWidth = UI.handleBorderWidth * pixelRatio;
drawHandle(context, startX, startY, UI.handleRadius * pixelRatio);
drawHandle(context, endX, endY, UI.handleRadius * pixelRatio);
}
context.restore();
});
}
}
function drawHandle(context: CanvasRenderingContext2D, x: number, y: number, radius: number): void {
context.beginPath();
context.arc(x, y, radius, 0, Math.PI * 2);
context.fill();
context.stroke();
}
function drawText(
context: CanvasRenderingContext2D,
centerX: number,
centerY: number,
text: string,
fontSize: number,
isBold: boolean,
isItalic: boolean,
textColor: string,
verticalPixelRatio: number,
): void {
const lines = text.split('\n');
const safeFontSize = Math.max(1, fontSize);
const fontSizePx = safeFontSize * verticalPixelRatio;
const lineHeight = safeFontSize * UI.textLineHeightMultiplier * verticalPixelRatio;
const fontWeight = isBold ? '700 ' : '';
const fontStyle = isItalic ? 'italic ' : '';
context.save();
context.font = `${fontStyle}${fontWeight}${fontSizePx}px Inter, sans-serif`;
context.fillStyle = textColor;
context.textAlign = 'center';
context.textBaseline = 'middle';
const textBlockHeight = lines.length * lineHeight;
const firstLineY = centerY - textBlockHeight / 2 + lineHeight / 2 - UI.textOffset * verticalPixelRatio;
lines.forEach((line, index) => {
context.fillText(line, centerX, firstLineY + index * lineHeight);
});
context.restore();
}