Canvas & 3D Animation using rN SKIA
Vilva Athiban P B
@vilvaathibanpb
Work
Omio, Berlin
Senior Engineer
Platform team
OOO
Love to teach
Inconsistent OSS contributor
Tech Speaker
Mentor
FE, BE & Tooling
Hobbies
Travel
Hiking
Cycling
Sleeping
ABOUT ME
How many hate maths class?
RN Skia?
Skia - 2D Graphics Library
Graphic engine for multiple platforms
Custom graphics/shapes
Custom Animations
Manipulating images
CANVAS and others
Canvas
Painting
Group
Shapes
Images
Text
Shaders and Filters
Animations in RN SKIA
useValue - Skia variables
useComputedValue - Computed from useValue
useClockValue - every Display frame
Canvas animations
Charts
export const BarChart = () => {
// D3 data Calculations
const path = () => {
const newPath = Skia.Path.Make();
data.forEach((dataPoint) => {
const rect = Skia.XYWHRect(
x(dataPoint.label) - GRAPH_BAR_WIDTH / 2,
graphHeight,
GRAPH_BAR_WIDTH,
y(dataPoint.value) * -1
);
const rrect = Skia.RRectXY(rect, 8, 8);
newPath.addRRect(rrect);
});
return newPath;
}
return (
<Canvas style={styles.canvas}>
<Path path={path()} color="green" />
</Canvas>
);
};
Charts
export const BarChart = () => {
// D3 data Calculations
const animationState = useValue(0);
const animate = () => {
animationState.current = 0;
runTiming(animationState, 1, {
duration: 1600,
easing: Easing.inOut(Easing.exp),
});
};
const path = useComputedValue(() => {
const newPath = Skia.Path.Make();
data.forEach((dataPoint) => {
const rect = Skia.XYWHRect(
x(dataPoint.label) - GRAPH_BAR_WIDTH / 2,
graphHeight,
GRAPH_BAR_WIDTH,
y(dataPoint.value * animationState.current) * -1
);
const rrect = Skia.RRectXY(rect, 8, 8);
newPath.addRRect(rrect);
});
return newPath;
}, [animationState]);
return (
<>
<Canvas style={styles.canvas}>
<Path path={path} color="green" />
</Canvas>
<Button title="Animate!" onPress={animate} />
</>
);
};
Charts
pulse
export const Pulse = () => {
const logo = useImage(require("./react-india.jpeg"));
return (
<Canvas style={{ height: 600, width: 600 }}>
<Circle cx={300} cy={300} r={100} opacity={1} color="#1C0F3F" />
<Image
image={logo}
fit="contain"
x={250}
y={250}
width={100}
height={100}
/>
</Canvas>
);
};
export const Pulse = () => {
const logo = useImage(require("./react-india.jpeg"));
const clockValue = useClockValue();
const scale = useComputedValue(() => {
return getScale(clockValue.current);
}, [clockValue]);
const opacity = useComputedValue(() => {
return getOpacity(clockValue.current);
}, [clockValue]);
const outerScale = useComputedValue(() => {
return getScaleOuter(clockValue.current);
}, [clockValue]);
const outerOpacity = useComputedValue(() => {
return getOpacityOuter(clockValue.current);
}, [clockValue]);
return (
<Canvas style={{ height: 600, width: 600 }}>
<Circle cx={300} cy={300} r={100} opacity={1} color="#1C0F3F" />
<Circle cx={300} cy={300} r={scale} opacity={opacity} color="#1C0F3F" />
<Circle cx={300} cy={300} r={outerScale} opacity={outerOpacity}
color="#1C0F3F"
/>
<Image
image={logo}
fit="contain"
x={250}
y={250}
width={100}
height={100}
/>
</Canvas>
);
};
export const ExpoLogo = () => {
const path = Skia.Path.Make();
path.moveTo(210, 300);
path.lineTo(300, 150);
path.lineTo(390, 300);
return (
<Canvas style={styles.animationContainer}>
<Fill color="black" />
<Path path={path} color="white" style="stroke" strokeWidth={50} strokeCap="round"
start={0}
end={1}
/>
</Canvas>
);
};
PATH ANIMATION
export const ExpoLogo = () => {
const interval = 1500;
const clock = useClockValue();
const forwardState = useComputedValue(() => {
if (clock.current % (interval * 2) < interval) {
return 1;
}
return (clock.current % interval) / interval;
}, [clock]);
const rewindState = useComputedValue(() => {
if (clock.current % (interval * 2) < interval) {
return (clock.current % interval) / interval;
}
return 0;
}, [clock]);
return (
// existing code
<Path
path={path}
// exisiting props
start={rewindState}
end={forwardState}
/>
);
};
PATH ANIMATION
PATH ANIMATION
Transitions
export const Transition = () => {
const image2 = useImage(require("./dp.jpeg"));
return (
<View style={{ flex: 1 }}>
<StatusBar />
<Canvas
style={{ flex: 1 }}
pointerEvents={"none"} >
<Image
x={0}
y={0}
width={width}
height={height}
image={image2}
/>
</Canvas>
</View>
);
};
Transitions
export const Transition = () => {
const circle = useSharedValue({ x: 0, y: 0, r: 0 });
const transition = useSharedValue(0);
const tap = Gesture.Tap().onEnd(async (e) => {
const r = Math.max(
...corners.map((corner) => dist(corner, { x: e.x, y: e.y }))
);
circle.value = { x: e.x, y: e.y, r };
transition.value = 0;
transition.value = withTiming(1, { duration: 2000, easing: Easing.ease });
});
const r = useDerivedValue(() => {
return mix(transition.value, 0, circle.value.r);
});
return (
<Canvas style={{ flex: 1 }} pointerEvents={"none"}>
<Rect x={0} y={0} width={width} height={height} color="black" />
<Circle c={circle} r={r}>
<ImageShader
image={image2}
x={0}
y={0}
width={width}
height={height}
fit="cover"
/>
</Circle>
</Canvas>
);
};
Transitions
3D in RN SKIA
Is it even possible? Recommended?
What is 3d?
CODE & DEMO TIME
THank you
vilvaathiban.com
for being a great audience. Connect with me for questions, feedback or networking
3D with RN skia
By Vilva Athiban
3D with RN skia
- 234