@obitrain/charts 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +125 -0
- package/lib/commonjs/Charts/BottomAxis/AxisLine.js +38 -0
- package/lib/commonjs/Charts/BottomAxis/AxisLine.js.map +1 -0
- package/lib/commonjs/Charts/BottomAxis/BottomAxis.js +45 -0
- package/lib/commonjs/Charts/BottomAxis/BottomAxis.js.map +1 -0
- package/lib/commonjs/Charts/BottomAxis/Tick.js +43 -0
- package/lib/commonjs/Charts/BottomAxis/Tick.js.map +1 -0
- package/lib/commonjs/Charts/BottomAxis/index.js +27 -0
- package/lib/commonjs/Charts/BottomAxis/index.js.map +1 -0
- package/lib/commonjs/Charts/Cursor.js +50 -0
- package/lib/commonjs/Charts/Cursor.js.map +1 -0
- package/lib/commonjs/Charts/Linechart/index.js +55 -0
- package/lib/commonjs/Charts/Linechart/index.js.map +1 -0
- package/lib/commonjs/Charts/ZoomableLinechart/ScalablePath.js +43 -0
- package/lib/commonjs/Charts/ZoomableLinechart/ScalablePath.js.map +1 -0
- package/lib/commonjs/Charts/ZoomableLinechart/index.js +43 -0
- package/lib/commonjs/Charts/ZoomableLinechart/index.js.map +1 -0
- package/lib/commonjs/Charts/gesture.js +185 -0
- package/lib/commonjs/Charts/gesture.js.map +1 -0
- package/lib/commonjs/Charts/graphUtils.js +131 -0
- package/lib/commonjs/Charts/graphUtils.js.map +1 -0
- package/lib/commonjs/Charts/index.js +151 -0
- package/lib/commonjs/Charts/index.js.map +1 -0
- package/lib/commonjs/Charts/maths.js +195 -0
- package/lib/commonjs/Charts/maths.js.map +1 -0
- package/lib/commonjs/Charts/types.js +6 -0
- package/lib/commonjs/Charts/types.js.map +1 -0
- package/lib/commonjs/dateUtils.js +151 -0
- package/lib/commonjs/dateUtils.js.map +1 -0
- package/lib/commonjs/index.js +39 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/textUtils.js +21 -0
- package/lib/commonjs/textUtils.js.map +1 -0
- package/lib/module/Charts/BottomAxis/AxisLine.js +34 -0
- package/lib/module/Charts/BottomAxis/AxisLine.js.map +1 -0
- package/lib/module/Charts/BottomAxis/BottomAxis.js +40 -0
- package/lib/module/Charts/BottomAxis/BottomAxis.js.map +1 -0
- package/lib/module/Charts/BottomAxis/Tick.js +37 -0
- package/lib/module/Charts/BottomAxis/Tick.js.map +1 -0
- package/lib/module/Charts/BottomAxis/index.js +6 -0
- package/lib/module/Charts/BottomAxis/index.js.map +1 -0
- package/lib/module/Charts/Cursor.js +46 -0
- package/lib/module/Charts/Cursor.js.map +1 -0
- package/lib/module/Charts/Linechart/index.js +50 -0
- package/lib/module/Charts/Linechart/index.js.map +1 -0
- package/lib/module/Charts/ZoomableLinechart/ScalablePath.js +39 -0
- package/lib/module/Charts/ZoomableLinechart/ScalablePath.js.map +1 -0
- package/lib/module/Charts/ZoomableLinechart/index.js +33 -0
- package/lib/module/Charts/ZoomableLinechart/index.js.map +1 -0
- package/lib/module/Charts/gesture.js +176 -0
- package/lib/module/Charts/gesture.js.map +1 -0
- package/lib/module/Charts/graphUtils.js +121 -0
- package/lib/module/Charts/graphUtils.js.map +1 -0
- package/lib/module/Charts/index.js +10 -0
- package/lib/module/Charts/index.js.map +1 -0
- package/lib/module/Charts/maths.js +187 -0
- package/lib/module/Charts/maths.js.map +1 -0
- package/lib/module/Charts/types.js +4 -0
- package/lib/module/Charts/types.js.map +1 -0
- package/lib/module/dateUtils.js +142 -0
- package/lib/module/dateUtils.js.map +1 -0
- package/lib/module/index.js +6 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/textUtils.js +16 -0
- package/lib/module/textUtils.js.map +1 -0
- package/lib/typescript/src/Charts/BottomAxis/AxisLine.d.ts +15 -0
- package/lib/typescript/src/Charts/BottomAxis/AxisLine.d.ts.map +1 -0
- package/lib/typescript/src/Charts/BottomAxis/BottomAxis.d.ts +17 -0
- package/lib/typescript/src/Charts/BottomAxis/BottomAxis.d.ts.map +1 -0
- package/lib/typescript/src/Charts/BottomAxis/Tick.d.ts +14 -0
- package/lib/typescript/src/Charts/BottomAxis/Tick.d.ts.map +1 -0
- package/lib/typescript/src/Charts/BottomAxis/index.d.ts +4 -0
- package/lib/typescript/src/Charts/BottomAxis/index.d.ts.map +1 -0
- package/lib/typescript/src/Charts/Cursor.d.ts +14 -0
- package/lib/typescript/src/Charts/Cursor.d.ts.map +1 -0
- package/lib/typescript/src/Charts/Linechart/index.d.ts +25 -0
- package/lib/typescript/src/Charts/Linechart/index.d.ts.map +1 -0
- package/lib/typescript/src/Charts/ZoomableLinechart/ScalablePath.d.ts +14 -0
- package/lib/typescript/src/Charts/ZoomableLinechart/ScalablePath.d.ts.map +1 -0
- package/lib/typescript/src/Charts/ZoomableLinechart/index.d.ts +23 -0
- package/lib/typescript/src/Charts/ZoomableLinechart/index.d.ts.map +1 -0
- package/lib/typescript/src/Charts/gesture.d.ts +54 -0
- package/lib/typescript/src/Charts/gesture.d.ts.map +1 -0
- package/lib/typescript/src/Charts/graphUtils.d.ts +43 -0
- package/lib/typescript/src/Charts/graphUtils.d.ts.map +1 -0
- package/lib/typescript/src/Charts/index.d.ts +14 -0
- package/lib/typescript/src/Charts/index.d.ts.map +1 -0
- package/lib/typescript/src/Charts/maths.d.ts +19 -0
- package/lib/typescript/src/Charts/maths.d.ts.map +1 -0
- package/lib/typescript/src/Charts/types.d.ts +14 -0
- package/lib/typescript/src/Charts/types.d.ts.map +1 -0
- package/lib/typescript/src/dateUtils.d.ts +37 -0
- package/lib/typescript/src/dateUtils.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +5 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/textUtils.d.ts +3 -0
- package/lib/typescript/src/textUtils.d.ts.map +1 -0
- package/package.json +226 -0
- package/src/Charts/BottomAxis/AxisLine.tsx +40 -0
- package/src/Charts/BottomAxis/BottomAxis.tsx +41 -0
- package/src/Charts/BottomAxis/Tick.tsx +54 -0
- package/src/Charts/BottomAxis/index.tsx +3 -0
- package/src/Charts/Cursor.tsx +51 -0
- package/src/Charts/Linechart/index.tsx +79 -0
- package/src/Charts/ZoomableLinechart/ScalablePath.tsx +49 -0
- package/src/Charts/ZoomableLinechart/index.tsx +47 -0
- package/src/Charts/gesture.ts +254 -0
- package/src/Charts/graphUtils.ts +198 -0
- package/src/Charts/index.tsx +32 -0
- package/src/Charts/maths.ts +245 -0
- package/src/Charts/types.ts +16 -0
- package/src/dateUtils.ts +163 -0
- package/src/index.tsx +4 -0
- package/src/textUtils.ts +21 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { clamp } from '@shopify/react-native-skia';
|
|
2
|
+
import { useCallback } from 'react';
|
|
3
|
+
import { Gesture, type PanGesture, type PinchGesture } from 'react-native-gesture-handler';
|
|
4
|
+
import {
|
|
5
|
+
runOnJS,
|
|
6
|
+
useAnimatedReaction,
|
|
7
|
+
useDerivedValue,
|
|
8
|
+
useSharedValue,
|
|
9
|
+
withTiming,
|
|
10
|
+
type SharedValue,
|
|
11
|
+
} from 'react-native-reanimated';
|
|
12
|
+
import type { DataPoint } from './types';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Get the position of a point on the axis,
|
|
16
|
+
* given the current focal, scale and offset
|
|
17
|
+
*/
|
|
18
|
+
export const getPositionWl = function (
|
|
19
|
+
position: number,
|
|
20
|
+
focalX: number,
|
|
21
|
+
scale: number,
|
|
22
|
+
offsetX: number
|
|
23
|
+
) {
|
|
24
|
+
'worklet';
|
|
25
|
+
return (position - focalX) * scale + focalX + offsetX;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type AxisGestureProps = {
|
|
29
|
+
width: number;
|
|
30
|
+
startOffset?: number;
|
|
31
|
+
startScale?: number;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type ScalableGesture = {
|
|
35
|
+
scale: SharedValue<number>;
|
|
36
|
+
focalX: SharedValue<number>;
|
|
37
|
+
pinchGesture: PinchGesture;
|
|
38
|
+
panGesture: PanGesture;
|
|
39
|
+
offsetX: SharedValue<number>;
|
|
40
|
+
reset: () => void;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Return utilities for zooming and panning the axis
|
|
45
|
+
*/
|
|
46
|
+
export const useScalableGesture = (props: AxisGestureProps): ScalableGesture => {
|
|
47
|
+
const { width } = props;
|
|
48
|
+
const startOffset = props.startOffset ?? 0;
|
|
49
|
+
const startScale = props.startScale ?? 1;
|
|
50
|
+
// For zooming
|
|
51
|
+
const scale = useSharedValue(startScale);
|
|
52
|
+
const lastScale = useSharedValue(startScale);
|
|
53
|
+
const focalX = useSharedValue(0);
|
|
54
|
+
const lastFocalX = useSharedValue(0);
|
|
55
|
+
|
|
56
|
+
// For panning
|
|
57
|
+
const offsetX = useSharedValue(startOffset);
|
|
58
|
+
const lastOffsetX = useSharedValue(startOffset);
|
|
59
|
+
|
|
60
|
+
const reset = useCallback(() => {
|
|
61
|
+
scale.value = startScale;
|
|
62
|
+
lastScale.value = startScale;
|
|
63
|
+
focalX.value = 0;
|
|
64
|
+
lastFocalX.value = 0;
|
|
65
|
+
offsetX.value = startOffset;
|
|
66
|
+
lastOffsetX.value = startOffset;
|
|
67
|
+
}, [
|
|
68
|
+
focalX,
|
|
69
|
+
lastFocalX,
|
|
70
|
+
lastOffsetX,
|
|
71
|
+
lastScale,
|
|
72
|
+
offsetX,
|
|
73
|
+
scale,
|
|
74
|
+
startOffset,
|
|
75
|
+
startScale,
|
|
76
|
+
]);
|
|
77
|
+
|
|
78
|
+
const animateFocalPoint = (newFocalX: number) => {
|
|
79
|
+
'worklet';
|
|
80
|
+
focalX.value = withTiming(newFocalX, { duration: 300 }); // Smooth transition with duration
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const panGesture = Gesture.Pan()
|
|
84
|
+
.onUpdate((event) => {
|
|
85
|
+
const newOffsetX = lastOffsetX.value + event.translationX;
|
|
86
|
+
offsetX.value = newOffsetX;
|
|
87
|
+
})
|
|
88
|
+
.onEnd(() => {
|
|
89
|
+
lastScale.value = scale.value;
|
|
90
|
+
lastFocalX.value = focalX.value;
|
|
91
|
+
|
|
92
|
+
const leftBound = getPositionWl(
|
|
93
|
+
0,
|
|
94
|
+
focalX.value,
|
|
95
|
+
scale.value,
|
|
96
|
+
offsetX.value
|
|
97
|
+
);
|
|
98
|
+
const rightBound = getPositionWl(
|
|
99
|
+
width,
|
|
100
|
+
focalX.value,
|
|
101
|
+
scale.value,
|
|
102
|
+
offsetX.value
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
let newOffset;
|
|
106
|
+
|
|
107
|
+
if (leftBound > startOffset) {
|
|
108
|
+
// console.log(`Left Boundary reached`);
|
|
109
|
+
newOffset = offsetX.value - leftBound + startOffset;
|
|
110
|
+
} else if (rightBound < width) {
|
|
111
|
+
// console.log(`Right Boundary reached`);
|
|
112
|
+
newOffset = offsetX.value + width - rightBound + startOffset;
|
|
113
|
+
} else {
|
|
114
|
+
newOffset = offsetX.value;
|
|
115
|
+
}
|
|
116
|
+
if (newOffset === undefined) throw new Error('newOffset is undefined');
|
|
117
|
+
|
|
118
|
+
offsetX.value = withTiming(newOffset, { duration: 300 });
|
|
119
|
+
lastOffsetX.value = newOffset;
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const pinchGesture = Gesture.Pinch()
|
|
123
|
+
.onUpdate((event) => {
|
|
124
|
+
const newScale = lastScale.value * event.scale;
|
|
125
|
+
scale.value = clamp(newScale, 1, Infinity); // Clamp the scale value
|
|
126
|
+
animateFocalPoint(event.focalX);
|
|
127
|
+
})
|
|
128
|
+
.onEnd(() => {
|
|
129
|
+
lastScale.value = scale.value;
|
|
130
|
+
lastFocalX.value = focalX.value;
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
scale,
|
|
135
|
+
focalX,
|
|
136
|
+
pinchGesture,
|
|
137
|
+
panGesture,
|
|
138
|
+
offsetX,
|
|
139
|
+
reset,
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export type UpdateAxisProps = {
|
|
144
|
+
scale: SharedValue<number>;
|
|
145
|
+
scales: number[];
|
|
146
|
+
onScaleChange?: (index: number) => void;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
export const useUpdateAxis = function (props: UpdateAxisProps) {
|
|
150
|
+
const { scale, scales, onScaleChange } = props;
|
|
151
|
+
const currentIndex = useSharedValue(0);
|
|
152
|
+
|
|
153
|
+
useAnimatedReaction(
|
|
154
|
+
() => scale.value,
|
|
155
|
+
(currentScale, _) => {
|
|
156
|
+
for (let i = 0; i < scales.length; i++) {
|
|
157
|
+
const _prevScale = scales[i - 1] ?? 0;
|
|
158
|
+
const _curScale = scales[i];
|
|
159
|
+
if (_curScale === undefined || _prevScale === undefined)
|
|
160
|
+
throw new Error('Got undefined scale');
|
|
161
|
+
|
|
162
|
+
if (
|
|
163
|
+
currentScale >= _prevScale &&
|
|
164
|
+
currentScale < _curScale &&
|
|
165
|
+
currentIndex.value !== i
|
|
166
|
+
) {
|
|
167
|
+
currentIndex.value = i;
|
|
168
|
+
if (onScaleChange !== undefined) runOnJS(onScaleChange)(i);
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
[]
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
return { currentIndex };
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export type UseCursorGestureProps = {
|
|
180
|
+
width: number;
|
|
181
|
+
height: number;
|
|
182
|
+
points?: SharedValue<DataPoint[]>;
|
|
183
|
+
closestDataPoint?: SharedValue<DataPoint>;
|
|
184
|
+
isContinuous?: boolean;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Get the closest point for a given x value
|
|
189
|
+
*/
|
|
190
|
+
export const getClosestPoint = function (
|
|
191
|
+
x: number,
|
|
192
|
+
dataPoints: DataPoint[]
|
|
193
|
+
): DataPoint {
|
|
194
|
+
'worklet';
|
|
195
|
+
let closestPoint = dataPoints[0];
|
|
196
|
+
if (closestPoint === undefined)
|
|
197
|
+
throw new Error('dataPoints array cannot be empty');
|
|
198
|
+
|
|
199
|
+
let minDistance = Math.abs(x - closestPoint.x);
|
|
200
|
+
for (let i = 1; i < dataPoints.length; i++) {
|
|
201
|
+
const point = dataPoints[i];
|
|
202
|
+
if (point === undefined) throw new Error('Point cannot be undefined');
|
|
203
|
+
const distance = Math.abs(x - point.x);
|
|
204
|
+
if (distance < minDistance) {
|
|
205
|
+
minDistance = distance;
|
|
206
|
+
closestPoint = point;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return closestPoint;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Get gesture utilities for the cursor
|
|
214
|
+
*/
|
|
215
|
+
export const useCursorGesture = function (props: UseCursorGestureProps) {
|
|
216
|
+
const {
|
|
217
|
+
width,
|
|
218
|
+
height,
|
|
219
|
+
points,
|
|
220
|
+
closestDataPoint,
|
|
221
|
+
isContinuous = true,
|
|
222
|
+
} = props;
|
|
223
|
+
|
|
224
|
+
const xPosition = useSharedValue(0);
|
|
225
|
+
const yPosition = useSharedValue(height); // GRAPH_HEIGHT
|
|
226
|
+
|
|
227
|
+
const panGesture = Gesture.Pan()
|
|
228
|
+
.onBegin((event) => {
|
|
229
|
+
xPosition.value = clamp(event.x, 0, width);
|
|
230
|
+
yPosition.value = clamp(event.y, 0, height);
|
|
231
|
+
})
|
|
232
|
+
.onUpdate((event) => {
|
|
233
|
+
xPosition.value = clamp(event.x, 0, width);
|
|
234
|
+
yPosition.value = clamp(event.y, 0, height);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const tapGesture = Gesture.Tap().onBegin((event) => {
|
|
238
|
+
xPosition.value = clamp(event.x, 0, width);
|
|
239
|
+
yPosition.value = clamp(event.y, 0, height);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
useDerivedValue(() => {
|
|
243
|
+
if (isContinuous) return;
|
|
244
|
+
if (points === undefined) {
|
|
245
|
+
console.warn('Points must be defined for non-continuous mode');
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const _closestDot = getClosestPoint(xPosition.value, points.value);
|
|
249
|
+
if (closestDataPoint !== undefined) closestDataPoint.value = _closestDot;
|
|
250
|
+
xPosition.value = _closestDot.x;
|
|
251
|
+
}, [xPosition, isContinuous]);
|
|
252
|
+
|
|
253
|
+
return { panGesture, tapGesture, xPosition, yPosition };
|
|
254
|
+
};
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PathVerb,
|
|
3
|
+
Skia,
|
|
4
|
+
type PathCommand,
|
|
5
|
+
type SkPath,
|
|
6
|
+
} from '@shopify/react-native-skia';
|
|
7
|
+
import { scaleLinear, type ScaleLinear } from 'd3-scale';
|
|
8
|
+
import * as shape from 'd3-shape';
|
|
9
|
+
import {
|
|
10
|
+
useAnimatedReaction,
|
|
11
|
+
withTiming,
|
|
12
|
+
type SharedValue,
|
|
13
|
+
} from 'react-native-reanimated';
|
|
14
|
+
import { getPositionWl } from './gesture';
|
|
15
|
+
import { getYForX } from './maths';
|
|
16
|
+
import type { AnimatedDot, DataPoint } from './types';
|
|
17
|
+
|
|
18
|
+
export type Config = {
|
|
19
|
+
minX?: number;
|
|
20
|
+
maxX?: number;
|
|
21
|
+
minY?: number;
|
|
22
|
+
maxY?: number;
|
|
23
|
+
// See https://d3js.org/d3-shape/curve
|
|
24
|
+
curve?: shape.CurveFactory | shape.CurveFactoryLineOnly;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type GraphData = {
|
|
28
|
+
data: [number, number][];
|
|
29
|
+
minY: number;
|
|
30
|
+
maxY: number;
|
|
31
|
+
path: string;
|
|
32
|
+
skiaPath: SkPath;
|
|
33
|
+
dataPoints: DataPoint[];
|
|
34
|
+
scaleX: ScaleLinear<number, number, never>;
|
|
35
|
+
scaleY: ScaleLinear<number, number, never>;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const buildGraph = function (
|
|
39
|
+
data: [number, number][],
|
|
40
|
+
width: number,
|
|
41
|
+
height: number,
|
|
42
|
+
config?: Config
|
|
43
|
+
): GraphData {
|
|
44
|
+
const minX = config?.minX ?? Math.min(...data.map((x) => x[0]));
|
|
45
|
+
const maxX = config?.maxX ?? Math.max(...data.map((x) => x[0]));
|
|
46
|
+
const scaleX = scaleLinear().domain([minX, maxX]).range([0, width]);
|
|
47
|
+
|
|
48
|
+
const minY = config?.minY ?? Math.min(...data.map((x) => x[1]));
|
|
49
|
+
const maxY = config?.maxY ?? Math.max(...data.map((x) => x[1]));
|
|
50
|
+
const scaleY = scaleLinear().domain([minY, maxY]).range([height, 0]);
|
|
51
|
+
|
|
52
|
+
const fmtValues = data.map((x) => [x[1], x[0]] as [number, number]);
|
|
53
|
+
|
|
54
|
+
const path = shape
|
|
55
|
+
.line()
|
|
56
|
+
.x(([, x]) => scaleX(x))
|
|
57
|
+
.y(([y]) => scaleY(y))
|
|
58
|
+
.curve(config?.curve ?? shape.curveBasis)(fmtValues) as string;
|
|
59
|
+
|
|
60
|
+
const dataPoints = fmtValues.map(([y, x]) => ({
|
|
61
|
+
x: scaleX(x),
|
|
62
|
+
y: scaleY(y),
|
|
63
|
+
value: y,
|
|
64
|
+
}));
|
|
65
|
+
|
|
66
|
+
const skiaPath = Skia.Path.MakeFromSVGString(path);
|
|
67
|
+
if (skiaPath == null) throw new Error('Path not found');
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
data: fmtValues,
|
|
71
|
+
minY,
|
|
72
|
+
maxY,
|
|
73
|
+
path,
|
|
74
|
+
dataPoints,
|
|
75
|
+
skiaPath,
|
|
76
|
+
scaleX,
|
|
77
|
+
scaleY,
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* For now, only support commands of type:
|
|
83
|
+
* - PathVerb.Move
|
|
84
|
+
* - PathVerb.Cubic
|
|
85
|
+
* - PathVerb.Quad
|
|
86
|
+
* - PathVerb.Line
|
|
87
|
+
*/
|
|
88
|
+
export const scaleCommands = function (
|
|
89
|
+
commands: PathCommand[],
|
|
90
|
+
scaleX: SharedValue<number>,
|
|
91
|
+
focalX: SharedValue<number>,
|
|
92
|
+
offsetX: SharedValue<number>,
|
|
93
|
+
scaleY?: SharedValue<number>
|
|
94
|
+
): PathCommand[] {
|
|
95
|
+
'worklet';
|
|
96
|
+
const _scaleY = scaleY ? scaleY.value : 1;
|
|
97
|
+
return commands.map((command) => {
|
|
98
|
+
const commandType = command[0];
|
|
99
|
+
if (commandType === undefined)
|
|
100
|
+
throw new Error('Got undefined command type');
|
|
101
|
+
|
|
102
|
+
if (commandType === PathVerb.Line) {
|
|
103
|
+
return [
|
|
104
|
+
commandType,
|
|
105
|
+
getPositionWl(command[1]!, focalX.value, scaleX.value, offsetX.value),
|
|
106
|
+
command[2]! * _scaleY,
|
|
107
|
+
];
|
|
108
|
+
}
|
|
109
|
+
if (commandType === PathVerb.Move) {
|
|
110
|
+
return [
|
|
111
|
+
commandType,
|
|
112
|
+
getPositionWl(command[1]!, focalX.value, scaleX.value, offsetX.value),
|
|
113
|
+
command[2]! * _scaleY,
|
|
114
|
+
];
|
|
115
|
+
}
|
|
116
|
+
if (commandType === PathVerb.Quad)
|
|
117
|
+
return [
|
|
118
|
+
commandType,
|
|
119
|
+
getPositionWl(command[1]!, focalX.value, scaleX.value, offsetX.value),
|
|
120
|
+
command[2]! * _scaleY,
|
|
121
|
+
getPositionWl(command[3]!, focalX.value, scaleX.value, offsetX.value),
|
|
122
|
+
command[4]! * _scaleY,
|
|
123
|
+
];
|
|
124
|
+
if (commandType === PathVerb.Cubic) {
|
|
125
|
+
return [
|
|
126
|
+
commandType,
|
|
127
|
+
// c1
|
|
128
|
+
getPositionWl(command[1]!, focalX.value, scaleX.value, offsetX.value),
|
|
129
|
+
command[2]! * _scaleY,
|
|
130
|
+
// c2
|
|
131
|
+
getPositionWl(command[3]!, focalX.value, scaleX.value, offsetX.value),
|
|
132
|
+
command[4]! * _scaleY,
|
|
133
|
+
// to
|
|
134
|
+
getPositionWl(command[5]!, focalX.value, scaleX.value, offsetX.value),
|
|
135
|
+
command[6]! * _scaleY,
|
|
136
|
+
];
|
|
137
|
+
}
|
|
138
|
+
if (commandType === PathVerb.Close) return command;
|
|
139
|
+
if (commandType === PathVerb.Move) return command;
|
|
140
|
+
throw new Error(`Unsupported command type ${commandType}`);
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export type UseDotAnimationProps = {
|
|
145
|
+
currentGraph: SharedValue<number>;
|
|
146
|
+
path: SharedValue<SkPath>;
|
|
147
|
+
dataPoints: DataPoint[][];
|
|
148
|
+
dots: AnimatedDot[];
|
|
149
|
+
opacityWl?: (opacity: number) => number;
|
|
150
|
+
translateWl?: (position: number) => number;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
export const defaultOpacityTransitionWl = function (opacity: number) {
|
|
154
|
+
'worklet';
|
|
155
|
+
return withTiming(opacity, { duration: 200 });
|
|
156
|
+
};
|
|
157
|
+
export const defaultTranslateTransitionWl = function (position: number) {
|
|
158
|
+
'worklet';
|
|
159
|
+
return withTiming(position, { duration: 1000 });
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export const useDotsTransition = function (props: UseDotAnimationProps) {
|
|
163
|
+
const {
|
|
164
|
+
currentGraph,
|
|
165
|
+
path,
|
|
166
|
+
dataPoints,
|
|
167
|
+
dots,
|
|
168
|
+
opacityWl = defaultOpacityTransitionWl,
|
|
169
|
+
translateWl = defaultTranslateTransitionWl,
|
|
170
|
+
} = props;
|
|
171
|
+
|
|
172
|
+
useAnimatedReaction(
|
|
173
|
+
() => ({
|
|
174
|
+
_currentGraph: currentGraph.value,
|
|
175
|
+
_currentCommands: path.value.toCmds(),
|
|
176
|
+
}),
|
|
177
|
+
({ _currentGraph, _currentCommands }) => {
|
|
178
|
+
const newDataPoints = dataPoints[_currentGraph];
|
|
179
|
+
if (newDataPoints === undefined)
|
|
180
|
+
throw new Error('Data points cannot be undefined');
|
|
181
|
+
dots.map((_dot, i) => {
|
|
182
|
+
const _newDot = newDataPoints[i];
|
|
183
|
+
if (!_newDot) {
|
|
184
|
+
_dot.opacity.value = opacityWl(0);
|
|
185
|
+
return;
|
|
186
|
+
} else {
|
|
187
|
+
_dot.x.value = translateWl(_newDot.x);
|
|
188
|
+
const newY = getYForX(_currentCommands, _dot.x.value);
|
|
189
|
+
if (newY !== undefined) {
|
|
190
|
+
_dot.y.value = newY;
|
|
191
|
+
}
|
|
192
|
+
// _dot.y.value = withTiming(newDots[i]!.y, { duration: 1000 });
|
|
193
|
+
_dot.opacity.value = opacityWl(1);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
);
|
|
198
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export { AxisLine, BottomAxis, Tick } from './BottomAxis';
|
|
2
|
+
export { Cursor } from './Cursor';
|
|
3
|
+
export type { CursorProps } from './Cursor';
|
|
4
|
+
export { LineChart } from './Linechart';
|
|
5
|
+
export type { LineChartProps, LinePath } from './Linechart';
|
|
6
|
+
export { ScalablePath, ZoomableLineChart } from './ZoomableLinechart';
|
|
7
|
+
export type { ZoomableLineChartProps } from './ZoomableLinechart';
|
|
8
|
+
export {
|
|
9
|
+
getClosestPoint,
|
|
10
|
+
getPositionWl,
|
|
11
|
+
useCursorGesture,
|
|
12
|
+
useScalableGesture,
|
|
13
|
+
useUpdateAxis,
|
|
14
|
+
} from './gesture';
|
|
15
|
+
export type { UseCursorGestureProps } from './gesture';
|
|
16
|
+
export {
|
|
17
|
+
buildGraph,
|
|
18
|
+
defaultOpacityTransitionWl,
|
|
19
|
+
defaultTranslateTransitionWl,
|
|
20
|
+
scaleCommands,
|
|
21
|
+
useDotsTransition,
|
|
22
|
+
} from './graphUtils';
|
|
23
|
+
export type { Config, GraphData, UseDotAnimationProps } from './graphUtils';
|
|
24
|
+
export {
|
|
25
|
+
commandsToBezier,
|
|
26
|
+
cubicBezierYForX,
|
|
27
|
+
getYForX,
|
|
28
|
+
magnitude,
|
|
29
|
+
normalize,
|
|
30
|
+
selectCurve,
|
|
31
|
+
} from './maths';
|
|
32
|
+
export type { AnimatedDot, DataPoint, LineGraphType } from './types';
|