Skip to content
Native Animation
GalleryCarouselAvatarsGestureReveal

Arc Avatar Switcher

A full-screen artwork selector whose portrait thumbnails rotate along a tactile arc and launch each new scene from the chosen image.

$9 for this animation

Add to your project

Copy the component and its assets into your app with the Native Animation CLI.

npx nativeanimation add arc-avatar-switcher

About this animation

A full-screen artwork selector whose portrait thumbnails rotate along a tactile arc and launch each new scene from the chosen image.

Why this one

Most full-screen selectors fade between static rows of thumbnails. This one turns the row into an inertial dial: portraits rise, enlarge, and gain a luminous ring at the apex, while the next artwork expands directly from its thumbnail in a cinematic circular reveal. Neighboring full-resolution images are prefetched so remote artwork is ready before selection.

How it works

  1. The component measures its own container, then derives the arc center, radius, thumbnail diameter, and full-screen reveal from those dimensions.
  2. One continuous position shared value drives every thumbnail's angle, coordinates, scale, opacity, and stacking order.
  3. Horizontal dragging maps distance to angular travel. Release velocity feeds a decay animation, then a spring snaps the nearest portrait to the top of the arc.
  4. Selection mounts the new full-screen image inside a circular, clipped layer centered on the chosen thumbnail. The circle grows from 0.15W to 2.4H in 650 ms.
  5. The previous and next remote images are prefetched into Expo Image's memory and disk cache after every selection.

Install

npx expo install expo-image expo-linear-gradient react-native-gesture-handler react-native-reanimated react-native-worklets

Usage

import ArcAvatarSwitcher from './ArcAvatarSwitcher';

export function GalleryScreen() {
  return (
    <ArcAvatarSwitcher
      items={[
        { image: require('./art/drift.jpg'), label: 'Drift' },
        { image: require('./art/bloom.jpg'), label: 'Bloom' },
        { image: require('./art/skyward.jpg'), label: 'Skyward' },
      ]}
    />
  );
}

Customize the dial and control selection from a ref:

import { useRef } from 'react';
import ArcAvatarSwitcher, {
  type ArcAvatarSwitcherRef,
} from './ArcAvatarSwitcher';

export function CuratedGallery() {
  const switcherRef = useRef<ArcAvatarSwitcherRef>(null);

  return (
    <ArcAvatarSwitcher
      ref={switcherRef}
      items={artworks}
      initialIndex={2}
      arcRadius={0.82}
      thumbSize={0.17}
      ringColor="#B7FFDA"
      fontFamily="YourSansMedium"
      onIndexChange={(index) => trackSelection(index)}
    />
  );
}

The forwarded ref exposes select(index).

Props

PropTypeDefaultDescription
items{ image, thumb?, label }[]RequiredFull-screen images, optional thumbnail crops, and accessible labels.
initialIndexnumber0Initially selected item.
arcRadiusnumber0.9Arc radius as a fraction of component width.
thumbSizenumber0.15Thumbnail diameter as a fraction of component width.
ringColorstring'#FFFFFF'Active thumbnail ring color.
fontFamilystringSystem fontFont used for the active label.
onIndexChange(index: number) => void—Called when the reveal completes.
styleStyleProp<ViewStyle>—Outer-container style.
testIDstring—Test identifier for the outer container.

Compatibility

  • Expo SDK 57
  • React Native 0.86
  • Reanimated 4.5 and Worklets 0.10
  • iOS: supported, not yet verified on a device
  • Android: supported, not yet verified on a device
  • Web: supported, not yet verified
  • Works in Expo Go because its native dependencies are included in the SDK

Accessibility

Every thumbnail is a labeled button and reports its selected state. The dial also exposes adjustable increment and decrement actions for screen readers, so every drag has a tap and assistive-technology alternative. With reduced motion enabled, inertial travel becomes a short timing transition and the expanding circle becomes a 200 ms full-screen fade.