JavaScript Freehand Drawing Tools

JavaScript Freehand Drawing Demo for trading and financial charts using SciChart.js. Can be used for drawing trends, arrow, markers, text, etc.

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

JavaScript Freehand Drawing Tools

This example demonstrates FreehandDrawingModifier and FreehandDrawingAnnotation. Select color, switch between drawing and edit modes, delete annotations using backspace button and delete icon. This tool can be used for drawing trend lines, event markers, text or any arbitrary drawing for financial and trading charts.

javascript Chart Examples & Demos

See Also: Financial Charts (13 Demos)

JavaScript Candlestick Chart | Online JavaScript Chart Examples

JavaScript Candlestick Chart

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

JavaScript OHLC Chart | Javascript Charts | SciChart.js Demo

JavaScript OHLC Chart

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

JavaScript Realtime Ticking Stock Chart | SciChart.js

JavaScript Realtime Ticking Stock Charts

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

JavaScript Orderbook Heatmap | Javascript Charts | SciChart.js

JavaScript Orderbook Heatmap

Create a Javascript heatmap chart showing historical orderbook levels using the high performance SciChart.js chart library. Get free demo now.

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

JavaScript Multi-Pane Stock Charts using Subcharts

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

Tenor Curves Demo | Javascript 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.

JavaScript Multi-Pane Stock Chart | View JavaScript Charts

JavaScript Multi-Pane Stock Charts using Sync Multi-Chart

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

Financial Data Filters | Javascript Charts | SciChart.js Demo

Financial Data Filters

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

JavaScript Market Depth Chart | Javascript Charts | SciChart.js

JavaScript Market Depth Chart

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

JavaScript Chart Hoverable Buy Sell Marker Annotations

JavaScript Chart Hoverable Buy Sell Marker Annotations

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

JavaScript User Annotated Stock Chart | SciChart.js Demo

JavaScript 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.

JavaScript Trading Drawing Tools | Javascript Charts | SciChart.js

JavaScript Trading Drawing Tools

Create an interactive JavaScript trading charts for technical analysis. Trading Drawing Tools Demo, which shows how to use Polylines, Extended Lines, Rays, Channels, Pitchforks, Pitchfans, Fibonnaci Retracements, Measure, Stop Loss and Take Profit chart drawing tools for Technical Analysis.

JavaScript Custom Paired Dashed Polyline Annotation

JavaScript Custom Paired Dashed Polyline Annotation

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

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