React Native Animated Glow

repository·main·Indexed 19 days ago

https://github.com/realimposter/react-native-animated-glow

A high-performance, GPU-powered component for creating customizable animated glow effects in React Native applications. Powered by Skia and Reanimated 3, it supports multi-layered glows, interactive states (default, hover, press) with configurable transitions, and a unified Skia shader for optimized rendering. Version 3.1.0 introduces a PresetConfig API for intelligent animation blending and improved type safety.

Tokens
6.7K
Snippets
17
Records
22
Agent score
64%

What's inside react-native-animated-glow

  1. Install react-native-animated-glow

    main

    To use this library, install the core package and its required peer dependencies. You must also follow the specific installation guides for Skia, Reanimated, and Gesture Handler to ensure they are correctly configured in your project (e.g., adding the Reanimated Babel plugin and wrapping your app in GestureHandlerRootView).

    # 1. Install the library
    npm install react-native-animated-glow
    
    # 2. Install Peer Dependencies
    npm install @shopify/react-native-skia react-native-reanimated react-native-gesture-handler
  2. Control AnimatedGlow states manually

    main

    The AnimatedGlow component is stateless by default. To control the animation (e.g., switching between 'default', 'hover', and 'press'), use the activeState prop. This is useful when connecting the glow to external gestures or state management systems. When using Pressable, you can manage the state using useState and useRef to handle both press and hover events (especially for Web/Desktop support).

    import React, { useState, useRef } from 'react';
    import { View, Text, StyleSheet, Pressable } from 'react-native';
    import AnimatedGlow, { glowPresets, type GlowEvent } from 'react-native-animated-glow';
    
    export default function MyInteractiveButton() {
      // 1. State for the active glow effect ('default', 'hover', 'press')
      const [glowState, setGlowState] = useState<GlowEvent>('default');
      // 2. Ref to track if the cursor is currently hovering over the element
      const isHovered = useRef(false);
    
      return (
        <AnimatedGlow 
          preset={glowPresets.defaultRainbow}
          // 3. Pass the state to the activeState prop to control the glow
          activeState={glowState}
        >
          <Pressable
            style={styles.button}
            onPress={() => console.log('Button Pressed!')}
            
            // --- Press Events ---
            onPressIn={() => setGlowState('press')}
            onPressOut={() => {
              setGlowState(isHovered.current ? 'hover' : 'default');
            }}
            
            // --- Hover Events (for Web, macOS, Windows) ---
            onHoverIn={() => {
              isHovered.current = true;
              if (glowState !== 'press') {
                setGlowState('hover');
              }
            }}
            onHoverOut={() => {
              isHovered.current = false;
              if (glowState !== 'press') {
                setGlowState('default');
              }
            }}
          >
            <Text style={styles.buttonText}>Tap or Hover Me</Text>
          </Pressable>
        </AnimatedGlow>
      );
    }
    
    const styles = StyleSheet.create({
      button: { 
        paddingVertical: 20, 
        paddingHorizontal: 40,
        backgroundColor: '#222'
      },
      buttonText: {
        color: 'white',
        fontWeight: 'bold',
        textAlign: 'center'
      }
    });
  3. Migrating from v2 to v3 (Breaking Changes)

    main

    Version 3.0.0 introduced significant breaking changes to the API to support intelligent animation blending and better type safety:

    • Preset API Overhaul: The preset prop no longer accepts flat objects. It now requires a PresetConfig object containing a metadata object and a states array. All visual styles (including the base look) must be defined within the states array.
    • Removal of randomness: The randomness prop has been removed from the core API.
    • Unified Rendering: All glow layers, placements (behind, inside, over), and animated borders are now rendered in a single Skia shader for performance.
  4. Manage Glow states and transitions

    main

    The component uses GlowState to handle different interaction modes. States are identified by a GlowEvent type: 'default', 'hover', or 'press'. Each state can define its own partial GlowConfig and a transition duration (in milliseconds) to animate between states.

    export type GlowEvent = 'default' | 'hover' | 'press';
    
    export interface GlowState {
      name: GlowEvent;
      preset: Partial<GlowConfig>;
      transition?: number;
    }
  5. Use AnimatedGlow with PresetConfig

    main

    The recommended way to use the component is by defining a PresetConfig object. This object allows you to define a metadata block and a states array. Each state (like default, hover, or press) contains its own preset configuration, including glowLayers, cornerRadius, outlineWidth, and borderColor. The library will automatically interpolate between these states using Reanimated.

    import React from 'react';
    import { View, Text, StyleSheet } from 'react-native';
    import AnimatedGlow, { type PresetConfig } from 'react-native-animated-glow';
    
    // 1. Define your preset
    const myCoolPreset: PresetConfig = {
      metadata: { 
        name: 'My Cool Preset', 
        textColor: '#FFFFFF', 
        category: 'Custom',
        tags: ['interactive']
      },
      states: [
        {
          name: 'default', // The base style for the component
          preset: {
            cornerRadius: 50,
            outlineWidth: 2,
            borderColor: '#E0FFFF',
            glowLayers: [
              { colors: ['#00BFFF', '#87CEEB'], opacity: 0.5, glowSize: 30 },
            ]
          }
        },
        // 2. Define interactive states
        {
          name: 'hover', 
          transition: 300, // 300ms transition into this state
          preset: {
            glowLayers: [{ glowSize: 40 }] // On hover, make the glow bigger
          } 
        },
        {
          name: 'press', 
          transition: 100, // A faster transition for press
          preset: {
            glowLayers: [{ glowSize: 45, opacity: 0.6 }] 
          } 
        }
      ]
    };
    
    // 3. Use it in your component
    export default function MyGlowingComponent() {
      return (
        <AnimatedGlow preset={myCoolPreset}>
          <View style={styles.box}>
            <Text style={styles.text}>I'm Interactive!</Text>
          </View>
        </AnimatedGlow>
      );
    }
    
    const styles = StyleSheet.create({
      box: { paddingVertical: 20, paddingHorizontal: 40, backgroundColor: '#222' },
      text: { color: 'white', fontWeight: 'bold' }
    });
  6. Access glowPresets

    main

    The library exports glowPresets, which contains predefined configurations for various glow effects. These can be used to quickly apply standard styles without manual configuration.

    import { glowPresets } from 'react-native-animated-glow';
    
    // Access a specific preset
    const myPreset = glowPresets.somePresetName;
  7. Ensure Skia is loaded for web environments

    main

    When using react-native-animated-glow on the web, you must ensure that @shopify/react-native-skia is properly initialized. Use ensureSkiaWebLoaded() to trigger the loading of CanvasKit via a CDN. This function manages a global loading state to prevent multiple simultaneous triggerings.

    import { ensureSkiaWebLoaded } from './path-to-your-skia-loader';
    
    // Call this early in your application lifecycle (e.g., in App.tsx or an index file)
    ensureSkiaWebLoaded();
  8. Configure the SkiaRoot component

    main

    The SkiaRoot component serves as the entry point for the Skia-based glow implementation. It requires several SharedValue objects from react-native-reanimated to drive the animation and configuration state. This component wraps UnifiedSkiaGlow and maps the provided Reanimated shared values to the underlying glow engine.

    Props

    PropTypeDescription
    layoutLayoutDefines the spatial dimensions and positioning for the glow.
    skiaOpacitySharedValue<number>A shared value controlling the overall opacity of the Skia layer.
    animationProgressSharedValue<number>A shared value (typically 0 to 1) driving the animation timeline.
    fromConfigSVSharedValue<GlowConfig>The starting configuration state as a shared value.
    toConfigSVSharedValue<GlowConfig>The target configuration state as a shared value.
    import { SkiaRoot } from './src/animated-glow/SkiaRoot';
    // Note: Props require SharedValues from react-native-reanimated
    <SkiaRoot
      layout={myLayout}
      skiaOpacity={opacitySV}
      animationProgress={progressSV}
      fromConfigSV={fromConfigSV}
      toConfigSV={toConfigSV}
    />
  9. Configure Skia for Web using SkiaRoot

    main

    When using react-native-animated-glow on the web, SkiaRoot acts as a wrapper that handles the asynchronous loading of the Skia/CanvasKit WASM engine via LoadSkiaWeb. It uses React.lazy and Suspense to ensure the UnifiedSkiaGlow component is only rendered once the Skia environment is fully initialized.

    To use it, you must provide several SharedValue objects from react-native-reanimated to drive the animation and configuration state.

    import { SkiaRoot } from 'react-native-animated-glow';
    import { useSharedValue } from 'react-native-reanimated';
    
    // Example props setup
    const layout = { /* ... */ };
    const skiaOpacity = useSharedValue(1);
    const animationProgress = useSharedValue(0);
    const fromConfigSV = useSharedValue({ /* ... */ });
    const toConfigSV = useSharedValue({ /* ... */ });
    
    <SkiaRoot 
      layout={layout} 
      skiaOpacity={skiaOpacity} 
      animationProgress={animationProgress} 
      fromConfigSV={fromConfigSV} 
      toConfigSV={toConfigSV} 
    />
  10. Monitor Skia web loading status with skiaWebState

    main

    You can monitor the loading progress of Skia on the web using the skiaWebState object. This is useful if you want to show a loading spinner or delay rendering components that depend on Skia until the engine is ready.

    skiaWebState.status can be one of:

    • 'idle': Loading has not been triggered.
    • 'loading': Skia is currently being fetched.
    • 'ready': Skia is loaded and ready for use.

    You can also subscribe to status changes by adding a callback to skiaWebState.subscribers.

    import { skiaWebState, ensureSkiaWebLoaded } from './path-to-your-skia-loader';
    
    // Example: React component that waits for Skia
    const MyComponent = () => {
      const [isReady, setIsReady] = useState(skiaWebState.status === 'ready');
    
      useEffect(() => {
        ensureSkiaWebLoaded();
    
        if (skiaWebState.status !== 'ready') {
          skiaWebState.subscribers.add(() => setIsReady(true));
        }
      }, []);
    
      if (!isReady) return <Text>Loading Skia...</Text>;
      return <AnimatedGlowComponent />;
    };
  11. Configure AnimatedGlow props

    main

    The AnimatedGlow component accepts the following primary props:

    PropTypeDescription
    presetPresetConfigA base configuration object containing default styles and states.
    statesGlowState[]An array of custom states to define. Overrides preset.states.
    initialStatestringThe state to use on mount (defaults to 'default').
    activeStatestringThe currently active state. Changing this triggers a transition.
    childrenReactNodeThe content to be wrapped by the glow effect.
    styleStyleProp<ViewStyle>Styles applied to the outermost container.
    wrapperStyleStyleProp<ViewStyle>Styles applied to the inner wrapper that holds the children.
    isVisiblebooleanControls whether the Skia renderer is active (defaults to true).
    ...overridePropsGlowConfigAny additional properties from GlowConfig (e.g., cornerRadius, borderColor, glowLayers) are merged into the base configuration.