or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

animatable.mdanimation.mddraggable.mdeasings.mdengine.mdindex.mdscope.mdscroll.mdsvg.mdtext.mdtimeline.mdtimer.mdutilities.mdwaapi.md
tile.json

scroll.mddocs/

Scroll

Scroll-based animation observer system for triggering and synchronizing animations based on scroll position with precise viewport control.

Capabilities

onScroll Function

Create scroll-based animation observer with automatic viewport detection and animation synchronization.

/**
 * Create scroll observer for triggering animations on scroll
 * @param parameters - Scroll observer configuration
 * @returns ScrollObserver instance
 */
function onScroll(parameters?: ScrollObserverParams): ScrollObserver;

Usage Examples:

import { onScroll, animate } from "animejs";

// Trigger animation when element enters viewport
onScroll({
  target: ".box",
  onEnter: (self) => {
    animate(self.target, {
      opacity: [0, 1],
      translateY: [50, 0],
    });
  },
});

// Sync animation progress to scroll position
const animation = animate(
  ".header",
  {
    opacity: [1, 0],
    translateY: [0, -100],
  },
  { autoplay: false }
);

onScroll({
  target: ".header",
  sync: true,
  linked: animation,
});

ScrollObserver Class

Observe scroll position and control animation playback based on viewport visibility.

/**
 * ScrollObserver class for scroll-based animation control
 */
class ScrollObserver {
  // ===== Properties =====

  /** Unique observer identifier */
  id: string | number;

  /** Scroll container element */
  container: ScrollContainer;

  /** Target element being observed */
  target: HTMLElement;

  /** Linked animation or timer */
  linked: Tickable | WAAPIAnimation | null;

  /** Whether target is currently in viewport */
  isInView: boolean;

  /** Whether observer has completed */
  completed: boolean;

  // ===== Getters =====

  /**
   * Scroll progress through viewport (0-1)
   * 0 = element at enter threshold
   * 1 = element at leave threshold
   */
  progress: number;

  /**
   * Current scroll position in pixels
   */
  scroll: number;

  /**
   * Scroll velocity in pixels per frame
   * Positive = scrolling down/right
   * Negative = scrolling up/left
   */
  velocity: number;

  /**
   * Whether scrolling backward (up or left)
   */
  backward: boolean;

  // ===== Configuration Properties =====

  /**
   * Synchronize linked animation to scroll progress
   * When true, animation progress matches scroll progress
   */
  sync: boolean;

  /**
   * Repeat callbacks when re-entering viewport
   * When false, callbacks only fire once
   */
  repeat: boolean;

  /**
   * Observe horizontal scroll instead of vertical
   */
  horizontal: boolean;

  /**
   * Enter threshold - when element enters viewport
   * Number: pixels from container edge
   * String: percentage "50%", "top", "center", "bottom"
   * Array: [element position, container position]
   */
  enter: ScrollThresholdParam;

  /**
   * Leave threshold - when element leaves viewport
   * Same format as enter
   */
  leave: ScrollThresholdParam;

  // ===== Callbacks =====

  /**
   * Called when target enters viewport
   * Fires in both directions unless using directional callbacks
   */
  onEnter: Callback<ScrollObserver>;

  /**
   * Called when target leaves viewport
   * Fires in both directions unless using directional callbacks
   */
  onLeave: Callback<ScrollObserver>;

  /**
   * Called when entering viewport while scrolling forward (down/right)
   */
  onEnterForward: Callback<ScrollObserver>;

  /**
   * Called when leaving viewport while scrolling forward
   */
  onLeaveForward: Callback<ScrollObserver>;

  /**
   * Called when entering viewport while scrolling backward (up/left)
   */
  onEnterBackward: Callback<ScrollObserver>;

  /**
   * Called when leaving viewport while scrolling backward
   */
  onLeaveBackward: Callback<ScrollObserver>;

  /**
   * Called on every scroll update while in range
   */
  onUpdate: Callback<ScrollObserver>;

  /**
   * Called when synchronized animation completes
   */
  onSyncComplete: Callback<ScrollObserver>;

  // ===== Methods =====

  /**
   * Link animation to scroll progress
   * @param animation - Animation or timer to link
   * @returns ScrollObserver instance for chaining
   */
  link(animation: Tickable | WAAPIAnimation): ScrollObserver;

  /**
   * Refresh observer bounds and calculations
   * Call after layout changes or element moves
   * @returns ScrollObserver instance for chaining
   */
  refresh(): ScrollObserver;

  /**
   * Show debug overlay with threshold lines
   * @returns ScrollObserver instance for chaining
   */
  debug(): ScrollObserver;

  /**
   * Remove debug overlay
   * @returns ScrollObserver instance for chaining
   */
  removeDebug(): ScrollObserver;

  /**
   * Revert observer and cleanup
   * Removes all event listeners and linked animations
   * @returns ScrollObserver instance for chaining
   */
  revert(): ScrollObserver;
}

