Recorded on iPhone. Swiped in portrait, then rotated to landscape, where more pages fan out along each edge.
Gesture
Rolling Carousel
Posters ride a surface that rolls away at the screen edges, like film around a spool.
Features
- Cards roll around both edges of the screen, like film over a spool
- A quick swipe keeps its momentum and glides to a stop
- Tap the left or right side to move one card; tap fast to skip ahead
- Can loop forever and play through the cards on its own
- Choose a soft, sharp or default curve, or fine-tune every detail
- Photos show in full at their own shape, never cropped
Built with
- Skia Vertices
- Reanimated worklets
- Gesture Handler
Installation
Usage
How it works
It is one continuous surface, not a row of rotated cards. A mesh is rebuilt every frame in a Reanimated worklet and drawn with Skia, so pages bend smoothly around the fold instead of hinging at a corner. Dragging, the spring and the drawing all run on the UI thread, and nothing redraws while the carousel is still.
Examples
Presets
Change the shape of the fold, then override any single value.
Three across, looping, auto-play
Several cards face-on, an endless strip, and a page every 2.5 seconds.
Driven from outside
Your own buttons and a page counter, with tapping turned off.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
images | DataSourceParam[] | Required | require() or URLs. Keep the array stable; a new array reloads. |
preset | "default" | "soft" | "sharp" | "default" | Shape of the fold: a sharp fold with a small roll, a rounder and broader roll, or a crisp crease. |
loop | boolean | false | After the last page comes the first. |
autoPlay | boolean | false | Move through the pages on a timer. Waits while a finger is down; stops at the last page unless loop is on. |
onIndexChange | (index: number) => void | — | Called as each page change starts. |
config | Partial<RollingCarouselConfig> | — | Fine tuning. See Advanced. |
style | StyleProp<ViewStyle> | — | Fills its parent unless you set a height or flex. |
Advanced
Everything most apps never need to touch.
Every field is optional and overrides the preset.
| Field | Default | Description |
|---|---|---|
cardsPerView | 1 | Cards face-on in the middle. Odd keeps one centred. |
viewWidth | "72%" | Width those cards fill together, in points or a percentage. The rest is folds. |
gap | 16 | Points between cards. |
perspective | 4 | Pages stacked past each fold: 2 → one, 4 → two, 6 → three. |
foldAngle | 89 | Degrees the strip turns at each fold, 0–90. |
foldRadius | 0.25 | Roundness of the fold in card widths. 0 is a crease. |
pageBow | 0.064 | How much each page curves back in the middle. 0 is flat. |
settleDuration | 1200 | Milliseconds a page takes to settle, roughly. |
settleDamping | 0.9 | 1 never overshoots; lower bounces. |
flickLookahead | 0.2 | Seconds of flick speed to look ahead. Higher travels further. |
overscrollResistance | 0.3 | Drag past the ends. 0 is a wall, 1 is free. |
autoPlayInterval | 3000 | Milliseconds each page rests. |
autoPlayStepDuration | 1200 | Milliseconds each step takes. |
autoPlayDirection | "forward" | "forward" or "backward". |
placeholderColors | "auto" | Gradient while an image loads, as two colours. Auto follows dark mode. |
meshColumns | 20 | Smoothness of the page curve. Higher costs more. |
| Method | Description |
|---|---|
next() | One page forward from where it rests or is heading. |
previous() | One page back. |
scrollTo(index, { animated }) | Settle on a page, or jump there with animated: false. With loop, it takes the shorter way round. |
| Prop | Default | Description |
|---|---|---|
initialIndex | 0 | Starting page. Read once; use the ref to move later. |
interactive | true | Off, swiping and tapping do nothing; auto-play, the ref and screen readers still move it. |
tapNavigation | true | Tap the left or right half to move one page. |
accessibilityLabel | "Gallery" | Read before "3 of 8". |
ref | — | A RollingCarouselHandle. See Ref methods. |
- Vertical swipes pass through to a parent ScrollView, so it works inside scrolling screens.
- Quick taps queue up: three taps move three pages.
onIndexChangefires when each move starts, not when it lands. - Cards keep their image's shape and grow as large as they fit. To make them bigger, make the carousel bigger.
- A spinner shows until the first few pages load (at most 4 seconds), then the carousel fades in, so it never appears half-loaded.
- Screen readers read it as "Gallery, 3 of 8", and swiping up or down moves one page.
- With Reduce Motion on, pages move with a short, gentle slide instead of a spring, and auto-play turns off.