import {TradeItem} from "../../../store/orderClusterSlice";
import {OrderFeedProps} from "./OrderFeed.props";
import {GPU} from "gpu.js";

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("webgl");
    if(!context) return;

    const gpu = new GPU({canvas: canvasRef, context});

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

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;

import {CanvasSize} from "./OrderFeed";
import orderFeedOptions from "./OrderFeed.options";
import orderFeedTools from "./OrderFeed.tools";
import {Theme} from "@mui/material/styles";
import {TradeItem} from "../../../workers/order-feed-builder";

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

const orderFeedDrawer = {
  clear: (ctx: WebGLRenderingContext, size: CanvasSize) => {
    ctx.clearRect(0, 0, size.width, size.height);
  },
  drawLine: (
    ctx: WebGLRenderingContext,
    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 startY = (fullHeightRowsCount % 2 ? fullHeightRowsCount - 1 : fullHeightRowsCount) / 2 * cupCellHeight;
    const halfCupCellHeight = cupCellHeight / 2;

    ctx.beginPath();
    ctx.strokeStyle = orderFeedOptions(theme).line.color;
    ctx.lineWidth = (dpiScale) => 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();
  },

Помимо CanvasRenderingContext2D есть еще WebGLRenderingContext.
Есть библиотека https://github.com/gpujs/gpu.js/ через которую можно инициализировать canvas с WebGL контекстом. перепиши синтаксис на библиотеку “gpu.js”: “^2.16.0”,