Skip to content

Commit 692ad7f

Browse files
authored
Merge branch 'main' into fix/building-from-source
2 parents 29659e9 + 9e7c601 commit 692ad7f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1013
-120
lines changed

docs/docs/shapes/pictures.md

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,54 @@ A Picture renders a previously recorded list of drawing operations on the canvas
1111
| :------ | :---------- | :---------------- |
1212
| picture | `SkPicture` | Picture to render |
1313

14-
### Example
14+
15+
## Hello World
16+
17+
```tsx twoslash
18+
import React, { useMemo } from "react";
19+
import {
20+
createPicture,
21+
Canvas,
22+
Picture,
23+
Skia,
24+
Group,
25+
BlendMode
26+
} from "@shopify/react-native-skia";
27+
28+
const size = 256;
29+
30+
export const HelloWorld = () => {
31+
// Create a picture
32+
const picture = useMemo(() => createPicture(
33+
{ width: size, height: size },
34+
(canvas) => {
35+
const r = 0.33 * size;
36+
const paint = Skia.Paint();
37+
paint.setBlendMode(BlendMode.Multiply);
38+
39+
paint.setColor(Skia.Color("cyan"));
40+
canvas.drawCircle(r, r, r, paint);
41+
42+
paint.setColor(Skia.Color("magenta"));
43+
canvas.drawCircle(size - r, r, r, paint);
44+
45+
paint.setColor(Skia.Color("yellow"));
46+
canvas.drawCircle(size / 2, size - r, r, paint);
47+
}
48+
), []);
49+
return (
50+
<Canvas style={{ flex: 1 }}>
51+
<Picture picture={picture} />
52+
</Canvas>
53+
);
54+
};
55+
```
56+
57+
## Serialization
58+
59+
You can serialize a picture to a byte array.
60+
Serialized pictures are only compatible with the version of Skia it was created with.
61+
You can use serialized pictures with the [Skia debugger](https://skia.org/docs/dev/tools/debugger/).
1562

1663
```tsx twoslash
1764
import React, { useMemo } from "react";
@@ -26,7 +73,7 @@ import {
2673
export const PictureExample = () => {
2774
// Create picture
2875
const picture = useMemo(() => createPicture(
29-
{ x: 0, y: 0, width: 100, height: 100 },
76+
{ width: 100, height: 100 },
3077
(canvas) => {
3178
const paint = Skia.Paint();
3279
paint.setColor(Skia.Color("pink"));

docs/src/pages/index.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@ function HomepageHeader() {
1212
<header className={clsx("hero hero--primary", styles.heroBanner)}>
1313
<div className="container">
1414
{/* <img src="/img/skia2.png" alt="Skia" height="200" width="auto" /> */}
15-
<h1 className="hero__title">{siteConfig.title}</h1>
16-
<p className="hero__subtitle">{siteConfig.tagline}</p>
15+
<h1 className="hero__title" style={{ color: "white" }}>
16+
{siteConfig.title}
17+
</h1>
18+
<p className="hero__subtitle" style={{ color: "white" }}>
19+
{siteConfig.tagline}
20+
</p>
1721
<div className={styles.buttons}>
1822
<Link
1923
className="button button--secondary button--lg"

example/src/App.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
Severance,
2828
Transitions,
2929
Stickers,
30+
FrostedCard,
3031
} from "./Examples";
3132
import { CI, Tests } from "./Tests";
3233
import { HomeScreen } from "./Home";
@@ -57,6 +58,7 @@ const linking: LinkingOptions<StackParamList> = {
5758
Tests: "test",
5859
Transitions: "transitions",
5960
Stickers: "stickers",
61+
FrostedCard: "frosted-card",
6062
},
6163
},
6264
prefixes: ["rnskia://"],
@@ -151,6 +153,13 @@ const App = () => {
151153
header: () => null,
152154
}}
153155
/>
156+
<Stack.Screen
157+
name="FrostedCard"
158+
component={FrostedCard}
159+
options={{
160+
header: () => null,
161+
}}
162+
/>
154163
<Stack.Screen name="Neumorphism" component={Neumorphism} />
155164
<Stack.Screen
156165
name="Wallpaper"
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import React from "react";
2+
import type { SkMatrix } from "@shopify/react-native-skia";
3+
import { RuntimeShader, vec } from "@shopify/react-native-skia";
4+
import type { SharedValue } from "react-native-gesture-handler/lib/typescript/handlers/gestures/reanimatedWrapper";
5+
import { useDerivedValue } from "react-native-reanimated";
6+
7+
import { generateShader } from "./Shader";
8+
9+
const source = generateShader();
10+
11+
interface BlurGradientProps {
12+
matrix: SharedValue<SkMatrix>;
13+
}
14+
15+
export const BlurMask = ({ matrix }: BlurGradientProps) => {
16+
const hUniforms = useDerivedValue(() => {
17+
return {
18+
direction: vec(1, 0),
19+
matrix: matrix.value.get(),
20+
};
21+
});
22+
const vUniforms = useDerivedValue(() => {
23+
return {
24+
direction: vec(0, 1),
25+
matrix: matrix.value.get(),
26+
};
27+
});
28+
return (
29+
<RuntimeShader source={source} uniforms={hUniforms}>
30+
<RuntimeShader source={source} uniforms={vUniforms} />
31+
</RuntimeShader>
32+
);
33+
};
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import {
2+
Canvas,
3+
Skia,
4+
processTransform3d,
5+
useImage,
6+
Image,
7+
BackdropFilter,
8+
Fill,
9+
Blur,
10+
notifyChange,
11+
} from "@shopify/react-native-skia";
12+
import React from "react";
13+
import { Dimensions, View } from "react-native";
14+
import { Gesture, GestureDetector } from "react-native-gesture-handler";
15+
import {
16+
useDerivedValue,
17+
useSharedValue,
18+
withSpring,
19+
} from "react-native-reanimated";
20+
21+
const { width, height } = Dimensions.get("window");
22+
const CARD_WIDTH = width * 0.9;
23+
const CARD_HEIGHT = CARD_WIDTH / 1.618;
24+
const rct = Skia.XYWHRect(
25+
(width - CARD_WIDTH) / 2,
26+
(height - CARD_HEIGHT) / 2,
27+
CARD_WIDTH,
28+
CARD_HEIGHT
29+
);
30+
const rrct = Skia.RRectXY(rct, 10, 10);
31+
32+
const sf = 300;
33+
const springConfig = (velocity: number) => {
34+
"worklet";
35+
return {
36+
mass: 1,
37+
damping: 1,
38+
stiffness: 100,
39+
overshootClamping: false,
40+
restDisplacementThreshold: 0.01,
41+
restSpeedThreshold: 2,
42+
velocity,
43+
};
44+
};
45+
46+
export const FrostedCard = () => {
47+
const image = useImage(require("./dynamo.jpg"));
48+
const rotateX = useSharedValue(0);
49+
const rotateY = useSharedValue(0);
50+
const path = useSharedValue(Skia.Path.Make());
51+
52+
const gesture = Gesture.Pan()
53+
.onChange((event) => {
54+
rotateY.value += event.changeX / sf;
55+
rotateX.value -= event.changeY / sf;
56+
})
57+
.onEnd(({ velocityX, velocityY }) => {
58+
rotateX.value = withSpring(0, springConfig(velocityY / sf));
59+
rotateY.value = withSpring(0, springConfig(velocityX / sf));
60+
});
61+
62+
useDerivedValue(() => {
63+
path.value.reset();
64+
path.value.addRRect(rrct);
65+
path.value.transform(
66+
processTransform3d([
67+
{ translate: [width / 2, height / 2] },
68+
{ perspective: 300 },
69+
{ rotateX: rotateX.value },
70+
{ rotateY: rotateY.value },
71+
{ translate: [-width / 2, -height / 2] },
72+
])
73+
);
74+
notifyChange(path);
75+
});
76+
77+
return (
78+
<View style={{ flex: 1 }}>
79+
<GestureDetector gesture={gesture}>
80+
<Canvas style={{ flex: 1 }}>
81+
<Image
82+
image={image}
83+
x={0}
84+
y={0}
85+
width={width}
86+
height={height}
87+
fit="cover"
88+
/>
89+
<BackdropFilter filter={<Blur blur={30} mode="clamp" />} clip={path}>
90+
<Fill color="rgba(255, 255, 255, 0.1)" />
91+
</BackdropFilter>
92+
</Canvas>
93+
</GestureDetector>
94+
</View>
95+
);
96+
};
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { Skia } from "@shopify/react-native-skia";
2+
3+
import { glsl } from "../../components/ShaderLib";
4+
5+
export const generateShader = () => {
6+
const maxSigma = 10;
7+
const k = 3;
8+
const windowSize = k * maxSigma;
9+
const halfWindowSize = (windowSize / 2).toFixed(1);
10+
const source = glsl`
11+
uniform shader image;
12+
uniform mat3 matrix;
13+
14+
uniform float2 direction;
15+
16+
// Function to calculate Gaussian weight
17+
float Gaussian(float x, float sigma) {
18+
return exp(-(x * x) / (2.0 * sigma * sigma)) / (2.0 * 3.14159 * sigma * sigma);
19+
}
20+
21+
// Function to perform blur in one direction
22+
vec3 blur(vec2 uv, vec2 direction, float sigma) {
23+
vec3 result = vec3(0.0);
24+
float totalWeight = 0.0;
25+
float window = sigma * ${k.toFixed(1)} * 0.5;
26+
27+
for (float i = ${-halfWindowSize}; i <= ${halfWindowSize}; i++) {
28+
if (abs(i) > window) {
29+
continue;
30+
}
31+
float weight = Gaussian(i, sigma);
32+
vec2 offset = vec2(direction * i);
33+
vec3 sample = image.eval(uv + offset).rgb;
34+
35+
result += sample * weight;
36+
totalWeight += weight;
37+
}
38+
39+
if (totalWeight > 0.0) {
40+
result /= totalWeight;
41+
}
42+
43+
return result;
44+
}
45+
46+
// main function
47+
vec4 main(vec2 xy) {
48+
vec3 prj = matrix * vec3(xy, 1.0);
49+
float amount = clamp(prj.z/200.0, 0.0, 1.0);
50+
if (amount == 0.0) {
51+
return image.eval(xy);
52+
}
53+
vec3 color = blur(xy, direction, mix(0.1, ${maxSigma.toFixed(1)}, amount));
54+
return vec4(color, 1.0);
55+
}
56+
`;
57+
return Skia.RuntimeEffect.Make(source)!;
58+
};
430 KB
Loading
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./FrostedCard";

example/src/Examples/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ export * from "./Wallet";
2121
export * from "./Severance";
2222
export * from "./Transitions";
2323
export * from "./Stickers";
24+
export * from "./FrostedCard";

example/src/Home/HomeScreen.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ export const HomeScreen = () => {
6262
description="Glassmorphism"
6363
route="Glassmorphism"
6464
/>
65+
<HomeScreenButton
66+
title="☃️ Frosted Card"
67+
description="Frosted Card"
68+
route="FrostedCard"
69+
/>
6570
<HomeScreenButton title="💳 Wallet" description="Wallet" route="Wallet" />
6671
<HomeScreenButton
6772
title="📉 Graphs"

0 commit comments

Comments
 (0)