About this animation
A scratch card with coverage-based reward reveal and configurable reward copy, labels, metallic colors, and fonts.
How it works
- A Skia layer builds brushed copper from a three-stop gradient, fine grain, and a moving specular highlight.
- A Gesture Handler pan appends points to a soft 34-point clear stroke inside a saved Skia layer.
- The gesture marks a 12 × 8 coverage grid instead of reading pixels, keeping the reveal deterministic and inexpensive.
- One shared reveal progress drives the remaining metal dissolve, reward lift, confetti burst, and wallet action.
- The imperative
play()method traces a measured zigzag for autoplay demos;reveal()provides a tap-accessible alternative.
Install
npx expo install @shopify/react-native-skia react-native-gesture-handler react-native-reanimated react-native-workletsFor web, initialize CanvasKit before rendering the component and make the CanvasKit WASM asset available to your bundler. Wrap your app root with GestureHandlerRootView on every platform.
Icons: Lucide (ISC).
Usage
import { ScratchCardReveal } from './ScratchCardReveal';
export function Reward() {
return (
<ScratchCardReveal
rewardTitle="Coffee on us"
rewardDetail="Any size · Valid through 10/31"
rewardCode="REWARD-24"
onAddToWallet={() => saveReward()}
/>
);
}With personality knobs and imperative control:
const card = useRef<ScratchCardRevealRef>(null);
<ScratchCardReveal
ref={card}
accentColor="#B8552F"
metalColors={['#E8C07A', '#B7793C', '#F3D49B']}
scratchWidth={36}
cardWidthRatio={0.88}
onReveal={() => announceReward()}
/>;
card.current?.play();Props
| Prop | Type | Default | Description |
|---|---|---|---|
eyebrow | string | 'FREE' | Short reward category. |
rewardTitle | string | 'Your favorite drink' | Main reward name. |
rewardDetail | string | 'Any size · Valid through 10/31' | Validity or offer detail. |
rewardCode | string | 'REWARD-24' | Redemption code. |
scratchLabel | string | 'Scratch here' | Engraved instruction. |
walletLabel | string | 'Add to wallet' | Wallet action label. |
replayLabel | string | 'Replay' | Reset action label. |
cardWidthRatio | number | 0.84 | Card width relative to the component. |
cornerRadius | number | 24 | Card corner radius. |
scratchWidth | number | 34 | Scratch stroke width. |
rewardColor | string | '#FFF6E8' | Reward surface color. |
metalColors | [string, string, string] | copper palette | Brushed metal gradient. |
accentColor | string | '#B8552F' | Reward and action accent. |
fontFamily | string | system | Interface font family. |
mediumFontFamily | string | system | Emphasized interface font family. |
titleFontFamily | string | system | Editorial reward font family. |
monoFontFamily | string | system | Redemption code font family. |
onReveal | () => void | — | Called once per reveal. |
onAddToWallet | () => void | — | Called by the wallet action. |
accessibilityLabel | string | 'Reveal reward' | Scratch surface label. |
style | StyleProp<ViewStyle> | — | Outer container style. |
testID | string | — | Outer container test identifier. |
The forwarded ref exposes play(), reveal(), and reset().
Compatibility
- Expo SDK 58
- React Native 0.88.0
- Reanimated 4.7.1 and Worklets 0.13.0
- React Native Skia 2.14.0
- iOS: supported
- Android: supported
- Web: supported
- Expo Go: supported because every native dependency is included in Expo Go for SDK 58
Accessibility
The scratch surface exposes a button role and an activate action, so a screen reader can reveal the prize without drawing. All controls meet the 44-point target minimum. Reduced Motion replaces the zigzag, lift, and particle travel with a short fade.
Why this one
Most scratch cards stop at a flat gray mask. This version couples measured grid coverage with a layered copper material, soft-edged removal, physical crumbs, a compact product-ready API, and a coordinated reward celebration—all without reading pixels or updating React state on every gesture frame.
Customization notes
Movement follows gestures, scroll position, or interruptible spring physics. A global duration is not exposed; reduced-motion fades retain their short fixed timing.




