Angular Freehand Drawing Tools Demo

Try the Angular Freehand Drawing Tools Demo for trading and financial charts using SciChart.js. This example can be used for drawing trends, arrow, markers, text, and more. View all the code samples and documentation links below.

Fullscreen

Edit

 Edit

Docs

drawExample.ts

index.tsx

tradingAnnotationExampleUtils.ts

Copy to clipboard
Minimise
Fullscreen
1import {
2    AnnotationHoverModifier,
3    ECursorStyle,
4    EObservableArrayChangedAction,
5    EXyDirection,
6    MouseWheelZoomModifier,
7    NumberRange,
8    ZoomExtentsModifier,
9} from "scichart";
10import {
11    EAnnotationVisibilityMode,
12    ESnapMode,
13    FreehandDrawingAnnotation,
14    FreehandDrawingModifier,
15    IFreehandDrawingAnnotationOptions,
16} from "scichart-financial-tools";
17import { createFinancialChart, TRADING_ANNOTATION_COLORS } from "../_shared/tradingAnnotationExampleUtils";
18
19export type TFreehandVariant = "editableOutline" | "nonEditableLine" | "thickHighlight" | "locked";
20
21const generateHandDrawnTrendline = () => {
22    const startX = 1705104000; // 2024-01-13 00:00 UTC
23    const endX = 1705276800; // 2024-01-15 00:00 UTC
24    const startY = 65000;
25    const endY = 67500;
26    const count = 110;
27    let seed = 1337;
28    const rand = () => {
29        seed = (seed * 9301 + 49297) % 233280;
30        return seed / 233280;
31    };
32    const points: { x: number; y: number }[] = [];
33    for (let i = 0; i < count; i++) {
34        const t = i / (count - 1);
35        const drift = Math.sin(t * Math.PI * 1.6 + 0.4) * 18;
36        const yJitter = (rand() - 0.5) * 16;
37        const xJitter = (rand() - 0.5) * ((endX - startX) / count) * 0.35;
38        points.push({
39            x: startX + (endX - startX) * t + xJitter,
40            y: startY + (endY - startY) * t + drift + yJitter,
41        });
42    }
43    return points;
44};
45
46const handDrawnTrendlinePoints = generateHandDrawnTrendline();
47
48type TInitialFreehandAnnotation = {
49    points: { x: number; y: number }[];
50    stroke: string;
51    strokeThickness: number;
52};
53
54const initialFreehandAnnotations: TInitialFreehandAnnotation[] = [
55    {
56        points: [
57            { x: 1705074424.9760892, y: 65731.6718827903 },
58            { x: 1705075600.6684432, y: 65642.12282942746 },
59            { x: 1705087445.885085, y: 65307.71857034999 },
60            { x: 1705085666.0820355, y: 65356.5315837517 },
61            { x: 1705081130.604812, y: 65410.78777490682 },
62            { x: 1705070214.7892404, y: 65438.14998565657 },
63            { x: 1705086971.8905394, y: 65356.648641337786 },
64            { x: 1705090526.8496335, y: 65388.83947751397 },
65            { x: 1705094908.9756804, y: 65460.77136416947 },
66            { x: 1705091521.3087788, y: 65449.416778318235 },
67        ],
68        stroke: "#F97066",
69        strokeThickness: 4,
70    },
71    {
72        points: [
73            { x: 1705025064.4852417, y: 66127.82401853298 },
74            { x: 1705025394.4226215, y: 65871.17526101925 },
75            { x: 1705024855.3700008, y: 65954.7251130947 },
76            { x: 1705029753.3136415, y: 65982.08732384446 },
77            { x: 1705035097.3697963, y: 65992.00795426602 },
78            { x: 1705040343.8388386, y: 65986.09654616822 },
79            { x: 1705044791.0229604, y: 65964.20677756841 },
80            { x: 1705045278.9585223, y: 65933.80106958018 },
81            { x: 1705043201.74713, y: 65906.67297400262 },
82            { x: 1705035543.48231, y: 65873.80905670639 },
83            { x: 1705027936.3345492, y: 65859.3231804271 },
84            { x: 1705022132.2248645, y: 65859.11832965145 },
85        ],
86        stroke: "#F97066",
87        strokeThickness: 4,
88    },
89    {
90        points: [
91            { x: 1705060251.609766, y: 65972.86903893946 },
92            { x: 1705060688.4282691, y: 65910.50660994725 },
93            { x: 1705065716.4880598, y: 65861.5180101664 },
94            { x: 1705069043.7438917, y: 65854.55308379373 },
95            { x: 1705070656.2547488, y: 65860.55228508111 },
96            { x: 1705072877.523307, y: 65910.76998951596 },
97            { x: 1705073388.6938958, y: 65974.47858074827 },
98            { x: 1705080935.4305873, y: 65866.11252042063 },
99        ],
100        stroke: "#F97066",
101        strokeThickness: 4,
102    },
103    {
104        points: [
105            { x: 1705091646.7779233, y: 65990.3691480607 },
106            { x: 1705091860.5401695, y: 65928.09451225805 },
107            { x: 1705094035.338674, y: 65896.19632004711 },
108            { x: 1705100819.966488, y: 65890.43123393191 },
109            { x: 1705104123.987293, y: 65914.89626942581 },
110            { x: 1705104742.0390048, y: 65990.60326323289 },
111            { x: 1705105169.5634973, y: 65837.34561863774 },
112            { x: 1705103979.9301271, y: 65712.03547272283 },
113            { x: 1705100095.033653, y: 65693.04287937887 },
114            { x: 1705092195.1245549, y: 65700.32971411331 },
115            { x: 1705088156.876904, y: 65734.97875959748 },
116            { x: 1705088291.6400592, y: 65779.40211352061 },
117            { x: 1705102172.2450452, y: 65854.20191103544 },
118            { x: 1705112846.4163384, y: 65896.78160797758 },
119        ],
120        stroke: "#F97066",
121        strokeThickness: 4,
122    },
123    {
124        points: [
125            { x: 1705271913.4095333, y: 68272.31899579709 },
126            { x: 1705254473.1984477, y: 68281.71286708124 },
127            { x: 1705243325.0326085, y: 68256.80886563948 },
128            { x: 1705241071.2350128, y: 68237.43583514073 },
129            { x: 1705240253.362071, y: 68204.68897543059 },
130            { x: 1705263358.2726805, y: 68162.25560047108 },
131            { x: 1705266676.2345016, y: 68137.556449805 },
132            { x: 1705266866.7617211, y: 68104.31209535395 },
133            { x: 1705263558.0939107, y: 68079.99338184268 },
134            { x: 1705255137.720213, y: 68056.46480703754 },
135            { x: 1705245188.4817545, y: 68058.92301634554 },
136            { x: 1705237929.859395, y: 68076.89135581115 },
137        ],
138        stroke: "#4EC385",
139        strokeThickness: 4,
140    },
141    {
142        points: [
143            { x: 1705282146.115318, y: 68129.39168317485 },
144            { x: 1705292318.4100335, y: 68129.39168317485 },
145            { x: 1705293675.335596, y: 68157.39771064813 },
146            { x: 1705293670.6885908, y: 68187.07180872327 },
147            { x: 1705292443.8791778, y: 68194.03673509593 },
148            { x: 1705280305.9011989, y: 68194.82687380207 },
149            { x: 1705275919.1281466, y: 68183.64787432998 },
150            { x: 1705274227.6181984, y: 68106.59471828281 },
151            { x: 1705277666.402159, y: 68075.6037223641 },
152            { x: 1705283721.4501324, y: 68055.26496678006 },
153            { x: 1705292304.4690173, y: 68053.74321816083 },
154            { x: 1705297546.2910542, y: 68064.07355013373 },
155        ],
156        stroke: "#4EC385",
157        strokeThickness: 4,
158    },
159    {
160        points: [
161            { x: 1705314953.9731023, y: 68304.74394714547 },
162            { x: 1705314517.1545992, y: 68072.50169633258 },
163        ],
164        stroke: "#4EC385",
165        strokeThickness: 4,
166    },
167    {
168        points: [
169            { x: 1705332956.4718356, y: 68300.50060964952 },
170            { x: 1705332956.4718356, y: 68178.23396097307 },
171            { x: 1705330897.8484647, y: 68051.95808997288 },
172        ],
173        stroke: "#4EC385",
174        strokeThickness: 4,
175    },
176    {
177        points: [
178            { x: 1705294609.3836718, y: 67929.01836017639 },
179            { x: 1705294474.6205165, y: 67825.6272472578 },
180            { x: 1705285682.4863908, y: 67528.6814157308 },
181            { x: 1705283005.8113081, y: 67676.43735377946 },
182            { x: 1705285306.0789573, y: 67543.25508519965 },
183            { x: 1705300334.4942653, y: 67679.27600024227 },
184        ],
185        stroke: "#4EC385",
186        strokeThickness: 4,
187    },
188];
189
190const variantOptions = (variant: TFreehandVariant, background: string): IFreehandDrawingAnnotationOptions => {
191    const base: IFreehandDrawingAnnotationOptions = {
192        isEditable: true,
193        strokeThickness: 4,
194        showBoxOutline: false,
195        boxOutlineStrokeDashArray: [6, 4],
196        snapMode: ESnapMode.None,
197        annotationsGripsRadius: 4,
198        gripSvgTemplate: (annotation: any, x: number, y: number) => {
199            const ann = annotation as FreehandDrawingAnnotation;
200            return `<circle cx="${x}" cy="${y}" r="${ann.annotationsGripsRadius}" fill="${background}" stroke="${ann.annotationsGripsStroke}" stroke-width="${ann.strokeThickness}" />`;
201        },
202    };
203
204    switch (variant) {
205        case "editableOutline":
206            return {
207                ...base,
208                stroke: TRADING_ANNOTATION_COLORS.freehand,
209                annotationsGripsStroke: TRADING_ANNOTATION_COLORS.freehand,
210                allowMove: true,
211                showBoxOutlineOnlyWhenSelected: false,
212            };
213        case "locked":
214            return {
215                ...base,
216                stroke: TRADING_ANNOTATION_COLORS.lockedFreehand,
217                annotationsGripsStroke: TRADING_ANNOTATION_COLORS.lockedFreehand,
218                keepAspectRatioOnResize: true,
219                forcedAspectRatio: 1,
220                showBoxOutlineOnlyWhenSelected: false,
221            };
222        case "nonEditableLine":
223            return {
224                ...base,
225                isEditable: false,
226                isSelected: false,
227                stroke: TRADING_ANNOTATION_COLORS.foreground,
228                strokeThickness: 2,
229                showBoxOutline: false,
230                gripVisibility: EAnnotationVisibilityMode.OnInteraction,
231                allowMove: false,
232                showBoxOutlineOnlyWhenSelected: false,
233            };
234        case "thickHighlight":
235            return {
236                ...base,
237                stroke: TRADING_ANNOTATION_COLORS.warning,
238                fill: `${TRADING_ANNOTATION_COLORS.warning}33`,
239                annotationsGripsStroke: TRADING_ANNOTATION_COLORS.warning,
240                strokeThickness: 4,
241                showBoxOutlineOnlyWhenSelected: false,
242            };
243    }
244};
245
246export const drawExample = async (rootElement: string | HTMLDivElement) => {
247    const { sciChartSurface, xAxis } = await createFinancialChart(rootElement, {
248        volatility: 0.0023,
249        title: "BTC / USDT - Freehand Drawing",
250        startDate: new Date("2024-01-01T00:00:00Z"),
251        dataSeed: 2024,
252    });
253
254    // Jan 20 2024 00:00 UTC
255    xAxis.visibleRange = new NumberRange(xAxis.visibleRange.min, 1705708800);
256
257    const freehandDrawingModifier = new FreehandDrawingModifier({
258        isDrawing: false,
259        keepDrawingAfterComplete: true,
260        pointSamplingDistancePx: 1,
261        simplifyTolerancePx: 0,
262        maxPoints: 6000,
263    });
264
265    sciChartSurface.chartModifiers.add(
266        new MouseWheelZoomModifier({ xyDirection: EXyDirection.XDirection }),
267        new ZoomExtentsModifier({ xyDirection: EXyDirection.XDirection }),
268        new AnnotationHoverModifier({
269            enableHover: true,
270            enableCursor: true,
271            idleCursor: ECursorStyle.Crosshair,
272        }),
273        freehandDrawingModifier
274    );
275
276    sciChartSurface.annotations.add(
277        ...initialFreehandAnnotations.map((data) =>
278            new FreehandDrawingAnnotation({
279                points: data.points,
280                stroke: data.stroke,
281                strokeThickness: data.strokeThickness,
282                isEditable: true,
283                isSelected: false,
284                allowMove: true,
285                showBoxOutline: false,
286                opacity: 0.9,
287                gripVisibility: EAnnotationVisibilityMode.OnInteraction,
288            })
289        ),
290        new FreehandDrawingAnnotation({
291            points: handDrawnTrendlinePoints,
292            isEditable: true,
293            isSelected: false,
294            allowMove: true,
295            showBoxOutline: false,
296            stroke: "#686c70",
297            annotationsGripsStroke: "#9AA0A6",
298            strokeThickness: 7,
299            opacity: 0.9,
300            gripVisibility: EAnnotationVisibilityMode.OnInteraction,
301        })
302    );
303
304    return {
305        sciChartSurface,
306        startDrawing: (variant: TFreehandVariant, color?: string) => {
307            const options = variantOptions(variant, sciChartSurface.background);
308            if (color) {
309                options.stroke = color;
310                options.annotationsGripsStroke = color;
311            }
312            freehandDrawingModifier.startDrawing(options);
313        },
314        stopDrawing: () => freehandDrawingModifier.stopDrawing(true),
315        clear: () => sciChartSurface.annotations.clear(true),
316        removeLast: () => {
317            const annotations = sciChartSurface.annotations;
318            if (annotations.size() > 0) {
319                annotations.removeAt(annotations.size() - 1, true);
320            }
321        },
322        exportAnnotations: () =>
323            sciChartSurface.annotations
324                .asArray()
325                .filter((a): a is FreehandDrawingAnnotation => a instanceof FreehandDrawingAnnotation)
326                .map((a) => ({
327                    points: a.points.map((p) => ({ x: p.x, y: p.y })),
328                    stroke: a.stroke,
329                    strokeThickness: a.strokeThickness,
330                })),
331        setKeepDrawingAfterComplete: (enabled: boolean) => {
332            (freehandDrawingModifier as any).keepDrawingAfterCompleteProperty = enabled;
333        },
334        setSampling: (pointSamplingDistancePx: number, simplifyTolerancePx: number, maxPoints: number) => {
335            freehandDrawingModifier.pointSamplingDistancePx = pointSamplingDistancePx;
336            (freehandDrawingModifier as any).simplifyTolerancePxProperty = simplifyTolerancePx;
337            (freehandDrawingModifier as any).maxPointsProperty = maxPoints;
338        },
339    };
340};
341

