Skip to content

Commit 97e0f2b

Browse files
committed
Add various enhancements to the drawing library
1 parent 08a3ef1 commit 97e0f2b

File tree

10 files changed

+866
-173
lines changed

10 files changed

+866
-173
lines changed

src/data-source.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
1-
import {
2-
IChartApi,
3-
ISeriesApi,
4-
SeriesOptionsMap,
5-
Time,
6-
} from 'lightweight-charts';
1+
import { IChartApi, ISeriesApi, SeriesOptionsMap, Time, Logical } from 'lightweight-charts';
72
import { ShapeDrawingOptions } from './options';
83

94
export interface Point {
105
time: Time;
116
price: number;
7+
logical?: Logical;
128
}
139

1410
export interface ShapeDrawingDataSource {
1511
chart: IChartApi;
1612
series: ISeriesApi<keyof SeriesOptionsMap>;
1713
options: ShapeDrawingOptions;
1814
points: Point[];
15+
highlightCorners: boolean;
1916
}

src/example/classes.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { HandleScaleOptions, HandleScrollOptions, LineStyle, Logical, Time } from "lightweight-charts";
2+
import { HoveredObject, ShapeDrawing } from "../shape-drawing";
3+
import { ShapeDrawingOptions } from "../options";
4+
5+
export const defaultShapeOptions: Partial<ShapeDrawingOptions> = {
6+
fillColor: '#0f0',
7+
fillOpacity: 0.5,
8+
borderColor: '#f0f',
9+
borderWidth: 2,
10+
borderStyle: LineStyle.Dashed,
11+
hoveredEdgeWidth: 4,
12+
hoveredFillOpacity: 0.6,
13+
};
14+
15+
export class State {
16+
shapeToDraw: string = ''; // Click a shape to draw
17+
18+
currentlySelectedShape: ShapeDrawing | null = null; // If escape is pressed, then this will be deselected
19+
currentlyDrawingShape: ShapeDrawing | null = null; // If escape is pressed, then this will be deleted
20+
21+
// Interactive drawing
22+
mouseDown: boolean = false;
23+
lastMouseDownPoint: Point | null = null;
24+
dragging: boolean = false;
25+
hoveredObject: HoveredObject | null = null;
26+
originalPoints: Point[] = [];
27+
crosshair: Point = new Point(0, 0 as Time);
28+
drawnObjects: Record<string, ShapeDrawing> = {};
29+
savedHandleScroll: HandleScrollOptions | boolean = true;
30+
savedHandleScale: HandleScaleOptions | boolean = true;
31+
edgeCount: number = 2;
32+
edges: Point[] = [];
33+
34+
// Used for both static and interactive drawing
35+
shapeOptions: Partial<ShapeDrawingOptions> = defaultShapeOptions;
36+
}
37+
38+
export class Point {
39+
price: number;
40+
time: Time;
41+
logical?: Logical;
42+
43+
constructor(price: number, time: Time, logical?: Logical) {
44+
this.price = price;
45+
this.time = time;
46+
this.logical = logical;
47+
}
48+
}

