Skip to content

Commit 5475301

Browse files
committed
feat: add cursor customizations, more documentation, and batched updates
Key Highlights: - Added cursorColor, cursorLineColor, cursorLineWidth, hideCursor, and hideCursorLine props to improve customizability of cursor - Improved documentation of types - Introduced batchedUpdates to reduce mount re-render - Added precision prop
1 parent 9bcc57c commit 5475301

File tree

13 files changed

+314
-104
lines changed

13 files changed

+314
-104
lines changed

example/app/chart.tsx

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
type PanGestureHandlerOnChangeEventPayload,
55
HoverGestureHandlerOnChangeEventPayload,
66
} from "@codeherence/react-native-graph";
7-
import { LinearGradient, vec } from "@shopify/react-native-skia";
7+
import { Paint } from "@shopify/react-native-skia";
88
import { useCallback, useMemo, useReducer } from "react";
99
import { Button, ScrollView, StyleSheet, Text } from "react-native";
1010
import { useAnimatedProps, useSharedValue } from "react-native-reanimated";
@@ -35,7 +35,7 @@ const uiFormatter = (price: number) => {
3535
};
3636

3737
const priceMap = {
38-
msft: msft_prices.results
38+
msft: [...msft_prices.results]
3939
.reverse()
4040
.map<[number, number]>((r) => [new Date(r.date).getTime(), r.close]),
4141
aapl: aapl_prices.results
@@ -52,10 +52,10 @@ const priceMap = {
5252
const priceMapKeys = Object.keys(priceMap) as (keyof typeof priceMap)[];
5353

5454
export default () => {
55-
const [counter, increment] = useReducer((s: number) => s + 1, 0);
56-
5755
const { bottom, left, right } = useSafeAreaInsets();
5856

57+
const [counter, increment] = useReducer((s: number) => s + 1, 0);
58+
5959
const latestPrice = useSharedValue("0");
6060
const symbol = priceMapKeys[counter % priceMapKeys.length]!;
6161
const data: [number, number][] = useMemo(() => {
@@ -85,31 +85,33 @@ export default () => {
8585
return (
8686
<ScrollView
8787
style={styles.container}
88-
contentContainerStyle={{
89-
paddingBottom: bottom,
90-
paddingLeft: left,
91-
paddingRight: right,
92-
}}
88+
contentContainerStyle={[
89+
styles.contentContainer,
90+
{
91+
paddingBottom: bottom,
92+
paddingLeft: left,
93+
paddingRight: right,
94+
},
95+
]}
9396
showsVerticalScrollIndicator={false}
9497
>
95-
<Button title={`Showing ${symbol}. Click to switch.`} onPress={increment} />
98+
<Button title={`Showing ${symbol}. Press to switch.`} onPress={increment} />
9699
<AnimatedText style={styles.price} animatedProps={animatedProps} />
97100
<LineChart
98101
points={data}
99102
style={styles.chart}
103+
cursorColor="#000"
104+
cursorLineColor="#000"
105+
cursorLineWidth={1}
100106
TopAxisLabel={AxisLabel}
101107
BottomAxisLabel={AxisLabel}
102108
onPanGestureChange={onGestureChangeWorklet}
103109
onPanGestureEnd={onEndWorklet}
104110
onHoverGestureEnd={onEndWorklet}
105111
onHoverGestureChange={onHoverChangeWorklet}
106112
strokeWidth={2}
107-
PathFill={({ width }) => (
108-
<LinearGradient
109-
start={vec(0, 0)}
110-
end={vec(width, 0)}
111-
colors={["blue", "red", "purple"]}
112-
/>
113+
PathFill={({ strokeWidth }) => (
114+
<Paint style="stroke" strokeWidth={strokeWidth} color="lightblue" />
113115
)}
114116
/>
115117
</ScrollView>
@@ -118,6 +120,7 @@ export default () => {
118120

119121
const styles = StyleSheet.create({
120122
container: { flex: 1 },
121-
chart: { flex: 1, minHeight: 400 },
123+
contentContainer: { flexGrow: 1 },
124+
chart: { flex: 1, maxHeight: 300 },
122125
price: { fontSize: 32 },
123126
});

example/app/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ import { useRouter } from "expo-router";
22
import { Pressable, ScrollView, StyleSheet, Text } from "react-native";
33

44
export default () => {
5-
const { push } = useRouter();
5+
const { navigate } = useRouter();
66

77
return (
88
<ScrollView
99
style={styles.container}
1010
contentContainerStyle={styles.contentContainer}
1111
showsVerticalScrollIndicator={false}
1212
>
13-
<Pressable onPress={() => push("/chart")}>
13+
<Pressable onPress={() => navigate("/chart")}>
1414
<Text style={styles.link}>Go to Charts</Text>
1515
</Pressable>
1616
</ScrollView>

example/ios/Podfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,7 +1016,7 @@ PODS:
10161016
- React-Core
10171017
- react-native-safe-area-context (4.8.2):
10181018
- React-Core
1019-
- react-native-skia (1.0.2):
1019+
- react-native-skia (1.2.3):
10201020
- glog
10211021
- RCT-Folly (= 2022.05.16.00)
10221022
- React
@@ -1508,7 +1508,7 @@ SPEC CHECKSUMS:
15081508
React-Mapbuffer: 63913773ed7f96b814a2521e13e6d010282096ad
15091509
react-native-animateable-text: 79b31ec28f4ed14dab76b304eb74a24eeb2fa9c0
15101510
react-native-safe-area-context: 0ee144a6170530ccc37a0fd9388e28d06f516a89
1511-
react-native-skia: 0c09a4baff5db09a88dc6c9bfba25bcf4f5bbe8a
1511+
react-native-skia: 61a8a1c161ebf4e677b2d39af880c997d8847313
15121512
React-nativeconfig: d7af5bae6da70fa15ce44f045621cf99ed24087c
15131513
React-NativeModulesApple: 0123905d5699853ac68519607555a9a4f5c7b3ac
15141514
React-perflogger: 8a1e1af5733004bdd91258dcefbde21e0d1faccd

example/ios/example.xcodeproj/project.pbxproj

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,10 @@
471471
ONLY_ACTIVE_ARCH = YES;
472472
OTHER_CFLAGS = "$(inherited)";
473473
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
474-
OTHER_LDFLAGS = "$(inherited) ";
474+
OTHER_LDFLAGS = (
475+
"$(inherited)",
476+
" ",
477+
);
475478
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
476479
SDKROOT = iphoneos;
477480
USE_HERMES = true;
@@ -528,7 +531,10 @@
528531
MTL_ENABLE_DEBUG_INFO = NO;
529532
OTHER_CFLAGS = "$(inherited)";
530533
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
531-
OTHER_LDFLAGS = "$(inherited) ";
534+
OTHER_LDFLAGS = (
535+
"$(inherited)",
536+
" ",
537+
);
532538
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
533539
SDKROOT = iphoneos;
534540
USE_HERMES = true;

example/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
},
1212
"dependencies": {
1313
"@expo/metro-runtime": "~3.1.3",
14-
"@shopify/react-native-skia": "1.0.2",
14+
"@shopify/react-native-skia": "^1.2.3",
1515
"expo": "~50.0.8",
1616
"expo-blur": "~12.9.2",
1717
"expo-constants": "~15.4.5",

example/yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2305,10 +2305,10 @@
23052305
component-type "^1.2.1"
23062306
join-component "^1.1.0"
23072307

2308-
"@shopify/react-native-skia@1.0.2":
2309-
version "1.0.2"
2310-
resolved "https://registry.yarnpkg.com/@shopify/react-native-skia/-/react-native-skia-1.0.2.tgz#3ffa54f5df524ebae0ce55bee1e9efbe3e7cf959"
2311-
integrity sha512-w6qS+jCeUGKDJAOok5QJV6flWlvFt0Vmc9mNXi2n4jfHkQ+uccYVHFCXazhCAc5b8mFjcfMNygy12SVgP4d46g==
2308+
"@shopify/react-native-skia@^1.2.3":
2309+
version "1.2.3"
2310+
resolved "https://registry.yarnpkg.com/@shopify/react-native-skia/-/react-native-skia-1.2.3.tgz#d87efd01c362b69f62375ce950c30e453586d6f9"
2311+
integrity sha512-gyUD/HGsMyZ+YAoWxVh24HYN5juwC/dZWINL/0sKP7Ttee/0igCRxWPneH1BbVH28dhyf+tvksQNUwpMM3VWbg==
23122312
dependencies:
23132313
canvaskit-wasm "0.39.1"
23142314
react-reconciler "0.27.0"

src/components/LineChart/AxisLabel.tsx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable @typescript-eslint/no-unused-vars */
22
import { useCallback, useState } from "react";
33
import { LayoutChangeEvent, StyleSheet, View } from "react-native";
4-
import Reanimated, {
4+
import Animated, {
55
clamp,
66
useAnimatedStyle,
77
useDerivedValue,
@@ -33,21 +33,18 @@ export const AxisLabelContainer: React.FC<AxisLabelContainerProps> = ({
3333
const halfWidth = Math.round(width / 2);
3434
const minX = 0;
3535
const maxX = containerWidth - width;
36-
3736
return withTiming(clamp(x - halfWidth, minX, maxX));
3837
}, [x, containerWidth, width]);
3938

4039
const transform = useAnimatedStyle(() => {
41-
return {
42-
transform: [{ translateX: translateX.value }],
43-
};
40+
return { transform: [{ translateX: translateX.value }] };
4441
});
4542

4643
return (
4744
<View style={styles.container}>
48-
<Reanimated.View style={[styles.child, transform]} onLayout={onLayout}>
45+
<Animated.View style={[styles.child, transform]} onLayout={onLayout}>
4946
{children}
50-
</Reanimated.View>
47+
</Animated.View>
5148
</View>
5249
);
5350
};

src/components/LineChart/Cursor.tsx

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,78 @@ import { Circle, Group, Line, vec } from "@shopify/react-native-skia";
22
import { useDerivedValue } from "react-native-reanimated";
33
import type { DerivedValue, SharedValue } from "react-native-reanimated";
44

5-
interface CursorProps {
5+
export interface CursorProps {
6+
/** The x position of the cursor */
67
x: SharedValue<number>;
8+
/** The y position of the cursor */
79
y: DerivedValue<number>;
10+
/** The canvas height */
811
height: number;
12+
/** The radius of the cursor */
913
cursorRadius: number;
14+
/** The color of the cursor */
15+
cursorColor: string;
16+
/**
17+
* The color of the cursor line.
18+
* @default "rgba(0, 0, 0, 0.5)"
19+
*/
20+
cursorLineColor: string;
21+
/**
22+
* The width of the cursor line.
23+
* @default 2
24+
*/
25+
cursorLineWidth: number;
26+
/**
27+
* Whether to hide the cursor line.
28+
* @default false
29+
*/
30+
hideCursorLine: boolean;
1031
}
1132

12-
export const Cursor: React.FC<CursorProps> = ({ x, y, height, cursorRadius }) => {
13-
const lineTransform = useDerivedValue(() => {
33+
type CursorLineProps = Pick<CursorProps, "x" | "height" | "cursorLineColor" | "cursorLineWidth">;
34+
35+
const CursorLine: React.FC<CursorLineProps> = ({ x, height, cursorLineColor, cursorLineWidth }) => {
36+
const transform = useDerivedValue(() => {
1437
return [{ translateX: x.value }];
1538
});
1639

17-
const transform = useDerivedValue(() => {
40+
return (
41+
<Line
42+
transform={transform}
43+
p1={vec(0, 0)}
44+
p2={vec(0, height)}
45+
color={cursorLineColor}
46+
strokeWidth={cursorLineWidth}
47+
/>
48+
);
49+
};
50+
51+
export const Cursor: React.FC<CursorProps> = ({
52+
x,
53+
y,
54+
height,
55+
cursorRadius,
56+
cursorColor,
57+
cursorLineColor,
58+
cursorLineWidth,
59+
hideCursorLine,
60+
}) => {
61+
const cursorTransform = useDerivedValue(() => {
1862
return [{ translateX: x.value }, { translateY: y.value }];
1963
}, []);
2064

2165
return (
2266
<>
23-
<Line
24-
transform={lineTransform}
25-
p1={vec(0, 0)}
26-
p2={vec(0, height)}
27-
color="rgba(0, 0, 0, 0.5)"
28-
strokeWidth={2}
29-
/>
30-
<Group transform={transform}>
31-
<Circle cx={0} cy={0} r={cursorRadius} color="black" />
67+
{!hideCursorLine && (
68+
<CursorLine
69+
x={x}
70+
height={height}
71+
cursorLineColor={cursorLineColor}
72+
cursorLineWidth={cursorLineWidth}
73+
/>
74+
)}
75+
<Group transform={cursorTransform}>
76+
<Circle cx={0} cy={0} r={cursorRadius} color={cursorColor} />
3277
</Group>
3378
</>
3479
);

src/components/LineChart/computations.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ interface RoundProps {
1010

1111
const round = ({ value, precision = 0 }: RoundProps): number => {
1212
"worklet";
13-
1413
const p = Math.pow(10, precision);
1514
return Math.round(value * p) / p;
1615
};
@@ -23,7 +22,6 @@ interface LinearYForXProps {
2322

2423
const linearYForX = ({ path, x, precision = 2 }: LinearYForXProps): number => {
2524
"worklet";
26-
2725
const cmds = path.toCmds();
2826
let from: Vector = vec(0, 0);
2927
let found = false;
@@ -33,8 +31,10 @@ const linearYForX = ({ path, x, precision = 2 }: LinearYForXProps): number => {
3331
const cmd = cmds[i];
3432
if (cmd == null) break;
3533
if (cmd[0] === PathVerb.Move) {
34+
// If the path starts with a move command, set the from point
3635
from = vec(cmd[1], cmd[2]);
3736
} else if (cmd[0] === PathVerb.Line) {
37+
// If the path contains a line command, check if the x value is within the bounds of the line
3838
const to = vec(cmd[1], cmd[2]);
3939
if ((x >= from.x && x <= to.x) || (x <= from.x && x >= to.x)) {
4040
const t = (x - from.x) / (to.x - from.x);
@@ -84,7 +84,6 @@ export const computePath = ({
8484
curveType,
8585
}: ComputePathProps): SkPath => {
8686
"worklet";
87-
8887
const straightLine = Skia.Path.Make()
8988
.moveTo(0, height / 2)
9089
.lineTo(width, height / 2);

src/components/LineChart/constants.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)