Загрузка данных
import { CanvasRenderingTarget2D } from 'fancy-canvas';
import { IPrimitivePaneRenderer } from 'lightweight-charts';
import { getThemeStore } from '@src/theme';
import { Rectangle } from './rectangle';
const UI = {
borderWidth: 1,
handleSize: 10,
handleBorderWidth: 1,
textOffset: 4,
textLineHeightMultiplier: 1.2,
};
export class RectanglePaneRenderer implements IPrimitivePaneRenderer {
private readonly rectangle: Rectangle;
constructor(rectangle: Rectangle) {
this.rectangle = rectangle;
}
public draw(target: CanvasRenderingTarget2D): void {
const data = this.rectangle.getRenderData();
if (!data) {
return;
}
const { colors } = getThemeStore();
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();
if (data.showFill) {
context.fillStyle = data.fillColor;
context.fillRect(left, top, right - left, bottom - top);
}
context.lineWidth = UI.borderWidth * pixelRatio;
context.strokeStyle = data.borderColor;
context.strokeRect(left, top, right - left, bottom - top);
drawRectangleText(context, {
left,
top,
text: data.text,
fontSize: data.fontSize,
isBold: data.isBold,
isItalic: data.isItalic,
textColor: data.textColor,
horizontalPixelRatio,
verticalPixelRatio,
});
if (data.showHandles) {
for (const handle of Object.values(data.handles)) {
drawHandle(
context,
handle.x * horizontalPixelRatio,
handle.y * verticalPixelRatio,
horizontalPixelRatio,
verticalPixelRatio,
colors.chartLineColor,
colors.chartBackground,
);
}
}
context.restore();
});
}
}
function drawRectangleText(
context: CanvasRenderingContext2D,
params: {
left: number;
top: number;
text: string;
fontSize: number;
isBold: boolean;
isItalic: boolean;
textColor: string;
horizontalPixelRatio: number;
verticalPixelRatio: number;
},
): void {
const { left, top, text, fontSize, isBold, isItalic, textColor, horizontalPixelRatio, verticalPixelRatio } = params;
if (!text.trim()) {
return;
}
const lines = text.split('\n');
const safeFontSize = Math.max(1, fontSize);
const fontSizePx = safeFontSize * verticalPixelRatio;
const lineHeight = safeFontSize * UI.textLineHeightMultiplier * verticalPixelRatio;
const offsetX = UI.textOffset * horizontalPixelRatio;
const offsetY = UI.textOffset * verticalPixelRatio;
const fontWeight = isBold ? '700 ' : '';
const fontStyle = isItalic ? 'italic ' : '';
const textX = left + offsetX;
const blockHeight = lines.length * lineHeight;
const firstLineY = top - offsetY - blockHeight + lineHeight / 2;
context.save();
context.font = `${fontStyle}${fontWeight}${fontSizePx}px Inter, sans-serif`;
context.fillStyle = textColor;
context.textAlign = 'left';
context.textBaseline = 'middle';
lines.forEach((line, index) => {
context.fillText(line, textX, firstLineY + index * lineHeight);
});
context.restore();
}
function drawHandle(
context: CanvasRenderingContext2D,
x: number,
y: number,
horizontalPixelRatio: number,
verticalPixelRatio: number,
strokeColor: string,
fillColor: string,
): void {
const width = UI.handleSize * horizontalPixelRatio;
const height = UI.handleSize * verticalPixelRatio;
const left = x - width / 2;
const top = y - height / 2;
context.save();
context.fillStyle = fillColor;
context.strokeStyle = strokeColor;
context.lineWidth = UI.handleBorderWidth * Math.max(horizontalPixelRatio, verticalPixelRatio);
context.beginPath();
context.rect(left, top, width, height);
context.fill();
context.stroke();
context.restore();
}