export interface CanvasSize {
  width: number;
  height: number;
}

const OrderFeed = ({workerRef, symbol, settings}: OrderFeedProps) => {
  const cupParams = useSelector((state: AppState) => state.cupSlice);
  const [dpiScale, setDpiScale] = useState(Math.ceil(window.devicePixelRatio));
  const [canvasSize, setCanvasSize] = useState<CanvasSize>({height: 0, width: 0});
  const containerRef = useRef<HTMLDivElement|null>(null);
  const canvasRef = useRef<HTMLCanvasElement|null>(null);
  const size = useComponentResizeListener(canvasRef);
  const theme = useTheme();

  const draw = (trades: TradeItem[], camera: number) => {
    if (null === canvasRef || !Array.isArray(trades)) return;

    const context = canvasRef.current?.getContext("2d");

    if (context) {

      orderFeedDrawer.clear(context, canvasSize);
      orderFeedDrawer.drawLine(
        context,
        canvasSize,
        trades,
        camera,
        cupParams.aggregatedPriceStep,
        cupParams.cellHeight,
        cupParams.quantityDivider,
        !settings.minQuantity ? 0 : parseFloat(settings.minQuantity),
        dpiScale,
        theme,
      );
      orderFeedDrawer.drawChain(
        context,
        canvasSize,
        trades,
        camera,
        cupParams.aggregatedPriceStep,
        cupParams.cellHeight,
        cupParams.quantityDivider,
        cupParams.priceDivider,
        !settings.minQuantity ? 0 : parseFloat(settings.minQuantity),
        dpiScale,
        theme,
        settings.volumeAsDollars,
      );
    }
  };
 useEffect(() => {
    if (!workerRef.current) return;
draw(event.data.trades, event.data.camera));
 }, [workerRef.current, canvasRef.current, canvasSize, symbol, cupParams
]);

  return <Box ref={containerRef} className={`${styles.canvasWrapper} scroll-block`}>
    <canvas
      ref={canvasRef}
      className={styles.canvas}
      width={canvasSize?.width}
      height={canvasSize?.height}
    />
  </Box>;
};

export default OrderFeed;

t clamp = (value: number, min: number, max: number): number => {
  return Math.min(Math.max(value, min), max);
};

