Skip to content

Animations

AkashJS provides a powerful animation system built on the Web Animations API. From simple property tweens to spring physics and state machines, everything is declarative and composable.

animate()

The animate() function is the core primitive. It wraps the Web Animations API with a convenient interface and returns an AnimationControl handle.

ts
import { animate } from '@akashjs/runtime';

const control = animate(element, {
  properties: {
    opacity: [0, 1],
    transform: ['translateY(20px)', 'translateY(0)'],
  },
  duration: 300,
  easing: 'ease-out',
  fill: 'forwards',
});

AnimationControl

The returned AnimationControl object lets you manage the animation imperatively:

ts
interface AnimationControl {
  play(): void;
  pause(): void;
  cancel(): void;
  reverse(): void;
  finish(): void;
  finished: Promise<void>;
  playState: () => AnimationPlayState;
}

Example usage:

ts
const ctrl = animate(el, {
  properties: { opacity: [1, 0] },
  duration: 500,
});

// Wait for completion
await ctrl.finished;
console.log('Animation done');

// Or control it manually
ctrl.pause();
ctrl.reverse();

Options

OptionTypeDefaultDescription
propertiesRecord<string, [from, to]>--CSS properties to animate
durationnumber300Duration in milliseconds
easingstring'ease'CSS easing function
delaynumber0Delay before starting in ms
fillFillMode'none'How styles apply before/after
iterationsnumber1Number of iterations (Infinity for loop)
directionPlaybackDirection'normal''normal', 'reverse', 'alternate'

animateStagger()

Animate a list of elements with a staggered delay between each one. Great for list entrances.

ts
import { animateStagger } from '@akashjs/runtime';

const items = document.querySelectorAll('.list-item');

animateStagger(items, {
  properties: {
    opacity: [0, 1],
    transform: ['translateY(10px)', 'translateY(0)'],
  },
  duration: 300,
  stagger: 50,         // 50ms delay between each item
  from: 'start',       // 'start' | 'center' | 'end'
});

Stagger from center

When from: 'center' is used, the middle element animates first and the delay fans outward to the edges:

ts
animateStagger(items, {
  properties: { scale: [0.8, 1] },
  duration: 200,
  stagger: 30,
  from: 'center',
});

Stagger from end

Reverse the stagger order so the last item animates first:

ts
animateStagger(items, {
  properties: { opacity: [0, 1] },
  duration: 250,
  stagger: 40,
  from: 'end',
});

animateSequence()

Run multiple animations one after another. Each step waits for the previous to finish before starting.

ts
import { animateSequence } from '@akashjs/runtime';

await animateSequence([
  { element: header, properties: { opacity: [0, 1] }, duration: 300 },
  { element: body,   properties: { opacity: [0, 1] }, duration: 300 },
  { element: footer, properties: { opacity: [0, 1] }, duration: 300 },
]);

Each entry in the array uses the same options as animate(), plus the element field to specify the target.

animateGroup()

Run multiple animations in parallel and wait for all of them to finish.

ts
import { animateGroup } from '@akashjs/runtime';

await animateGroup([
  { element: sidebar, properties: { transform: ['translateX(-100%)', 'translateX(0)'] }, duration: 400 },
  { element: content, properties: { opacity: [0, 1] }, duration: 400 },
]);

This is useful when you want several elements to animate simultaneously but need a single promise that resolves when everything is done.

animateSpring()

Physics-based spring animation. Instead of a fixed duration and easing curve, the motion is controlled by spring parameters.

ts
import { animateSpring } from '@akashjs/runtime';

animateSpring(element, {
  properties: { transform: ['scale(0.5)', 'scale(1)'] },
  stiffness: 300,   // Spring stiffness (default: 170)
  damping: 20,      // Damping ratio (default: 26)
  mass: 1,          // Mass of the object (default: 1)
});

Spring parameters

ParameterDefaultEffect
stiffness170Higher = snappier motion
damping26Higher = less oscillation
mass1Higher = more inertia, slower movement
ts
// Bouncy spring
animateSpring(el, {
  properties: { transform: ['translateY(50px)', 'translateY(0)'] },
  stiffness: 200,
  damping: 10,
  mass: 1,
});

// Stiff, no-bounce spring
animateSpring(el, {
  properties: { transform: ['translateY(50px)', 'translateY(0)'] },
  stiffness: 400,
  damping: 40,
  mass: 1,
});

keyframes()

The keyframes() helper lets you define multi-step animations with offsets, rather than simple from/to pairs.

ts
import { keyframes } from '@akashjs/runtime';

animate(element, {
  properties: keyframes([
    { offset: 0,   opacity: 0, transform: 'translateY(20px)' },
    { offset: 0.5, opacity: 1, transform: 'translateY(-5px)' },
    { offset: 1,   opacity: 1, transform: 'translateY(0)' },
  ]),
  duration: 600,
  easing: 'ease-out',
});

Each keyframe object has an offset (0 to 1) plus any CSS properties. This maps directly to the Web Animations API keyframe format.

Pulse example

ts
animate(button, {
  properties: keyframes([
    { offset: 0,    transform: 'scale(1)' },
    { offset: 0.25, transform: 'scale(1.1)' },
    { offset: 0.5,  transform: 'scale(0.95)' },
    { offset: 1,    transform: 'scale(1)' },
  ]),
  duration: 400,
});

defineStates()

Create a state-based animation machine where transitions between named states are animated automatically.

ts
import { defineStates } from '@akashjs/runtime';

const machine = defineStates(element, {
  states: {
    idle: { opacity: 1, transform: 'scale(1)' },
    hover: { opacity: 1, transform: 'scale(1.05)' },
    pressed: { opacity: 0.9, transform: 'scale(0.98)' },
    disabled: { opacity: 0.5, transform: 'scale(1)' },
  },
  transitions: {
    duration: 200,
    easing: 'ease-in-out',
  },
  initial: 'idle',
});

// Transition to a new state
machine.go('hover');

// Read the current state
console.log(machine.current()); // 'hover'

// Listen for state changes
machine.onTransition((from, to) => {
  console.log(`${from} -> ${to}`);
});

Per-transition overrides

You can override duration and easing for specific state transitions:

ts
const machine = defineStates(element, {
  states: {
    open: { height: 'auto', opacity: 1 },
    closed: { height: '0px', opacity: 0 },
  },
  transitions: {
    duration: 300,
    easing: 'ease-out',
    overrides: {
      'open->closed': { duration: 200, easing: 'ease-in' },
    },
  },
  initial: 'closed',
});

Combining animations

All animation primitives return promises or controls, so you can compose them freely:

ts
// Stagger in, then sequence a highlight
const items = document.querySelectorAll('.card');

await animateStagger(items, {
  properties: { opacity: [0, 1], transform: ['translateY(20px)', 'translateY(0)'] },
  duration: 300,
  stagger: 50,
});

await animateSequence([
  { element: items[0], properties: { background: ['white', '#ffffcc'] }, duration: 200 },
  { element: items[0], properties: { background: ['#ffffcc', 'white'] }, duration: 200 },
]);

Released under the MIT License.