Skip to content
Native Animation
Year Digits Rollover animation preview

This preview uses fictional demo content. See the props below for the customization options available in this component.

npx nativeanimation add year-digits-rollover
Year in reviewSkia

Year Digits Rollover

An interactive year reveal with alternating bands on changing digits, with configurable start year, end year, colors, and numeral font asset.

$9 for this animation

Add to your project

Copy the component and its assets into your app with the Native Animation CLI, or paste the agent prompt into Claude Code, Codex or Cursor once the MCP server is connected.

About this animation

An interactive year reveal with alternating bands on changing digits, with configurable start year, end year, colors, and numeral font asset.

Why this one

The unchanged digits stay anchored. Four slices travel in opposite directions with a velocity-sensitive vertical blur, then a diagonal light sweep and six four-point stars mark the new year. Both an outlined recap cover and a warm gradient celebration use the same component.

How it works

  1. The container measures itself with onLayout. Skia's getTextWidth sizes four digit slots to 92% of its width; the larger glyph in each old/new pair reserves the slot.
  2. One UI-thread roll value drives every changing digit. Band windows are offset by 70 ms, with a 620 ms cubic in/out travel and a small settling rebound. Unchanged digits have no rolling layers.
  3. Each band clips a vertical old/new glyph strip. Alternating travel directions create the split effect, with a vertical Skia blur that fades to zero at rest.
  4. A masked diagonal highlight sweeps across the finished year for 500 ms. The grid shifts by 12 points and six stars scale into view.
  5. Drag vertically to scrub and release to a spring with explicit mass and gesture velocity. Tap to toggle. Caption lines rise eight points in 260 ms, staggered by 240 ms.

Install

npx expo install @shopify/react-native-skia react-native-gesture-handler react-native-reanimated react-native-worklets

Use a GestureHandlerRootView at your application root. Configure Reanimated for your Expo project. Web requires CanvasKit following the Skia web setup. No additional native module beyond those included in the project's Expo Go setup is introduced.

Usage

import { YearDigitsRollover } from './year-digits-rollover';

<View style={{ height: 400 }}>
  <YearDigitsRollover
    titleFont={require('./assets/Display-ExtraBold.ttf')}
    caption={'Year\nin review'}
  />
</View>

Pass a locally bundled extra-bold font asset for consistent native and web rendering. Without one, a guarded system-font fallback is attempted on native; web waits for a supplied font. The demo supplies Geist ExtraBold. No font or theme imports are required by the component.

const cover = useRef<YearDigitsRolloverRef>(null);

<YearDigitsRollover
  ref={cover}
  from={2029}
  to={2030}
  variant="solid"
  titleFont={require('./assets/Display-ExtraBold.ttf')}
  caption={'Happy\nNew Year'}
  colors={{
    background: '#2346E8',
    digits: '#FFD84D',
    gradient: '#FF6B4A',
    stroke: '#FFD84D',
    flash: '#F7F3EA',
    caption: '#F7F3EA',
    grid: '#F7F3EA',
  }}
  onRolled={year => saveSelectedYear(year)}
/>
// Call cover.current?.roll() from your own button or event handler.

Import YearDigitsRolloverRef as a type alongside the component. roll() and play() animate to to; reset() crossfades to from over 300 ms; replay() restarts from from and runs the transition again. Callbacks receive completed destinations, including completed resets; interrupted transitions do not notify.

Props

PropTypeDefaultPurpose
fromnumber2026Starting year, rounded and clamped to 0000–9999.
tonumber2027Destination year.
variant'solid' | 'outline''outline'Vertical gradient fill or two-point stroke.
captionstring'Year in review'Caption; newline separates entrance lines.
colorsPartial<YearDigitsRolloverColors>Ink/violet paletteBackground, digits, gradient endpoint, stroke, flash, caption, and grid.
stripCountnumber4Bands per changing digit, clamped to 1–12.
durationnumber620Per-band travel in milliseconds, plus the fixed 70 ms stagger.
onRolled(year: number) => void—Completion callback on the JS thread.
titleFontDataSourceParam—Skia display-font asset; required for web text.
fontFamilystringSystemReact Native caption font.
accessibilityLabelstringCurrent year + captionOverride the accessible name.
styleStyleProp<ViewStyle>—Container sizing and styling.
testIDstring'year-digits-rollover'Root identifier.

The default palette uses #101028 background, #8B7BFF digits/stroke, #C9C0FF gradient endpoint, and #F7F3EA caption/grid/flash. The parent owns safe area insets. The component fills its box; the display compresses vertically in short landscape containers while retaining the 92% width. Caption text scales with system preferences. Reserve sufficient height for custom multiline or enlarged captions.

Compatibility

Targets Expo SDK 58, React Native 0.88.0, Reanimated 4.7.1, Worklets 0.13.0, Gesture Handler 3.3, and Skia 2.14.0. Designed for iOS, Android, and web. Autoplay belongs to the demo, which rolls, holds 1.6 seconds after the sweep, crossfades back, and alternates palettes.

Accessibility

The entire year is a button with the current year and caption as its label. Screen reader activation toggles the year; increment/decrement actions choose the destination/start. Dragging is optional. With reduced motion, year changes use a 300 ms crossfade, with no rolling, blur, grid parallax, highlight sweep, or stars. Caption entrances retain opacity only.