Загрузка данных
import { getThemeStore } from '@src/theme';
import { SettingField, SettingsTab, SettingsValues } from '@src/types';
export interface AxisLineStyle {
lineColor: string;
}
export interface AxisLineTextStyle {
text: string;
fontSize: number;
isBold: boolean;
isItalic: boolean;
textColor: string;
}
export type AxisLineSettings = AxisLineStyle & AxisLineTextStyle & SettingsValues;
export function createDefaultSettings(): AxisLineSettings {
const { colors } = getThemeStore();
return {
lineColor: colors.chartLineColor,
text: '',
fontSize: 14,
isBold: false,
isItalic: false,
textColor: colors.chartPriceLineText,
};
}
export function getAxisLineSettingsTabs(settings: AxisLineSettings): 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 { AxisLine } from './axisLine';
const UI = {
lineWidth: 1,
handleSize: 10,
handleBorderWidth: 1,
textLineHeightMultiplier: 1.2,
textOffset: 6,
};
export class AxisLinePaneRenderer implements IPrimitivePaneRenderer {
private readonly axisLine: AxisLine;
constructor(axisLine: AxisLine) {
this.axisLine = axisLine;
}
public draw(target: CanvasRenderingTarget2D): void {
const data = this.axisLine.getRenderData();
if (!data) {
return;
}
const { colors } = getThemeStore();
target.useBitmapCoordinateSpace(({ context, bitmapSize, horizontalPixelRatio, verticalPixelRatio }) => {
const pixelRatio = Math.max(horizontalPixelRatio, verticalPixelRatio);
context.save();
context.fillStyle = data.lineColor;
if (data.direction === 'vertical') {
const x = Math.round(data.coordinate * horizontalPixelRatio);
const width = Math.max(1, UI.lineWidth * pixelRatio);
context.fillRect(x - width / 2, 0, width, bitmapSize.height);
}
if (data.direction === 'horizontal') {
const y = Math.round(data.coordinate * verticalPixelRatio);
const height = Math.max(1, UI.lineWidth * pixelRatio);
context.fillRect(0, y - height / 2, bitmapSize.width, height);
}
drawTextAlongAxisLine(context, {
direction: data.direction,
coordinate: data.coordinate,
text: data.text,
fontSize: data.fontSize,
isBold: data.isBold,
isItalic: data.isItalic,
textColor: data.textColor,
bitmapWidth: bitmapSize.width,
bitmapHeight: bitmapSize.height,
horizontalPixelRatio,
verticalPixelRatio,
});
if (data.showHandle) {
drawHandle(
context,
data.handle.x * horizontalPixelRatio,
data.handle.y * verticalPixelRatio,
horizontalPixelRatio,
verticalPixelRatio,
colors.chartLineColor,
colors.chartBackground,
);
}
context.restore();
});
}
}
function drawTextAlongAxisLine(
context: CanvasRenderingContext2D,
params: {
direction: 'vertical' | 'horizontal';
coordinate: number;
text: string;
fontSize: number;
isBold: boolean;
isItalic: boolean;
textColor: string;
bitmapWidth: number;
bitmapHeight: number;
horizontalPixelRatio: number;
verticalPixelRatio: number;
},
): void {
const {
direction,
coordinate,
text,
fontSize,
isBold,
isItalic,
textColor,
bitmapWidth,
bitmapHeight,
horizontalPixelRatio,
verticalPixelRatio,
} = params;
if (!text.trim()) {
return;
}
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 ' : '';
const textOffset = UI.textOffset * Math.max(horizontalPixelRatio, verticalPixelRatio);
const x = direction === 'vertical' ? coordinate * horizontalPixelRatio : bitmapWidth / 2;
const y = direction === 'horizontal' ? coordinate * verticalPixelRatio : bitmapHeight / 2;
const angle = direction === 'vertical' ? -Math.PI / 2 : 0;
context.save();
context.translate(x, y);
context.rotate(angle);
context.font = `${fontStyle}${fontWeight}${fontSizePx}px Inter, sans-serif`;
context.fillStyle = textColor;
context.textAlign = 'center';
context.textBaseline = 'middle';
const blockHeight = lines.length * lineHeight;
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,
horizontalPixelRatio: number,
verticalPixelRatio: number,
strokeColor: string,
fillColor: string,
): void {
const width = UI.handleSize * horizontalPixelRatio;
const height = UI.handleSize * verticalPixelRatio;
const left = x - width / 2;
const top = y - height / 2;
context.save();
context.fillStyle = fillColor;
context.strokeStyle = strokeColor;
context.lineWidth = UI.handleBorderWidth * Math.max(horizontalPixelRatio, verticalPixelRatio);
context.beginPath();
context.rect(left, top, width, height);
context.fill();
context.stroke();
context.restore();
}