Загрузка данных
import { CanvasRenderingTarget2D } from 'fancy-canvas';
import { IPrimitivePaneRenderer } from 'lightweight-charts';
import { getThemeStore } from '@src/theme';
import type { VolumeProfile } from './volumeProfile';
const UI = {
borderWidth: 1,
handleRadius: 5,
handleBorderWidth: 2,
rowGap: 1,
pocLineWidth: 1,
};
export class VolumeProfilePaneRenderer implements IPrimitivePaneRenderer {
private readonly volumeProfile: VolumeProfile;
constructor(volumeProfile: VolumeProfile) {
this.volumeProfile = volumeProfile;
}
public draw(target: CanvasRenderingTarget2D): void {
const data = this.volumeProfile.getRenderData();
if (!data) {
return;
}
target.useBitmapCoordinateSpace(({ context, horizontalPixelRatio, verticalPixelRatio }) => {
const pixelRatio = Math.max(horizontalPixelRatio, verticalPixelRatio);
const left = data.left * horizontalPixelRatio;
const right = data.right * horizontalPixelRatio;
const top = data.top * verticalPixelRatio;
const bottom = data.bottom * verticalPixelRatio;
context.save();
context.fillStyle = data.areaFillColor;
context.fillRect(left, top, right - left, bottom - top);
data.rows.forEach((row) => {
const rowTop = row.top * verticalPixelRatio;
const rowHeight = row.height * verticalPixelRatio;
const buyWidth = row.buyWidth * horizontalPixelRatio;
const sellWidth = row.sellWidth * horizontalPixelRatio;
drawVolumeRow(context, left, rowTop, rowHeight, buyWidth, sellWidth, pixelRatio, data.buyFillColor, data.sellFillColor);
});
if (data.pocY !== null) {
context.strokeStyle = data.pocLineColor;
context.lineWidth = UI.pocLineWidth * pixelRatio;
context.beginPath();
context.moveTo(left, data.pocY * verticalPixelRatio);
context.lineTo(right, data.pocY * verticalPixelRatio);
context.stroke();
}
drawSideBorders(context, left, right, top, bottom, pixelRatio, data.borderColor);
if (data.showHandles) {
drawHandle(
context,
data.startPoint.x * horizontalPixelRatio,
data.startPoint.y * verticalPixelRatio,
pixelRatio,
);
drawHandle(
context,
data.endPoint.x * horizontalPixelRatio,
data.endPoint.y * verticalPixelRatio,
pixelRatio,
);
}
context.restore();
});
}
}
function drawVolumeRow(
context: CanvasRenderingContext2D,
left: number,
top: number,
height: number,
buyWidth: number,
sellWidth: number,
pixelRatio: number,
buyFillColor: string,
sellFillColor: string,
): void {
const safeHeight = Math.max(1, height - UI.rowGap * pixelRatio);
if (buyWidth <= 0 && sellWidth <= 0) {
return;
}
if (buyWidth > 0 && sellWidth > 0) {
const halfHeight = Math.max(1, safeHeight / 2);
context.fillStyle = buyFillColor;
context.fillRect(left, top, buyWidth, halfHeight);
context.fillStyle = sellFillColor;
context.fillRect(left, top + halfHeight, sellWidth, halfHeight);
return;
}
context.fillStyle = buyWidth > 0 ? buyFillColor : sellFillColor;
context.fillRect(left, top, Math.max(buyWidth, sellWidth), safeHeight);
}
function drawSideBorders(
context: CanvasRenderingContext2D,
left: number,
right: number,
top: number,
bottom: number,
pixelRatio: number,
borderColor: string,
): void {
context.strokeStyle = borderColor;
context.lineWidth = UI.borderWidth * pixelRatio;
context.beginPath();
context.moveTo(Math.round(left), top);
context.lineTo(Math.round(left), bottom);
context.moveTo(Math.round(right), top);
context.lineTo(Math.round(right), bottom);
context.stroke();
}
function drawHandle(context: CanvasRenderingContext2D, x: number, y: number, pixelRatio: number): void {
context.save();
const { colors } = getThemeStore();
context.fillStyle = colors.chartBackground;
context.strokeStyle = colors.chartLineColor;
context.lineWidth = UI.handleBorderWidth * pixelRatio;
context.beginPath();
context.arc(x, y, UI.handleRadius * pixelRatio, 0, Math.PI * 2);
context.fill();
context.stroke();
context.restore();
}
import { getThemeStore } from '@src/theme';
import { SettingField, SettingsTab, SettingsValues } from '@src/types';
export interface VolumeProfileStyle {
areaFillColor: string;
buyFillColor: string;
sellFillColor: string;
pocLineColor: string;
borderColor: string;
}
export type VolumeProfileSettings = SettingsValues & VolumeProfileStyle;
export function createDefaultSettings(): VolumeProfileSettings {
const { colors } = getThemeStore();
return {
areaFillColor: colors.volumeProfileAreaFill,
buyFillColor: colors.volumeProfileBuyFill,
sellFillColor: colors.volumeProfileSellFill,
pocLineColor: colors.volumeProfilePocLine,
borderColor: colors.chartLineColor,
};
}
export function getVolumeProfileSettingsTabs(settings: VolumeProfileSettings): SettingsTab[] {
const fields: SettingField[] = [
{
key: 'areaFillColor',
label: 'Цвет области',
type: 'color',
defaultValue: settings.areaFillColor,
},
{
key: 'buyFillColor',
label: 'Цвет buy-зоны',
type: 'color',
defaultValue: settings.buyFillColor,
},
{
key: 'sellFillColor',
label: 'Цвет sell-зоны',
type: 'color',
defaultValue: settings.sellFillColor,
},
{
key: 'pocLineColor',
label: 'Цвет POC-линии',
type: 'color',
defaultValue: settings.pocLineColor,
},
{
key: 'borderColor',
label: 'Цвет границы',
type: 'color',
defaultValue: settings.borderColor,
},
];
return [{ key: 'style', label: 'Стиль', fields }];
}