const BigOrders = ({workerRef, symbol}: BigOrdersProps) => {
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 draw = (bigOrders: BigOrder[]) => {
if (null === canvasRef || !Array.isArray(bigOrders)) return;

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

const rowsOnScreenCount = cupTools.getRowsCountOnScreen(
canvasSize.height,
bigOrdersOptions(darkMode).cell.defaultHeight * dpiScale,
);
const realCellHeight = parseInt((canvasSize.height / rowsOnScreenCount).toFixed(0));

if (context) {
bigOrdersDrawer.clear(context, canvasSize);
bigOrdersDrawer.drawChain(
context,
canvasSize,
bigOrders,
realCellHeight,
dpiScale,
);
}
};
useEffect(() => {
if (!workerRef.current) return;
let animationFrameId: number|null = null;

interface UpdateBigOrderEvent {
type: “update_big_order”,
bigOrders: BigOrder[],
}

workerRef.current.onmessage = (event: MessageEvent<UpdateBigOrderEvent>) => {
if (event?.data?.type === “update_big_order”) {
if (null !== animationFrameId) {
cancelAnimationFrame(animationFrameId);
}

animationFrameId = requestAnimationFrame(() => draw(event.data.bigOrders));
}
};

return () => {
if (null !== animationFrameId) {
cancelAnimationFrame(animationFrameId);
}
};
}, [workerRef.current, canvasSize, darkMode, dpiScale]);
useEffect(() => {
if (!size) {
return;
}

setCanvasSize({
width: Math.floor(size.width) * dpiScale,
height: Math.floor(size.height) * dpiScale,
});
}, [dpiScale, size]);
return <div ref={containerRef} className={styles.canvasWrapper}>
<canvas
ref={canvasRef}
className={styles.canvas}
width={canvasSize?.width}
height={canvasSize?.height}
/>
</div>;
};

export default BigOrders;

import {BigOrder} from “…/…/…/workers/big-order-builder”;
import {CanvasSize} from “./BigOrders”;
import bigOrdersOptions from “./BigOrders.options”;

const bigOrdersDrawer = {
clear: (ctx: CanvasRenderingContext2D, size: CanvasSize) => {
ctx.clearRect(0, 0, size.width, size.height);
},
drawChain: (
ctx: CanvasRenderingContext2D,
canvasSize: CanvasSize,
bigOrders: BigOrder[],
cellHeight: number,
dpiScale: number,
) => {
bigOrders.forEach((order: BigOrder, index: number) => {
const yPosition = cellHeight * index;
bigOrdersDrawer.drawSymbol(
ctx,
order.symbol,
yPosition,
cellHeight,
canvasSize.width,
dpiScale,
true
);
drawSymbol: (
ctx: CanvasRenderingContext2D,
symbol: string,
yPosition: number,
cellHeight: number,
fullWidth: number,
dpiScale: number,
darkMode: boolean,
) => {
ctx.textBaseline = “middle”;
ctx.font = ${bigOrdersOptions().font.weight} ${bigOrdersOptions().font.sizePx * dpiScale}px ${bigOrdersOptions().font.family};
ctx.fillStyle = ${bigOrdersOptions().font.color};
ctx.fillText(
symbol,
0,
yPosition + cellHeight / 2,
);
},

export default bigOrdersDrawer;

высота канвас 200px,
cellHeight // 20px,
// bigOrders.length 100-350

const yPosition = cellHeight * index;
получается на каждый раз когда пробегаемся по массиву через forEach, то yPosition увеличивается // сначала будет равно 0, потом 20, потом 40, потом 60, потом 80 и т.д. Если bigOrders длина 200, то последний элемент будет отображен на оси Y 4000, а canvas всего 200.
Нужно, чтобы canvas можно было скроллить мышкой от самого первого, до самого последнего