src/example/data.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { CrosshairMode, LastPriceAnimationMode, LineSeries, createChart } from 'lightweight-charts';
2+
import { State } from './classes';
3+
import { generateLineData } from './sample-data';
4+
import { shapeDrawingSelection } from './helpers';
5+
6+
export const chartOptions = {
7+
autoSize: true,
8+
crosshair: {
9+
mode: CrosshairMode.Normal,
10+
},
11+
layout: {
12+
background: {
13+
color: '#ccc',
14+
},
15+
},
16+
};
17+
export const seriesPriceSize = 0.01;
18+
export const seriesPricePrecision = 2;
19+
20+
export const shapeDrawingSelectionElement = document.getElementById('shapeDrawingSelection') as HTMLDivElement;
21+
22+
// Shape drawing options
23+
export const fillOpacityElement = document.getElementById('fillOpacity') as HTMLInputElement;
24+
25+
// Initial setup
26+
27+
export const chart = ((window as any).chart = createChart('chart', chartOptions));
28+
export const lineSeries = chart.addSeries(LineSeries, {
29+
color: '#000000',
30+
lastPriceAnimation: LastPriceAnimationMode.Disabled,
31+
crosshairMarkerVisible: false,
32+
priceFormat: {
33+
type: 'price',
34+
precision: seriesPricePrecision,
35+
minMove: seriesPriceSize,
36+
},
37+
});
38+
39+
lineSeries.setData(generateLineData());
40+
41+
shapeDrawingSelectionElement.addEventListener('click', shapeDrawingSelection);
42+
43+
// Change the shape's fill opacity
44+
fillOpacityElement.addEventListener('input', (event) => {
45+
if (state.currentlySelectedShape) {
46+
state.shapeOptions['fillOpacity'] = parseFloat((event.target as HTMLInputElement).value);
47+
state.currentlySelectedShape.applyOptions(state.shapeOptions);
48+
}
49+
});
50+
51+
export const chartElement = document.getElementById('chart');
52+
53+
export const state = new State();

src/example/example.ts

Lines changed: 16 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,12 @@
1-
import { CrosshairMode, LastPriceAnimationMode, LineSeries, LineStyle, createChart, Time, MouseEventParams } from 'lightweight-charts';
2-
import { generateLineData } from './sample-data';
1+
import { LineStyle } from 'lightweight-charts';
32
import { ShapeDrawing } from '../shape-drawing';
3+
import { chart, lineSeries, state, chartElement } from './data';
4+
import { chartCrosshairMoveEvent, chartMouseDownEvent, chartMouseMoveEvent, chartMouseUpEvent, keyUpEvent } from './helpers';
45

5-
const priceSize = 0.01;
6-
const pricePrecision = 2;
7-
const chart = ((window as unknown as any).chart = createChart('chart', {
8-
autoSize: true,
9-
crosshair: {
10-
mode: CrosshairMode.Normal,
11-
},
12-
layout: {
13-
background: {
14-
color: '#ccc',
15-
},
16-
},
17-
}));
18-
19-
/************************** Statically add some shapes **************************/
20-
21-
const lineSeries = chart.addSeries(LineSeries, {
22-
color: '#000000',
23-
lastPriceAnimation: LastPriceAnimationMode.Disabled,
24-
crosshairMarkerVisible: false,
25-
priceFormat: {
26-
type: 'price',
27-
precision: pricePrecision,
28-
minMove: priceSize,
29-
},
30-
});
31-
const data = generateLineData();
32-
lineSeries.setData(data);
6+
/************************** Statically add a shape **************************/
337

34-
const time1a = data[data.length - 50].time;
35-
const time2a = data[data.length - 10].time;
8+
const time1a = lineSeries.data()[lineSeries.data().length - 50].time;
9+
const time2a = lineSeries.data()[lineSeries.data().length - 10].time;
3610

