Skip to content

Commit 5fab08b

Browse files
authored
Merge branch 'main' into feature/offscreen-surface-snapshots
2 parents 026ccdd + 4d13cc7 commit 5fab08b

File tree

12 files changed

+357
-5
lines changed

12 files changed

+357
-5
lines changed

example/src/App.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import React from "react";
33
import { createNativeStackNavigator } from "@react-navigation/native-stack";
44
import { StatusBar } from "react-native";
55

6-
import { AnimationExample, DrawingExample } from "./Examples";
6+
import { AnimationExample, DrawingExample, GraphsScreen } from "./Examples";
77
import { API } from "./Examples/API";
88
import { Breathe } from "./Examples/Breathe";
99
import { Filters } from "./Examples/Filters";
@@ -40,6 +40,7 @@ const App = () => {
4040
}}
4141
/>
4242
<Stack.Screen name="Drawing" component={DrawingExample} />
43+
<Stack.Screen name="Graphs" component={GraphsScreen} />
4344
<Stack.Screen name="Animation" component={AnimationExample} />
4445
</Stack.Navigator>
4546
</NavigationContainer>
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import {
2+
Canvas,
3+
Fill,
4+
LinearGradient,
5+
Paint,
6+
Path,
7+
Spring,
8+
useValue,
9+
vec,
10+
} from "@shopify/react-native-skia";
11+
import { runSpring } from "@shopify/react-native-skia/src/animation/Animation/functions";
12+
import React, { useCallback, useEffect, useMemo, useState } from "react";
13+
import { StyleSheet, Text, View } from "react-native";
14+
15+
import { createGraphPath } from "./createGraphPath";
16+
import type { GraphProps } from "./types";
17+
18+
export const Interpolation: React.FC<GraphProps> = ({ height, width }) => {
19+
const path = useMemo(
20+
() => createGraphPath(width, height, 60),
21+
[height, width]
22+
);
23+
const path2 = useMemo(
24+
() => createGraphPath(width, height, 60),
25+
[height, width]
26+
);
27+
28+
const progress = useValue(0);
29+
const [toggled, setToggled] = useState(false);
30+
const onPress = useCallback(() => setToggled((p) => !p), []);
31+
useEffect(() => {
32+
runSpring(progress, toggled ? 1 : 0, Spring.Config.Gentle);
33+
}, [progress, toggled]);
34+
35+
return (
36+
<View style={{ height, marginBottom: 10 }} onTouchEnd={onPress}>
37+
<Canvas style={styles.graph}>
38+
<Fill color="black" />
39+
<Paint>
40+
<LinearGradient
41+
start={vec(0, height * 0.5)}
42+
end={vec(width * 0.5, height * 0.5)}
43+
colors={["black", "#cccc66"]}
44+
/>
45+
</Paint>
46+
<Path
47+
path={() => path.interpolate(path2, progress.value)}
48+
strokeWidth={4}
49+
style="stroke"
50+
strokeJoin="round"
51+
strokeCap="round"
52+
/>
53+
</Canvas>
54+
<Text>Touch graph to interpolate</Text>
55+
</View>
56+
);
57+
};
58+
59+
const styles = StyleSheet.create({
60+
graph: {
61+
flex: 1,
62+
},
63+
});
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import {
2+
Canvas,
3+
Easing,
4+
Fill,
5+
LinearGradient,
6+
Paint,
7+
Path,
8+
useValue,
9+
vec,
10+
} from "@shopify/react-native-skia";
11+
import { runTiming } from "@shopify/react-native-skia/src/animation/Animation/functions";
12+
import React, { useCallback, useEffect, useMemo, useState } from "react";
13+
import { StyleSheet, Text, View } from "react-native";
14+
15+
import { createGraphPath, createZeroPath } from "./createGraphPath";
16+
import type { GraphProps } from "./types";
17+
18+
export const MountAnimation: React.FC<GraphProps> = ({ height, width }) => {
19+
const zeroPath = useMemo(
20+
() => createZeroPath(width, height, 60),
21+
[height, width]
22+
);
23+
const path = useMemo(
24+
() => createGraphPath(width, height, 60, false),
25+
[height, width]
26+
);
27+
28+
const progress = useValue(0);
29+
const [toggled, setToggled] = useState(false);
30+
const onPress = useCallback(() => setToggled((p) => !p), []);
31+
32+
useEffect(() => {
33+
runTiming(progress, {
34+
to: toggled ? 1 : 0,
35+
duration: 350,
36+
easing: Easing.inOut(Easing.cubic),
37+
});
38+
}, [progress, toggled]);
39+
40+
return (
41+
<View style={{ height, marginBottom: 10 }} onTouchEnd={onPress}>
42+
<Canvas style={styles.graph}>
43+
<Fill color="black" />
44+
<Paint>
45+
<LinearGradient
46+
start={vec(0, height * 0.5)}
47+
end={vec(width * 0.5, height * 0.5)}
48+
colors={["black", "#3B8EA5"]}
49+
/>
50+
</Paint>
51+
<Path
52+
path={() => path.interpolate(zeroPath, progress.value)}
53+
strokeWidth={4}
54+
style="stroke"
55+
strokeJoin="round"
56+
strokeCap="round"
57+
/>
58+
</Canvas>
59+
<Text>Touch to toggle between "unmounted" and "mounted"</Text>
60+
</View>
61+
);
62+
};
63+
64+
const styles = StyleSheet.create({
65+
graph: {
66+
flex: 1,
67+
},
68+
});
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import type { IPath } from "@shopify/react-native-skia";
2+
import {
3+
Line,
4+
Canvas,
5+
Circle,
6+
Fill,
7+
LinearGradient,
8+
Paint,
9+
Path,
10+
useTouchHandler,
11+
useValue,
12+
Text as SkiaText,
13+
vec,
14+
} from "@shopify/react-native-skia";
15+
import React, { useMemo } from "react";
16+
import { StyleSheet, Text, View } from "react-native";
17+
18+
import { createGraphPath } from "./createGraphPath";
19+
import type { GraphProps } from "./types";
20+
21+
export const Slider: React.FC<GraphProps> = ({ height, width }) => {
22+
const path = useMemo(
23+
() => createGraphPath(width, height, 60, false),
24+
[height, width]
25+
);
26+
27+
const progress = useValue(
28+
getPointAtPositionInPath(width / 2, width, 60, path)
29+
);
30+
31+
const touchHandler = useTouchHandler({
32+
onActive: ({ x }) =>
33+
(progress.value = getPointAtPositionInPath(x, width, 60, path)),
34+
});
35+
36+
return (
37+
<View style={{ height, marginBottom: 10 }}>
38+
<Canvas style={styles.graph} onTouch={touchHandler}>
39+
<Fill color="black" />
40+
<Paint>
41+
<LinearGradient
42+
start={vec(0, height * 0.5)}
43+
end={vec(width * 0.5, height * 0.5)}
44+
colors={["black", "#DA4167"]}
45+
/>
46+
</Paint>
47+
<Path
48+
path={path}
49+
strokeWidth={4}
50+
style="stroke"
51+
strokeJoin="round"
52+
strokeCap="round"
53+
/>
54+
<Paint color="#fff" />
55+
<Circle c={() => progress.value} r={10} />
56+
<Circle color="#DA4167" c={() => progress.value} r={7.5} />
57+
<SkiaText
58+
familyName="Arial"
59+
size={12}
60+
x={() => progress.value.x - 24}
61+
y={() => progress.value.y - 18}
62+
value={() => "$ " + progress.value.x.toFixed(2)}
63+
/>
64+
<Line
65+
p1={() => vec(progress.value.x, progress.value.y + 14)}
66+
p2={() => vec(progress.value.x, height)}
67+
/>
68+
</Canvas>
69+
<Text>Touch and drag to move center point</Text>
70+
</View>
71+
);
72+
};
73+
74+
const getPointAtPositionInPath = (
75+
x: number,
76+
width: number,
77+
steps: number,
78+
path: IPath
79+
) => {
80+
const index = Math.max(0, Math.floor(x / (width / steps)));
81+
const fraction = (x / (width / steps)) % 1;
82+
const p1 = path.getPoint(index);
83+
if (index < path.countPoints() - 1) {
84+
const p2 = path.getPoint(index + 1);
85+
// Interpolate between p1 and p2
86+
return {
87+
x: p1.x + (p2.x - p1.x) * fraction,
88+
y: p1.y + (p2.y - p1.y) * fraction,
89+
};
90+
}
91+
return p1;
92+
};
93+
94+
const styles = StyleSheet.create({
95+
graph: {
96+
flex: 1,
97+
},
98+
});
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Skia } from "@shopify/react-native-skia";
2+
3+
export const createGraphPath = (
4+
width: number,
5+
height: number,
6+
steps: number,
7+
round = true
8+
) => {
9+
const retVal = Skia.Path.Make();
10+
let y = height / 2;
11+
retVal.moveTo(0, y);
12+
const prevPt = { x: 0, y };
13+
for (let i = 0; i < width; i += width / steps) {
14+
// increase y by a random amount between -10 and 10
15+
y += Math.random() * 30 - 15;
16+
y = Math.max(height * 0.2, Math.min(y, height * 0.7));
17+
18+
if (round && i > 0) {
19+
const xMid = (prevPt.x + i) / 2;
20+
const yMid = (prevPt!.y + y) / 2;
21+
retVal.quadTo(prevPt.x, prevPt.y, xMid, yMid);
22+
prevPt.x = i;
23+
prevPt.y = y;
24+
} else {
25+
retVal.lineTo(i, y);
26+
}
27+
}
28+
return retVal;
29+
};
30+
31+
export const createZeroPath = (
32+
width: number,
33+
height: number,
34+
steps: number
35+
) => {
36+
const retVal = Skia.Path.Make();
37+
const y = height / 2;
38+
retVal.moveTo(0, y);
39+
for (let i = 0; i < width; i += width / steps) {
40+
retVal.lineTo(i, y);
41+
}
42+
return retVal;
43+
};
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React from "react";
2+
import { View, StyleSheet, useWindowDimensions } from "react-native";
3+
4+
import { Interpolation } from "./Interpolation";
5+
import { MountAnimation } from "./Mount";
6+
import { Slider } from "./Slider";
7+
8+
const Padding = 10;
9+
10+
export const GraphsScreen: React.FC = () => {
11+
const { width, height } = useWindowDimensions();
12+
return (
13+
<View style={styles.container}>
14+
<MountAnimation height={height * 0.25} width={width - Padding * 2} />
15+
<Interpolation height={height * 0.25} width={width - Padding * 2} />
16+
<Slider height={height * 0.25} width={width - Padding * 2} />
17+
</View>
18+
);
19+
};
20+
21+
const styles = StyleSheet.create({
22+
container: {
23+
flex: 1,
24+
padding: Padding,
25+
},
26+
});
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export type GraphProps = {
2+
height: number;
3+
width: number;
4+
};

example/src/Examples/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ export * from "./Drawing";
55
export * from "./Filters";
66
export * from "./Gooey";
77
export * from "./Matrix";
8+
export * from "./Graphs";

example/src/Home/HomeScreen.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ export const HomeScreen: React.FC = () => {
3737
description="Use touches to draw with Skia"
3838
route="Drawing"
3939
/>
40+
<HomeScreenButton
41+
title="📉 Graphs"
42+
description="Animated graphs with Skia"
43+
route="Graphs"
44+
/>
4045
<HomeScreenButton
4146
title="🎥 Animation"
4247
description="Animated with Skia"

example/yarn.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5036,9 +5036,9 @@ node-dir@^0.1.17:
50365036
minimatch "^3.0.2"
50375037

50385038
node-fetch@^2.2.0, node-fetch@^2.6.0:
5039-
version "2.6.6"
5040-
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89"
5041-
integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==
5039+
version "2.6.7"
5040+
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
5041+
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
50425042
dependencies:
50435043
whatwg-url "^5.0.0"
50445044

0 commit comments

Comments
 (0)