Custom drawings
For source code owners only!
How to create and add a custom drawing
DXcharts includes a wide range of built-in drawings. If you're maintaining the DXcharts codebase and need to implement a custom drawing, this guide walks you through the process of creating and registering one step by step.
🔸 AbstractFigure
All drawings extend the AbstractFigure
class:
export abstract class AbstractFigure<T> {// number of points being used in the process of drawing creationabstract points: number;public data: unknown;// type of drawing, usually each drawing has it's own typeabstract readonly type: DrawingType;// hide the drawinghidden?: StringTMap<number>;// add drawing after clicking 'Enter' buttonpublic readonly commitOnEnter: boolean = false;// additional vector to drawings which contain text (TextNote, Callout)textToolPoint?: Vector;// edit mode turns off text container to drawings which contain text (TextNote, Callout)isEditing?: boolean;// update props when drawing is hidden and can't be reachedupdateHiddenProps?: (points: Vector[]) => void;// returns labels of Y axisgetYAxisLabels?: (viewModel: DrawingViewModel,ctx: CanvasRenderingContext2D,chartBootstrap: ChartWithDrawings,) => YAxisLabelDrawProps[];// returns labels of X axisgetXAxisLabels?: (chart: ChartWithDrawings,viewModel: DrawingViewModel,properties: CommonFigureProps,points: Vector[],) => Array<XAxisLabel>;constraint?: FigureConstraint;// returns key viewpoints valuescalculateKeyViewPoints?: (points: ViewPoint[]) => ViewPoint[];resize?: (viewModel: DrawingViewModel, movedPoint: PointEvent) => void;rotate?: (viewModel: DrawingViewModel, movedPoint: PointEvent, match: DragDrawingInfo) => void;borderMatchFunction?: (viewModel: DrawingViewModel,initialPoint: IndexValuePoint,chartBootstrap: ChartWithDrawings,) => boolean;// setup some custom action when figure is being movedcustomMoveFigureAction?: (viewModel: DrawingViewModel,chartBootstrap: ChartWithDrawings,drawings: DrawingsModel,initial: IndexValuePoint,initialValues: IndexValuePoint[],point: PointEvent,) => void;wheelFigureAction?: (increment: number) => void;// setup some custom action when mouse up event is triggered for current figureonUp?: (viewModel: DrawingViewModel, drawings: DrawingsModel) => void;disablePointDrag?: boolean;showPoints?: (viewModel: DrawingViewModel) => boolean;// return value indicates whether we should commit figure or notbeforeCommit?: (model: DrawingViewModel, drawings: DrawingsModel) => boolean;// return value indicates whether we should cancel figure or notbeforeCancel?: (model: DrawingModel<DrawingType>, drawings: DrawingsModel) => boolean;// used in drawing mode to start a new drawing after the old one is finishedgetClone(model: DrawingModel<DrawingType>): NullableDrawingModel {return cloneUnsafe(model);}pointConstraint?: (point: PointEvent, viewModel: DrawingViewModel, chartBootstrap: ChartWithDrawings) => boolean;downMoveHandler?: (point: PointEvent, viewModel: DrawingViewModel, drawings: DrawingsModel) => void;isDisallowed?: (model: DrawingModel<DrawingType>, pointEvent: PointEvent) => boolean;// set a cursor appearancegetCursor?: (point: ViewPoint, viewModel: DrawingViewModel, chart: ChartWithDrawings) => CursorType | undefined;// implementation of drawing logicabstract draw(viewModel: DrawingViewModel,ctx: ExtendedContext,properties: T,chartBootstrap: ChartWithDrawings,paneComponent: PaneComponent,drawings: DrawingsModel,): void;drawPoints?(vm: DrawingViewModel,ctx: ExtendedContext,chartConfig: FullChartConfigDrawings,drawings: DrawingsModel,radiusExtension: number,): void;toString() {return this.type;}}
You must implement the following:
points
DrawingType
draw
method
These are the core properties required for any custom drawing.
Creating a drawing
DXcharts already includes geometric shapes like Oval
and Rectangle
.
In this example, you'll create a custom Triangle
.
Start by creating a file that extends the AbstractFigure
class.
By convention, these files are located in chart-core-modules/modules/drawings/figures/
folder, but you can choose any location.
export class Triangle extends AbstractFigure<TriangleProperties> {public points: number = 3;//@ts-ignore triangle drawing type is not implemented by defaultpublic type: DrawingType = 'triangle';draw(viewModel: DrawingViewModel, ctx: ExtendedContext, properties: TriangleProperties): void {const points = viewModel.keyViewPoints;const lineProperties = getLineProperties(viewModel.model, properties);if (points.length === 2) {ctx.prepareStroke(lineProperties);ctx.beginPath();// TODO fix TS error// @ts-ignorectx.addSegmentPath(points[0], points[1]);ctx.stroke();}if (points.length === 3) {if (getProperty(properties.style, canvasPropertiesMap.PROP_FILL_BACKGROUND, true)) {ctx.prepareFill(lineProperties);ctx.beginPath();// TODO fix TS error// @ts-ignorethis.addTrianglePath(points[0], points[1], points[2], ctx);ctx.fill();}ctx.prepareStroke(lineProperties);ctx.beginPath();// TODO fix TS error// @ts-ignorethis.addTrianglePath(points[0], points[1], points[2], ctx);ctx.stroke();}}addTrianglePath = (a: Vector, b: Vector, c: Vector, context: ExtendedContext): void => {context.context.moveTo(a.x, a.y);context.context.lineTo(b.x, b.y);context.context.lineTo(c.x, c.y);context.context.lineTo(a.x, a.y);context.closePath();};}
Be sure to implement the points
, type
, and draw
properties.
The rest of the implementation is up to you.
🔸 Registering the drawing in the chart
After implementing the drawing, register it by updating the following files:
1. FigureFactory.ts
Import your drawing (e.g., Triangle.ts
) and add a new case
in the switch statement:
case 'triangle': {return new Triangle();}
You can optionally pass formatterProvider
to the constructor if needed.
2. PropertiesByType.ts
Import the drawing and its properties interface, then update the PropertiesByType
interface:
export interface TriangleProperties {line: LineProperties;style: {fillBackground: boolean;};showTime?: boolean;showPrice?: boolean;}
3. DrawingTypes.ts
Add the new drawing to the availableDrawingTypes
array to satisfy the TypeScript check:
export const availableDrawingTypes = ['trend','line','horizontal_line','horizontal_ray','vertical_line','extended_line','ellipse','pitchfork','extended','ray','curve','arc','info_line','brush','path','date_price_range','date_range','price_range','highlighter','icon','rectangle','gann_box','gann_square','fibonacci_ark','fibonacci_circles','fibonacci_rays','gann_fan','trend_channel','multichannel','fibonacci_retracements','text','callout','price_label','base_isolation_tool','magnifying_tool_rectangle','magnifying_tool_time_range','magnifying_tool_time_range_wheel','vertical_arrow_up','vertical_arrow_down','arrow','elliott_wave','elliott_correction_wave','fibonacci_projection','fibonacci_channel','fibonacci_time_zones','regression_trend','fibonacci_spiral','cycle_brackets','fibonacci_time_extension','fibonacci_time_ratios','range_volume_by_price','anchored_volume_by_price',] as const;
Starting a custom drawing
Use the DrawingsComponent
as the entry point. Call the startDrawing
method to place the first point and begin the drawing process:
export const startTriangleDrawing = (chart: ChartWithDrawings ) => {// @ts-ignore triangle drawing type is not implemented by defaultconst triangleDrawingConfig: NewDrawingConfig<'triangle'> = {properties: {line: {lineColor: '#FFAA00',lineWidth: 1,lineDash: [],},background: {fillStyle: '#FFAA00',opacity: 0.2,},showTime: true,showPrice: true,},// @ts-ignore triangle drawing type is not implemented by defaulttype: 'triangle',};chart.drawings.startDrawing(triangleDrawingConfig);}
Optional AbstractFigure
properties
The required properties are the minimum needed to create a drawing.
However, AbstractFigure
also includes several optional properties and methods that can enhance behavior.
For example:
customEndMoveFigureAction
: triggers a callback when the drawing move endsgetCursor
: defines a custom mouse cursor
Use these options to customize interaction and improve the drawing experience.