Skip to content

Commit e3c9e31

Browse files
authored
chore(🌈): update ZIndex example (#3559)
1 parent 2565483 commit e3c9e31

File tree

14 files changed

+272
-157
lines changed

14 files changed

+272
-157
lines changed

‎apps/docs/docs/animations/gestures.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ slug: /animations/gestures
77

88
When integrating with [reanimated](/docs/animations/animations), we recommend using [react-native-gesture-handler](https://docs.swmansion.com/react-native-gesture-handler/docs/).
99

10-
We've prepared a few [tutorials](/docs/tutorials#gestures) that showcase the use of advanced gestures within the context of Skia drawings.
10+
We've prepared a few [tutorials](/docs/tutorials#-gestures) that showcase the use of advanced gestures within the context of Skia drawings.
1111

1212
```tsx twoslash
1313
import { useWindowDimensions } from "react-native";

‎apps/docs/docs/canvas/canvas.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Behind the scenes, it is using its own React renderer.
1313
|:-----|:---------|:-----------------|
1414
| style? | `ViewStyle` | View style |
1515
| ref? | `Ref<SkiaView>` | Reference to the `SkiaView` object |
16-
| onSize? | `SharedValue<Size>` | Reanimated value to which the canvas size will be assigned (see [canvas size](/docs/animations/hooks#canvas-size)) |
16+
| onSize? | `SharedValue<Size>` | Reanimated value to which the canvas size will be assigned (see [canvas size](#canvas-size)) |
1717

1818
## Canvas size
1919

‎apps/docs/docs/color-filters.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ Creates a color filter with the given color and blend mode.
5050
| Name | Type | Description |
5151
| :-------- | :------------ | :------------------------------------------------- |
5252
| color | `Color` | Color |
53-
| mode | `BlendMode` | see [blend modes](paint/properties.md#blend-mode). |
53+
| mode | `BlendMode` | see [blend modes](paint/properties.md#blendmode). |
5454
| children? | `ColorFilter` | Optional color filter to be applied first. |
5555

5656
```tsx twoslash

‎apps/docs/docs/group.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ It can apply the following operations to its children:
1212
- [Paint properties](#paint-properties)
1313
- [Transformations](#transformations)
1414
- [Clipping operations](#clipping-operations)
15-
- [Bitmap Effects](#bitmap-effects)
15+
- [Layer Effects](#layer-effects)
1616

1717
| Name | Type | Description |
1818
| :---------- | :------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

‎apps/docs/docs/image-svg.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ If your SVG doesn't render correctly and you've considered all the items below,
300300
### CSS Styles
301301

302302
CSS styles included in SVG are not supported.
303-
A tool like [SVGO](#using-svgo) can help with converting CSS style attributes to SVG attributes if possible.
303+
A tool like SVGO can help with converting CSS style attributes to SVG attributes if possible.
304304
You can use it online [here](https://jakearchibald.github.io/svgomg/).
305305
For instance, it can normalize CSS style attributes that contain transformations to the proper `transform` property.
306306

‎apps/docs/docs/shaders/colors.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Returns a shader that combines the given shaders with a BlendMode.
1111

1212
| Name | Type | Description |
1313
|:------------|:---------------|:--------------------------------|
14-
| mode | `BlendMode` | see [blend modes](paint/properties.md#blend-mode). |
14+
| mode | `BlendMode` | see [blend modes](paint/properties.md#blendmode). |
1515
| children | `ReactNode` | Shaders to blend |
1616

1717
### Example

‎apps/docs/docusaurus.config.js‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ const config = {
1010
url: "https://shopify.github.io/",
1111
baseUrl: "/react-native-skia/",
1212
onBrokenLinks: "throw",
13-
onBrokenMarkdownLinks: "warn",
13+
onBrokenMarkdownLinks: "throw",
14+
onBrokenAnchors: "throw",
1415
favicon: "img/favicon.ico",
1516
organizationName: "shopify", // Usually your GitHub org/user name.
1617
projectName: "react-native-skia", // Usually your repo name.
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import React, { useMemo } from "react";
2+
import { Dimensions, ScrollView, Text, View } from "react-native";
3+
import { Canvas, Circle, Group, useClock } from "@shopify/react-native-skia";
4+
import type { SharedValue } from "react-native-reanimated";
5+
import { useDerivedValue } from "react-native-reanimated";
6+
7+
const { width } = Dimensions.get("window");
8+
const CANVAS_HEIGHT = 350;
9+
const CENTER = { x: width / 2, y: CANVAS_HEIGHT / 2 };
10+
const NUM_POINTS = 150;
11+
const SPHERE_RADIUS = width * 0.35;
12+
13+
// Fibonacci Sphere Algorithm to distribute points evenly
14+
const getSpherePoints = (n: number) => {
15+
const points = [];
16+
const phi = Math.PI * (3 - Math.sqrt(5)); // golden angle
17+
18+
for (let i = 0; i < n; i++) {
19+
const y = 1 - (i / (n - 1)) * 2; // y goes from 1 to -1
20+
const radius = Math.sqrt(1 - y * y); // radius at y
21+
22+
const theta = phi * i; // golden angle increment
23+
24+
const x = Math.cos(theta) * radius;
25+
const z = Math.sin(theta) * radius;
26+
27+
points.push({ x, y, z });
28+
}
29+
return points;
30+
};
31+
32+
const POINTS = getSpherePoints(NUM_POINTS).sort(() => Math.random() - 0.5);
33+
34+
interface Point3D {
35+
x: number;
36+
y: number;
37+
z: number;
38+
}
39+
40+
const SpherePoint = ({
41+
point,
42+
rotation,
43+
enableZIndex,
44+
}: {
45+
point: Point3D;
46+
rotation: { x: SharedValue<number>; y: SharedValue<number> };
47+
enableZIndex: boolean;
48+
}) => {
49+
const derived = useDerivedValue(() => {
50+
// Rotate around Y axis
51+
const cosY = Math.cos(rotation.y.value);
52+
const sinY = Math.sin(rotation.y.value);
53+
54+
// Rotate around X axis
55+
const cosX = Math.cos(rotation.x.value);
56+
const sinX = Math.sin(rotation.x.value);
57+
58+
let { x } = point;
59+
let { y } = point;
60+
let { z } = point;
61+
62+
// Apply Y rotation
63+
const x1 = x * cosY - z * sinY;
64+
const z1 = z * cosY + x * sinY;
65+
x = x1;
66+
z = z1;
67+
68+
// Apply X rotation
69+
const y2 = y * cosX - z * sinX;
70+
const z2 = z * cosX + y * sinX;
71+
y = y2;
72+
z = z2;
73+
74+
// Project to 2D
75+
const scale = (z + 2) / 3; // Simple perspective projection
76+
const screenX = CENTER.x + x * SPHERE_RADIUS;
77+
const screenY = CENTER.y + y * SPHERE_RADIUS;
78+
79+
// Map z (-1 to 1) to zIndex
80+
// We want points with higher z (closer to camera) to have higher zIndex
81+
const zIndex = Math.round(z * 1000);
82+
83+
return {
84+
cx: screenX,
85+
cy: screenY,
86+
r: 20 * scale,
87+
zIndex: enableZIndex ? zIndex : 0,
88+
color: `hsl(${((x + 1) / 2) * 360}, 80%, ${((z + 2) / 3) * 60}%)`,
89+
};
90+
});
91+
92+
const cx = useDerivedValue(() => derived.value.cx);
93+
const cy = useDerivedValue(() => derived.value.cy);
94+
const r = useDerivedValue(() => derived.value.r);
95+
const color = useDerivedValue(() => derived.value.color);
96+
const zIndex = useDerivedValue(() => derived.value.zIndex);
97+
98+
return (
99+
<Group zIndex={zIndex}>
100+
<Circle cx={cx} cy={cy} r={r} color={color} />
101+
<Circle
102+
cx={cx}
103+
cy={cy}
104+
r={r}
105+
color="black"
106+
style="stroke"
107+
strokeWidth={1}
108+
/>
109+
</Group>
110+
);
111+
};
112+
113+
const Sphere = ({ enableZIndex }: { enableZIndex: boolean }) => {
114+
const clock = useClock();
115+
116+
const rotationY = useDerivedValue(() => {
117+
return (clock.value / 4000) * Math.PI * 2;
118+
});
119+
120+
const rotationX = useDerivedValue(() => {
121+
return (clock.value / 6000) * Math.PI * 2;
122+
});
123+
124+
const rotationValues = useMemo(
125+
() => ({ x: rotationX, y: rotationY }),
126+
[rotationX, rotationY]
127+
);
128+
129+
return (
130+
<Group>
131+
{POINTS.map((point, i) => (
132+
<SpherePoint
133+
key={i}
134+
point={point}
135+
rotation={rotationValues}
136+
enableZIndex={enableZIndex}
137+
/>
138+
))}
139+
</Group>
140+
);
141+
};
142+
143+
export const ZIndexExample = () => {
144+
return (
145+
<ScrollView style={{ flex: 1, backgroundColor: "#111" }}>
146+
<View style={{ padding: 20, paddingBottom: 0 }}>
147+
<Text
148+
style={{
149+
color: "white",
150+
fontSize: 24,
151+
fontWeight: "bold",
152+
marginBottom: 10,
153+
}}
154+
>
155+
With zIndex
156+
</Text>
157+
</View>
158+
<Canvas style={{ width, height: CANVAS_HEIGHT }}>
159+
<Sphere enableZIndex={true} />
160+
</Canvas>
161+
162+
<View style={{ padding: 20, paddingBottom: 0 }}>
163+
<Text
164+
style={{
165+
color: "white",
166+
fontSize: 24,
167+
fontWeight: "bold",
168+
marginBottom: 10,
169+
}}
170+
>
171+
Without zIndex
172+
</Text>
173+
</View>
174+
<Canvas style={{ width, height: CANVAS_HEIGHT }}>
175+
<Sphere enableZIndex={false} />
176+
</Canvas>
177+
</ScrollView>
178+
);
179+
};

‎apps/example/src/Examples/API/index.tsx‎

Lines changed: 3 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
1-
import React, { useEffect } from "react";
2-
import { BlurMask, Canvas, Circle, Group } from "@shopify/react-native-skia";
1+
import React from "react";
32
import { createNativeStackNavigator } from "@react-navigation/native-stack";
4-
import {
5-
Easing,
6-
useDerivedValue,
7-
useSharedValue,
8-
withRepeat,
9-
withTiming,
10-
} from "react-native-reanimated";
113

124
import { ImageLoading } from "../ImageLoading/ImageLoading";
135

@@ -44,66 +36,10 @@ import { StressTest2 } from "./StressTest2";
4436
import { StressTest3 } from "./StressTest3";
4537
import { StressTest4 } from "./StressTest4";
4638
import { FirstFrame } from "./FirstFrame";
39+
import { ZIndexExample } from "./ZIndex";
4740

4841
const Stack = createNativeStackNavigator<Routes>();
4942

50-
const cycleDuration = 3600;
51-
52-
const computeLayerOrder = (value: number, id: number) => {
53-
"worklet";
54-
const stage = Math.floor(value) % 3;
55-
const relative = (id - stage + 3) % 3;
56-
return 2 - relative;
57-
};
58-
59-
export const ZIndexDemo = () => {
60-
const r = 80;
61-
const width = 256;
62-
const height = 256;
63-
const phase = useSharedValue(0);
64-
65-
useEffect(() => {
66-
phase.value = withRepeat(
67-
withTiming(3, {
68-
duration: cycleDuration,
69-
easing: Easing.linear,
70-
}),
71-
-1,
72-
false
73-
);
74-
}, [phase]);
75-
76-
const cyanZ = useDerivedValue(
77-
() => computeLayerOrder(phase.value, 0),
78-
[phase]
79-
);
80-
const magentaZ = useDerivedValue(
81-
() => computeLayerOrder(phase.value, 1),
82-
[phase]
83-
);
84-
const yellowZ = useDerivedValue(
85-
() => computeLayerOrder(phase.value, 2),
86-
[phase]
87-
);
88-
89-
return (
90-
<Canvas style={{ width, height }}>
91-
<Group>
92-
<BlurMask style="solid" blur={10} />
93-
<Circle cx={r} cy={r} r={r} color="cyan" zIndex={cyanZ} />
94-
<Circle cx={width - r} cy={r} r={r} color="magenta" zIndex={magentaZ} />
95-
<Circle
96-
cx={width / 2}
97-
cy={height - r}
98-
r={r}
99-
color="yellow"
100-
zIndex={yellowZ}
101-
/>
102-
</Group>
103-
</Canvas>
104-
);
105-
};
106-
10743
export const API = () => {
10844
return (
10945
<Stack.Navigator>
@@ -229,7 +165,7 @@ export const API = () => {
229165
/>
230166
<Stack.Screen
231167
name="ZIndex"
232-
component={ZIndexDemo}
168+
component={ZIndexExample}
233169
options={{
234170
title: "🧱 zIndex",
235171
}}

‎packages/skia/src/renderer/__tests__/e2e/Picture.spec.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ describe("Pictures", () => {
144144
const picture = recorder.finishRecordingAsPicture();
145145
const serialized = picture.serialize()!;
146146
const deserialized = Skia.Picture.MakePicture(
147-
serialized.buffer as ArrayBuffer,
147+
serialized.buffer as ArrayBuffer
148148
);
149149
mainCanvas.drawPicture(deserialized!);
150150
});

0 commit comments

Comments
 (0)