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


import { CanvasRenderingTarget2D } from 'fancy-canvas';
import { IPrimitivePaneRenderer } from 'lightweight-charts';

import { getThemeStore } from '@src/theme';

import { Text } from './text';

const UI = {
  borderWidth: 1,
  borderRadius: 4,
  padding: 6,
  selectionBorderWidth: 1,
  selectionDashLength: 4,
  selectionDashGap: 3,
};

export class TextPaneRenderer implements IPrimitivePaneRenderer {
  private readonly text: Text;

  constructor(text: Text) {
    this.text = text;
  }

  public draw(target: CanvasRenderingTarget2D): void {
    const data = this.text.getRenderData();

    if (!data) {
      return;
    }

    target.useBitmapCoordinateSpace(({ context, horizontalPixelRatio, verticalPixelRatio }) => {
      const pixelRatio = Math.max(horizontalPixelRatio, verticalPixelRatio);
      const left = data.left * horizontalPixelRatio;
      const top = data.top * verticalPixelRatio;
      const width = data.width * horizontalPixelRatio;
      const height = data.height * verticalPixelRatio;
      const paddingX = UI.padding * horizontalPixelRatio;
      const paddingY = UI.padding * verticalPixelRatio;
      const borderRadius = UI.borderRadius * pixelRatio;

      context.save();

      context.fillStyle = data.backgroundColor;
      fillRoundedRect(context, left, top, width, height, borderRadius);

      context.strokeStyle = data.borderColor;
      context.lineWidth = UI.borderWidth * pixelRatio;
      strokeRoundedRect(context, left, top, width, height, borderRadius);

      context.save();
      context.beginPath();
      context.rect(left, top, width, height);
      context.clip();

      context.font = data.font;
      context.fillStyle = data.textColor;
      context.textAlign = 'left';
      context.textBaseline = 'top';

      data.lines.forEach((line, index) => {
        context.fillText(
          line,
          left + paddingX,
          top + paddingY + index * data.lineHeight * verticalPixelRatio,
        );
      });

      context.restore();

      if (data.showSelectionBorder) {
        drawSelectionBorder(context, left, top, width, height, borderRadius, pixelRatio);
      }

      context.restore();
    });
  }
}

function drawSelectionBorder(
  context: CanvasRenderingContext2D,
  x: number,
  y: number,
  width: number,
  height: number,
  radius: number,
  pixelRatio: number,
): void {
  const { colors } = getThemeStore();

  context.save();
  context.strokeStyle = colors.chartLineColor;
  context.lineWidth = UI.selectionBorderWidth * pixelRatio;
  context.setLineDash([
    UI.selectionDashLength * pixelRatio,
    UI.selectionDashGap * pixelRatio,
  ]);

  strokeRoundedRect(context, x, y, width, height, radius);

  context.restore();
}

function fillRoundedRect(
  context: CanvasRenderingContext2D,
  x: number,
  y: number,
  width: number,
  height: number,
  radius: number,
): void {
  context.beginPath();
  buildRoundedRectPath(context, x, y, width, height, radius);
  context.fill();
}

function strokeRoundedRect(
  context: CanvasRenderingContext2D,
  x: number,
  y: number,
  width: number,
  height: number,
  radius: number,
): void {
  context.beginPath();
  buildRoundedRectPath(context, x, y, width, height, radius);
  context.stroke();
}

function buildRoundedRectPath(
  context: CanvasRenderingContext2D,
  x: number,
  y: number,
  width: number,
  height: number,
  radius: number,
): void {
  const safeRadius = Math.min(radius, width / 2, height / 2);

  context.moveTo(x + safeRadius, y);
  context.arcTo(x + width, y, x + width, y + height, safeRadius);
  context.arcTo(x + width, y + height, x, y + height, safeRadius);
  context.arcTo(x, y + height, x, y, safeRadius);
  context.arcTo(x, y, x + width, y, safeRadius);
  context.closePath();
}