Skip to content

Commit aabf93b

Browse files
authored
feat(🍭): Add support for Skottie animation and dynamic properties (#3143)
1 parent f8f760a commit aabf93b

Some content is hidden

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

66 files changed

+112679
-86
lines changed

β€Žapps/docs/docs/skottie.md

Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
---
2+
id: skottie
3+
title: Skottie
4+
sidebar_label: Skottie
5+
slug: /skottie
6+
---
7+
8+
Skottie is a Lottie animation renderer built on Skia. It allows you to load and render After Effects animations exported via Bodymovin/Lottie in React Native Skia.
9+
It provides a powerful way to integrate After Effects animations into your React Native Skia applications with full programmatic control over animation properties.
10+
11+
## Rendering Animations
12+
13+
### Using the Skottie Component
14+
15+
React Native Skia provides a `Skottie` component for easy integration:
16+
17+
```tsx twoslash
18+
import React from "react";
19+
import { Canvas, Group, Skottie, Skia } from "@shopify/react-native-skia";
20+
21+
const legoAnimationJSON = require("./assets/lego_loader.json");
22+
const animation = Skia.Skottie.Make(JSON.stringify(legoAnimationJSON));
23+
24+
const SkottieExample = () => {
25+
return (
26+
<Canvas style={{ width: 400, height: 300 }}>
27+
<Group transform={[{ scale: 0.5 }]}>
28+
<Skottie animation={animation} frame={41} />
29+
</Group>
30+
</Canvas>
31+
);
32+
};
33+
```
34+
35+
### Animated Playback with Reanimated
36+
37+
For smooth animation playback, combine Skottie with React Native Reanimated:
38+
39+
```tsx twoslash
40+
import React from "react";
41+
import {
42+
Skia,
43+
Canvas,
44+
useClock,
45+
Group,
46+
Skottie,
47+
} from "@shopify/react-native-skia";
48+
import { useDerivedValue } from "react-native-reanimated";
49+
50+
const legoAnimationJSON = require("./assets/lego_loader.json");
51+
const animation = Skia.Skottie.Make(JSON.stringify(legoAnimationJSON));
52+
53+
const AnimatedSkottieExample = () => {
54+
const clock = useClock();
55+
const frame = useDerivedValue(() => {
56+
const fps = animation.fps();
57+
const duration = animation.duration();
58+
const currentFrame =
59+
Math.floor((clock.value / 1000) * fps) % (duration * fps);
60+
return currentFrame;
61+
});
62+
63+
return (
64+
<Canvas style={{ flex: 1 }}>
65+
<Group transform={[{ scale: 0.5 }]}>
66+
<Skottie animation={animation} frame={frame} />
67+
</Group>
68+
</Canvas>
69+
);
70+
};
71+
```
72+
73+
### Basic Rendering
74+
75+
```tsx twoslash
76+
import { Skia, Canvas } from "@shopify/react-native-skia";
77+
const animation = {} as any;
78+
// ---cut---
79+
const surface = Skia.Surface.MakeOffscreen(800, 600);
80+
if (!surface) {
81+
throw new Error("Failed to create surface");
82+
}
83+
const canvas = surface.getCanvas();
84+
85+
// Seek to a specific frame
86+
animation.seekFrame(41);
87+
88+
// Render the animation
89+
animation.render(canvas);
90+
91+
surface.flush();
92+
const image = surface.makeImageSnapshot();
93+
```
94+
95+
## Creating a Skottie Animation
96+
97+
To create a Skottie animation, use `Skia.Skottie.Make()` with your Lottie JSON data:
98+
99+
```tsx twoslash
100+
import { Skia } from "@shopify/react-native-skia";
101+
102+
const legoAnimationJSON = require("./assets/lego_loader.json");
103+
104+
const animation = Skia.Skottie.Make(JSON.stringify(legoAnimationJSON));
105+
```
106+
107+
### With Assets
108+
109+
Many Lottie animations include external assets like fonts and images. You can provide these when creating the animation:
110+
111+
```tsx twoslash
112+
import { Skia } from "@shopify/react-native-skia";
113+
114+
const basicSlotsJSON = require("./assets/basic_slots.json");
115+
116+
const assets = {
117+
NotoSerif: Skia.Data.fromBytes(new Uint8Array([])),
118+
"img_0.png": Skia.Data.fromBytes(new Uint8Array([])),
119+
};
120+
121+
const animation = Skia.Skottie.Make(JSON.stringify(basicSlotsJSON), assets);
122+
```
123+
124+
## Animation Properties
125+
126+
### Basic Information
127+
128+
Get basic information about your animation:
129+
130+
```tsx twoslash
131+
import { Skia } from "@shopify/react-native-skia";
132+
const animation = {} as any;
133+
// ---cut---
134+
// Duration in seconds
135+
const duration = animation.duration();
136+
137+
// Frames per second
138+
const fps = animation.fps();
139+
140+
// Lottie version
141+
const version = animation.version();
142+
143+
// Animation dimensions
144+
const size = animation.size(); // { width: 800, height: 600 }
145+
```
146+
147+
## Slot Management
148+
149+
Slots are placeholders built into the design of Lottie animations that allow for dynamic content replacement at runtime. This is incredibly convenient for customizing animations without recreating them - designers can create slots in After Effects, and developers can programmatically replace colors, text, images, and other properties.
150+
151+
Skottie supports slots for dynamic content replacement:
152+
153+
### Getting Slot Information
154+
155+
```tsx twoslash
156+
const animation = {} as any;
157+
// ---cut---
158+
const slotInfo = animation.getSlotInfo();
159+
// Returns:
160+
// {
161+
// colorSlotIDs: ["FillsGroup", "StrokeGroup"],
162+
// imageSlotIDs: ["ImageSource"],
163+
// scalarSlotIDs: ["Opacity"],
164+
// textSlotIDs: ["TextSource"],
165+
// vec2SlotIDs: ["ScaleGroup"]
166+
// }
167+
```
168+
169+
### Setting Color Slots
170+
171+
```tsx twoslash
172+
import { Skia } from "@shopify/react-native-skia";
173+
const animation = {} as any;
174+
// ---cut---
175+
animation.setColorSlot("FillsGroup", Skia.Color("cyan"));
176+
animation.setColorSlot("StrokeGroup", Skia.Color("magenta"));
177+
```
178+
179+
## Property Access and Modification
180+
181+
Beyond slots, Skottie provides powerful introspection capabilities that allow you to modify virtually any property of the animation at runtime. This gives you complete programmatic control over colors, text, opacity, transforms, and more - making it possible to create highly dynamic and interactive animations.
182+
183+
### Color Properties
184+
185+
```tsx twoslash
186+
import { Skia } from "@shopify/react-native-skia";
187+
const animation = {} as any;
188+
// ---cut---
189+
// Get all color properties
190+
const colorProps = animation.getColorProps();
191+
// Returns array of: { key: string, value: Float32Array }
192+
193+
// Set a specific color property
194+
const colorProp = colorProps[0];
195+
animation.setColor(colorProp.key, Skia.Color("rgb(60, 120, 255)"));
196+
```
197+
198+
### Text Properties
199+
200+
```tsx twoslash
201+
const animation = {} as any;
202+
// ---cut---
203+
// Get all text properties
204+
const textProps = animation.getTextProps();
205+
// Returns array of: { key: string, value: { text: string, size: number } }
206+
207+
// Set text content
208+
animation.setText("hello!", "World", 164);
209+
```
210+
211+
### Opacity Properties
212+
213+
```tsx twoslash
214+
const animation = {} as any;
215+
// ---cut---
216+
// Get all opacity properties
217+
const opacityProps = animation.getOpacityProps();
218+
// Returns array of: { key: string, value: number }
219+
```
220+
221+
### Transform Properties
222+
223+
```tsx twoslash
224+
const animation = {} as any;
225+
// ---cut---
226+
// Get all transform properties
227+
const transformProps = animation.getTransformProps();
228+
// Returns array of: {
229+
// key: string,
230+
// value: {
231+
// anchor: { x: number, y: number },
232+
// position: { x: number, y: number },
233+
// scale: { x: number, y: number },
234+
// rotation: number,
235+
// skew: number,
236+
// skewAxis: number
237+
// }
238+
// }
239+
```
240+
241+
## Complete Example
242+
243+
Here's a complete example showing how to load and render a Skottie animation with dynamic properties:
244+
245+
```tsx twoslash
246+
import React, { useEffect, useState } from "react";
247+
import { View } from "react-native";
248+
import {
249+
Canvas,
250+
Skia,
251+
useCanvasRef,
252+
Image,
253+
SkSkottieAnimation,
254+
SkImage
255+
} from "@shopify/react-native-skia";
256+
257+
const animationJSON = require("./assets/fingerprint.json");
258+
259+
const SkottiePlayer = () => {
260+
const [animation, setAnimation] = useState<SkSkottieAnimation | null>(null);
261+
const [image, setImage] = useState<SkImage | null>(null);
262+
263+
useEffect(() => {
264+
const skottieAnimation = Skia.Skottie.Make(JSON.stringify(animationJSON));
265+
if (!skottieAnimation) {
266+
throw new Error("Failed to create animation");
267+
}
268+
setAnimation(skottieAnimation);
269+
270+
// Get animation properties
271+
const colorProps = skottieAnimation.getColorProps();
272+
if (colorProps.length > 0) {
273+
// Change the first color property
274+
skottieAnimation.setColor(colorProps[0].key, Skia.Color("rgb(60, 120, 255)"));
275+
}
276+
277+
// Render a frame
278+
const size = skottieAnimation.size();
279+
const surface = Skia.Surface.MakeOffscreen(size.width, size.height);
280+
if (!surface) {
281+
throw new Error("Failed to create surface");
282+
}
283+
const canvas = surface.getCanvas();
284+
285+
skottieAnimation.seekFrame(120);
286+
skottieAnimation.render(canvas);
287+
surface.flush();
288+
289+
const snapshot = surface.makeImageSnapshot();
290+
setImage(snapshot);
291+
}, []);
292+
293+
if (!image) return <View />;
294+
295+
return (
296+
<Canvas style={{ width: 400, height: 400 }}>
297+
<Image
298+
image={image}
299+
x={0}
300+
y={0}
301+
width={400}
302+
height={400}
303+
fit="contain"
304+
/>
305+
</Canvas>
306+
);
307+
};
308+
```

β€Žapps/docs/sidebars.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ const sidebars = {
6868
label: "Video",
6969
id: "video",
7070
},
71+
{
72+
type: "doc",
73+
label: "Skottie",
74+
id: "skottie",
75+
},
7176
{
7277
collapsed: true,
7378
type: "category",
358 KB
Loading
357 KB
Loading
27.5 KB
Loading
21.5 KB
Loading
155 KB
Loading
17.5 KB
Loading
8.33 KB
Loading
30 KB
Loading

0 commit comments

Comments
Β (0)