A lightweight, declarative animation timeline hook for React Native β perfect for scenarios like onboarding, tutorials, interactive demos, and more. Easily control complex UI animations step-by-step or automatically.
Works with React Native and Expo.
- β Declarative animation timeline with step-by-step control
- π§ Supports
set,move,delay,parallel,callback,vibrate,hold,label,goto,resume,use,ifJump,ifThenandusesteps - π Works in both
"auto"and"manual"step modes - π Loopable scenarios
- π¦ Minimal dependencies β just React Native + Animated
- π§© Drop-in
TimelineViewfor debug or dev overlay - π Safe callback and ref validation
- β documentation added.
- π Fixed an issue where the `stop() step didnβt always correctly interrupt the scenario execution.
- β Improved overall code structure and performance with internal refactoring.
- β
ifThen,ifElse,ifEnd: New block-style conditional logic, familiar to developers. Example:
ifThen(() => score.current > 5),
move("x", 100, 500),
ifElse(),
move("x", -100, 500),
ifEnd()
- β Nested conditionals are supported and validated at compile time.
- β Improved compiler validation to detect malformed blocks or unclosed conditionals.
- β
Relative values in
move()using helper functions likeinc(value)ordec(value). Example:
move("x", inc(50), 500)
- β
Arguments in
callback()steps now supported. - β
Jump to label in
nextStep(label): You can resume or redirect flow from UI or interaction code.
- β
resumestep: jump back to the point after the lastgoto()call - β
setstep: set a value directly or from a function - β
stopstep: stop and reset the animation - β
ifJump(condition, labelTrue, labelFalse?)step: jump based on condition - π‘ Label validation for
gotoandifJumpsteps now included in scenario compilation - βοΈ Cleaned and centralized all validation logic in
compileScenario()
- β
goto("label")step: jump to a specific label in your scenario - β
label("name")step: define named labels - β
Nested reusable blocks with
use("blockName") - β
Full
reset()andstop()logic - β
TimelineViewfor visual debug - β
useScreenLifecycle(onFocus, onBlur)helper
npm install react-native-animation-scenario
useAnimationScenario(); // Main hook
defineScenario([]); // Safely define your scenario
move(), delay(), callback(), ... // Step helpers
useScreenLifecycle(); // Hook for screen focus lifecycle
import {
useAnimationScenario,
defineScenario,
move,
delay,
parallel,
callback,
hold,
} from "react-native-animation-scenario";
const scenario = defineScenario([
move("opacity", 1, 500),
delay(300),
parallel([
move("scale", 1.1, 500),
move("translateY", 50, 500),
]),
hold("waitForTap"),
callback("onFinish"),
]);
const MyComponent = () => {
const {
refs,
start,
reset,
nextStep,
TimelineView,
} = useAnimationScenario({
scenario,
initialValues: {
opacity: 0,
scale: 1,
translateY: 0,
},
callbacks: {
onFinish: () => console.log("Done!"),
},
});
useEffect(() => { start(); }, []);
return (
<Animated.View style={{
opacity: refs.opacity,
transform: [
{ scale: refs.scale },
{ translateY: refs.translateY }
]
}}>
<Text>Hello Animation</Text>
<TimelineView />
</Animated.View>
);
};| Type | Description | Version |
|---|---|---|
move |
Animate a ref value to a target | 1.0 |
delay |
Pause for a given duration | 1.0 |
parallel |
Animate multiple values simultaneously |
1.0 |
callback |
Run external logic (sync or async) | 1.0 |
vibrate |
Trigger haptic feedback | 1.0 |
hold |
Pause animation until nextStep() is called |
1.0 |
label |
Mark jump targets | 1.2 |
goto |
Jump to a label | 1.2 |
use |
Insert a block | 1.2 |
set |
Set a value directly or from a function | 1.3 |
stop |
Stop and reset the animation | 1.3 |
resume |
Continue from the point after last goto() |
1.3 |
ifJump |
Conditionally jump to a label | 1.3 |
ifThen |
Block-style conditional logic | 1.4 |
- mode: "auto" β steps play sequentially
- mode: "manual" β steps only run via nextStep()
- loop: true β repeat the scenario forever
const blocks = {
bounce: defineScenario([
move("y", -30, 200),
move("y", 0, 200),
]),
};
const scenario = defineScenario([
callback("showStart"),
use("bounce"),
delay(500),
use("bounce"),
]);Use ifJump() to dynamically choose the next step at runtime.
const MyComponent = () => {
const directionRef = useRef(1);
const scenario = defineScenario([
hold(), // Wait for user input before branching
ifJump(() => directionRef.current === 1, "moveRight", "moveLeft"),
label("moveRight"),
move("x", 200, 500),
goto("resumePoint"),
label("moveLeft"),
move("x", 0, 500),
label("resumePoint"),
]);
const { refs, start, nextStep } = useAnimationScenario({
scenario,
initialValues: { x: 100 },
});
useEffect(() => { start(); }, []);
return (
<View>
<Animated.View
style={{
width: 50,
height: 50,
backgroundColor: "blue",
transform: [{ translateX: refs.x }],
marginBottom: 20,
}}
/>
<Text>Choose Direction:</Text>
<View style={{ flexDirection: "row", gap: 10 }}>
<Button title="Left" onPress={() => {
directionRef.current = 0;
nextStep();
}} />
<Button title="Right" onPress={() => {
directionRef.current = 1;
nextStep();
}} />
</View>
</View>
);
};You can define reusable branching logic in callbacks:
const scenario = defineScenario([
hold(),
ifJump("checkDirection", "goRight", "goLeft"),
label("goRight"),
move("x", 100, 300),
goto("end"),
label("goLeft"),
move("x", -100, 300),
label("end"),
]);
const { refs, nextStep, start } = useAnimationScenario({
scenario,
initialValues: { x: 0 },
callbacks: {
checkDirection: () => Math.random() > 0.5,
},
});This scenario demonstrates how to use the new set, goto, resume, and stop steps for conditional or non-linear animation flows.
import { useAnimationScenario,defineScenario, label, vibrate, move, goto, stop, set, resume } from "react-native-animation-scenario";
const scenario = defineScenario([
label("start"),
vibrate(),
move("x", 100, 2000),
goto("reverse"), // Jump to reverse path
move("x", 200, 500, "after-resume"), // Will resume here after `resume`
stop(), // Stops the flow here
label("reverse"),
set("x", 0), // Instantly reset position
move("x", -100, 500),
resume(),
]);
const MyComponent = () => {
const {refs,start,TimelineView,} = useAnimationScenario({
scenario,
initialValues: { x: 0, direction: 0 },
callbacks: {
checkDirection: () => {
// Example: dynamically decide to reverse
if (Math.random() > 0.5) return goto("reverse");
},
},
});
return (
<Animated.View style={{ transform: [{ translateX: refs.x }] }} >
<Button title="Start" onPress={start} />
<TimelineView />
</Animated.View>
);
};Add the TimelineView to display step progress:
<TimelineView /> <!-- Optional -->Great for development or visual debugging of onboarding flows.
- π« Throws error if an initialValue or callback is missing
- π All animations use Animated.Value and native drivers by default
This helper hook requires @react-navigation/native and should be imported separately:
import { useScreenLifecycle } from 'react-native-animation-scenario/src/useScreenLifecycle';It allows you to trigger logic when the screen gains or loses focus.
MIT β Created by Cyril Adam
- Fixed a bug where the
stop()step didn't correctly interrupt scenario execution in all cases. - Improved internal handling of the
shouldStopflag to prevent race conditions and ensure proper reset behavior. reset()now explicitly clearsholdResolverandcallingStepIndexRef, making the scenario restart more predictable and clean.- Minor log improvements under debug mode.
- Factored out reusable logic into
evalStepValue()andevalStepCondition()to unify async/sync step evaluation acrossset,ifThen, andcallbacksteps. - Jump logic now centralized via
jumpTo()utility, ensuring consistent block traversal acrossifThen,ifElse, andifEnd. - Internal state handling made more robust for future scalability and debugging ease.
- Scenario validation (
compileScenario) now checksparallelcontent blocks.
ifThen(condition),ifElse(),ifEnd()block structure: familiar conditional logic using nested blocks.nextStep(label): allows you to jump to a specific label manually, even after ahold()step.move(...)now supports relative values usinginc(x)anddec(x).- Extended support for
callback()to accept parameters, including dynamic values. - Scenario validation (
compileScenario) now checks for properly closedifThen/ifElse/ifEndblocks. - Modular refactoring for better extensibility.
- Ensured
ifJump()does not rely on refs directly fromdefineScenario(). - Improved safety and label resolution for nested conditional logic.
- Fixed corner cases in
hold()+ jump logic fornextStep().
- Jest tests added for malformed conditional structures (
ifThenwithoutifEnd, duplicateifElse, etc.) - Unit tests extended to cover relative moves.
- Move useScreenLifecycle to a separate export to avoid dependency issues on Snack
set(target, value): set a ref value directly, from a function, or from an async callbackstop(): terminates the current animation sequence cleanlyresume(): continues execution from the step after the lastgoto()jumpifJump(condition, labelTrue, labelFalse?): conditional branching based on a sync or async function or a callback name- Scenario validation now checks label existence for
gotoandifJump - Improved label jump tracking with
callingStepIndexReffor `resume()
- Ensured
nextStep()respects theshouldStopflag - Cleaned up validation logic (moved to
compileScenario) - Deduplicated validation errors for clearer debug output
- New unit test coverage for scenario compilation including nested `use() blocks and label resolution.
- Some validation moved from runtime to compile-time, simplifying runtime hook logic.
goto("label")step to jump within the scenariolabel("name")step to declare jump targets- Support for
use("blockName")reusable block structure __sourceBlockannotation for debugging block origins- Visual timeline label display (
TimelineView) - Full
reset()support restores animated values stop()now halts looping scenarios safelyuseScreenLifecycle(onFocus, onBlur)hook
- Scenario would continue running after screen unmount (fixed with
shouldStop) reset()didn't clear active animation state properly- Label detection and step validation improved
- Added Jest tests and modular
compileScenariosystem - Internal compilation now flattens nested
use()blocks
- PRs welcome! Especially for:
- TypeScript types
- New step types (e.g. spring)
- Helper tooling