react-native-vroom-chart 0.13.0 → 0.14.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/android/CMakeLists.txt +8 -0
- package/android/README.md +11 -10
- package/cpp/VroomChartHostObject.cpp +155 -54
- package/cpp/VroomChartHostObject.h +1 -1
- package/cpp/VroomJsiInstaller.cpp +1 -1
- package/cpp/_core_include/vroom/vroom_chart.h +6 -7
- package/cpp/_core_src/candles.h +7 -5
- package/cpp/_core_src/chart.cpp +271 -152
- package/cpp/_core_src/chart.h +6 -3
- package/cpp/_core_src/chart_facade.cpp +5 -1
- package/cpp/_core_src/viewport.h +4 -0
- package/lib/index.d.mts +52 -6
- package/lib/index.d.ts +52 -6
- package/lib/index.js +68 -36
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +77 -38
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/VroomChart.tsx +94 -44
- package/src/index.ts +2 -0
- package/src/jsi.d.ts +18 -17
- package/src/types.ts +2 -0
- package/src/useChartCore.ts +11 -7
package/lib/index.mjs
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
// src/VroomChart.tsx
|
|
2
2
|
import React, { useEffect as useEffect2, useRef as useRef2, useCallback as useCallback2, useState as useState2, useMemo } from "react";
|
|
3
|
-
import { View } from "react-native";
|
|
4
|
-
import {
|
|
3
|
+
import { PixelRatio, View } from "react-native";
|
|
4
|
+
import {
|
|
5
|
+
AlphaType,
|
|
6
|
+
Canvas,
|
|
7
|
+
ColorType,
|
|
8
|
+
Image,
|
|
9
|
+
Picture,
|
|
10
|
+
Skia
|
|
11
|
+
} from "@shopify/react-native-skia";
|
|
5
12
|
import {
|
|
6
13
|
Gesture,
|
|
7
14
|
GestureDetector,
|
|
@@ -349,11 +356,12 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
349
356
|
ensureInstalled();
|
|
350
357
|
handleRef.current = globalThis.VroomChartJSI.create();
|
|
351
358
|
}
|
|
352
|
-
const animRef = useRef({ ms: 300, easing: void 0, reduceMotion: false });
|
|
359
|
+
const animRef = useRef({ ms: 300, easing: void 0, reduceMotion: false, interval: "transform" });
|
|
353
360
|
animRef.current = {
|
|
354
361
|
ms: Math.max(0, transition?.transitionMs ?? 300),
|
|
355
362
|
easing: transition?.transitionEasing,
|
|
356
|
-
reduceMotion: transition?.reduceMotion ?? false
|
|
363
|
+
reduceMotion: transition?.reduceMotion ?? false,
|
|
364
|
+
interval: transition?.intervalTransition === "fade" ? "fade" : "transform"
|
|
357
365
|
};
|
|
358
366
|
const onFrameRef = useRef(transition?.onFrame);
|
|
359
367
|
onFrameRef.current = transition?.onFrame;
|
|
@@ -426,7 +434,7 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
426
434
|
morphing = animRef.current.ms > 0 && !animRef.current.reduceMotion && onFrameRef.current != null;
|
|
427
435
|
if (morphing) {
|
|
428
436
|
endIntervalMorph();
|
|
429
|
-
h.beginIntervalMorph();
|
|
437
|
+
h.beginIntervalMorph(animRef.current.interval);
|
|
430
438
|
}
|
|
431
439
|
} else if (transitionKind === "initial" || transitionKind === "reset") {
|
|
432
440
|
endIntervalMorph();
|
|
@@ -479,6 +487,9 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
479
487
|
}
|
|
480
488
|
|
|
481
489
|
// src/VroomChart.tsx
|
|
490
|
+
function isSkImage(frame) {
|
|
491
|
+
return typeof frame.getImageInfo === "function";
|
|
492
|
+
}
|
|
482
493
|
function VroomChart(props) {
|
|
483
494
|
const {
|
|
484
495
|
candles,
|
|
@@ -491,6 +502,7 @@ function VroomChart(props) {
|
|
|
491
502
|
chartType,
|
|
492
503
|
transitionMs,
|
|
493
504
|
transitionEasing,
|
|
505
|
+
intervalTransition,
|
|
494
506
|
theme,
|
|
495
507
|
rsi,
|
|
496
508
|
macd,
|
|
@@ -530,17 +542,38 @@ function VroomChart(props) {
|
|
|
530
542
|
rec.beginRecording(Skia.XYWHRect(0, 0, 1, 1));
|
|
531
543
|
return rec.finishRecordingAsPicture();
|
|
532
544
|
}, []);
|
|
545
|
+
const emptyImage = useMemo(() => {
|
|
546
|
+
const data = Skia.Data.fromBytes(new Uint8Array(4));
|
|
547
|
+
return Skia.Image.MakeImage(
|
|
548
|
+
{
|
|
549
|
+
width: 1,
|
|
550
|
+
height: 1,
|
|
551
|
+
colorType: ColorType.RGBA_8888,
|
|
552
|
+
alphaType: AlphaType.Premul
|
|
553
|
+
},
|
|
554
|
+
data,
|
|
555
|
+
4
|
|
556
|
+
);
|
|
557
|
+
}, []);
|
|
533
558
|
const pictureSV = useSharedValue(emptyPicture);
|
|
559
|
+
const imageSV = useSharedValue(emptyImage);
|
|
560
|
+
const applyFrame = useCallback2(
|
|
561
|
+
(frame) => {
|
|
562
|
+
if (isSkImage(frame)) imageSV.value = frame;
|
|
563
|
+
else pictureSV.value = frame;
|
|
564
|
+
},
|
|
565
|
+
[imageSV, pictureSV]
|
|
566
|
+
);
|
|
534
567
|
const reduceMotion = useReducedMotion();
|
|
535
568
|
const onFrame = useCallback2(
|
|
536
569
|
(p) => {
|
|
537
|
-
|
|
570
|
+
applyFrame(p);
|
|
538
571
|
},
|
|
539
|
-
[
|
|
572
|
+
[applyFrame]
|
|
540
573
|
);
|
|
541
574
|
const { handle, picture, volumeCollapseRef } = useChartCore(
|
|
542
575
|
candles,
|
|
543
|
-
{ width, height },
|
|
576
|
+
{ width, height, pxRatio: PixelRatio.get() },
|
|
544
577
|
visibleRange,
|
|
545
578
|
defaultCandleWidth,
|
|
546
579
|
chartType,
|
|
@@ -552,7 +585,7 @@ function VroomChart(props) {
|
|
|
552
585
|
bollingerBands,
|
|
553
586
|
volume,
|
|
554
587
|
priceLinesProp,
|
|
555
|
-
{ seriesKey, transitionMs, transitionEasing, reduceMotion, onFrame }
|
|
588
|
+
{ seriesKey, transitionMs, transitionEasing, intervalTransition, reduceMotion, onFrame }
|
|
556
589
|
);
|
|
557
590
|
const crosshairActive = useRef2(false);
|
|
558
591
|
const lastCrosshairTime = useRef2(null);
|
|
@@ -569,11 +602,11 @@ function VroomChart(props) {
|
|
|
569
602
|
animRaf.current = null;
|
|
570
603
|
if (!handle) return;
|
|
571
604
|
const next = handle.render();
|
|
572
|
-
if (next)
|
|
605
|
+
if (next) applyFrame(next);
|
|
573
606
|
if (handle.isAnimating()) {
|
|
574
607
|
animRaf.current = requestAnimationFrame(animTick);
|
|
575
608
|
}
|
|
576
|
-
}, [handle,
|
|
609
|
+
}, [handle, applyFrame]);
|
|
577
610
|
const maybeStartAnim = useCallback2(() => {
|
|
578
611
|
if (animRaf.current != null) return;
|
|
579
612
|
if (!handle?.isAnimating()) return;
|
|
@@ -588,9 +621,9 @@ function VroomChart(props) {
|
|
|
588
621
|
};
|
|
589
622
|
}, []);
|
|
590
623
|
useEffect2(() => {
|
|
591
|
-
if (picture)
|
|
624
|
+
if (picture) applyFrame(picture);
|
|
592
625
|
maybeStartAnim();
|
|
593
|
-
}, [picture,
|
|
626
|
+
}, [picture, applyFrame, maybeStartAnim]);
|
|
594
627
|
const morphRaf = useRef2(null);
|
|
595
628
|
const morphFade = useRef2(null);
|
|
596
629
|
const morphHandle = useRef2(null);
|
|
@@ -604,7 +637,7 @@ function VroomChart(props) {
|
|
|
604
637
|
morphFade.current = target;
|
|
605
638
|
handle.setChartType(target);
|
|
606
639
|
const p = handle.render();
|
|
607
|
-
if (p)
|
|
640
|
+
if (p) applyFrame(p);
|
|
608
641
|
maybeStartAnim();
|
|
609
642
|
return void 0;
|
|
610
643
|
}
|
|
@@ -621,7 +654,7 @@ function VroomChart(props) {
|
|
|
621
654
|
morphFade.current = target;
|
|
622
655
|
handle.setChartType(target);
|
|
623
656
|
const p = handle.render();
|
|
624
|
-
if (p)
|
|
657
|
+
if (p) applyFrame(p);
|
|
625
658
|
maybeStartAnim();
|
|
626
659
|
return void 0;
|
|
627
660
|
}
|
|
@@ -634,7 +667,7 @@ function VroomChart(props) {
|
|
|
634
667
|
morphFade.current = fade;
|
|
635
668
|
handle.setMorph(reduceMotion ? 0 : fade, fade);
|
|
636
669
|
const p = handle.render();
|
|
637
|
-
if (p)
|
|
670
|
+
if (p) applyFrame(p);
|
|
638
671
|
if (prog < 1) {
|
|
639
672
|
morphRaf.current = requestAnimationFrame(step);
|
|
640
673
|
} else {
|
|
@@ -642,7 +675,7 @@ function VroomChart(props) {
|
|
|
642
675
|
morphFade.current = target;
|
|
643
676
|
handle.setChartType(target);
|
|
644
677
|
const q = handle.render();
|
|
645
|
-
if (q)
|
|
678
|
+
if (q) applyFrame(q);
|
|
646
679
|
maybeStartAnim();
|
|
647
680
|
}
|
|
648
681
|
};
|
|
@@ -653,7 +686,7 @@ function VroomChart(props) {
|
|
|
653
686
|
morphRaf.current = null;
|
|
654
687
|
}
|
|
655
688
|
};
|
|
656
|
-
}, [handle, chartType, transitionMs, reduceMotion,
|
|
689
|
+
}, [handle, chartType, transitionMs, reduceMotion, applyFrame, maybeStartAnim]);
|
|
657
690
|
const volumeRaf = useRef2(null);
|
|
658
691
|
const volumeHandle = useRef2(null);
|
|
659
692
|
useEffect2(() => {
|
|
@@ -679,7 +712,7 @@ function VroomChart(props) {
|
|
|
679
712
|
volumeCollapseRef.current = { t: target, easing };
|
|
680
713
|
handle.setVolumeCollapse(target, easing);
|
|
681
714
|
const p = handle.render();
|
|
682
|
-
if (p)
|
|
715
|
+
if (p) applyFrame(p);
|
|
683
716
|
maybeStartAnim();
|
|
684
717
|
return void 0;
|
|
685
718
|
}
|
|
@@ -693,7 +726,7 @@ function VroomChart(props) {
|
|
|
693
726
|
volumeCollapseRef.current = { t, easing: kind };
|
|
694
727
|
handle.setVolumeCollapse(t, kind);
|
|
695
728
|
const p = handle.render();
|
|
696
|
-
if (p)
|
|
729
|
+
if (p) applyFrame(p);
|
|
697
730
|
if (prog < 1) {
|
|
698
731
|
volumeRaf.current = requestAnimationFrame(step);
|
|
699
732
|
} else {
|
|
@@ -713,7 +746,7 @@ function VroomChart(props) {
|
|
|
713
746
|
volume?.enabled,
|
|
714
747
|
transitionMs,
|
|
715
748
|
reduceMotion,
|
|
716
|
-
|
|
749
|
+
applyFrame,
|
|
717
750
|
volumeCollapseRef,
|
|
718
751
|
maybeStartAnim
|
|
719
752
|
]);
|
|
@@ -731,7 +764,7 @@ function VroomChart(props) {
|
|
|
731
764
|
axisCollapse.current = { y: targetY, x: targetX };
|
|
732
765
|
handle.setAxisCollapse(targetY, targetX);
|
|
733
766
|
const p = handle.render();
|
|
734
|
-
if (p)
|
|
767
|
+
if (p) applyFrame(p);
|
|
735
768
|
maybeStartAnim();
|
|
736
769
|
return void 0;
|
|
737
770
|
}
|
|
@@ -750,7 +783,7 @@ function VroomChart(props) {
|
|
|
750
783
|
axisCollapse.current = { y: targetY, x: targetX };
|
|
751
784
|
handle.setAxisCollapse(targetY, targetX);
|
|
752
785
|
const p = handle.render();
|
|
753
|
-
if (p)
|
|
786
|
+
if (p) applyFrame(p);
|
|
754
787
|
maybeStartAnim();
|
|
755
788
|
return void 0;
|
|
756
789
|
}
|
|
@@ -764,7 +797,7 @@ function VroomChart(props) {
|
|
|
764
797
|
axisCollapse.current = { y, x };
|
|
765
798
|
handle.setAxisCollapse(y, x);
|
|
766
799
|
const p = handle.render();
|
|
767
|
-
if (p)
|
|
800
|
+
if (p) applyFrame(p);
|
|
768
801
|
if (prog < 1) {
|
|
769
802
|
axisRaf.current = requestAnimationFrame(step);
|
|
770
803
|
} else {
|
|
@@ -785,7 +818,7 @@ function VroomChart(props) {
|
|
|
785
818
|
showXAxis,
|
|
786
819
|
transitionMs,
|
|
787
820
|
reduceMotion,
|
|
788
|
-
|
|
821
|
+
applyFrame,
|
|
789
822
|
maybeStartAnim
|
|
790
823
|
]);
|
|
791
824
|
const hitAxis = useCallback2(
|
|
@@ -825,7 +858,7 @@ function VroomChart(props) {
|
|
|
825
858
|
priceDrag.current = { index: pl.index, id: pl.line.id, price: pl.line.price };
|
|
826
859
|
handle.setPriceLineDrag(pl.index, pl.line.price);
|
|
827
860
|
const p = handle.render();
|
|
828
|
-
if (p)
|
|
861
|
+
if (p) applyFrame(p);
|
|
829
862
|
}
|
|
830
863
|
}
|
|
831
864
|
}).onChange((e) => {
|
|
@@ -848,7 +881,7 @@ function VroomChart(props) {
|
|
|
848
881
|
next = handle.render();
|
|
849
882
|
} else if (crosshairActive.current) {
|
|
850
883
|
const ch = handle.setCrosshair(e.x, e.y - crosshairOffset);
|
|
851
|
-
if (ch)
|
|
884
|
+
if (ch) applyFrame(ch);
|
|
852
885
|
const info = handle.getCrosshairInfo();
|
|
853
886
|
const t = info?.timeMs ?? null;
|
|
854
887
|
if (t !== lastCrosshairTime.current) {
|
|
@@ -859,7 +892,7 @@ function VroomChart(props) {
|
|
|
859
892
|
} else {
|
|
860
893
|
next = handle.translate(e.changeX, e.changeY);
|
|
861
894
|
}
|
|
862
|
-
if (next)
|
|
895
|
+
if (next) applyFrame(next);
|
|
863
896
|
maybeStartAnim();
|
|
864
897
|
}).onEnd((e) => {
|
|
865
898
|
if (!handle) return;
|
|
@@ -868,7 +901,7 @@ function VroomChart(props) {
|
|
|
868
901
|
priceDrag.current = null;
|
|
869
902
|
handle.setPriceLineDrag(-1, 0);
|
|
870
903
|
const p = handle.render();
|
|
871
|
-
if (p)
|
|
904
|
+
if (p) applyFrame(p);
|
|
872
905
|
if (g) onPriceLineDragEnd?.(g.id, g.price);
|
|
873
906
|
return;
|
|
874
907
|
}
|
|
@@ -888,7 +921,7 @@ function VroomChart(props) {
|
|
|
888
921
|
velocity *= Math.pow(0.5, dt / HALF_LIFE_S);
|
|
889
922
|
const dx = velocity * dt;
|
|
890
923
|
const next = handle.pan(dx, 0);
|
|
891
|
-
if (next)
|
|
924
|
+
if (next) applyFrame(next);
|
|
892
925
|
maybeStartAnim();
|
|
893
926
|
if (Math.abs(velocity) > MIN_STOP) {
|
|
894
927
|
decayRaf.current = requestAnimationFrame(tick);
|
|
@@ -942,7 +975,7 @@ function VroomChart(props) {
|
|
|
942
975
|
}
|
|
943
976
|
if (frameX === 1 && frameY === 1) return;
|
|
944
977
|
const next = handle.zoom(frameX, frameY, focalX, focalY);
|
|
945
|
-
if (next)
|
|
978
|
+
if (next) applyFrame(next);
|
|
946
979
|
maybeStartAnim();
|
|
947
980
|
});
|
|
948
981
|
const longPress = Gesture.LongPress().runOnJS(true).onStart((e) => {
|
|
@@ -952,7 +985,7 @@ function VroomChart(props) {
|
|
|
952
985
|
cancelDecay();
|
|
953
986
|
crosshairActive.current = true;
|
|
954
987
|
const ch = handle.setCrosshair(e.x, e.y - crosshairOffset);
|
|
955
|
-
if (ch)
|
|
988
|
+
if (ch) applyFrame(ch);
|
|
956
989
|
const info = handle.getCrosshairInfo();
|
|
957
990
|
lastCrosshairTime.current = info?.timeMs ?? null;
|
|
958
991
|
onCrosshair?.({
|
|
@@ -975,7 +1008,7 @@ function VroomChart(props) {
|
|
|
975
1008
|
if (hitAxis(e.x, e.y) !== "chart") return;
|
|
976
1009
|
crosshairActive.current = false;
|
|
977
1010
|
const ch = handle.clearCrosshair();
|
|
978
|
-
if (ch)
|
|
1011
|
+
if (ch) applyFrame(ch);
|
|
979
1012
|
lastCrosshairTime.current = null;
|
|
980
1013
|
onCrosshair?.({ active: false, candle: null, timeMs: null, price: null, reason: "hide" });
|
|
981
1014
|
});
|
|
@@ -990,11 +1023,17 @@ function VroomChart(props) {
|
|
|
990
1023
|
style
|
|
991
1024
|
]
|
|
992
1025
|
},
|
|
993
|
-
/* @__PURE__ */ React.createElement(GestureDetector, { gesture }, /* @__PURE__ */ React.createElement(View, { style: { flex: 1 } }, /* @__PURE__ */ React.createElement(Canvas, { style: { flex: 1 } }, width > 0 && height > 0 ? (
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1026
|
+
/* @__PURE__ */ React.createElement(GestureDetector, { gesture }, /* @__PURE__ */ React.createElement(View, { style: { flex: 1 } }, /* @__PURE__ */ React.createElement(Canvas, { style: { flex: 1 } }, width > 0 && height > 0 ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Picture, { picture: pictureSV }), /* @__PURE__ */ React.createElement(
|
|
1027
|
+
Image,
|
|
1028
|
+
{
|
|
1029
|
+
image: imageSV,
|
|
1030
|
+
x: 0,
|
|
1031
|
+
y: 0,
|
|
1032
|
+
width,
|
|
1033
|
+
height,
|
|
1034
|
+
fit: "fill"
|
|
1035
|
+
}
|
|
1036
|
+
)) : null)))
|
|
998
1037
|
);
|
|
999
1038
|
}
|
|
1000
1039
|
export {
|