3711
const shape1 = new ShapeDrawing(
3812
[
@@ -44,129 +18,26 @@ const shape1 = new ShapeDrawing(
4418
fillColor: '#ff0',
4519
fillOpacity: 0.5,
4620
borderColor: '#0ff',
47-
borderWidth: 5,
21+
borderWidth: 3,
4822
borderStyle: LineStyle.Dashed,
4923
showTimeAxisLabels: true,
5024
showPriceAxisLabels: true,
5125
labelColor: '#aaa',
5226
labelTextColor: '#000',
27+
hoveredEdgeWidth: 8,
28+
hoveredFillOpacity: 0.6,
5329
},
5430
);
5531

5632
lineSeries.attachPrimitive(shape1);
5733

58-
const time1b = data[data.length - 150].time;
59-
const time2b = data[data.length - 130].time;
60-
const time3b = data[data.length - 110].time;
61-
const time4b = data[data.length - 90].time;
62-
63-
const shape2 = new ShapeDrawing(
64-
[
65-
{ price: 600, time: time1b },
66-
{ price: 200, time: time2b },
67-
{ price: 200, time: time3b },
68-
{ price: 600, time: time4b },
69-
{ price: 1000, time: time3b },
70-
{ price: 1000, time: time2b },
71-
],
72-
{
73-
fillColor: '#ff6b6b',
74-
fillOpacity: 0.7,
75-
borderColor: '#f00',
76-
borderWidth: 3,
77-
borderStyle: LineStyle.LargeDashed,
78-
showTimeAxisLabels: true,
79-
showPriceAxisLabels: true,
80-
labelColor: '#666',
81-
labelTextColor: '#fff',
82-
},
83-
);
84-
85-
lineSeries.attachPrimitive(shape2);
86-
87-
/************************** Interactively add some shapes **************************/
34+
// Allows for interactivity with the shape
35+
state.drawnObjects[shape1.objectId] = shape1;
8836

89-
class Point {
90-
price: number;
91-
time: Time;
37+
/************************** Interactively add shapes **************************/
9238

93-
constructor(price: number, time: Time) {
94-
this.price = price;
95-
this.time = time;
96-
}
97-
}
98-
99-
const edgeCountElement = document.getElementById('edge-count') as HTMLInputElement;
100-
const edgesElement = document.getElementById('edges') as HTMLUListElement;
101-
let edgeCount = 2;
102-
let edges: Point[] = [];
103-
104-
edgeCountElement.addEventListener('input', () => {
105-
edgeCount = parseInt(edgeCountElement.value);
106-
edgesElement.innerHTML = '';
107-
});
108-
109-
const chartElement = document.getElementById('chart');
110-
let crosshairPrice = 0;
111-
let crosshairTime: Time | null = null;
112-
113-
window.addEventListener('keyup', (event) => {
114-
if (event.key === 'Escape') {
115-
edges = [];
116-
edgesElement.innerHTML = '';
117-
}
118-
});
119-
120-
chartElement!.addEventListener('click', chartClickEvent);
39+
window.addEventListener('keyup', keyUpEvent);
40+
chartElement!.addEventListener('mousedown', chartMouseDownEvent);
41+
chartElement!.addEventListener('mousemove', chartMouseMoveEvent);
42+
chartElement!.addEventListener('mouseup', chartMouseUpEvent);
12143
chart.subscribeCrosshairMove(chartCrosshairMoveEvent);
122-
123-
function chartClickEvent() {
124-
addEdge();
125-
}
126-
127-
function chartCrosshairMoveEvent(event: MouseEventParams<Time>) {
128-
if (!event.point || event.seriesData.size === 0) {
129-
return;
130-
}
131-
132-
const seriesValues = event.seriesData.entries().next().value;
133-
134-
crosshairPrice = roundNumber(
135-
seriesValues![0].coordinateToPrice(event.point!.y) || 0,
136-
pricePrecision,
137-
);
138-
139-
crosshairTime = seriesValues![1].time || null;
140-
}
141-
142-
function addEdge() {
143-
const newPoint = new Point(crosshairPrice, crosshairTime!);
144-
145-
edges.push(newPoint);
146-
147-
const li = document.createElement('li');
148-
li.textContent = `X: ${newPoint.price}, Y: ${newPoint.time}`;
149-
edgesElement.append(li);
150-
151-
if (edges.length === edgeCount) {
152-
const shape = new ShapeDrawing(edges, {
153-
fillColor: '#0f0',
154-
fillOpacity: 0.5,
155-
borderColor: '#f0f',
156-
borderWidth: 5,
157-
borderStyle: LineStyle.Dashed,
158-
});
159-
160-
lineSeries.attachPrimitive(shape);
161-
162-
edges = [];
163-
edgesElement.innerHTML = '';
164-
}
165-
}
166-
167-
function roundNumber(n: number, dp: number) {
168-
const shiftedNumber = +`${Number(n).toFixed(20)}e+${dp}`;
169-
const roundedShiftedNumber = Math.round(shiftedNumber);
170-
const result = +`${roundedShiftedNumber}e-${dp}`;
171-
return result;
172-
}

0 commit comments

Comments
 (0)