angular Chart Examples & Demos

See Also: Financial Charts (13 Demos)

Angular Candlestick Chart | Online JavaScript Chart Examples

Angular Candlestick Chart

Discover how to create a Angular Candlestick Chart or Stock Chart using SciChart.js. For high Performance JavaScript Charts, get your free demo now.

Angular OHLC Chart | Angular Charts | SciChart.js Demo

Angular OHLC Chart

Easily create Angular OHLC Chart or Stock Chart using feature-rich SciChart.js chart library. Supports custom colors. Get your free trial now.

Angular Realtime Ticking Stock Chart | SciChart.js Demo

Angular Realtime Ticking Stock Charts

Create a Angular Realtime Ticking Candlestick / Stock Chart with live ticking and updating, using the high performance SciChart.js chart library. Get free demo now.

Angular Orderbook Heatmap | Angular Charts | SciChart.js Demo

Angular Orderbook Heatmap

Create an Angular heatmap chart showing historical orderbook levels, using the high performance SciChart.js chart library. Get free demo now.

Angular Multi-Pane Stock Chart using Subcharts | View JavaScript Charts

Angular Multi-Pane Stock Charts using Subcharts

Create a Angular Multi-Pane Candlestick / Stock Chart with indicator panels, synchronized zooming, panning and cursors. Get your free trial of SciChart.js now.

