Skip to content

cadam68/react-native-animation-scenario

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

36 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🎬 react-native-animation-scenario

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.


✨ Features

  • βœ… Declarative animation timeline with step-by-step control
  • 🧠 Supports set, move, delay, parallel, callback, vibrate, hold, label, goto, resume, use, ifJump, ifThen and use steps
  • πŸŽ› Works in both "auto" and "manual" step modes
  • πŸ” Loopable scenarios
  • πŸ“¦ Minimal dependencies – just React Native + Animated
  • 🧩 Drop-in TimelineView for debug or dev overlay
  • πŸ” Safe callback and ref validation

⚑ Try it live

Snack demo on Expo


πŸ”₯ What's New in 1.4.3

  • βœ… documentation added.

πŸ”₯ What's New in 1.4.2 Patch Release

  • πŸ›  Fixed an issue where the `stop() step didn’t always correctly interrupt the scenario execution.
  • βœ… Improved overall code structure and performance with internal refactoring.

πŸ”₯ What's New in 1.4.0

  • βœ… 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 like inc(value) or dec(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.

πŸ”₯ What's New in 1.3.0

  • βœ… resume step: jump back to the point after the last goto() call
  • βœ… set step: set a value directly or from a function
  • βœ… stop step: stop and reset the animation
  • βœ… ifJump(condition, labelTrue, labelFalse?) step: jump based on condition
  • πŸ›‘ Label validation for goto and ifJump steps now included in scenario compilation
  • βš™οΈ Cleaned and centralized all validation logic in compileScenario()

πŸ”₯ What's New in 1.2.0

  • βœ… 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() and stop() logic
  • βœ… TimelineView for visual debug
  • βœ… useScreenLifecycle(onFocus, onBlur) helper

πŸ“¦ Installation

npm install react-native-animation-scenario

πŸ“¦ Exports

useAnimationScenario();             // Main hook
defineScenario([]);                 // Safely define your scenario
move(), delay(), callback(), ...    // Step helpers
useScreenLifecycle();               // Hook for screen focus lifecycle

πŸ›  Usage

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>
  );
};

πŸ”§ Step Types

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

🧠 Modes

  • mode: "auto" – steps play sequentially
  • mode: "manual" – steps only run via nextStep()
  • loop: true – repeat the scenario forever

🧩 Reusable Blocks

const blocks = {
bounce: defineScenario([
move("y", -30, 200),
move("y", 0, 200),
]),
};

const scenario = defineScenario([
callback("showStart"),
use("bounce"),
delay(500),
use("bounce"),
]);

πŸ€– Conditional Branching with ifJump()

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>
  );
};

πŸ” ifJump() using a named callback

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,
  },
});

πŸ§ͺ Advanced Flow Example: set, goto, resume, and stop

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>
  );
};

πŸ§ͺ Timeline Debug

Add the TimelineView to display step progress:

<TimelineView /> <!-- Optional -->

Great for development or visual debugging of onboarding flows.


πŸ›‘ Safeguards

  • 🚫 Throws error if an initialValue or callback is missing
  • πŸ”’ All animations use Animated.Value and native drivers by default

🧩 Optional: Screen Lifecycle Hook

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.


πŸ“„ License

MIT – Created by Cyril Adam


πŸ“¦ CHANGELOG for v1.4.2 and v1.4.1

πŸ”§ Fixes

  • Fixed a bug where the stop() step didn't correctly interrupt scenario execution in all cases.
  • Improved internal handling of the shouldStop flag to prevent race conditions and ensure proper reset behavior.
  • reset() now explicitly clears holdResolver and callingStepIndexRef, making the scenario restart more predictable and clean.
  • Minor log improvements under debug mode.

πŸ’‘ Internal Improvements

  • Factored out reusable logic into evalStepValue() and evalStepCondition() to unify async/sync step evaluation across set, ifThen, and callback steps.
  • Jump logic now centralized via jumpTo() utility, ensuring consistent block traversal across ifThen, ifElse, and ifEnd.
  • Internal state handling made more robust for future scalability and debugging ease.
  • Scenario validation (compileScenario) now checks parallel content blocks.

πŸ“œ CHANGELOG for v1.4.0

✨ Added

  • 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 a hold() step.
  • move(...) now supports relative values using inc(x) and dec(x).
  • Extended support for callback() to accept parameters, including dynamic values.
  • Scenario validation (compileScenario) now checks for properly closed ifThen/ifElse/ifEnd blocks.
  • Modular refactoring for better extensibility.

🐞 Fixed

  • Ensured ifJump() does not rely on refs directly from defineScenario().
  • Improved safety and label resolution for nested conditional logic.
  • Fixed corner cases in hold() + jump logic for nextStep().

πŸ§ͺ Testing

  • Jest tests added for malformed conditional structures (ifThen without ifEnd, duplicate ifElse, etc.)
  • Unit tests extended to cover relative moves.

πŸ“œ CHANGELOG for v1.3.1

🐞 Fixed

  • Move useScreenLifecycle to a separate export to avoid dependency issues on Snack

πŸ“œ CHANGELOG for v1.3.0

✨ Added

  • set(target, value): set a ref value directly, from a function, or from an async callback
  • stop(): terminates the current animation sequence cleanly
  • resume(): continues execution from the step after the last goto() jump
  • ifJump(condition, labelTrue, labelFalse?): conditional branching based on a sync or async function or a callback name
  • Scenario validation now checks label existence for goto and ifJump
  • Improved label jump tracking with callingStepIndexRef for `resume()

🐞 Fixed

  • Ensured nextStep() respects the shouldStop flag
  • Cleaned up validation logic (moved to compileScenario)
  • Deduplicated validation errors for clearer debug output

πŸ§ͺ Testing

  • 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.

πŸ“œ CHANGELOG for v1.2.0

✨ Added

  • goto("label") step to jump within the scenario
  • label("name") step to declare jump targets
  • Support for use("blockName") reusable block structure
  • __sourceBlock annotation for debugging block origins
  • Visual timeline label display (TimelineView)
  • Full reset() support restores animated values
  • stop() now halts looping scenarios safely
  • useScreenLifecycle(onFocus, onBlur) hook

🐞 Fixed

  • Scenario would continue running after screen unmount (fixed with shouldStop)
  • reset() didn't clear active animation state properly
  • Label detection and step validation improved

πŸ§ͺ Testing

  • Added Jest tests and modular compileScenario system
  • Internal compilation now flattens nested use() blocks

🀝 Contributing

  • PRs welcome! Especially for:
  • TypeScript types
  • New step types (e.g. spring)
  • Helper tooling

About

Declarative animation scenario hook for React Native

Resources

Stars

Watchers

Forks

Packages

No packages published