Skip to content

Commit

Permalink
Merge pull request #102 from devexperts/feat-improve-performace-by-de…
Browse files Browse the repository at this point in the history
…leting-redraw-background-canvas-area

feat: removed redrawBackgroundChartArea and improved performace
  • Loading branch information
KirillBobkov authored Dec 22, 2023
2 parents d206bc0 + 46319bf commit 5fa9dad
Show file tree
Hide file tree
Showing 13 changed files with 37 additions and 56 deletions.
3 changes: 0 additions & 3 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 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
24 changes: 12 additions & 12 deletions src/chart/components/y_axis/price_labels/price-label.drawer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
FullChartColors,
getFontFromConfig,
YAxisAlign,
YAxisLabelAppearanceType, YAxisLabelMode,
YAxisLabelAppearanceType,
YAxisLabelMode,
} from '../../../chart.config';
import { redrawBackgroundArea } from '../../../drawers/chart-background.drawer';
import { Bounds } from '../../../model/bounds.model';
Expand Down Expand Up @@ -47,7 +48,7 @@ export function drawLabel(
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 +77,7 @@ export function drawLabel(
const showLine = isLineVisible(bounds, labelY, labelBoxHeight);

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

let lineXStart: number;
let lineXEnd: number;
Expand All @@ -94,29 +95,29 @@ 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();
_drawDescription();
}
};

const drawLineLabelLabel = () => {
_drawLine();
_drawLabel();
_drawDescription();
}
};

const drawLabelLabel = () => {
_drawDescription();
_drawLabel();
}
};

const labelDrawerByMode: Record<Exclude<YAxisLabelMode, 'none'>, () => void> = {
'line': drawLineLabel,
line: drawLineLabel,
'line-label': drawLineLabelLabel,
'label': drawLabelLabel,
}
label: drawLabelLabel,
};

if (mode !== 'none' && checkLabelInBoundaries(centralY, bounds, labelBoxHeight)) {
labelDrawerByMode[mode]();
Expand All @@ -134,9 +135,9 @@ function drawDescription(
labelBounds: Bounds,
paneBounds: Bounds,
visualLabel: VisualYAxisLabel,
align: YAxisAlign = 'right',
yAxisState: YAxisConfig,
): void {
const align: YAxisAlign = yAxisState.align || 'right';
const description = visualLabel.description;
if (!description || description.length === 0) {
return;
Expand Down Expand Up @@ -170,6 +171,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 @@ -47,7 +47,7 @@ export class YAxisPriceLabelsDrawer implements Drawer {
vl,
this.canvasBoundsContainer,
extent.yAxis.state,
this.fullConfig.colors.yAxis,
this.fullConfig.colors,
),
);
});
Expand All @@ -61,7 +61,7 @@ export class YAxisPriceLabelsDrawer implements Drawer {
l,
this.canvasBoundsContainer,
extent.yAxis.state,
this.fullConfig.colors.yAxis,
this.fullConfig.colors,
),
);
}
Expand Down
11 changes: 5 additions & 6 deletions src/chart/components/y_axis/y-axis-labels.drawer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
* If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { YAxisConfig, FullChartColors, getFontFromConfig } from '../../chart.config';
import { redrawBackgroundArea } from '../../drawers/chart-background.drawer';
import { Bounds } from '../../model/bounds.model';
import { drawPriceLabel, drawRoundedRect } from '../../utils/canvas/canvas-drawing-functions.utils';
import { calculateSymbolHeight, calculateTextWidth } from '../../utils/canvas/canvas-font-measure-tool.utils';
Expand Down Expand Up @@ -200,11 +199,10 @@ export function drawPlainLabel(
yAxisState: YAxisConfig,
yAxisColors: FullChartColors['yAxis'],
checkBoundaries: boolean = true,
backgroundCtx?: CanvasRenderingContext2D,
) {
const align = yAxisState.align;
const textFont = config.textFont ?? getFontFromConfig(yAxisState);
const bgColor = config.bgColor;
const bgColor = yAxisColors.backgroundColor;
const textColor =
config.textColor ??
getLabelTextColorByBackgroundColor(bgColor, yAxisColors.labelTextColor, yAxisColors.labelInvertedTextColor);
Expand All @@ -230,14 +228,15 @@ export function drawPlainLabel(
const xTextOffset = yAxisState.labelBoxMargin.end;
ctx.font = textFont;
const textWidth = calculateTextWidth(text, ctx, textFont);

const marginEnd = xTextOffset - paddingEnd;
const width = paddingStart !== undefined ? textWidth + paddingStart + paddingEnd : bounds.width - marginEnd;
const x = align === 'right' ? bounds.x + bounds.width - marginEnd - width : bounds.x + marginEnd;

const textX = align === 'right' ? bounds.x + bounds.width - textWidth - xTextOffset : xTextOffset;

backgroundCtx && redrawBackgroundArea(backgroundCtx, ctx, x, labelBoxTopY, width, labelBoxHeight);
// label can overlap with regular price y-axis label, so we need to hide regular y-axis label
ctx.fillStyle = bgColor;
ctx.strokeStyle = bgColor;
ctx.fillRect(x, labelBoxTopY, width, labelBoxHeight);

ctx.fillStyle = textColor;
ctx.fillText(text, textX, centralY + fontHeight / 2 - 1); // -1 for font height adjustment
Expand Down
3 changes: 2 additions & 1 deletion src/chart/drawers/chart-background.drawer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class BackgroundDrawer implements Drawer {
this.canvasModel.clear();
const ctx = this.canvasModel.ctx;
if (this.config.colors.chartAreaTheme.backgroundMode === 'gradient') {
const grd = ctx.createLinearGradient(0, 0, this.canvasModel.width, this.canvasModel.height);
const grd = ctx.createLinearGradient(0, 0 + this.canvasModel.height / 2, this.canvasModel.width, 0 + this.canvasModel.height / 2);
grd.addColorStop(0, this.config.colors.chartAreaTheme.backgroundGradientTopColor);
grd.addColorStop(1, this.config.colors.chartAreaTheme.backgroundGradientBottomColor);
ctx.fillStyle = grd;
Expand All @@ -45,6 +45,7 @@ export class BackgroundDrawer implements Drawer {

// this function in used in case when
// some entity can overlap with another chart entity, so we need to hide the another entity
// it has very (!!!) poor perfomance, use it carefully
export const redrawBackgroundArea = (
backgroundCtx: CanvasRenderingContext2D,
ctx: CanvasRenderingContext2D,
Expand Down

0 comments on commit 5fa9dad

Please sign in to comment.