Skip to content

Commit

Permalink
Merge branch 'main' into feature/offscreen-surface-snapshots
Browse files Browse the repository at this point in the history
  • Loading branch information
chrfalch authored Jan 28, 2022
2 parents 026ccdd + 4d13cc7 commit 5fab08b
Show file tree
Hide file tree
Showing 12 changed files with 357 additions and 5 deletions.
3 changes: 2 additions & 1 deletion example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React from "react";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import { StatusBar } from "react-native";

import { AnimationExample, DrawingExample } from "./Examples";
import { AnimationExample, DrawingExample, GraphsScreen } from "./Examples";
import { API } from "./Examples/API";
import { Breathe } from "./Examples/Breathe";
import { Filters } from "./Examples/Filters";
Expand Down Expand Up @@ -40,6 +40,7 @@ const App = () => {
}}
/>
<Stack.Screen name="Drawing" component={DrawingExample} />
<Stack.Screen name="Graphs" component={GraphsScreen} />
<Stack.Screen name="Animation" component={AnimationExample} />
</Stack.Navigator>
</NavigationContainer>
Expand Down
63 changes: 63 additions & 0 deletions example/src/Examples/Graphs/Interpolation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {
Canvas,
Fill,
LinearGradient,
Paint,
Path,
Spring,
useValue,
vec,
} from "@shopify/react-native-skia";
import { runSpring } from "@shopify/react-native-skia/src/animation/Animation/functions";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { StyleSheet, Text, View } from "react-native";

import { createGraphPath } from "./createGraphPath";
import type { GraphProps } from "./types";

export const Interpolation: React.FC<GraphProps> = ({ height, width }) => {
const path = useMemo(
() => createGraphPath(width, height, 60),
[height, width]
);
const path2 = useMemo(
() => createGraphPath(width, height, 60),
[height, width]
);

const progress = useValue(0);
const [toggled, setToggled] = useState(false);
const onPress = useCallback(() => setToggled((p) => !p), []);
useEffect(() => {
runSpring(progress, toggled ? 1 : 0, Spring.Config.Gentle);
}, [progress, toggled]);

return (
<View style={{ height, marginBottom: 10 }} onTouchEnd={onPress}>
<Canvas style={styles.graph}>
<Fill color="black" />
<Paint>
<LinearGradient
start={vec(0, height * 0.5)}
end={vec(width * 0.5, height * 0.5)}
colors={["black", "#cccc66"]}
/>
</Paint>
<Path
path={() => path.interpolate(path2, progress.value)}
strokeWidth={4}
style="stroke"
strokeJoin="round"
strokeCap="round"
/>
</Canvas>
<Text>Touch graph to interpolate</Text>
</View>
);
};

const styles = StyleSheet.create({
graph: {
flex: 1,
},
});
68 changes: 68 additions & 0 deletions example/src/Examples/Graphs/Mount.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import {
Canvas,
Easing,
Fill,
LinearGradient,
Paint,
Path,
useValue,
vec,
} from "@shopify/react-native-skia";
import { runTiming } from "@shopify/react-native-skia/src/animation/Animation/functions";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { StyleSheet, Text, View } from "react-native";

import { createGraphPath, createZeroPath } from "./createGraphPath";
import type { GraphProps } from "./types";

export const MountAnimation: React.FC<GraphProps> = ({ height, width }) => {
const zeroPath = useMemo(
() => createZeroPath(width, height, 60),
[height, width]
);
const path = useMemo(
() => createGraphPath(width, height, 60, false),
[height, width]
);

const progress = useValue(0);
const [toggled, setToggled] = useState(false);
const onPress = useCallback(() => setToggled((p) => !p), []);

useEffect(() => {
runTiming(progress, {
to: toggled ? 1 : 0,
duration: 350,
easing: Easing.inOut(Easing.cubic),
});
}, [progress, toggled]);

return (
<View style={{ height, marginBottom: 10 }} onTouchEnd={onPress}>
<Canvas style={styles.graph}>
<Fill color="black" />
<Paint>
<LinearGradient
start={vec(0, height * 0.5)}
end={vec(width * 0.5, height * 0.5)}
colors={["black", "#3B8EA5"]}
/>
</Paint>
<Path
path={() => path.interpolate(zeroPath, progress.value)}
strokeWidth={4}
style="stroke"
strokeJoin="round"
strokeCap="round"
/>
</Canvas>
<Text>Touch to toggle between "unmounted" and "mounted"</Text>
</View>
);
};

