react-native-livechart 4.18.0 → 4.19.0
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/dist/components/CrosshairOverlay.d.ts +3 -1
- package/dist/components/CrosshairOverlay.d.ts.map +1 -1
- package/dist/components/DotOverlay.d.ts +4 -2
- package/dist/components/DotOverlay.d.ts.map +1 -1
- package/dist/components/LiveChart.d.ts.map +1 -1
- package/dist/components/LiveChartSeries.d.ts.map +1 -1
- package/dist/components/MultiSeriesDots.d.ts +5 -3
- package/dist/components/MultiSeriesDots.d.ts.map +1 -1
- package/dist/constants.d.ts +6 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/core/resolveConfig.d.ts +17 -3
- package/dist/core/resolveConfig.d.ts.map +1 -1
- package/dist/core/useLiveChartEngine.d.ts.map +1 -1
- package/dist/draw/candle.d.ts +21 -0
- package/dist/draw/candle.d.ts.map +1 -1
- package/dist/draw/line.d.ts +7 -1
- package/dist/draw/line.d.ts.map +1 -1
- package/dist/hooks/resolveChartLayout.d.ts +8 -0
- package/dist/hooks/resolveChartLayout.d.ts.map +1 -1
- package/dist/hooks/useCandlePaths.d.ts +3 -1
- package/dist/hooks/useCandlePaths.d.ts.map +1 -1
- package/dist/hooks/useChartPaths.d.ts +3 -1
- package/dist/hooks/useChartPaths.d.ts.map +1 -1
- package/dist/hooks/useDegen.d.ts +1 -3
- package/dist/hooks/useDegen.d.ts.map +1 -1
- package/dist/hooks/useMarkers.d.ts.map +1 -1
- package/dist/hooks/useMultiSeriesLinePaths.d.ts +3 -1
- package/dist/hooks/useMultiSeriesLinePaths.d.ts.map +1 -1
- package/dist/hooks/useSeriesIndicatorOpacity.d.ts +13 -0
- package/dist/hooks/useSeriesIndicatorOpacity.d.ts.map +1 -0
- package/dist/hooks/useTradeStream.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/math/simplify.d.ts +20 -0
- package/dist/math/simplify.d.ts.map +1 -0
- package/dist/types.d.ts +77 -16
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/CrosshairOverlay.tsx +14 -9
- package/src/components/DotOverlay.tsx +17 -1
- package/src/components/LiveChart.tsx +194 -54
- package/src/components/LiveChartSeries.tsx +28 -10
- package/src/components/MultiSeriesDots.tsx +22 -3
- package/src/constants.ts +7 -0
- package/src/core/resolveConfig.ts +61 -13
- package/src/core/useLiveChartEngine.ts +6 -1
- package/src/draw/candle.ts +78 -0
- package/src/draw/line.ts +20 -7
- package/src/hooks/resolveChartLayout.ts +30 -18
- package/src/hooks/useCandlePaths.ts +9 -1
- package/src/hooks/useChartPaths.ts +37 -7
- package/src/hooks/useDegen.ts +2 -6
- package/src/hooks/useMarkers.ts +6 -2
- package/src/hooks/useMultiSeriesLinePaths.ts +31 -4
- package/src/hooks/useSeriesIndicatorOpacity.ts +56 -0
- package/src/hooks/useTradeStream.ts +6 -1
- package/src/index.ts +1 -0
- package/src/math/simplify.ts +197 -0
- package/src/types.ts +78 -16
|
@@ -4,6 +4,10 @@ import { useDerivedValue, type SharedValue } from "react-native-reanimated";
|
|
|
4
4
|
import { MAX_MULTI_SERIES } from "../constants";
|
|
5
5
|
import type { MultiEngineState } from "../core/useLiveChartEngine";
|
|
6
6
|
import { buildLinePoints, type ChartPadding } from "../draw/line";
|
|
7
|
+
import {
|
|
8
|
+
makeLineSimplifyScratch,
|
|
9
|
+
simplifyLinePoints,
|
|
10
|
+
} from "../math/simplify";
|
|
7
11
|
import { drawSpline, makeSplineScratch } from "../math/spline";
|
|
8
12
|
import { usePathBuilders } from "./usePathBuilder";
|
|
9
13
|
|
|
@@ -21,25 +25,31 @@ export function useMultiSeriesLinePaths(
|
|
|
21
25
|
engine: MultiEngineState,
|
|
22
26
|
padding: ChartPadding,
|
|
23
27
|
activeSeriesCount = MAX_MULTI_SERIES,
|
|
28
|
+
/** Shared screen-space simplification tolerance; a series can override it. */
|
|
29
|
+
simplifyTolerance = 0,
|
|
24
30
|
): SharedValue<SkPath[]> {
|
|
25
31
|
const builders = usePathBuilders(MAX_MULTI_SERIES);
|
|
26
32
|
|
|
27
33
|
const poolRef = useRef<{
|
|
28
34
|
empty: SkPath;
|
|
29
35
|
ptsBuf: number[];
|
|
36
|
+
simplifiedPts: number[];
|
|
30
37
|
pathsA: SkPath[];
|
|
31
38
|
pathsB: SkPath[];
|
|
32
39
|
pathsTick: boolean;
|
|
33
40
|
scratch: ReturnType<typeof makeSplineScratch>;
|
|
41
|
+
simplifyScratch: ReturnType<typeof makeLineSimplifyScratch>;
|
|
34
42
|
} | null>(null);
|
|
35
43
|
if (poolRef.current === null) {
|
|
36
44
|
poolRef.current = {
|
|
37
45
|
empty: Skia.Path.Make(),
|
|
38
46
|
ptsBuf: [] as number[],
|
|
47
|
+
simplifiedPts: [] as number[],
|
|
39
48
|
pathsA: [] as SkPath[],
|
|
40
49
|
pathsB: [] as SkPath[],
|
|
41
50
|
pathsTick: false,
|
|
42
51
|
scratch: makeSplineScratch(),
|
|
52
|
+
simplifyScratch: makeLineSimplifyScratch(),
|
|
43
53
|
};
|
|
44
54
|
}
|
|
45
55
|
|
|
@@ -52,19 +62,36 @@ export function useMultiSeriesLinePaths(
|
|
|
52
62
|
const out = pool.pathsTick ? pool.pathsA : pool.pathsB;
|
|
53
63
|
out.length = 0;
|
|
54
64
|
const count = Math.min(activeSeriesCount, s.length, MAX_MULTI_SERIES);
|
|
65
|
+
const now = engine.timestamp.get();
|
|
66
|
+
const windowSecs = engine.displayWindow.get();
|
|
67
|
+
const canvasWidth = engine.canvasWidth.get();
|
|
68
|
+
const chartWidth = canvasWidth - padding.left - padding.right;
|
|
69
|
+
const absoluteXOffset =
|
|
70
|
+
windowSecs > 0 ? (now - windowSecs) * (chartWidth / windowSecs) : 0;
|
|
55
71
|
for (let i = 0; i < count; i++) {
|
|
56
|
-
const
|
|
72
|
+
const rawPts = buildLinePoints(
|
|
57
73
|
s[i].data,
|
|
58
74
|
displays[i] ?? s[i].value,
|
|
59
|
-
|
|
60
|
-
|
|
75
|
+
now,
|
|
76
|
+
windowSecs,
|
|
61
77
|
engine.displayMin.get(),
|
|
62
78
|
engine.displayMax.get(),
|
|
63
|
-
|
|
79
|
+
canvasWidth,
|
|
64
80
|
engine.canvasHeight.get(),
|
|
65
81
|
padding,
|
|
66
82
|
pool.ptsBuf,
|
|
67
83
|
);
|
|
84
|
+
const seriesTolerance = s[i].simplify ?? simplifyTolerance;
|
|
85
|
+
const pts =
|
|
86
|
+
Number.isFinite(seriesTolerance) && seriesTolerance > 0
|
|
87
|
+
? simplifyLinePoints(
|
|
88
|
+
rawPts,
|
|
89
|
+
seriesTolerance,
|
|
90
|
+
pool.simplifiedPts,
|
|
91
|
+
pool.simplifyScratch,
|
|
92
|
+
absoluteXOffset,
|
|
93
|
+
)
|
|
94
|
+
: rawPts;
|
|
68
95
|
const n = pts.length >> 1;
|
|
69
96
|
if (n >= 2) {
|
|
70
97
|
const b = slots[i];
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Easing,
|
|
3
|
+
useAnimatedReaction,
|
|
4
|
+
useSharedValue,
|
|
5
|
+
withTiming,
|
|
6
|
+
type SharedValue,
|
|
7
|
+
} from "react-native-reanimated";
|
|
8
|
+
|
|
9
|
+
/** Short independent fade used when replacement data dims the plotted series. */
|
|
10
|
+
export const SERIES_INDICATOR_FADE_MS = 180;
|
|
11
|
+
|
|
12
|
+
/** Treat tiny animation residue at the top end as fully restored. */
|
|
13
|
+
const SERIES_OPACITY_VISIBLE_EPSILON = 0.001;
|
|
14
|
+
|
|
15
|
+
/** Worklet-safe target for live dots and their pulse rings. */
|
|
16
|
+
export function seriesIndicatorOpacityTarget(seriesOpacity: number): 0 | 1 {
|
|
17
|
+
"worklet";
|
|
18
|
+
return seriesOpacity >= 1 - SERIES_OPACITY_VISIBLE_EPSILON ? 1 : 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Fades live dots and pulse rings away while `seriesOpacity` dims replacement
|
|
23
|
+
* data, then restores them once the plotted series is fully opaque again.
|
|
24
|
+
* Keeping this as a separate opacity avoids compositing a translucent dot over
|
|
25
|
+
* its own line, where the stroke would otherwise shine through.
|
|
26
|
+
*/
|
|
27
|
+
export function useSeriesIndicatorOpacity(
|
|
28
|
+
seriesOpacity: SharedValue<number>,
|
|
29
|
+
duration: number = SERIES_INDICATOR_FADE_MS,
|
|
30
|
+
): SharedValue<number> {
|
|
31
|
+
// Fully visible is the common mount state. The reaction snaps an initially
|
|
32
|
+
// dimmed series to 0 on its first UI-thread run so it never flashes a dot.
|
|
33
|
+
const opacity = useSharedValue(1);
|
|
34
|
+
|
|
35
|
+
useAnimatedReaction(
|
|
36
|
+
() => seriesIndicatorOpacityTarget(seriesOpacity.get()),
|
|
37
|
+
(target, previous) => {
|
|
38
|
+
"worklet";
|
|
39
|
+
if (previous === null || previous === undefined || duration === 0) {
|
|
40
|
+
opacity.set(target);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (target !== previous) {
|
|
44
|
+
opacity.set(
|
|
45
|
+
withTiming(target, {
|
|
46
|
+
duration,
|
|
47
|
+
easing: Easing.inOut(Easing.ease),
|
|
48
|
+
}),
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
[seriesOpacity, duration],
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
return opacity;
|
|
56
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
1
2
|
import {
|
|
2
3
|
useFrameCallback,
|
|
3
4
|
useSharedValue,
|
|
@@ -31,7 +32,7 @@ export function useTradeStream(
|
|
|
31
32
|
const markers = useSharedValue<TradeMarker[]>([]);
|
|
32
33
|
const state = useSharedValue<TradeStreamState>(createTradeStreamState());
|
|
33
34
|
|
|
34
|
-
useFrameCallback(
|
|
35
|
+
const tradeFrameCallback = useFrameCallback(
|
|
35
36
|
/* istanbul ignore next -- worklet runs on UI thread, not in Jest */ (
|
|
36
37
|
frameInfo,
|
|
37
38
|
) => {
|
|
@@ -62,6 +63,10 @@ export function useTradeStream(
|
|
|
62
63
|
},
|
|
63
64
|
autostart,
|
|
64
65
|
);
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
// Reanimated treats `autostart` as mount-time state; synchronize changes.
|
|
68
|
+
tradeFrameCallback.setActive(autostart);
|
|
69
|
+
}, [autostart, tradeFrameCallback]);
|
|
65
70
|
|
|
66
71
|
return markers;
|
|
67
72
|
}
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/** Reusable buffers for {@link simplifyLinePoints}. */
|
|
2
|
+
export interface LineSimplifyScratch {
|
|
3
|
+
keep: number[];
|
|
4
|
+
stack: number[];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/** Allocate simplification scratch once, outside the per-frame worklet. */
|
|
8
|
+
export function makeLineSimplifyScratch(): LineSimplifyScratch {
|
|
9
|
+
return { keep: [], stack: [] };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Maximum number of points simplified as one Ramer-Douglas-Peucker range.
|
|
14
|
+
*
|
|
15
|
+
* The visible line is already bounded to roughly two points per horizontal
|
|
16
|
+
* pixel. Splitting that bounded input into overlapping ranges also bounds the
|
|
17
|
+
* simplifier's worst-case work: a pathological zig-zag cannot turn a frame into
|
|
18
|
+
* an O(n²) scan. Range endpoints are retained, so chunking can only keep a few
|
|
19
|
+
* extra points; it cannot erase additional structure.
|
|
20
|
+
*/
|
|
21
|
+
const MAX_RDP_RANGE_POINTS = 64;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Width of each RDP range on the stable, absolute screen-pixel grid.
|
|
25
|
+
*
|
|
26
|
+
* Dense line input contains at most two representatives per pixel bucket. A
|
|
27
|
+
* 30 px range therefore stays within the 64-point work bound even when the
|
|
28
|
+
* decimator and simplifier grids have different phases. Fixed-width ranges
|
|
29
|
+
* also keep their membership stable as the viewport advances.
|
|
30
|
+
*/
|
|
31
|
+
const RDP_RANGE_WIDTH_PX = 30;
|
|
32
|
+
|
|
33
|
+
function pointSegmentDistanceSq(
|
|
34
|
+
px: number,
|
|
35
|
+
py: number,
|
|
36
|
+
ax: number,
|
|
37
|
+
ay: number,
|
|
38
|
+
bx: number,
|
|
39
|
+
by: number,
|
|
40
|
+
): number {
|
|
41
|
+
"worklet";
|
|
42
|
+
const dx = bx - ax;
|
|
43
|
+
const dy = by - ay;
|
|
44
|
+
const lengthSq = dx * dx + dy * dy;
|
|
45
|
+
if (lengthSq === 0) {
|
|
46
|
+
const ex = px - ax;
|
|
47
|
+
const ey = py - ay;
|
|
48
|
+
return ex * ex + ey * ey;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let t = ((px - ax) * dx + (py - ay) * dy) / lengthSq;
|
|
52
|
+
if (t < 0) t = 0;
|
|
53
|
+
else if (t > 1) t = 1;
|
|
54
|
+
const ex = px - (ax + t * dx);
|
|
55
|
+
const ey = py - (ay + t * dy);
|
|
56
|
+
return ex * ex + ey * ey;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function simplifyRdpGroup(
|
|
60
|
+
points: number[],
|
|
61
|
+
keep: number[],
|
|
62
|
+
stack: number[],
|
|
63
|
+
toleranceSq: number,
|
|
64
|
+
rangeStart: number,
|
|
65
|
+
rangeEnd: number,
|
|
66
|
+
): void {
|
|
67
|
+
"worklet";
|
|
68
|
+
if (rangeStart === rangeEnd) {
|
|
69
|
+
keep[rangeStart] = 1;
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Oversized fixed-X groups are split into overlapping subranges. The group
|
|
74
|
+
// contents are themselves stable, so this point-count guard cannot move
|
|
75
|
+
// until that absolute group reaches a viewport edge.
|
|
76
|
+
for (let chunkStart = rangeStart; chunkStart < rangeEnd; ) {
|
|
77
|
+
const chunkEnd = Math.min(
|
|
78
|
+
chunkStart + MAX_RDP_RANGE_POINTS - 1,
|
|
79
|
+
rangeEnd,
|
|
80
|
+
);
|
|
81
|
+
keep[chunkStart] = 1;
|
|
82
|
+
keep[chunkEnd] = 1;
|
|
83
|
+
stack.push(chunkStart, chunkEnd);
|
|
84
|
+
|
|
85
|
+
while (stack.length >= 2) {
|
|
86
|
+
const end = stack.pop()!;
|
|
87
|
+
const start = stack.pop()!;
|
|
88
|
+
if (end <= start + 1) continue;
|
|
89
|
+
|
|
90
|
+
const ax = points[start * 2];
|
|
91
|
+
const ay = points[start * 2 + 1];
|
|
92
|
+
const bx = points[end * 2];
|
|
93
|
+
const by = points[end * 2 + 1];
|
|
94
|
+
let furthest = -1;
|
|
95
|
+
let maxDistanceSq = toleranceSq;
|
|
96
|
+
|
|
97
|
+
for (let i = start + 1; i < end; i++) {
|
|
98
|
+
const distanceSq = pointSegmentDistanceSq(
|
|
99
|
+
points[i * 2],
|
|
100
|
+
points[i * 2 + 1],
|
|
101
|
+
ax,
|
|
102
|
+
ay,
|
|
103
|
+
bx,
|
|
104
|
+
by,
|
|
105
|
+
);
|
|
106
|
+
if (distanceSq > maxDistanceSq) {
|
|
107
|
+
maxDistanceSq = distanceSq;
|
|
108
|
+
furthest = i;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (furthest >= 0) {
|
|
113
|
+
keep[furthest] = 1;
|
|
114
|
+
stack.push(start, furthest, furthest, end);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
chunkStart = chunkEnd;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Simplify flat screen-space points (`[x0, y0, x1, y1, ...]`) with a bounded
|
|
124
|
+
* Ramer-Douglas-Peucker pass.
|
|
125
|
+
*
|
|
126
|
+
* `tolerance` is the maximum removable deviation in pixels. The first and last
|
|
127
|
+
* point, larger peaks/valleys, and the original point order are retained. Pass
|
|
128
|
+
* distinct reusable `out` and `scratch` buffers to avoid per-frame allocation.
|
|
129
|
+
* `absoluteXOffset` maps the viewport-relative X coordinates back onto a fixed
|
|
130
|
+
* screen-pixel grid; live chart callers should pass `winStart * xScale` so
|
|
131
|
+
* historical range membership does not change as the window advances.
|
|
132
|
+
*/
|
|
133
|
+
export function simplifyLinePoints(
|
|
134
|
+
points: number[],
|
|
135
|
+
tolerance: number,
|
|
136
|
+
out: number[],
|
|
137
|
+
scratch: LineSimplifyScratch,
|
|
138
|
+
absoluteXOffset = 0,
|
|
139
|
+
): number[] {
|
|
140
|
+
"worklet";
|
|
141
|
+
if (out === points) return points;
|
|
142
|
+
|
|
143
|
+
out.length = 0;
|
|
144
|
+
const count = points.length >> 1;
|
|
145
|
+
if (!(tolerance > 0) || !Number.isFinite(tolerance) || count <= 2) {
|
|
146
|
+
for (let i = 0; i < count * 2; i++) out.push(points[i]);
|
|
147
|
+
return out;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const keep = scratch.keep;
|
|
151
|
+
keep.length = count;
|
|
152
|
+
for (let i = 0; i < count; i++) keep[i] = 0;
|
|
153
|
+
|
|
154
|
+
const stack = scratch.stack;
|
|
155
|
+
stack.length = 0;
|
|
156
|
+
const toleranceSq = tolerance * tolerance;
|
|
157
|
+
const stableXOffset = Number.isFinite(absoluteXOffset) ? absoluteXOffset : 0;
|
|
158
|
+
|
|
159
|
+
// Partition on a fixed absolute-X grid rather than by the first visible
|
|
160
|
+
// point's array index. Dropping a point from the left edge then affects only
|
|
161
|
+
// that edge range; fully visible historical ranges keep identical endpoints
|
|
162
|
+
// and RDP choices while translating across the viewport.
|
|
163
|
+
let groupStart = 0;
|
|
164
|
+
let groupKey = Math.floor(
|
|
165
|
+
(points[0] + stableXOffset) / RDP_RANGE_WIDTH_PX,
|
|
166
|
+
);
|
|
167
|
+
for (let i = 1; i < count; i++) {
|
|
168
|
+
const nextKey = Math.floor(
|
|
169
|
+
(points[i * 2] + stableXOffset) / RDP_RANGE_WIDTH_PX,
|
|
170
|
+
);
|
|
171
|
+
if (nextKey !== groupKey) {
|
|
172
|
+
simplifyRdpGroup(
|
|
173
|
+
points,
|
|
174
|
+
keep,
|
|
175
|
+
stack,
|
|
176
|
+
toleranceSq,
|
|
177
|
+
groupStart,
|
|
178
|
+
i - 1,
|
|
179
|
+
);
|
|
180
|
+
groupStart = i;
|
|
181
|
+
groupKey = nextKey;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
simplifyRdpGroup(
|
|
185
|
+
points,
|
|
186
|
+
keep,
|
|
187
|
+
stack,
|
|
188
|
+
toleranceSq,
|
|
189
|
+
groupStart,
|
|
190
|
+
count - 1,
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
for (let i = 0; i < count; i++) {
|
|
194
|
+
if (keep[i] === 1) out.push(points[i * 2], points[i * 2 + 1]);
|
|
195
|
+
}
|
|
196
|
+
return out;
|
|
197
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -422,6 +422,15 @@ export interface LineConfig {
|
|
|
422
422
|
* pair with `join: "miter"` + `cap: "butt"` for true sharp corners (no rounding).
|
|
423
423
|
*/
|
|
424
424
|
curve?: "monotone" | "linear";
|
|
425
|
+
/**
|
|
426
|
+
* Screen-space path simplification tolerance in pixels. Values above `0`
|
|
427
|
+
* remove intermediate points that deviate by no more than this amount while
|
|
428
|
+
* retaining endpoints and larger peaks/valleys. This changes only the drawn
|
|
429
|
+
* line/fill; axis fitting, live values, markers, and scrubbing still use the
|
|
430
|
+
* original data. Default `0` (off). Start around `0.75`–`1.5` for sub-pixel
|
|
431
|
+
* noise reduction.
|
|
432
|
+
*/
|
|
433
|
+
simplify?: number;
|
|
425
434
|
/** Line color override. Defaults to palette-derived accent. */
|
|
426
435
|
color?: string;
|
|
427
436
|
/**
|
|
@@ -854,13 +863,36 @@ export interface ScrubConfig {
|
|
|
854
863
|
*/
|
|
855
864
|
seriesTooltip?: boolean | PerSeriesTooltipConfig;
|
|
856
865
|
/**
|
|
857
|
-
*
|
|
858
|
-
*
|
|
859
|
-
*
|
|
860
|
-
*
|
|
861
|
-
*
|
|
866
|
+
* Which content {@link dimOpacity} fades while scrubbing.
|
|
867
|
+
*
|
|
868
|
+
* - `"future"` fades all chart content to the right of the crosshair.
|
|
869
|
+
* - `"otherCandles"` keeps the candle under the finger at full strength and
|
|
870
|
+
* fades every other candle body and wick. Outside candle mode it falls back
|
|
871
|
+
* to the standard `"future"` behavior. When the finger is in a gap between
|
|
872
|
+
* candle buckets no candle is selected. Volume bars are unchanged. The
|
|
873
|
+
* vertical crosshair and scrub selection dot are hidden so the focused
|
|
874
|
+
* candle is the sole position indicator.
|
|
875
|
+
*
|
|
876
|
+
* Default `"future"`.
|
|
877
|
+
*/
|
|
878
|
+
dimTarget?: "future" | "otherCandles";
|
|
879
|
+
/**
|
|
880
|
+
* Opacity of the content selected by {@link dimTarget} while scrubbing — `0`
|
|
881
|
+
* fully fades it out, `1` disables the dim. Values are clamped to `[0, 1]`.
|
|
882
|
+
*
|
|
883
|
+
* With the default `dimTarget: "future"`, the fade is implemented by erasing
|
|
884
|
+
* the trailing content's alpha (`dstOut`), so it reveals the real background
|
|
885
|
+
* and works on any background color. Default `0.3`. `crosshairDimColor`
|
|
886
|
+
* overrides this opacity only for the `"future"` target.
|
|
862
887
|
*/
|
|
863
888
|
dimOpacity?: number;
|
|
889
|
+
/**
|
|
890
|
+
* Duration in milliseconds for other candles to ease toward
|
|
891
|
+
* {@link dimOpacity} when a focused-candle scrub starts, and back to full
|
|
892
|
+
* strength on release. Used only with `dimTarget: "otherCandles"`.
|
|
893
|
+
* Set `0` for an instant change. Negative values clamp to `0`. Default `60`.
|
|
894
|
+
*/
|
|
895
|
+
dimFadeMs?: number;
|
|
864
896
|
/** Vertical crosshair line stroke. Omit to use theme `crosshairLine`. */
|
|
865
897
|
crosshairLineColor?: string;
|
|
866
898
|
/** Vertical crosshair line width in px. Default `1`. */
|
|
@@ -1522,11 +1554,23 @@ export interface DegenShakePayload {
|
|
|
1522
1554
|
direction: "up" | "down";
|
|
1523
1555
|
}
|
|
1524
1556
|
|
|
1525
|
-
/**
|
|
1526
|
-
|
|
1527
|
-
|
|
1557
|
+
/** Optional soft, static color glow drawn behind a live dot. */
|
|
1558
|
+
export interface DotGlowConfig {
|
|
1559
|
+
/** Glow color. Defaults to the dot's resolved fill color. */
|
|
1560
|
+
color?: string;
|
|
1561
|
+
/** Radius of the glow's source circle in pixels. Default `7`. */
|
|
1562
|
+
radius?: number;
|
|
1563
|
+
/** Skia blur radius in pixels. Default `5`. */
|
|
1564
|
+
blur?: number;
|
|
1565
|
+
/** Glow opacity (0–1). Default `0.18`. */
|
|
1566
|
+
opacity?: number;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
/** Contrasting outer ring drawn behind each series dot — the crisp backing
|
|
1570
|
+
* circle the single-series live dot has, so dots stand out against the lines
|
|
1571
|
+
* and one another. */
|
|
1528
1572
|
export interface DotRingConfig {
|
|
1529
|
-
/** Ring color. Default: theme `badgeOuterBg` (a near-background
|
|
1573
|
+
/** Ring color. Default: theme `badgeOuterBg` (a near-background backing). */
|
|
1530
1574
|
color?: string;
|
|
1531
1575
|
/** Ring thickness in pixels — how far the halo extends past the dot. Default `2.5`. */
|
|
1532
1576
|
width?: number;
|
|
@@ -1535,7 +1579,8 @@ export interface DotRingConfig {
|
|
|
1535
1579
|
/**
|
|
1536
1580
|
* Shared live-dot styling, used by both `LiveChart` (`dot`) and
|
|
1537
1581
|
* `LiveChartSeries` (`dot`, which extends this). A dot is a color-filled circle
|
|
1538
|
-
* of `radius` with an optional contrasting outer `ring`
|
|
1582
|
+
* of `radius` with an optional contrasting outer `ring` and opt-in soft
|
|
1583
|
+
* `glow`. The glow is static; use `pulse` for an animated heartbeat.
|
|
1539
1584
|
*/
|
|
1540
1585
|
export interface DotConfig {
|
|
1541
1586
|
/** Radius of the (color-filled) dot in pixels. Default `3.5`. */
|
|
@@ -1546,6 +1591,12 @@ export interface DotConfig {
|
|
|
1546
1591
|
* `DotRingConfig`. Default `true`.
|
|
1547
1592
|
*/
|
|
1548
1593
|
ring?: boolean | DotRingConfig;
|
|
1594
|
+
/**
|
|
1595
|
+
* Soft static glow behind the dot. `true` = restrained defaults, `false` =
|
|
1596
|
+
* off, or pass `DotGlowConfig` to tune its color, radius, blur, and opacity.
|
|
1597
|
+
* Default `false`.
|
|
1598
|
+
*/
|
|
1599
|
+
glow?: boolean | DotGlowConfig;
|
|
1549
1600
|
/**
|
|
1550
1601
|
* Show the dot. `false` hides it (line, badge, and labels still render). Default `true`.
|
|
1551
1602
|
*
|
|
@@ -1637,6 +1688,13 @@ export interface SeriesConfig {
|
|
|
1637
1688
|
* anchored to this series snap to the straight chord to match.
|
|
1638
1689
|
*/
|
|
1639
1690
|
curve?: "monotone" | "linear";
|
|
1691
|
+
/**
|
|
1692
|
+
* Per-series screen-space path simplification tolerance in pixels. Overrides
|
|
1693
|
+
* `LiveChartSeries.line.simplify`; `0` disables simplification for this series.
|
|
1694
|
+
* Only rendered geometry is simplified — values, range fitting, and scrubbing
|
|
1695
|
+
* continue to use the original points.
|
|
1696
|
+
*/
|
|
1697
|
+
simplify?: number;
|
|
1640
1698
|
/** Per-series stroke width override (px). Falls back to the chart line width. */
|
|
1641
1699
|
strokeWidth?: number;
|
|
1642
1700
|
/** Render a soft glow behind this series' line. Default `false`. */
|
|
@@ -1986,9 +2044,11 @@ export interface LiveChartCoreProps {
|
|
|
1986
2044
|
/**
|
|
1987
2045
|
* UI-thread opacity multiplier for the plotted data. Set a value from `0`
|
|
1988
2046
|
* (hidden) to `1` (fully visible), such as `0.5` while replacement data loads.
|
|
1989
|
-
* Applies to every plotted series — line/fill/candles,
|
|
1990
|
-
*
|
|
1991
|
-
*
|
|
2047
|
+
* Applies to every plotted series — line/fill/candles, value lines, and inline
|
|
2048
|
+
* values — without affecting axes, reference lines, legends, or scrub UI.
|
|
2049
|
+
* While the multiplier is below `1`, live dots and pulse rings fade completely
|
|
2050
|
+
* out so the dimmed stroke cannot show through them; they fade back in when it
|
|
2051
|
+
* returns to `1`. Default `1`.
|
|
1992
2052
|
*/
|
|
1993
2053
|
seriesOpacity?: SharedValue<number>;
|
|
1994
2054
|
/**
|
|
@@ -2326,7 +2386,8 @@ export interface LiveChartProps extends LiveChartCoreProps {
|
|
|
2326
2386
|
pulse?: boolean | PulseConfig;
|
|
2327
2387
|
/**
|
|
2328
2388
|
* Live dot styling. `true`/omitted = shown defaults, `false` = hidden, or pass
|
|
2329
|
-
* `DotConfig` (`radius`, `ring
|
|
2389
|
+
* `DotConfig` (`radius`, `ring`, opt-in `glow`, `color`). See
|
|
2390
|
+
* {@link DotConfig}. Default `true`.
|
|
2330
2391
|
*/
|
|
2331
2392
|
dot?: boolean | DotConfig;
|
|
2332
2393
|
/** Horizontal dashed line at the current live value. `true` = defaults, or pass `ValueLineConfig`. */
|
|
@@ -2374,8 +2435,9 @@ export interface LiveChartProps extends LiveChartCoreProps {
|
|
|
2374
2435
|
/** Render once with no per-frame animation loop — for many small charts (sparklines)
|
|
2375
2436
|
* in a list. The continuous animations (pulse, degen, the entry reveal) are disabled,
|
|
2376
2437
|
* but `scrub` / `scrubAction` stay available — they're on-demand touch gestures with
|
|
2377
|
-
* no per-frame cost, so a still chart is still scrubbable.
|
|
2378
|
-
*
|
|
2438
|
+
* no per-frame cost, so a still chart is still scrubbable. May be toggled at runtime:
|
|
2439
|
+
* switching back to live restarts the suspended loops and catches up. Frame the data
|
|
2440
|
+
* with `timeWindow` + `nowOverride` (see the historical-data-fill pattern). */
|
|
2379
2441
|
static?: boolean;
|
|
2380
2442
|
/**
|
|
2381
2443
|
* Render a custom overlay floated over the chart canvas, handed a price↔pixel /
|