feat: add reusable AnimationCanvas component

Standalone Skia canvas for wallpaper thumbnail previews
with configurable FPS cap for battery efficiency.
This commit is contained in:
Mathis Pruvot
2026-05-28 11:49:34 +00:00
parent 1a2fa0ffb5
commit 5cddb440e6
2 changed files with 83 additions and 0 deletions

View File

@@ -0,0 +1,82 @@
import React, { useMemo } from "react";
import { StyleSheet, ViewStyle } from "react-native";
import {
Canvas,
Image as SkImage,
Shader,
Fill,
useImage,
} from "@shopify/react-native-skia";
import { useSharedValue, useFrameCallback } from "react-native-reanimated";
import { shaders } from "@/shaders";
import type { AnimationType, AnimationUniforms } from "@/types/wallpaper";
interface AnimationCanvasProps {
sourceUri: string;
animation: AnimationType;
uniforms: AnimationUniforms;
width: number;
height: number;
fps?: number;
style?: ViewStyle;
}
/**
* Standalone animated preview canvas.
* Used in wallpaper cards on the home screen (mini previews at reduced FPS).
*/
export function AnimationCanvas({
sourceUri,
animation,
uniforms,
width,
height,
fps = 15,
style,
}: AnimationCanvasProps) {
const image = useImage(sourceUri);
const shader = useMemo(() => shaders[animation], [animation]);
const time = useSharedValue(0);
const frameInterval = 1000 / fps;
let lastFrame = 0;
useFrameCallback((info) => {
const now = info.timeSinceFirstFrame ?? 0;
if (now - lastFrame >= frameInterval) {
time.value = now / 1000;
lastFrame = now;
}
});
if (!image || !shader) return null;
return (
<Canvas style={[{ width, height }, style]}>
<Fill>
<Shader
source={shader}
uniforms={{
resolution: [width, height],
time: time.value,
intensity: uniforms.intensity,
speed: uniforms.speed,
direction: uniforms.direction ?? 0,
particleType: 0,
gyroX: 0,
gyroY: 0,
}}
>
<SkImage
image={image}
x={0}
y={0}
width={width}
height={height}
fit="cover"
/>
</Shader>
</Fill>
</Canvas>
);
}

View File

@@ -0,0 +1 @@
export { AnimationCanvas } from "./AnimationCanvas";