Usage Examples:

import { onScroll, animate } from "animejs";

// Basic enter/leave callbacks
onScroll({
  target: ".fade-in",
  enter: "50%", // Enter when element is 50% into viewport
  onEnter: (self) => {
    animate(self.target, {
      opacity: [0, 1],
      translateY: [100, 0],
    });
  },
});

// Directional callbacks
onScroll({
  target: ".section",
  onEnterForward: (self) => {
    console.log("Scrolling down into view");
  },
  onEnterBackward: (self) => {
    console.log("Scrolling up into view");
  },
  onLeaveForward: (self) => {
    console.log("Scrolled past going down");
  },
  onLeaveBackward: (self) => {
    console.log("Scrolled past going up");
  },
});

// Sync animation to scroll
const anim = animate(
  ".parallax",
  {
    translateY: [0, 200],
  },
  { autoplay: false }
);

onScroll({
  target: ".parallax",
  sync: true,
  linked: anim,
  onUpdate: (self) => {
    console.log(`Scroll progress: ${self.progress}`);
  },
});

Scroll Threshold Configuration

Flexible threshold configuration for precise viewport control:

/**
 * Scroll threshold parameter formats
 */
type ScrollThresholdParam =
  | number // Pixels from container edge: 100
  | string // Percentage or keyword: "50%", "top", "center", "bottom"
  | [string | number, string | number]; // [element position, container position]

Examples:

import { onScroll } from "animejs";

// Pixel offset from top
onScroll({
  target: ".box",
  enter: 100, // Enter when 100px from top of viewport
  leave: -100, // Leave when 100px past top
});

// Percentage of viewport
onScroll({
  target: ".box",
  enter: "75%", // Enter when 75% down viewport
  leave: "25%", // Leave when 25% down viewport
});

// Keywords
onScroll({
  target: ".box",
  enter: "bottom", // Enter when element bottom touches viewport bottom
  leave: "top", // Leave when element top leaves viewport top
});

// Precise alignment: [element position, container position]
onScroll({
  target: ".box",
  enter: ["top", "bottom"], // Element top enters at container bottom
  leave: ["bottom", "top"], // Element bottom leaves at container top
});

onScroll({
  target: ".box",
  enter: ["center", "center"], // Element center at viewport center
  leave: ["center", "top"], // Element center reaches viewport top
});

// Mix formats
onScroll({
  target: ".box",
  enter: ["top", "80%"], // Element top enters at 80% down viewport
  leave: [100, "top"], // 100px of element past viewport top
});

Synchronized Scroll Animations

Link animation progress to scroll position:

import { onScroll, animate } from "animejs";

// Create animation without autoplay
const animation = animate(
  ".parallax-bg",
  {
    translateY: [0, -500],
    scale: [1, 1.2],
  },
  { autoplay: false }
);

// Sync to scroll
const observer = onScroll({
  target: ".parallax-section",
  sync: true,
  linked: animation,
  enter: "top bottom", // Start when section top hits viewport bottom
  leave: "bottom top", // End when section bottom hits viewport top
});

// Animation progress now matches scroll progress
// progress: 0 at enter threshold
// progress: 1 at leave threshold

Scroll Progress Tracking

Track scroll progress and state:

const observer = onScroll({
  target: ".section",
  onUpdate: (self) => {
    console.log(`Progress: ${self.progress}`); // 0-1 through viewport
    console.log(`Scroll: ${self.scroll}px`); // Scroll position
    console.log(`Velocity: ${self.velocity}`); // Scroll speed
    console.log(`Backward: ${self.backward}`); // Scroll direction
    console.log(`In view: ${self.isInView}`); // Visibility state
  },
});

Repeating Scroll Animations

Control whether animations repeat on each entry:

// Fire once, don't repeat
onScroll({
  target: ".animate-once",
  repeat: false,
  onEnter: (self) => {
    animate(self.target, { opacity: [0, 1] });
  },
});

// Fire every time entering viewport
onScroll({
  target: ".animate-always",
  repeat: true,
  onEnter: (self) => {
    animate(self.target, { opacity: [0, 1] });
  },
  onLeave: (self) => {
    animate(self.target, { opacity: [1, 0] });
  },
});

Horizontal Scroll

Observe horizontal scroll position:

onScroll({
  target: ".horizontal-section",
  container: ".horizontal-scroller", // Custom scroll container
  horizontal: true,
  enter: "left right", // Enter from right side
  leave: "right left", // Leave from left side
  onEnter: (self) => {
    animate(self.target, {
      opacity: [0, 1],
      translateX: [100, 0],
    });
  },
});

Custom Scroll Container

Observe scroll in specific container:

onScroll({
  target: ".item",
  container: ".scroll-container", // Custom scroll container
  enter: "top bottom",
  leave: "bottom top",
  onEnter: (self) => {
    animate(self.target, { scale: [0.8, 1] });
  },
});

