feat: add reusable AnimationCanvas component
Standalone Skia canvas for wallpaper thumbnail previews with configurable FPS cap for battery efficiency.
This commit is contained in:
82
src/components/animation/AnimationCanvas.tsx
Normal file
82
src/components/animation/AnimationCanvas.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
1
src/components/animation/index.ts
Normal file
1
src/components/animation/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { AnimationCanvas } from "./AnimationCanvas";
|
||||||
Reference in New Issue
Block a user