Tenor Curves Demo | Angular Charts | SciChart.js Demo

Tenor Curves Demo

Demonstrating the capability of SciChart.js to create a composite 2D &amp; 3D Chart application. An example like this could be used to visualize Tenor curves in a financial setting, or other 2D/3D data combined on a single screen.

Angular Multi-Pane Stock Chart | View JavaScript Charts

Angular Multi-Pane Stock Charts using Sync Multi-Chart

Create a Angular Multi-Pane Candlestick / Stock Chart with indicator panels, synchronized zooming, panning and cursors. Get your free trial of SciChart.js now.

Financial Data Filters | Angular Charts | SciChart.js Demo

Financial Data Filters

Shows one seeded OHLC data set toggled between no filter, Heikin-Ashi, Renko, and Point & Figure views.

Angular Market Depth Chart | Angular Charts | SciChart.js

Angular Market Depth Chart

Create a Angular Depth Chart, using the high performance SciChart.js chart library. Get free demo now.

Angular Chart Hoverable Buy Sell Marker Annotations

Angular Chart Hoverable Buy Sell Marker Annotations

Demonstrates how to place Buy/Sell arrow markers on a Angular Stock Chart using SciChart.js - Annotations API

Angular User Annotated Stock Chart | SciChart.js Demo

Angular User Annotated Stock Chart

This demo shows you how to create a <strong>{frameworkName} User Annotated Stock Chart</strong> using SciChart.js. Custom modifiers allow you to add lines and markers, then use the built in serialisation functions to save and reload the chart, including the data and all your custom annotations.

Angular Trading Drawing Tools for Devs | Try SciChart Demo

Angular Trading Drawing Tools Demo

Build high-performance financial apps with Angular trading drawing tools. Try SciChart’s demo to see the advanced drawing tools for trading apps in action.

Angular Custom Paired Dashed Polyline Annotation | SciChart

Angular Custom Paired Dashed Polyline Annotation

Create a custom Angular stock chart annotation by extending SciChart.js PolyLineAnnotation with paired dashed connectors.

SciChart Ltd, 16 Beaufort Court, Admirals Way, Docklands, London, E14 9XL.