Загрузка данных
import { getThemeStore } from '@src/theme';
import { SettingField, SettingsTab, SettingsValues } from '@src/types';
export interface RayStyle {
lineColor: string;
}
export interface RayTextStyle {
text: string;
fontSize: number;
isBold: boolean;
isItalic: boolean;
textColor: string;
}
export type RaySettings = RayStyle & RayTextStyle & SettingsValues;
export function createDefaultSettings(): RaySettings {
const { colors } = getThemeStore();
return {
lineColor: colors.chartLineColor,
text: '',
fontSize: 14,
isBold: false,
isItalic: false,
textColor: colors.chartPriceLineText,
};
}
export function getRaySettingTabs(settings: RaySettings): SettingsTab[] {
const styleFields: SettingField[] = [
{
key: 'lineColor',
label: 'Цвет линии',
type: 'color',
defaultValue: settings.lineColor,
},
];
const textFields: SettingField[] = [
{
key: 'fontSize',
label: 'Размер текста',
type: 'number',
defaultValue: settings.fontSize,
min: 8,
max: 24,
},
{
key: 'text',
label: 'Текст',
type: 'textarea',
defaultValue: settings.text,
placeholder: 'Введите текст',
},
{
key: 'isBold',
label: 'Жирный текст',
type: 'boolean',
defaultValue: settings.isBold,
},
{
key: 'isItalic',
label: 'Курсив',
type: 'boolean',
defaultValue: settings.isItalic,
},
{
key: 'textColor',
label: 'Цвет текста',
type: 'color',
defaultValue: settings.textColor,
},
];
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 { Ray } from './ray';
const UI = {
lineWidth: 2,
handleRadius: 5,
handleBorderWidth: 2,
textLineHeightMultiplier: 1.2,
textOffset: 8,
};
export class RayPaneRenderer implements IPrimitivePaneRenderer {
private readonly ray: Ray;
constructor(ray: Ray) {
this.ray = ray;
}
public draw(target: CanvasRenderingTarget2D): void {
const data = this.ray.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 directionX = data.directionPoint.x * horizontalPixelRatio;
const directionY = data.directionPoint.y * verticalPixelRatio;
const endX = data.rayEndPoint.x * horizontalPixelRatio;
const endY = data.rayEndPoint.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();
drawRayText(context, {
startX,
startY,
directionX,
directionY,
text: data.text,
fontSize: data.fontSize,
isBold: data.isBold,
isItalic: data.isItalic,
textColor: data.textColor,
pixelRatio,
});
if (data.showHandles) {
context.fillStyle = colors.chartBackground;
context.strokeStyle = data.lineColor;
context.lineWidth = UI.handleBorderWidth * pixelRatio;
drawHandle(context, startX, startY, UI.handleRadius * pixelRatio);
drawHandle(context, directionX, directionY, UI.handleRadius * pixelRatio);
}
context.restore();
});
}
}
function drawRayText(
context: CanvasRenderingContext2D,
params: {
startX: number;
startY: number;
directionX: number;
directionY: number;
text: string;
fontSize: number;
isBold: boolean;
isItalic: boolean;
textColor: string;
pixelRatio: number;
},
): void {
const { startX, startY, directionX, directionY, text, fontSize, isBold, isItalic, textColor, pixelRatio } = params;
if (!text.trim()) {
return;
}
const dx = directionX - startX;
const dy = directionY - startY;
if (dx === 0 && dy === 0) {
return;
}
const lines = text.split('\n');
const safeFontSize = Math.max(1, fontSize);
const fontSizePx = safeFontSize * pixelRatio;
const lineHeight = safeFontSize * UI.textLineHeightMultiplier * pixelRatio;
const fontWeight = isBold ? '700 ' : '';
const fontStyle = isItalic ? 'italic ' : '';
const angle = Math.atan2(dy, dx);
const textX = (startX + directionX) / 2;
const textY = (startY + directionY) / 2;
const textOffset = UI.textOffset * pixelRatio;
const blockHeight = lines.length * lineHeight;
context.save();
context.translate(textX, textY);
context.rotate(angle);
context.font = `${fontStyle}${fontWeight}${fontSizePx}px Inter, sans-serif`;
context.fillStyle = textColor;
context.textAlign = 'center';
context.textBaseline = 'middle';
const textCenterY = -(blockHeight / 2 + textOffset);
const startLineY = textCenterY - blockHeight / 2 + lineHeight / 2;
lines.forEach((line, index) => {
context.fillText(line, 0, startLineY + index * lineHeight);
});
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();
}