Enter
Navigation

DateCarousel

A horizontal picker. Tap a date, drag it, flick it, or use the arrow keys. The highlight resists being moved and stretches wide on the way.

20
21
22
23
24
25
26
27
28

Usage

<DateCarousel
  defaultIndex={4}
  itemWidth={96}
  height={120}
  momentum={0.18}
  stickiness={0.7}
  spring="smooth"
  items={days.map((d) => ({
    key: String(d),
    primary: d,
    suffix: "日",
  }))}
/>

How the motion works

Two springs drive it. The scroll spring holds the row position in pixels and reaches the selected slot first; a much looser second spring chases it and is always behind. The highlight spans between them — leading edge at the fast spring, trailing edge at the slow one — so moving left pulls it wider to the right and it draws back into a circle as the lag closes. Only the span is animated: the height and the fully-round ends are fixed, which is what keeps it reading as one solid pill. The gap between the springs is the deformation: nothing keyframes the stretch, it falls out of one spring outrunning the other. On release the flick velocity is projected forward to choose the landing index, and past the ends the offset is rubber-banded so overscroll resists instead of stopping dead.

useSpringValuepointer capturevelocity sampling

Props

PropTypeDefaultDescription
itemsCarouselItem[]Each has key, primary and optional suffix.
index / defaultIndexnumberControlled or initial selected index.
onChange(index: number) => voidFires when the selection settles.
itemWidthnumber96Slot width and highlight diameter, in px.
momentumnumber0.18How far a flick carries past the release point.
stickinessnumber0.70–1. How far the trailing edge lags behind, and so how far the pill stretches.