CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-d3-transition

Animated transitions for D3 selections with smooth interpolation between DOM states over time.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

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

Install with Tessl CLI

npx tessl i tessl/npm-d3-transition

docs

animation-properties.md

control-flow.md

index.md

selection-operations.md

timing-control.md

transition-creation.md

tile.json