Skip to content

Commit

Permalink
feat: removed redrawBackgroundChartArea and improved performace with …
Browse files Browse the repository at this point in the history
…native ctx.rect
  • Loading branch information
KirillBobkov committed Dec 15, 2023
1 parent 9e84700 commit 98cf058
Show file tree
Hide file tree
Showing 13 changed files with 43 additions and 60 deletions.
4 changes: 0 additions & 4 deletions src/chart/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,6 @@ export default class ChartBootstrap {
timeZoneModel,
chartPanComponent,
this.cursorHandler,
backgroundCanvasModel,
);
this.chartComponents.push(this.xAxisComponent);
this.userInputListenerComponents.push(this.xAxisComponent.xAxisScaleHandler);
Expand Down Expand Up @@ -512,7 +511,6 @@ export default class ChartBootstrap {
paneManager,
this.crossEventProducer,
this.hoverProducer,
this.backgroundCanvasModel,
);

this.chartComponents.push(this.crossToolComponent);
Expand All @@ -538,7 +536,6 @@ export default class ChartBootstrap {
drawingManager,
formatterFactory,
this.cursorHandler,
backgroundCanvasModel,
);
this.eventsComponent = eventsComponent;
this.chartComponents.push(eventsComponent);
Expand All @@ -564,7 +561,6 @@ export default class ChartBootstrap {

const yAxisLabelsDrawer = new YAxisPriceLabelsDrawer(
yAxisLabelsCanvasModel,
this.backgroundCanvasModel,
this.canvasBoundsContainer,
this.config,
this.paneManager,
Expand Down
4 changes: 2 additions & 2 deletions src/chart/chart.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ export const getDefaultConfig = (): FullChartConfig => ({
},
],
yAxis: {
backgroundColor: 'transparent',
backgroundColor: 'rgba(20,20,19,1)',
labelBoxColor: 'rgba(20,20,19,1)',
labelTextColor: 'rgba(128,128,128,1)',
labelInvertedTextColor: 'rgba(20,20,19,1)',
Expand Down Expand Up @@ -538,7 +538,7 @@ export const getDefaultConfig = (): FullChartConfig => ({
prevDayClose: { boxColor: 'rgba(107,96,86,1)', textColor: 'rgba(255,255,255,1)' },
},
xAxis: {
backgroundColor: 'transparent',
backgroundColor: 'rgba(20,20,19,1)',
labelTextColor: 'rgba(128,128,128,1)',
},
navigationMap: {
Expand Down
3 changes: 0 additions & 3 deletions src/chart/components/cross_tool/cross-tool.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ export class CrossToolComponent extends ChartBaseElement {
private paneManager: PaneManager,
crossEventProducer: CrossEventProducerComponent,
hoverProducer: HoverProducerComponent,
private backgroundCanvasModel: CanvasModel,
) {
super();
this.model = new CrossToolModel(
Expand Down Expand Up @@ -68,7 +67,6 @@ export class CrossToolComponent extends ChartBaseElement {
this.config,
this.canvasBoundsContainer,
this.paneManager,
this.backgroundCanvasModel,
() => true,
),
);
Expand All @@ -78,7 +76,6 @@ export class CrossToolComponent extends ChartBaseElement {
this.config,
this.canvasBoundsContainer,
this.paneManager,
this.backgroundCanvasModel,
() => true,
true,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
*/
import { CanvasBoundsContainer, CanvasElement } from '../../../canvas/canvas-bounds-container';
import { FullChartConfig } from '../../../chart.config';
import { CanvasModel } from '../../../model/canvas.model';
import { avoidAntialiasing, drawRoundedRect } from '../../../utils/canvas/canvas-drawing-functions.utils';
import { PaneManager } from '../../pane/pane-manager.component';
import { priceLabelDrawersMap } from '../../y_axis/price_labels/price-label.drawer';
Expand All @@ -22,7 +21,6 @@ export class CrossAndLabelsDrawerType implements CrossToolTypeDrawer {
private config: FullChartConfig,
private canvasBoundsContainer: CanvasBoundsContainer,
private paneManager: PaneManager,
private backgroundCanvasModel: CanvasModel,
private crossDrawPredicate: () => boolean = () => true,
private noLines?: boolean,
) {}
Expand Down Expand Up @@ -165,7 +163,6 @@ export class CrossAndLabelsDrawerType implements CrossToolTypeDrawer {
extent.yAxis.state,
this.config.colors.yAxis,
true,
this.backgroundCanvasModel.ctx,
);
}
}
Expand Down
2 changes: 0 additions & 2 deletions src/chart/components/events/events.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ export class EventsComponent extends ChartBaseElement {
private drawingManager: DrawingManager,
private formatterFactory: DateTimeFormatterFactory,
cursorHandler: CursorHandler,
backgroundCanvasModel: CanvasModel,
) {
super();
this.eventsXAxisLabelFormatterProvider = () =>
Expand All @@ -46,7 +45,6 @@ export class EventsComponent extends ChartBaseElement {
hitTestCanvasModel.addSubscriber(eventsModel);

const eventsDrawer = new EventsDrawer(
backgroundCanvasModel,
canvasModel,
chartModel,
config,
Expand Down
16 changes: 5 additions & 11 deletions src/chart/components/events/events.drawer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { Bounds } from '../../model/bounds.model';
import { CanvasBoundsContainer, CanvasElement } from '../../canvas/canvas-bounds-container';
import { CustomIcon, FullChartConfig } from '../../chart.config';
import { CanvasModel } from '../../model/canvas.model';
import { redrawBackgroundArea } from '../../drawers/chart-background.drawer';
import { Drawer } from '../../drawers/drawing-manager';
import { DateTimeFormatter } from '../../model/date-time.formatter';
import { ChartModel } from '../chart/chart.model';
Expand All @@ -31,7 +30,6 @@ export class EventsDrawer implements Drawer {
private customIcons: Record<string, CreatedCustomIcon> = {};

constructor(
private backgroundCanvas: CanvasModel,
private canvasModel: CanvasModel,
private chartModel: ChartModel,
private config: FullChartConfig,
Expand Down Expand Up @@ -185,7 +183,7 @@ export class EventsDrawer implements Drawer {
/**
* This function is responsible for drawing a label on the canvas at a given x coordinate. The label contains a formatted timestamp of a given event. The function takes two parameters: x, which is the x coordinate where the label will be drawn, and event, which is an object containing information about the event, including its timestamp and type.
* The function first gets the canvas context and the bounds of the x-axis. It then retrieves the font family, font height, and top padding from the configuration object. The y coordinate of the label is calculated based on the font height, top padding, and the y coordinate of the x-axis bounds. The font is set using the retrieved font family and font height.
* The timestamp of the event is formatted using a formatter provider function. The width of the label is calculated using the canvas context's measureText() method. The function then calls another function, redrawBackgroundArea(), to hide the regular x-axis label that may overlap with the event label.
* The timestamp of the event is formatted using a formatter provider function. The width of the label is calculated using the canvas context's measureText() method. The function then draws rectangle with xAxis background, to be on to hide the regular x-axis label that may overlap with the event label.
* Finally, the function sets the fill style of the canvas context to the color associated with the event type in the configuration object. The label text is then drawn on the canvas context at the calculated x and y coordinates.
*/
drawLabel(x: number, event: EventWithId) {
Expand All @@ -199,14 +197,10 @@ export class EventsDrawer implements Drawer {
const labelText = this.formatterProvider()(event.timestamp);
const width = ctx.measureText(labelText).width;
// label can overlap with regular x-axis label, so we need to hide regular x-axis label
redrawBackgroundArea(
this.backgroundCanvas.ctx,
ctx,
x - width / 2,
xAxisBounds.y + 1,
width,
xAxisBounds.height - 1,
);
ctx.fillStyle = this.config.colors.xAxis.backgroundColor;
ctx.strokeStyle = this.config.colors.xAxis.backgroundColor;
ctx.fillRect(x - width / 2, xAxisBounds.y + 1, width, xAxisBounds.height - 1);


ctx.fillStyle = this.config.colors.events[event.type].color;
ctx.fillText(labelText, x - width / 2, y);
Expand Down
15 changes: 8 additions & 7 deletions src/chart/components/x_axis/x-axis-draw.functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
*/
import { CanvasBoundsContainer, CanvasElement } from '../../canvas/canvas-bounds-container';
import { FullChartConfig } from '../../chart.config';
import { redrawBackgroundArea } from '../../drawers/chart-background.drawer';
import { XAxisLabel } from './x-axis-labels.model';

const DEFAULT_X_LABEL_PADDING = { x: 4, y: 4 };
Expand All @@ -20,17 +19,17 @@ export type LabelAlign = 'start' | 'end' | 'middle';
* @param label
*/
export function drawXAxisLabel(
backgroundCtx: CanvasRenderingContext2D,
ctx: CanvasRenderingContext2D,
canvasBoundsContainer: CanvasBoundsContainer,
config: FullChartConfig,
label: XAxisLabel,
): void {
const alignType = label.alignType ?? 'middle';
const { fontSize, fontFamily, padding } = config.components.xAxis;
const xAxisColors = config.colors.xAxis;
const offsetTop = padding.top ?? 0;

const xAxis = canvasBoundsContainer.getBounds(CanvasElement.X_AXIS);
const xAxisBounds = canvasBoundsContainer.getBounds(CanvasElement.X_AXIS);
ctx.save();
ctx.font = `bold ${fontSize}px ${fontFamily}`;
const labelWidth = ctx.measureText(label.text).width;
Expand All @@ -50,19 +49,21 @@ export function drawXAxisLabel(
break;
}
// label can overlap with regular x-axis label, so we need to hide regular x-axis label
redrawBackgroundArea(backgroundCtx, ctx, boxStart, xAxis.y, boxWidth, xAxis.height - 1);
ctx.fillStyle = xAxisColors.backgroundColor;
ctx.strokeStyle = xAxisColors.backgroundColor;
ctx.fillRect(boxStart, xAxisBounds.y, boxWidth, xAxisBounds.height);

if (alignType !== 'middle') {
ctx.strokeStyle = label.color;
ctx.beginPath();
ctx.moveTo(x, xAxis.y);
ctx.lineTo(x, xAxis.y + xAxis.height);
ctx.moveTo(x, xAxisBounds.y);
ctx.lineTo(x, xAxisBounds.y + xAxisBounds.height);
ctx.stroke();
}

ctx.fillStyle = label.color;
const xTextPos = boxStart + DEFAULT_X_LABEL_PADDING.x;
const yTextPos = xAxis.y + offsetTop + fontSize; // -2 for vertical adjustment
const yTextPos = xAxisBounds.y + offsetTop + fontSize; // -2 for vertical adjustment
ctx.fillText(label.text, xTextPos, yTextPos);
ctx.restore();
}
3 changes: 1 addition & 2 deletions src/chart/components/x_axis/x-axis-labels.drawer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import { fillRect } from '../../utils/canvas/canvas-drawing-functions.utils';
*/
export class XAxisLabelsDrawer implements Drawer {
constructor(
private backgroundCanvasModel: CanvasModel,
private config: FullChartConfig,
private canvasModel: CanvasModel,
private canvasBoundsContainer: CanvasBoundsContainer,
Expand All @@ -36,7 +35,7 @@ export class XAxisLabelsDrawer implements Drawer {
const ctx = this.canvasModel.ctx;
this.drawHighlightedBackgroundBetweenLabels();
this.xAxisLabelsModel.labels.forEach(l => {
drawXAxisLabel(this.backgroundCanvasModel.ctx, ctx, this.canvasBoundsContainer, this.config, l);
drawXAxisLabel(ctx, this.canvasBoundsContainer, this.config, l);
});
}

Expand Down
2 changes: 0 additions & 2 deletions src/chart/components/x_axis/x-axis.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ export class XAxisComponent extends ChartBaseElement {
private timeZoneModel: TimeZoneModel,
chartPanComponent: ChartPanComponent,
cursorHandler: CursorHandler,
backgroundCanvasModel: CanvasModel,
) {
super();
const xAxisLabelsGenerator = new XAxisTimeLabelsGenerator(
Expand Down Expand Up @@ -79,7 +78,6 @@ export class XAxisComponent extends ChartBaseElement {
);
xAxisCompositeDrawer.addDrawer(this.xAxisDrawer);
this.xAxisLabelsDrawer = new XAxisLabelsDrawer(
backgroundCanvasModel,
config,
canvasModel,
canvasBoundsContainer,
Expand Down
29 changes: 18 additions & 11 deletions src/chart/components/y_axis/price_labels/price-label.drawer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
YAxisAlign,
YAxisLabelAppearanceType, YAxisLabelMode,
} from '../../../chart.config';
import { redrawBackgroundArea } from '../../../drawers/chart-background.drawer';
import { Bounds } from '../../../model/bounds.model';
import { avoidAntialiasing, drawLine } from '../../../utils/canvas/canvas-drawing-functions.utils';
import { calculateSymbolHeight, calculateTextWidth } from '../../../utils/canvas/canvas-font-measure-tool.utils';
Expand Down Expand Up @@ -41,13 +40,12 @@ export const priceLabelDrawersMap: Record<YAxisVisualLabelType, LabelDrawer> = {
*/
export function drawLabel(
ctx: CanvasRenderingContext2D,
backgroundCtx: CanvasRenderingContext2D,
bounds: Bounds,
paneBounds: Bounds,
visualLabel: VisualYAxisLabel,
canvasBoundsContainer: CanvasBoundsContainer,
config: YAxisConfig,
colors: FullChartColors['yAxis'],
colors: FullChartColors,
) {
const centralY = visualLabel.y;
const text = visualLabel.labelText;
Expand Down Expand Up @@ -76,7 +74,7 @@ export function drawLabel(
const showLine = isLineVisible(bounds, labelY, labelBoxHeight);

const _drawDescription = () =>
showDescription && drawDescription(backgroundCtx, ctx, bounds, paneBounds, visualLabel, config.align, config);
showDescription && drawDescription(ctx, paneBounds, visualLabel, config, colors);

let lineXStart: number;
let lineXEnd: number;
Expand All @@ -94,7 +92,7 @@ export function drawLabel(
const lineY = visualLabel.lineY ?? visualLabel.y;
const _drawLine = () =>
showLine && avoidAntialiasing(ctx, () => drawLine(ctx, lineXStart, lineY, lineXEnd, lineY, 1));
const _drawLabel = () => drawLabel(ctx, bounds, text, centralY, visualLabel, config, colors, false, backgroundCtx);
const _drawLabel = () => drawLabel(ctx, bounds, text, centralY, visualLabel, config, colors.yAxis, false);

const drawLineLabel = () => {
_drawLine();
Expand Down Expand Up @@ -129,14 +127,13 @@ const isLineVisible = (bounds: Bounds, labelY: number, labelBoxHeight: number) =
labelY > bounds.y + labelBoxHeight / 2 && labelY < bounds.y + bounds.height - labelBoxHeight / 2;

function drawDescription(
backgroundCtx: CanvasRenderingContext2D,
ctx: CanvasRenderingContext2D,
labelBounds: Bounds,
paneBounds: Bounds,
visualLabel: VisualYAxisLabel,
align: YAxisAlign = 'right',
yAxisState: YAxisConfig,
colors: FullChartColors,
): void {
const align: YAxisAlign = yAxisState.align || 'right';
const description = visualLabel.description;
if (!description || description.length === 0) {
return;
Expand All @@ -160,8 +157,19 @@ function drawDescription(
const boundsEnd = paneBounds.x + paneBounds.width;
const x = align === 'right' ? boundsEnd - rectWidth : paneBounds.x + descriptionPadding;

redrawBackgroundArea(backgroundCtx, ctx, x, labelBoxY, width, labelBoxHeight, 0.8);

if (colors.chartAreaTheme.backgroundMode === 'gradient' && align === 'right') {
ctx.fillStyle = colors.chartAreaTheme.backgroundGradientBottomColor;
ctx.strokeStyle = colors.chartAreaTheme.backgroundGradientBottomColor;
}
if (colors.chartAreaTheme.backgroundMode === 'gradient' && align === 'left') {
ctx.fillStyle = colors.chartAreaTheme.backgroundGradientTopColor;
ctx.strokeStyle = colors.chartAreaTheme.backgroundGradientTopColor;
}
if (colors.chartAreaTheme.backgroundMode === 'regular') {
ctx.fillStyle = colors.chartAreaTheme.backgroundColor;
ctx.strokeStyle = colors.chartAreaTheme.backgroundColor;
}
ctx.fillRect(x, labelBoxY, width, labelBoxHeight);
ctx.fillStyle = visualLabel.descColor ?? visualLabel.bgColor;
ctx.font = textFont;
const xTextBounds =
Expand All @@ -170,6 +178,5 @@ function drawDescription(
: paneBounds.x + descriptionPadding * 2;

ctx.fillText(description, xTextBounds, centralY + fontHeight / 2 - 1); // -1 for font height adjustment

ctx.restore();
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,13 @@ import { LabelGroup, VisualYAxisLabel } from './y-axis-labels.model';
export class YAxisPriceLabelsDrawer implements Drawer {
constructor(
private yAxisLabelsCanvasModel: CanvasModel,
private backgroundCanvasModel: CanvasModel,
private canvasBoundsContainer: CanvasBoundsContainer,
private fullConfig: FullChartConfig,
private paneManager: PaneManager,
) {}

draw() {
const ctx = this.yAxisLabelsCanvasModel.ctx;
const backgroundCtx = this.backgroundCanvasModel.ctx;

this.paneManager.yExtents.forEach(extent => {
if (extent.yAxis.state.visible) {
Expand All @@ -41,27 +39,25 @@ export class YAxisPriceLabelsDrawer implements Drawer {
l.labels.forEach(vl =>
drawLabel(
ctx,
backgroundCtx,
bounds,
paneBounds,
vl,
this.canvasBoundsContainer,
extent.yAxis.state,
this.fullConfig.colors.yAxis,
this.fullConfig.colors,
),
);
});
// TODO I added this as a simple mechanism to add custom labels, we need to review it
Object.values(extent.yAxis.model.fancyLabelsModel.customLabels).forEach(l =>
drawLabel(
ctx,
backgroundCtx,
yAxisBounds,
paneBounds,
l,
this.canvasBoundsContainer,
extent.yAxis.state,
this.fullConfig.colors.yAxis,
this.fullConfig.colors,
),
);
}
Expand Down
Loading

0 comments on commit 98cf058

Please sign in to comment.