Pictures
React Native Skia works in retained mode: every re-render, we create a display list with support for animation values. This is great to animate property values. However, if you want to execute a variable number of drawing commands, this is where you need to use pictures.
A Picture contains a list of drawing operations to be drawn on a canvas. The picture is immutable and cannot be edited or changed after it has been created. It can be used multiple times in any canvas.
| Name | Type | Description |
|---|---|---|
| picture | SkPicture | Picture to render |
Hello World
In this example, we animate a trail of circles. The number of circles in the trail changes over time, which is why we need to use pictures: we can't animated on the number of circle components.
import React, { useEffect } from "react";
import { Canvas, Picture, Skia } from "@shopify/react-native-skia";
import {
useDerivedValue,
useSharedValue,
withRepeat,
withTiming,
} from "react-native-reanimated";
const size = 256;
const n = 20;
const paint = Skia.Paint();
const recorder = Skia.PictureRecorder();
export const HelloWorld = () => {
const progress = useSharedValue(0);
useEffect(() => {
progress.value = withRepeat(withTiming(1, { duration: 3000 }), -1, true);
}, [progress]);
const picture = useDerivedValue(() => {
"worklet";
const canvas = recorder.beginRecording(Skia.XYWHRect(0, 0, size, size));
const numberOfCircles = Math.floor(progress.value * n);
for (let i = 0; i < numberOfCircles; i++) {
const alpha = ((i + 1) / n) * 255;
const r = ((i + 1) / n) * (size / 2);
paint.setColor(Skia.Color(`rgba(0, 122, 255, ${alpha / 255})`));
canvas.drawCircle(size / 2, size / 2, r, paint);
}
return recorder.finishRecordingAsPicture();
});
return (
<Canvas style={{ flex: 1 }}>
<Picture picture={picture} />
</Canvas>
);
};
| progress=0.25 | progress=0.5 | progress=1 |
|---|---|---|
![]() | ![]() |
Applying Effects
The Picture component doesn't follow the same painting rules as other components.
However you can apply effects using the layer property.
For instance, in the example below, we apply a blur image filter.
import React, { useMemo } from "react";
import { Canvas, Skia, Group, Paint, Blur, BlendMode, Picture } from "@shopify/react-native-skia";
export const Demo = () => {
const picture = useMemo(() => {
const recorder = Skia.PictureRecorder();
const size = 256;
const canvas = recorder.beginRecording(Skia.XYWHRect(0, 0, size, size));
const r = 0.33 * size;
const paint = Skia.Paint();
paint.setBlendMode(BlendMode.Multiply);
paint.setColor(Skia.Color("cyan"));
canvas.drawCircle(r, r, r, paint);
paint.setColor(Skia.Color("magenta"));
canvas.drawCircle(size - r, r, r, paint);
paint.setColor(Skia.Color("yellow"));
canvas.drawCircle(size / 2, size - r, r, paint);
return recorder.finishRecordingAsPicture();
}, []);
return (
<Canvas style={{ flex: 1 }}>
<Group layer={<Paint><Blur blur={10} /></Paint>}>
<Picture picture={picture} />
</Group>
</Canvas>
);
};
Serialization
You can serialize a picture to a byte array. Serialized pictures are only compatible with the version of Skia it was created with. You can use serialized pictures with the Skia debugger.
import React, { useMemo } from "react";
import {
Canvas,
Picture,
Skia,
Group,
} from "@shopify/react-native-skia";
export const PictureExample = () => {
// Create picture
const picture = useMemo(() => {
const recorder = Skia.PictureRecorder();
const canvas = recorder.beginRecording(Skia.XYWHRect(0, 0, 100, 100));
const paint = Skia.Paint();
paint.setColor(Skia.Color("pink"));
canvas.drawRect({ x: 0, y: 0, width: 100, height: 100 }, paint);
const circlePaint = Skia.Paint();
circlePaint.setColor(Skia.Color("orange"));
canvas.drawCircle(50, 50, 50, circlePaint);
return recorder.finishRecordingAsPicture();
}, []);
// Serialize the picture
const serialized = useMemo(() => picture.serialize(), [picture]);
// Create a copy from serialized data
const copyOfPicture = useMemo(
() => (serialized ? Skia.Picture.MakePicture(serialized) : null),
[serialized]
);
return (
<Canvas style={{ flex: 1 }}>
<Picture picture={picture} />
<Group transform={[{ translateX: 200 }]}>
{copyOfPicture && <Picture picture={copyOfPicture} />}
</Group>
</Canvas>
);
};
Instance Methods
| Name | Description |
|---|---|
| makeShader | Returns a new shader that will draw with this picture. |
| serialize | Returns a UInt8Array representing the drawing operations stored in the image. |

