Загрузка данных


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,
};

const { colors } = getThemeStore();

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 = colors.volumeProfileAreaFill;
      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);
      });

      if (data.pocY !== null) {
        context.strokeStyle = colors.volumeProfilePocLine;
        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);

      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,
): 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 = colors.volumeProfileBuyFill;
    context.fillRect(left, top, buyWidth, halfHeight);

    context.fillStyle = colors.volumeProfileSellFill;
    context.fillRect(left, top + halfHeight, sellWidth, halfHeight);

    return;
  }

  context.fillStyle = buyWidth > 0 ? colors.volumeProfileBuyFill : colors.volumeProfileSellFill;
  context.fillRect(left, top, Math.max(buyWidth, sellWidth), safeHeight);
}

function drawSideBorders(
  context: CanvasRenderingContext2D,
  left: number,
  right: number,
  top: number,
  bottom: number,
  pixelRatio: number,
): void {
  context.strokeStyle = colors.chartLineColor;
  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();

  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();
}