Skip to content
Native Animation
Newton Cradle Approval animation preview

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

npx nativeanimation add newton-cradle-approval
LoadingSkia

Newton Cradle Approval

An approval flow where a swinging cradle becomes a coin and offer, with configurable offer data, coin image, colors, and fonts.

$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 approval flow where a swinging cradle becomes a coin and offer, with configurable offer data, coin image, colors, and fonts.

Why this one

A spinner cannot respond to your touch. This waiting object exchanges momentum between its end balls, lets you pull and release either end, and turns its final ball into the outcome. Chrome highlights, a pink contact pulse, a violet atmosphere, and an iridescent coin give the flow its own identity.

How it works

  1. onLayout measures the host container. The cradle and coin use local dimensions, a capped width, and a compact layout; overflow remains scrollable.
  2. A UI frame callback integrates angular acceleration −ω² sin(θ) with four substeps and low damping. Contact transfers angular velocity to the opposite end; the middle three balls remain fixed. The default complete exchange takes about 1.1 seconds.
  3. Gesture Handler combines exclusive pan and tap gestures. Pulling suspends integration; release resumes from that angle. Press feedback has a separate spring with explicit mass.
  4. start() calls the supplied check. Only its current request can approve; rejection leaves a retryable checking state. finish() can approve manually, including while the promise is pending.
  5. One approval progress drives the last ball's flight, circle growth, perspective rotation, coin crossfade, landing ring, amount, and staggered rate cards. Digit columns and separator widths derive from the amount on UI and settle at exact integers.
  6. The demo runs four seconds of checks followed by three seconds of approval. Touch stops future autoplay cycles while the active check still completes.

Install

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

Use a GestureHandlerRootView in the host app. For Skia on web, configure CanvasKit using the installed Skia package's web setup and load it before mounting the component. The catalog host must register this demo with its Skia loader. No registration changes are included in this folder.

Usage

import { useRef } from 'react';
import { Button, View } from 'react-native';
import NewtonCradleApproval, {
  type NewtonCradleApprovalRef,
} from './NewtonCradleApproval';

export function ApplicationScreen() {
  const approval = useRef<NewtonCradleApprovalRef>(null);
  return (
    <View style={{ flex: 1 }}>
      <Button title="Check my offer" onPress={() => approval.current?.start()} />
      <NewtonCradleApproval
        ref={approval}
        check={async () => { await submitApplication(); }}
        offer={{ amount: 3000, annualRate: '15.9%', fee: '$45–$120' }}
        coinSource={require('./assets/coin.jpg')}
        onContinue={openOffer}
      />
    </View>
  );
}

submitApplication and openOffer are host-provided functions. Copy your own image; the reusable component never imports demo assets. The image is decorative, clipped to a circle and enlarged by 1.28× to emphasize the coin.

For a slower, quieter variant, pass period={1.4}, releaseAngle={20}, stepDuration={1800}, and approvalDuration={1000}. Supply a complete light palette through colors to change background, atmosphere, ink, surfaces, wires, chrome, and controls together. Fonts are supplied by the host; the demo uses Geist and Geist Mono and locally loads Geist 600/700 from the installed font package.

The forwarded ref exposes start() and finish(). The initial state is checking; the promise is invoked only by start(). Without a check callback the cradle continues until finish(). A rejected check never displays approval and can be retried by tapping the cradle. onContinue belongs to the host; in the demo it starts another check.

Props

PropTypeDefaultDescription
offerNewtonOfferRequiredWhole-unit amount, formatted annual rate, formatted fee. Amount is clamped to 0–999,999,999.
stepsreadonly string[]Three generic statusesChecking messages; empty lists use the waiting title.
check() => Promise<unknown>NoneApproval on resolution, retry on rejection; stale results ignored.
coinSourceImageSourcePropTypeNoneImage inside the circle; chrome gradient fallback.
colorsPartial<NewtonPalette>Dark violet/pink paletteEvery functional color, including chrome stops and shadows.
waitingTitlestringHold tight…Checking headline.
waitingBodystringWe're checking a few things. This takes a few seconds.Checking explanation.
approvedTitlestringYou're pre-approved!Result headline.
offerLabelstringYour offerAmount caption.
rateLabel / feeLabelstringAnnual rate / Fee fromCard captions.
continueLabelstringContinueAction text and accessibility label.
gestureHintstringPull an end ball. Let momentum do the rest.Visible interaction instruction.
errorLabelstringCheck interrupted. Tap to try again.Error copy and announcement.
currencySymbol / groupingSeparatorstring$ / ,Formatting for whole-unit rolling figures. Decimal amounts are not supported.
cradleLabelstringInteractive Newton cradleAccessible object name.
cradleHintstringDrag an end ball or tap to release it.Accessible instruction.
approvalAnnouncementstringYour offer is ready.Important state announcement.
fontFamilystringSystemBody typeface.
headingFontFamilystringSystemHeading and action typeface.
amountFontFamilystringSystemTabular figure typeface.
monoFontFamilystringSystemStatus typeface.
periodnumber1.1Complete exchange period, seconds; minimum 0.5.
releaseAnglenumber28Initial/tap angle, degrees; clamped to 1–55.
stepDurationnumber1400Status interval, ms; minimum 400.
approvalDurationnumber900Full result entrance, ms; minimum 400. The flight finishes at 65% (585 ms by default).
onApproved() => voidNoneCalled once per approval.
onCheckError(error: unknown) => voidNoneFailed check callback.
onInteraction() => voidNoneTouch signal for stopping host autoplay.
onContinue() => voidNoneContinue action.
styleStyleProp<ViewStyle>NoneRoot style. Host supplies safe-area insets.
testIDstringnewton-cradle-approvalRoot; -cradle and -continue identify controls.

Compatibility

Expo SDK 58, React Native 0.88.0, Reanimated 4.7.1, Worklets 0.13.0, Skia 2.14.0. All dependencies are already installed in this project. Expo Go includes the required native modules for this SDK.

Supports iOS, Android, and web. No video recording is included. The scroll layout supports constrained containers including 300×400 and landscape; the host should provide a bounded height.

Accessibility

The cradle is a labeled button with a screen-reader activation action and a tap alternative to dragging. Continue is a labeled button with a 54-point minimum height. Approval and errors are announced. The offer exposes one formatted amount rather than reading its individual rolling digits. Checking content is excluded from accessibility after approval.

Reduced motion stops the pendulum, removes flights, rotation, digit rolling, contact pulses, ring expansion, and card travel, and replaces the result entrance with a 200 ms fade. The host must supply localized visible and accessible text through props.

Icons: Lucide (ISC), copied from the repository kit. Demo brand: Aster from that kit.