const styles = StyleSheet.create({
graph: {
flex: 1,
},
});
98 changes: 98 additions & 0 deletions example/src/Examples/Graphs/Slider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import type { IPath } from "@shopify/react-native-skia";
import {
Line,
Canvas,
Circle,
Fill,
LinearGradient,
Paint,
Path,
useTouchHandler,
useValue,
Text as SkiaText,
vec,
} from "@shopify/react-native-skia";
import React, { useMemo } from "react";
import { StyleSheet, Text, View } from "react-native";

import { createGraphPath } from "./createGraphPath";
import type { GraphProps } from "./types";

export const Slider: React.FC<GraphProps> = ({ height, width }) => {
const path = useMemo(
() => createGraphPath(width, height, 60, false),
[height, width]
);

const progress = useValue(
getPointAtPositionInPath(width / 2, width, 60, path)
);

const touchHandler = useTouchHandler({
onActive: ({ x }) =>
(progress.value = getPointAtPositionInPath(x, width, 60, path)),
});

return (
<View style={{ height, marginBottom: 10 }}>
<Canvas style={styles.graph} onTouch={touchHandler}>
<Fill color="black" />
<Paint>
<LinearGradient
start={vec(0, height * 0.5)}
end={vec(width * 0.5, height * 0.5)}
colors={["black", "#DA4167"]}
/>
</Paint>
<Path
path={path}
strokeWidth={4}
style="stroke"
strokeJoin="round"
strokeCap="round"
/>
<Paint color="#fff" />
<Circle c={() => progress.value} r={10} />
<Circle color="#DA4167" c={() => progress.value} r={7.5} />
<SkiaText
familyName="Arial"
size={12}
x={() => progress.value.x - 24}
y={() => progress.value.y - 18}
value={() => "$ " + progress.value.x.toFixed(2)}
/>
<Line
p1={() => vec(progress.value.x, progress.value.y + 14)}
p2={() => vec(progress.value.x, height)}
/>
</Canvas>
<Text>Touch and drag to move center point</Text>
</View>
);
};

const getPointAtPositionInPath = (
x: number,
width: number,
steps: number,
path: IPath
) => {
const index = Math.max(0, Math.floor(x / (width / steps)));
const fraction = (x / (width / steps)) % 1;
const p1 = path.getPoint(index);
if (index < path.countPoints() - 1) {
const p2 = path.getPoint(index + 1);
// Interpolate between p1 and p2
return {
x: p1.x + (p2.x - p1.x) * fraction,
y: p1.y + (p2.y - p1.y) * fraction,
};
}
return p1;
};

const styles = StyleSheet.create({
graph: {
flex: 1,
},
});
43 changes: 43 additions & 0 deletions example/src/Examples/Graphs/createGraphPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Skia } from "@shopify/react-native-skia";

export const createGraphPath = (
width: number,
height: number,
steps: number,
round = true
) => {
const retVal = Skia.Path.Make();
let y = height / 2;
retVal.moveTo(0, y);
const prevPt = { x: 0, y };
for (let i = 0; i < width; i += width / steps) {
// increase y by a random amount between -10 and 10
y += Math.random() * 30 - 15;
y = Math.max(height * 0.2, Math.min(y, height * 0.7));

if (round && i > 0) {
const xMid = (prevPt.x + i) / 2;
const yMid = (prevPt!.y + y) / 2;
retVal.quadTo(prevPt.x, prevPt.y, xMid, yMid);
prevPt.x = i;
prevPt.y = y;
} else {
retVal.lineTo(i, y);
}
}
return retVal;
};