Linking Animations After Creation

Link animations to observer dynamically:

const observer = onScroll({
  target: ".element",
  sync: true,
});

// Create and link animation later
const animation = animate(
  ".element",
  {
    rotate: 360,
    scale: [1, 1.5],
  },
  { autoplay: false }
);

observer.link(animation);

Debugging Scroll Observers

Visual debugging of scroll thresholds:

const observer = onScroll({
  target: ".section",
  enter: "top 80%",
  leave: "bottom 20%",
});

// Show debug overlay with threshold lines
observer.debug();

// Remove debug overlay
observer.removeDebug();

Refreshing Observer Bounds

Update observer after layout changes:

const observer = onScroll({
  target: ".dynamic-content",
  sync: true,
  linked: animation,
});

// After content loads or layout changes
loadContent().then(() => {
  observer.refresh(); // Recalculate bounds and positions
});

// Window resize
window.addEventListener("resize", () => {
  observer.refresh();
});

Scroll Container Management

Global scroll container registry:

/**
 * Map of all scroll containers
 * Automatically manages scroll event listeners
 */
const scrollContainers: Map<Element | Window, ScrollContainer>;

Usage Example:

import { scrollContainers } from "animejs/events";

// Access scroll containers
for (const [element, container] of scrollContainers) {
  console.log(element, container);
}

Advanced Scroll Patterns

Parallax Effect

const background = animate(
  ".bg",
  { translateY: [0, -300] },
  { autoplay: false }
);
const foreground = animate(
  ".fg",
  { translateY: [0, -600] },
  { autoplay: false }
);

onScroll({
  target: ".parallax-section",
  sync: true,
  linked: background,
});
onScroll({
  target: ".parallax-section",
  sync: true,
  linked: foreground,
});

Sticky Header with Fade

const header = document.querySelector(".header");

onScroll({
  target: document.body,
  enter: 0,
  leave: 200,
  onUpdate: (self) => {
    header.style.opacity = 1 - self.progress;
  },
});

Sequential Reveals

document.querySelectorAll(".reveal").forEach((el, i) => {
  onScroll({
    target: el,
    enter: "top 80%",
    onEnter: () => {
      animate(el, {
        opacity: [0, 1],
        translateY: [50, 0],
        delay: i * 100, // Stagger based on index
      });
    },
  });
});

Scrubbing Timeline

import { createTimeline, onScroll } from "animejs";

const tl = createTimeline({ autoplay: false });
tl.add(".box1", { x: 100 })
  .add(".box2", { y: 100 })
  .add(".box3", { rotate: 360 });

onScroll({
  target: ".timeline-section",
  sync: true,
  linked: tl,
  enter: "top bottom",
  leave: "bottom top",
});

Types

/**
 * Scroll observer configuration parameters
 */
interface ScrollObserverParams {
  /** Observer identifier */
  id?: string | number;

  /** Target element to observe (required if not linking existing animation) */
  target?: string | HTMLElement;

  /** Scroll container (defaults to window) */
  container?: string | HTMLElement;

  /** Animation to link to scroll progress */
  linked?: Tickable | WAAPIAnimation;

  /** Synchronize animation to scroll progress */
  sync?: boolean;

  /** Repeat callbacks on re-entry */
  repeat?: boolean;

  /** Observe horizontal scroll */
  horizontal?: boolean;

  /** Enter viewport threshold */
  enter?: ScrollThresholdParam;

  /** Leave viewport threshold */
  leave?: ScrollThresholdParam;

  /** Called when entering viewport */
  onEnter?: Callback<ScrollObserver>;

  /** Called when leaving viewport */
  onLeave?: Callback<ScrollObserver>;

  /** Called when entering while scrolling forward */
  onEnterForward?: Callback<ScrollObserver>;

  /** Called when leaving while scrolling forward */
  onLeaveForward?: Callback<ScrollObserver>;

  /** Called when entering while scrolling backward */
  onEnterBackward?: Callback<ScrollObserver>;

  /** Called when leaving while scrolling backward */
  onLeaveBackward?: Callback<ScrollObserver>;

  /** Called on scroll update */
  onUpdate?: Callback<ScrollObserver>;

  /** Called when synchronized animation completes */
  onSyncComplete?: Callback<ScrollObserver>;
}

/**
 * Scroll threshold parameter
 */
type ScrollThresholdParam =
  | number // Pixels from edge
  | string // Percentage or keyword
  | [string | number, string | number]; // [element pos, container pos]

/**
 * Scroll container wrapper
 */
interface ScrollContainer {
  element: Element | Window;
  scrollTop: number;
  scrollLeft: number;
  scrollHeight: number;
  scrollWidth: number;
  clientHeight: number;
  clientWidth: number;
}

/**
 * Callback function type
 */
type Callback<T> = (instance: T) => void;