Медленный огонь во фрагментном шейдере: у нижнего края тлеют угли, пламя с цветом абсолютно чёрного тела поднимается сквозь дрожащее марево, а три слоя искр взлетают, мерцают и гаснут. Касание (или курсор в вебе) раздувает пламя и расталкивает искры вокруг пальца.
Шейдер считается на GPU через Skia, время и следование за пальцем идут на UI-потоке Reanimated. Анимация замирает при «Уменьшении движения», в фоне, при paused или (в вебе) вне экрана. Работает на iOS, Android и в вебе.
Поставьте зависимости:
npm install @shopify/react-native-skia react-native-reanimated react-native-gesture-handlerpnpm add @shopify/react-native-skia react-native-reanimated react-native-gesture-handleryarn add @shopify/react-native-skia react-native-reanimated react-native-gesture-handlerbun add @shopify/react-native-skia react-native-reanimated react-native-gesture-handlerСкопируйте эти файлы в src/shared/components/organisms/embers/:
Проверьте алиас путей. Импорты в коде выше записаны так, как их пишет astra-rn add: @/shared/components, @/shared/utils и @/shared/hooks. Нужен алиас "@/*": ["./src/*"] в tsconfig.json — или поправьте импорты под свои пути.
import React from "react";
import { StyleSheet, View } from "react-native";
import { Embers } from "@/shared/components/organisms/embers";
import { Showcase, useCustomize } from "~/showcase";
const SPARK = "#ffb852";
const GLOW = "#ff470a";
const BACKGROUND = "#030101";
const pick = (value: string, fallback: string): string | undefined =>
value.toLowerCase() === fallback ? undefined : value;
const CUSTOMIZE = [
{ key: "heat", label: "Жар", type: "slider", min: 0, max: 1, step: 0.01, default: 0.6 },
{ key: "speed", label: "Скорость", type: "slider", min: 0, max: 3, step: 0.05, unit: "×", default: 1 },
{ key: "exposure", label: "Яркость", type: "slider", min: 0.3, max: 3, step: 0.05, default: 1.2 },
{ key: "sparkColor", label: "Цвет искр", type: "color", default: SPARK },
{ key: "glowColor", label: "Ореол искр", type: "color", default: GLOW },
{ key: "background", label: "Цвет фона", type: "color", default: BACKGROUND },
{ key: "interactive", label: "Реакция на курсор", type: "toggle", default: true },
] as const;
export default function EmbersScreen() {
const custom = useCustomize(CUSTOMIZE);
return (
<Showcase>
<View style={styles.container}>
<Embers
heat={custom.heat}
speed={custom.speed}
exposure={custom.exposure}
interactive={custom.interactive}
sparkColor={pick(custom.sparkColor, SPARK)}
glowColor={pick(custom.glowColor, GLOW)}
background={pick(custom.background, BACKGROUND)}
/>
</View>
</Showcase>
);
}
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
backgroundColor: "#000000",
},
});Высота огня задаётся heat от 0 до 1, цвета принимают строку или тройку RGB от 0 до 1.
speed?Тип number
heat?Тип number
interactive?Тип boolean
sparkColor?Тип ShaderColor
glowColor?Тип ShaderColor
background?Тип ShaderColor
exposure?Тип number
paused?Тип boolean
children?Тип ReactNode
style?Тип StyleProp<ViewStyle>