const orderFeedDrawer = {
  clear: (ctx: CanvasRenderingContext2D, size: CanvasSize) => {
ctx.clearRect(0, 0, size.width, size.height);
 },
  drawLine: (
    ctx: CanvasRenderingContext2D,
    canvasSize: CanvasSize,
    trades: TradeItem[],
    camera: number,
    aggregatedPriceStep: number,
    cupCellHeight: number,
    quantityDivider: number,
    minQuantity: number,
    dpiScale: number,
    theme: Theme,
  ) => {
    let startX = canvasSize.width - 10 * dpiScale;
    const fullHeightRowsCount = parseInt((canvasSize.height / cupCellHeight).toFixed(0));
    const halfCupCellHeight = cupCellHeight / 2;
    const startY = (fullHeightRowsCount % 2 ? fullHeightRowsCount - 1 : fullHeightRowsCount) / 2 * cupCellHeight;

    ctx.beginPath();
    ctx.strokeStyle = orderFeedOptions(theme).line.color;
    ctx.lineWidth = 2 * dpiScale;

    trades.forEach(trade => {
      const yModifier = (trade.price - (trade.price - (trade?.secondPrice || trade.price)) / 2 - camera) / aggregatedPriceStep * cupCellHeight + halfCupCellHeight;
      const width = 1.75 * clamp((trade.quantity / quantityDivider < minQuantity ? 0 : trade.radius), 5, 50) * dpiScale;

      ctx.lineTo(startX - width / 2, startY - yModifier);
      startX = startX - width;
    });

    ctx.stroke();
  },
 drawChain: (
    ctx: CanvasRenderingContext2D,
    canvasSize: CanvasSize,
    trades: TradeItem[],
    camera: number,
    aggregatedPriceStep: number,
    cupCellHeight: number,
    quantityDivider: number,
    priceDivider: number,
    minQuantity: number,
    dpiScale: number,
    theme: Theme,
    volumeAsDollars: boolean,
  ) => {
    let startX = canvasSize.width - 10 * dpiScale;
    const fullHeightRowsCount = parseInt((canvasSize.height / cupCellHeight).toFixed(0));
    const startY = (fullHeightRowsCount % 2 ? fullHeightRowsCount - 1 : fullHeightRowsCount) / 2 * cupCellHeight;
    const halfCupCellHeight = cupCellHeight / 2;
    let timestamp: number|null = null;

    trades.forEach(trade => {
      if (startX < 0) return;

      const isSquare = trade?.secondPrice && trade.price !== trade.secondPrice;
      const yModifier = (trade.price - camera) / aggregatedPriceStep * cupCellHeight + halfCupCellHeight;
      const prevYModifier = isSquare ? ((trade?.secondPrice || 0) - camera) / aggregatedPriceStep * cupCellHeight + halfCupCellHeight : 0;
      const quantity = volumeAsDollars ? (trade.quantity / quantityDivider) * (trade.price / priceDivider) : trade.quantity / quantityDivider;

      if (isSquare) {
        startX = orderFeedDrawer.drawSquare(
          ctx,
          trade.price,
          trade.quantity / quantityDivider < minQuantity ? 0 : trade.radius,
          quantity,
          trade.side,
          dpiScale,
          startX,
          startY - yModifier,
          startY - prevYModifier,
          theme,
        );
      } else {
        startX = orderFeedDrawer.drawCircle(
          ctx,
          trade.price,
          trade.quantity / quantityDivider < minQuantity ? 0 : trade.radius,
          quantity,
          trade.side,
          dpiScale,
          startX,
          startY - yModifier,
          theme,
          timestamp === trade.timestamp,
        );
      }

      timestamp = trade.timestamp;
    });
  },
  drawCircle: (
    ctx: CanvasRenderingContext2D,
    price: number,
    tradeRadius: number,
    quantity: number,
    side: "Buy" | "Sell",
    dpiScale: number,
    startX: number,
    startY: number,
    theme: Theme,
    inOneTimestamp: boolean,
  ): number => {
    const label = orderFeedTools.abbreviateNumber(quantity);
    const {width: textWidth} = ctx.measureText(label);
    const radius = clamp(tradeRadius, 5, 50) * dpiScale;
    const bubbleX = startX - (radius * 0.75);
    const bubbleY = startY;

    ctx.beginPath();
    ctx.fillStyle = side === "Buy"
      ? orderFeedOptions(theme).sell.backgroundColor
      : orderFeedOptions(theme).buy.backgroundColor;
    ctx.strokeStyle = side === "Buy"
      ? orderFeedOptions(theme).sell.borderColor
      : orderFeedOptions(theme).buy.borderColor;
    ctx.lineWidth = dpiScale;

    ctx.arc(bubbleX, bubbleY, radius, 0, 2 * Math.PI);
    ctx.fill();
    ctx.stroke();

    if (tradeRadius > 0) {
      ctx.textBaseline = "middle";
      ctx.font = `${orderFeedOptions(theme).font.weight} ${orderFeedOptions(theme).font.sizePx * dpiScale}px ${orderFeedOptions(theme).font.family}`;
      ctx.fillStyle = side === "Buy"
        ? orderFeedOptions(theme).sell.textColor
        : orderFeedOptions(theme).buy.textColor;
      ctx.fillText(label, bubbleX - (textWidth / 2), bubbleY);
    }

    return bubbleX - radius;
  },
  drawSquare: (
    ctx: CanvasRenderingContext2D,
    price: number,
    tradeRadius: number,
    quantity: number,
    side: "Buy" | "Sell",
    dpiScale: number,
    startX: number,
    startY: number,
    prevStartY: number,
    theme: Theme,
  ): number => {
    const label = orderFeedTools.abbreviateNumber(quantity);
    const {width: textWidth} = ctx.measureText(label);
    const radius = clamp(tradeRadius, 5, 50) * dpiScale;
    const width = radius * 1.75;
    const height = prevStartY - startY;

    ctx.beginPath();
    ctx.fillStyle = side === "Buy"
      ? orderFeedOptions(theme).sell.backgroundColor
      : orderFeedOptions(theme).buy.backgroundColor;
    ctx.strokeStyle = side === "Buy"
      ? orderFeedOptions(theme).sell.borderColor
      : orderFeedOptions(theme).buy.borderColor;
    ctx.lineWidth = dpiScale;

    ctx.rect(startX - width + 3 * dpiScale, startY, width, height);
    ctx.fill();
    ctx.stroke();

    if (tradeRadius > 0) {
      ctx.textBaseline = "middle";
      ctx.font = `${orderFeedOptions(theme).font.weight} ${orderFeedOptions(theme).font.sizePx * dpiScale}px ${orderFeedOptions(theme).font.family}`;
      ctx.fillStyle = side === "Buy"
        ? orderFeedOptions(theme).sell.textColor
        : orderFeedOptions(theme).buy.textColor;
      ctx.fillText(label, startX - (width / 2) + 3 * dpiScale - (textWidth / 2), startY + (height / 2));
    }

    return startX - width;
  },
};

export default orderFeedDrawer;


нужно переписать отрисовку на библиотеку Three.js