export const createZeroPath = (
width: number,
height: number,
steps: number
) => {
const retVal = Skia.Path.Make();
const y = height / 2;
retVal.moveTo(0, y);
for (let i = 0; i < width; i += width / steps) {
retVal.lineTo(i, y);
}
return retVal;
};
26 changes: 26 additions & 0 deletions example/src/Examples/Graphs/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from "react";
import { View, StyleSheet, useWindowDimensions } from "react-native";

import { Interpolation } from "./Interpolation";
import { MountAnimation } from "./Mount";
import { Slider } from "./Slider";

const Padding = 10;

export const GraphsScreen: React.FC = () => {
const { width, height } = useWindowDimensions();
return (
<View style={styles.container}>
<MountAnimation height={height * 0.25} width={width - Padding * 2} />
<Interpolation height={height * 0.25} width={width - Padding * 2} />
<Slider height={height * 0.25} width={width - Padding * 2} />
</View>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
padding: Padding,
},
});
4 changes: 4 additions & 0 deletions example/src/Examples/Graphs/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type GraphProps = {
height: number;
width: number;
};
1 change: 1 addition & 0 deletions example/src/Examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from "./Drawing";
export * from "./Filters";
export * from "./Gooey";
export * from "./Matrix";
export * from "./Graphs";
5 changes: 5 additions & 0 deletions example/src/Home/HomeScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ export const HomeScreen: React.FC = () => {
description="Use touches to draw with Skia"
route="Drawing"
/>
<HomeScreenButton
title="📉 Graphs"
description="Animated graphs with Skia"
route="Graphs"
/>
<HomeScreenButton
title="🎥 Animation"
description="Animated with Skia"
Expand Down
6 changes: 3 additions & 3 deletions example/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5036,9 +5036,9 @@ node-dir@^0.1.17:
minimatch "^3.0.2"

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

Expand Down
17 changes: 16 additions & 1 deletion package/cpp/api/JsiSkPath.h
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,20 @@ class JsiSkPath : public JsiSkWrappingSharedPtrHostObject<SkPath> {
return jsi::Value(false);
}

JSI_HOST_FUNCTION(isInterpolatable) {
auto path2 = JsiSkPath::fromValue(runtime, arguments[0]);
return getObject()->isInterpolatable(*path2);
}

JSI_HOST_FUNCTION(interpolate) {
auto path2 = JsiSkPath::fromValue(runtime, arguments[0]);
auto weight = arguments[1].asNumber();
SkPath result;
getObject()->interpolate(*path2, weight, &result);
return jsi::Object::createFromHostObject(
runtime, std::make_shared<JsiSkPath>(getContext(), result));
}

JSI_EXPORT_FUNCTIONS(
JSI_EXPORT_FUNC(JsiSkPath, addArc), JSI_EXPORT_FUNC(JsiSkPath, addOval),
JSI_EXPORT_FUNC(JsiSkPath, addPoly), JSI_EXPORT_FUNC(JsiSkPath, addRect),
Expand Down Expand Up @@ -491,7 +505,8 @@ class JsiSkPath : public JsiSkWrappingSharedPtrHostObject<SkPath> {
JSI_EXPORT_FUNC(JsiSkPath, getLastPt), JSI_EXPORT_FUNC(JsiSkPath, close),
JSI_EXPORT_FUNC(JsiSkPath, simplify),
JSI_EXPORT_FUNC(JsiSkPath, countPoints), JSI_EXPORT_FUNC(JsiSkPath, copy),
JSI_EXPORT_FUNC(JsiSkPath, fromText), JSI_EXPORT_FUNC(JsiSkPath, op))
JSI_EXPORT_FUNC(JsiSkPath, fromText), JSI_EXPORT_FUNC(JsiSkPath, op),
JSI_EXPORT_FUNC(JsiSkPath, isInterpolatable), JSI_EXPORT_FUNC(JsiSkPath, interpolate))

JsiSkPath(std::shared_ptr<RNSkPlatformContext> context, SkPath path)
: JsiSkWrappingSharedPtrHostObject<SkPath>(
Expand Down
Loading

0 comments on commit 5fab08b

Please sign in to comment.