React Native Awesome Gallery

repository·main·Indexed 20 days ago

https://github.com/pavelbabenko/react-native-awesome-gallery

A high-performance photo gallery component for React Native powered by Reanimated v3 and react-native-gesture-handler. It features pinch-to-zoom, double-tap to scale, infinite looping, and programmatic control via GalleryRef. The library provides a customizable Gallery component with support for custom image rendering and a useVector hook for managing 2D animated offsets.

Tokens
3.9K
Snippets
10
Records
13
Agent score
70%

What's inside react-native-awesome-gallery

  1. Install React Native Awesome Gallery

    main

    To use react-native-awesome-gallery, you must first ensure that react-native-reanimated (v3 or higher) and react-native-gesture-handler are installed and configured in your project according to their respective documentation.

    Once the dependencies are set up, install the gallery package using yarn:

    yarn add react-native-awesome-gallery
  2. Understand RenderItemInfo

    main

    When providing a custom renderItem function, you receive a RenderItemInfo<T> object. It is crucial to call setImageDimensions once the image has loaded to ensure the gallery's zoom and pan calculations are accurate.

    Properties:

    • item: T: The data item for the current index.
    • index: number: The index of the current item.
    • setImageDimensions: (imageDimensions: { height: number; width: number }) => void: A function to inform the gallery of the actual image dimensions.
    renderItem={({ item, index, setImageDimensions }) => (
      <Image
        source={{ uri: item }}
        onLoad={(e) => {
          const { height: h, width: w } = e.nativeEvent.source;
          setImageDimensions({ height: h, width: w });
        }}
      />
    )}
  3. Use the Gallery component

    main

    The Gallery component is the main entry point for creating an interactive image gallery in React Native. It supports zooming (pinch/double-tap), swiping between images, and looping. It uses react-native-reanimated and react-native-gesture-handler for high-performance animations and gestures.

    To use it, provide a data array and a renderItem function. By default, it provides a simple image renderer, but you can customize it to render any component.

    import React, { useRef } from 'react';
    import Gallery, { GalleryRef } from 'react-native-awesome-gallery';
    
    const MyGallery = () => {
      const galleryRef = useRef<GalleryRef>(null);
      const images = ['https://example.com/1.jpg', 'https://example.com/2.jpg'];
    
      return (
        <Gallery
          ref={galleryRef}
          data={images}
          renderItem={({ item, index, setImageDimensions }) => (
            <Image
              source={{ uri: item }}
              onLoad={(e) => {
                const { height: h, width: w } = e.nativeEvent.source;
                setImageDimensions({ height: h, width: w });
              }}
              style={{ width: '100%', height: '100%' }}
            />
          )}
        />
      );
    };
  4. Basic Usage of React Native Awesome Gallery

    main

    To implement a basic gallery, import the Gallery component and provide an array of items to the data prop. You can also listen to index changes via the onIndexChange prop.

    import Gallery from 'react-native-awesome-gallery';
    
    // ...
    
    const images = ['https://image1', 'https://image2'];
    
    return (
      <Gallery
        data={images}
        onIndexChange={(newIndex) => {
          console.log(newIndex);
        }}
      />
    );
  5. Configure the Gallery component with Props

    main

    The Gallery component accepts several props to control its behavior, appearance, and interaction model:

    Data and Rendering

    • data: T[] - Array of items to render.
    • renderItem: (renderItemInfo: {item: T, index: number, setImageDimensions: Function}) => React.ReactElement - Callback to render custom image components (e.g., FastImage). Important: You must call setImageDimensions({width, height}) after the image has loaded.
    • keyExtractor: (item: T, index: number) => string | number - Provides unique keys for items.
    • initialIndex: number - The starting index (defaults to 0).
    • numToRender: number - Number of items rendered simultaneously (defaults to 5).

    Gestures and Scaling

    • doubleTapEnabled: boolean - Enables/disables double tap (defaults to true).
    • doubleTapScale: number - Scale factor when double tap occurs (defaults to 3).
    • doubleTapInterval: number - Milliseconds between single and double tap (defaults to 500).
    • pinchEnabled: boolean - Enables/disables pinch gesture (defaults to true).
    • swipeEnabled: boolean - Enables/disables pan gesture (defaults to true).
    • maxScale: number - Maximum scale allowed via gestures (defaults to 6).
    • disableVerticalSwipe: boolean - Disables vertical swipe when scale is 1.
    • disableSwipeUp: boolean - Disables swipe up when scale is 1.
    • disableTransitionOnScaledImage: boolean - Prevents transitioning to next/previous image when scale > 1.
    • hideAdjacentImagesOnScaledImage: boolean - Hides adjacent images when scale > 1.

    Layout and Behavior

    • loop: boolean - Enables infinite swiping (requires data.length > 1).
    • emptySpaceWidth: number - Width of space between items (defaults to 30).
    • containerDimensions: {width: number, height: number} - Dimensions of the wrapping View (defaults to useWindowDimensions() values).
    • style: ViewStyle - Style for the container.

    Events

    • onIndexChange: (newIndex: number) => void - Called when the active item index changes.
    • onScaleChange: (scale: number) => void - Called when the scale changes.
    • onScaleChangeRange: {start: number, end: number} - Defines the scale range for onScaleChange calls.
  6. Use the GalleryRef to control the gallery

    main

    To programmatically control the gallery, use the GalleryRef type with a useRef hook. This allows you to access methods for changing the active index or resetting the view state.

    import Gallery, { GalleryRef } from 'react-native-awesome-gallery';
    import { useRef }
    
    // ...
    
    const ref = useRef<GalleryRef>(null);
  7. Control the gallery with setIndex and reset methods

    main

    The GalleryRef exposes the following methods:

    • setIndex(newIndex: number, animated?: boolean): Sets the active image index. Use animated: true for a smooth transition.
    • reset(animated?: boolean): Resets the current scale and translation (zoom/pan) of the image back to the default state.
    | Prop | Description | Type |
    |----------|------------------------------------------------------------------|--------------------------------------------------|
    | setIndex | Sets active index | `(newIndex: number, animated?: boolean) => void` |
    | reset | Resets scale, translation | `(animated?: boolean) => void` |
  8. Configure Metro for monorepo peer dependency resolution

    main

    When using this project within a monorepo or a library development environment (like the provided example folder), you must configure Metro to prevent multiple versions of peerDependencies from being loaded. This is achieved by:

    1. Blacklisting the node_modules located at the project root to prevent Metro from resolving them.
    2. Aliasing those same modules to the specific versions installed within the local example/node_modules using extraNodeModules.

    This ensures that the application uses the exact versions of peer dependencies intended for the example/app context, avoiding conflicts.

    const config = {
      ...defaultConfig,
      projectRoot: __dirname,
      watchFolders: [root],
      resolver: {
        ...defaultConfig.resolver,
        // 1. Block peerDependencies at the root
        blacklistRE: exclusionList(
          modules.map(
            (m) =>
              new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`)
          )
        ),
        // 2. Alias them to the local node_modules
        extraNodeModules: modules.reduce((acc, name) => {
          acc[name] = path.join(__dirname, 'node_modules', name);
          return acc;
        }, {}),
      },
    };
  9. Configure GalleryProps

    main

    The Gallery component accepts several props to customize its behavior and appearance:

    PropTypeDefaultDescription
    dataT[]RequiredArray of items to render in the gallery
    renderItemRenderItem<T>defaultRenderImageFunction to render each item. Receives item, index, and setImageDimensions
    initialIndexnumber0The index of the item to show first
    keyExtractor(item: T, index: number) => string | number-Function to extract a unique key for each item
    onIndexChange(index: number) => void-Callback triggered when the current index changes
    loopbooleanfalseWhether to loop from last to first and vice versa
    emptySpaceWidthnumber40The width of the space between images
    maxScalenumber6The maximum zoom level
    doubleTapScalenumber3The scale factor applied on double tap
    doubleTapIntervalnumber500Max time between taps for a double tap to trigger
    pinchEnabledbooleantrueEnable/disable pinch-to-zoom
    swipeEnabledbooleantrueEnable/disable swiping between images
    doubleTapEnabledbooleantrueEnable/disable double-tap to zoom
    disableVerticalSwipebooleanfalseIf true, prevents vertical swiping when scale is 1
    disableSwipeUpbooleanfalseIf true, prevents swiping up to close the gallery
    onScaleChange(scale: number) => void-Callback triggered when the scale changes
    onScaleChangeRange{ start: number; end: number }-Limits when onScaleChange is triggered
    containerDimensions{ width: number; height: number }-Override the dimensions used for calculations
    styleViewStyle-Style for the gallery container
  10. Reference the Gallery event props

    main

    The Gallery component provides several event handlers to respond to user interactions. Note that onTranslationYChange is a 'worklet'; and must be implemented as a Reanimated worklet for performance.

    | Prop | Description | Type |
    |------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------|------------|
    | onSwipeToClose() | Fired when user swiped to top/bottom | `Function` |
    | onTranslationYChange(translationY: number, shouldClose: boolean) | `'worklet';` Fired when user is swiping vertically to close the gallery | `Worklet` |
    | onTap() | Fired when user tap on image | `Function` |
    | onDoubleTap(toScale: number) | Fired when user double tap on image | `Function` |
    | onLongPress() | Fired when long press is detected | `Function` |
    | onScaleStart(scale: number) | Fired when pinch gesture starts | `Function` |
    | onScaleEnd(scale: number) | Fired when pinch gesture ends. Use case: add haptic feedback when user finished gesture with `scale > maxScale` or `scale < 1` | `Function` |
    | onPanStart() | Fired when pan gesture starts | `Function` |
  11. Use the useVector hook

    main

    The useVector hook is a utility provided by the library to create a pair of Reanimated shared values representing an (x, y) coordinate. This is useful for managing 2D offsets or positions in an animated context.

    const { x, y } = useVector(initialX, initialY);
    import { useVector } from 'react-native-awesome-gallery';
    
    const { x, y } = useVector(0, 0);
  12. Control the Gallery via GalleryRef

    main

    You can interact with the gallery programmatically using a ref of type GalleryRef. This allows you to change the current image or reset the zoom state of all images.

    Methods:

    • setIndex(newIndex: number, animated?: boolean): Moves the gallery to a specific image index. If animated is true, it uses a spring animation.
    • reset(animated?: boolean): Resets all images to their original scale (1) and position.
    const galleryRef = useRef<GalleryRef>(null);
    
    // To jump to the third image with animation
    galleryRef.current?.setIndex(2, true);
    
    // To reset all images to normal scale
    galleryRef.current?.reset(true);