or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

animation-properties.mdcontrol-flow.mdindex.mdselection-operations.mdtiming-control.mdtransition-creation.md
tile.json

control-flow.mddocs/

Control Flow

Advanced methods for transition coordination, event handling, and lifecycle management.

Capabilities

Promise-Based Control

end Method

Returns a promise that resolves when all elements in the transition complete successfully, or rejects if any transition is cancelled or interrupted.

/**
 * Returns a promise that resolves when the transition completes
 * @returns Promise that resolves when all elements finish transitioning
 */
transition.end(): Promise<void>;

Usage Examples:

// Wait for transition to complete
const t = select("circle")
  .transition()
  .attr("r", 100);

t.end().then(() => {
  console.log("Transition completed!");
}).catch(() => {
  console.log("Transition was interrupted");
});

// Sequential animations with async/await
async function animateSequence() {
  await select(".item1").transition().attr("x", 100).end();
  await select(".item2").transition().attr("x", 100).end();
  console.log("All animations complete");
}

// Parallel animations
const t1 = select(".group1").transition().attr("opacity", 0).end();
const t2 = select(".group2").transition().attr("opacity", 1).end();

Promise.all([t1, t2]).then(() => {
  console.log("Both transitions finished");
});

// Handle interruption
select("button").on("click", function() {
  const transition = select("circle").transition().attr("r", 50);
  
  transition.end()
    .then(() => console.log("Animation completed normally"))
    .catch(() => console.log("Animation was interrupted"));
    
  // Later interruption
  setTimeout(() => {
    select("circle").interrupt();
  }, 100);
});

Event Handling

on Method

Adds or removes event listeners for transition lifecycle events.

/**
 * Adds or removes event listeners for transition events
 * @param typenames - Event type names (space-separated)
 * @param listener - Event listener function, or null to remove
 * @returns The transition instance for chaining, or current listener if no listener provided
 */
transition.on(typenames: string, listener?: EventListener): Transition | EventListener;

Event Types:

  • start - Fired when transition starts
  • end - Fired when transition ends normally
  • interrupt - Fired when transition is interrupted
  • cancel - Fired when transition is cancelled

Usage Examples:

// Basic event handling
transition
  .on("start", function(d, i) {
    console.log("Transition started on element", i);
  })
  .on("end", function(d, i) {
    console.log("Transition ended on element", i);
  })
  .attr("r", 20);

// Multiple event types
transition.on("start end", function() {
  console.log("Transition event:", d3.event.type);
});

// Named event listeners (allows multiple listeners per type)
transition
  .on("start.log", function() { console.log("Started"); })
  .on("start.highlight", function() { this.style.stroke = "red"; })
  .on("end.log", function() { console.log("Ended"); })
  .on("end.cleanup", function() { this.style.stroke = null; });

// Remove specific listener
transition.on("start.log", null);

// Remove all listeners for a type
transition.on("start", null);

// Get current listener
const startListener = transition.on("start");

// Interruption handling
transition
  .on("interrupt", function() {
    console.log("Animation was interrupted");
    // Cleanup code here
  })
  .on("cancel", function() {
    console.log("Animation was cancelled");
  });

Chained Transitions

transition Method

Creates a new transition that starts when the current transition ends, inheriting timing configuration.

/**
 * Creates a chained transition that starts when current transition ends
 * @returns New transition scheduled to start after current transition
 */
transition.transition(): Transition;

Usage Examples:

// Sequential animations
select("circle")
  .transition()
    .duration(500)
    .attr("cx", 100)
  .transition()        // Starts after first transition
    .duration(750)
    .attr("cy", 100)
  .transition()        // Starts after second transition  
    .attr("fill", "red");

// Complex sequence with different timings
select(".element")
  .transition()
    .duration(300)
    .ease(d3.easeLinear)
    .style("opacity", 0)
  .transition()
    .duration(0)       // Immediate
    .style("display", "none")
    .style("transform", "scale(0)")
  .transition()
    .delay(500)
    .style("display", "block")
  .transition()
    .duration(400)
    .ease(d3.easeBounceOut)
    .style("opacity", 1)
    .style("transform", "scale(1)");

// Infinite animation loop
function pulse() {
  select("circle")
    .transition()
    .duration(1000)
    .attr("r", 20)
    .transition()
    .duration(1000)
    .attr("r", 10)
    .on("end", pulse);  // Restart the animation
}
pulse();

Selection Conversion

selection Method

Returns the d3-selection corresponding to this transition.

/**
 * Returns the selection corresponding to this transition
 * @returns d3-selection Selection instance
 */
transition.selection(): Selection;

Usage Examples:

// Convert transition to selection for immediate operations
const t = selectAll("circle").transition();

// Immediate operations on selection
t.selection()
  .classed("animating", true)
  .attr("data-transition-id", "pulse");

// Then continue with transition
t.attr("r", 15);

// Mixed immediate and animated changes
const selection = selectAll(".item");
const transition = selection.transition();

// Immediate changes
transition.selection()
  .style("border", "1px solid red")
  .classed("transitioning", true);

// Animated changes
transition
  .style("transform", "translateX(100px)")
  .on("end", function() {
    // Remove class when done
    select(this).classed("transitioning", false);
  });

Utility Methods

each Method

Invokes a function for each element in the transition.

/**
 * Invokes a function for each element in the transition
 * @param function - Function to invoke for each element
 * @returns The transition instance for chaining
 */
transition.each(function: EachFunction): Transition;

call Method

Invokes a function once with the transition as the first argument, enabling reusable transition configurations.

/**
 * Invokes a function with the transition and optional arguments
 * @param function - Function to invoke with transition
 * @param args - Additional arguments to pass to function
 * @returns The transition instance for chaining
 */
transition.call(function: CallFunction, ...args: any[]): Transition;

Usage Examples:

// each: perform action on each element
transition.each(function(d, i) {
  console.log("Element", i, "data:", d);
  // 'this' is the DOM element
  this.dataset.index = i;
});

// call: reusable transition configurations
function fadeAndSlide(transition, direction, distance) {
  return transition
    .style("opacity", 0)
    .style("transform", `translate${direction}(${distance}px)`);
}

// Use the reusable function
selectAll(".item")
  .transition()
  .call(fadeAndSlide, "X", 100)
  .duration(500);

// Multiple reusable functions
function setTiming(transition, duration, delay) {
  return transition.duration(duration).delay(delay);
}

function setEasing(transition, easeFunction) {
  return transition.ease(easeFunction);
}

selectAll("circle")
  .transition()
  .call(setTiming, 1000, 200)
  .call(setEasing, d3.easeBounceOut)
  .attr("r", 20);

Node and Selection Utility Methods

/**
 * Utility methods inherited from d3-selection
 */

// Returns true if transition contains no elements
transition.empty(): boolean;

// Returns array of all elements in transition
transition.nodes(): Element[];

// Returns first element in transition
transition.node(): Element | null;

// Returns total number of elements in transition
transition.size(): number;

Advanced Control Flow Patterns

Disco Mode (Infinite Loop)

selectAll("circle").transition()
  .delay((d, i) => i * 50)
  .on("start", function repeat() {
    active(this)
      .style("fill", "red")
      .transition()
      .style("fill", "green")
      .transition()
      .style("fill", "blue")
      .transition()
      .on("start", repeat);
  });

State Machine Animation

const states = ["idle", "hover", "active", "disabled"];
let currentState = 0;

function nextState() {
  currentState = (currentState + 1) % states.length;
  const state = states[currentState];
  
  select(".button")
    .transition()
    .duration(300)
    .attr("class", `button ${state}`)
    .on("end", () => {
      setTimeout(nextState, 1000);
    });
}

nextState();

Synchronized Multi-Element Animation

const masterTransition = transition().duration(2000);

// All elements synchronized to same timeline
selectAll(".group1").transition(masterTransition)
  .attr("transform", "translate(100, 0)");

selectAll(".group2").transition(masterTransition)
  .attr("opacity", 0.5);

selectAll(".group3").transition(masterTransition)
  .style("fill", "red");

// All finish at the same time
masterTransition.end().then(() => {
  console.log("All synchronized animations complete");
});

Function Signatures

type EventListener = (this: Element, d: any, i: number, group: Element[]) => void;
type EachFunction = (this: Element, d: any, i: number, group: Element[]) => void;
type CallFunction = (transition: Transition, ...args: any[]) => void;

Event Lifecycle

  1. Creation: Transition is created but not yet scheduled
  2. Scheduling: Transition is scheduled for execution
  3. Start: start event fired, tweens initialized
  4. Active: Tweens execute each frame
  5. End: end event fired (normal completion) or interrupt/cancel events fired