xiaohongshu
xiaohongshu
d3-interaction-animation
Use when creating interactive visualizations with transitions, animations, drag/zoom/brush behaviors, or DOM manipulation. Invoke for data binding with .join(), animated transitions, interactive behaviors, user input handling, or selection operations.
Full skill instructions
Expert knowledge of D3's DOM manipulation, data binding, transitions, and interactive behaviors. Covers selections, data joins, transitions, easing, drag, zoom, brush, timers, and event handling.
Invoke this skill when:
Location: /Users/zach/Documents/cc-skills/docs/d3/
Coverage (280 files):
Selections (55 files):
Transitions (34 files):
Easing (34 files):
Interpolation (30 files):
Drag (12 files):
Zoom (34 files):
Brush (14 files):
Timer (8 files):
Time (45 files):
Time Format (13 files):
Question: "How do I bind data to DOM elements with D3?"
Response:
import * as d3 from 'd3';
// Sample data
let data = [
{ id: 1, value: 30, color: 'red' },
{ id: 2, value: 80, color: 'blue' },
{ id: 3, value: 45, color: 'green' },
];
// Create SVG
const svg = d3.create('svg')
.attr('width', 800)
.attr('height', 400);
// Initial render
function update(data) {
const circles = svg.selectAll('circle')
.data(data, d => d.id) // Key function for object constancy
.join(
// Enter: new elements
enter => enter.append('circle')
.attr('cx', (d, i) => i * 100 + 50)
.attr('cy', 200)
.attr('r', 0) // Start small
.attr('fill', d => d.color)
.call(enter => enter.transition()
.duration(500)
.attr('r', d => d.value / 2)
),
// Update: existing elements
update => update
.call(update => update.transition()
.duration(500)
.attr('r', d => d.value / 2)
.attr('fill', d => d.color)
),
// Exit: removed elements
exit => exit
.call(exit => exit.transition()
.duration(500)
.attr('r', 0)
.remove()
)
);
}
// Initial render
update(data);
// Update after 2 seconds
setTimeout(() => {
data = [
{ id: 1, value: 50, color: 'red' }, // Updated
{ id: 3, value: 60, color: 'yellow' }, // Updated
{ id: 4, value: 70, color: 'purple' }, // New
];
update(data);
}, 2000);
Simple .join() (default behavior):
svg.selectAll('circle')
.data(data)
.join('circle') // Handles enter/update/exit automatically
.attr('cx', (d, i) => i * 100 + 50)
.attr('cy', 200)
.attr('r', d => d.value / 2)
.attr('fill', d => d.color);
Old-style enter/update/exit (verbose):
const circles = svg.selectAll('circle')
.data(data);
// Enter
circles.enter().append('circle')
.attr('r', 0)
.merge(circles) // Merge with update selection
.attr('cx', (d, i) => i * 100 + 50)
.attr('cy', 200)
.transition()
.attr('r', d => d.value / 2);
// Exit
circles.exit().remove();
References:
docs/d3/selection/join.mddocs/d3/selection/data.mdQuestion: "How do I create smooth animated transitions?"
Response:
import * as d3 from 'd3';
const svg = d3.create('svg')
.attr('width', 800)
.attr('height', 400);
const circle = svg.append('circle')
.attr('cx', 50)
.attr('cy', 200)
.attr('r', 20)
.attr('fill', 'steelblue');
// Basic transition
circle.transition()
.duration(2000)
.attr('cx', 750);
// Transition with easing
circle.transition()
.duration(2000)
.ease(d3.easeCubicInOut) // Smooth acceleration/deceleration
.attr('cx', 750);
// Chained transitions
circle.transition()
.duration(1000)
.ease(d3.easeElasticOut) // Bounce effect
.attr('cy', 100)
.transition() // Chain next transition
.duration(1000)
.ease(d3.easeBounceOut)
.attr('cy', 300);
// Transition with delay
circle.transition()
.delay(500)
.duration(1000)
.attr('r', 40);
// Staggered transitions
svg.selectAll('circle')
.data([1, 2, 3, 4, 5])
.join('circle')
.attr('cx', (d, i) => i * 100 + 50)
.attr('cy', 200)
.attr('r', 0)
.transition()
.delay((d, i) => i * 100) // Stagger by 100ms
.duration(500)
.attr('r', 20);
// Named transitions (independent)
circle.transition('move')
.duration(2000)
.attr('cx', 750);
circle.transition('grow') // Runs simultaneously
.duration(1000)
.attr('r', 40);
// Custom tween
circle.transition()
.duration(2000)
.attrTween('r', function() {
const i = d3.interpolate(20, 60);
return t => i(Math.sin(t * Math.PI)); // Pulse in and out
});
// Transition events
circle.transition()
.duration(1000)
.attr('cx', 750)
.on('start', function() { console.log('Started'); })
.on('interrupt', function() { console.log('Interrupted'); })
.on('end', function() { console.log('Ended'); });
// Cancel transition
circle.interrupt(); // Stop current transition
Common Easing Functions:
easeLinear - Constant speedeaseCubicInOut - Smooth start and endeaseElasticOut - Bounce/spring effecteaseBounceOut - Bouncing balleaseExpOut - Exponential decayeaseBackOut - Overshoot and settleReferences:
docs/d3/transition/docs/d3/ease/Question: "How do I implement drag-and-drop?"
Response:
import * as d3 from 'd3';
const svg = d3.create('svg')
.attr('width', 800)
.attr('height', 600);
// Sample data
const nodes = [
{ x: 100, y: 100, r: 30, color: 'red' },
{ x: 200, y: 150, r: 40, color: 'blue' },
{ x: 300, y: 200, r: 25, color: 'green' },
];
// Create drag behavior
const drag = d3.drag()
.on('start', function(event, d) {
d3.select(this)
.raise() // Move to front
.attr('stroke', 'black')
.attr('stroke-width', 3);
})
.on('drag', function(event, d) {
// Update data
d.x = event.x;
d.y = event.y;
// Update position
d3.select(this)
.attr('cx', d.x)
.attr('cy', d.y);
})
.on('end', function(event, d) {
d3.select(this)
.attr('stroke', null);
});
// Draw circles
const circles = svg.selectAll('circle')
.data(nodes)
.join('circle')
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('r', d => d.r)
.attr('fill', d => d.color)
.call(drag); // Apply drag behavior
// Constrained drag (stay in bounds)
const constrainedDrag = d3.drag()
.on('drag', function(event, d) {
const r = d.r;
d.x = Math.max(r, Math.min(800 - r, event.x));
d.y = Math.max(r, Math.min(600 - r, event.y));
d3.select(this)
.attr('cx', d.x)
.attr('cy', d.y);
});
// Drag with container constraint
const containerDrag = d3.drag()
.container(function() {
return this.parentNode; // Use parent as coordinate system
})
.on('drag', function(event, d) {
d3.select(this)
.attr('cx', event.x)
.attr('cy', event.y);
});
// Drag subject (custom hit detection)
const customDrag = d3.drag()
.subject(function(event) {
// Find closest node within 50px
const [mx, my] = d3.pointer(event);
let closest = null;
let minDist = 50;
nodes.forEach(node => {
const dx = node.x - mx;
const dy = node.y - my;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < minDist) {
closest = node;
minDist = dist;
}
});
return closest;
})
.on('drag', function(event) {
if (event.subject) {
event.subject.x = event.x;
event.subject.y = event.y;
updateCircles();
}
});
function updateCircles() {
svg.selectAll('circle')
.attr('cx', d => d.x)
.attr('cy', d => d.y);
}
References:
docs/d3/drag/Question: "How do I add zoom and pan to a visualization?"
Response:
import * as d3 from 'd3';
const width = 800;
const height = 600;
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height);
// Create group for zoomable content
const g = svg.append('g');
// Sample data
const circles = Array.from({ length: 50 }, (_, i) => ({
x: Math.random() * width,
y: Math.random() * height,
r: Math.random() * 20 + 5,
}));
// Draw circles
g.selectAll('circle')
.data(circles)
.join('circle')
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('r', d => d.r)
.attr('fill', 'steelblue')
.attr('opacity', 0.6);
// Create zoom behavior
const zoom = d3.zoom()
.scaleExtent([0.5, 10]) // Min and max zoom
.translateExtent([[0, 0], [width, height]]) // Pan bounds
.on('zoom', (event) => {
// Apply transform to group
g.attr('transform', event.transform);
});
// Apply zoom to SVG
svg.call(zoom);
// Programmatic zoom
function zoomToArea(x, y, width, height) {
svg.transition()
.duration(750)
.call(zoom.transform, d3.zoomIdentity
.translate(400, 300)
.scale(Math.min(8, 0.9 / Math.max(width / 800, height / 600)))
.translate(-x - width / 2, -y - height / 2)
);
}
// Reset zoom
function resetZoom() {
svg.transition()
.duration(750)
.call(zoom.transform, d3.zoomIdentity);
}
// Zoom to fit all circles
function zoomToFit() {
const bounds = g.node().getBBox();
const fullWidth = bounds.width;
const fullHeight = bounds.height;
const midX = bounds.x + fullWidth / 2;
const midY = bounds.y + fullHeight / 2;
const scale = 0.9 / Math.max(fullWidth / width, fullHeight / height);
const translate = [width / 2 - scale * midX, height / 2 - scale * midY];
svg.transition()
.duration(750)
.call(zoom.transform, d3.zoomIdentity
.translate(translate[0], translate[1])
.scale(scale)
);
}
// Zoom on double-click
svg.on('dblclick.zoom', null); // Disable default behavior
svg.on('dblclick', function(event) {
const [x, y] = d3.pointer(event);
svg.transition()
.duration(500)
.call(zoom.scaleBy, 2, [x, y]); // Zoom in 2x at cursor
});
// Zoom with mouse wheel only (disable pan)
const wheelZoom = d3.zoom()
.filter(event => event.type === 'wheel')
.on('zoom', (event) => {
g.attr('transform', event.transform);
});
// Constrain zoom to semantic zoom (update data, not transform)
const semanticZoom = d3.zoom()
.on('zoom', (event) => {
const newRadius = d => d.r * event.transform.k;
g.selectAll('circle')
.attr('r', newRadius);
});
Zoom Events:
zoom.transform - Current transformzoom.scaleBy(k) - Zoom by factor kzoom.scaleTo(k) - Zoom to scale kzoom.translateBy(x, y) - Pan by [x, y]zoom.translateTo(x, y) - Pan to [x, y]References:
docs/d3/zoom/Question: "How do I create a brush for filtering data?"
Response:
import * as d3 from 'd3';
const width = 800;
const height = 600;
const margin = { top: 20, right: 20, bottom: 30, left: 40 };
// Sample data
const data = Array.from({ length: 100 }, () => ({
x: Math.random() * 100,
y: Math.random() * 100,
}));
// Create scales
const xScale = d3.scaleLinear()
.domain([0, 100])
.range([margin.left, width - margin.right]);
const yScale = d3.scaleLinear()
.domain([0, 100])
.range([height - margin.bottom, margin.top]);
// Create SVG
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height);
// Draw points
const circles = svg.append('g')
.selectAll('circle')
.data(data)
.join('circle')
.attr('cx', d => xScale(d.x))
.attr('cy', d => yScale(d.y))
.attr('r', 4)
.attr('fill', 'steelblue')
.attr('opacity', 0.6);
// Create 2D brush
const brush = d3.brush()
.extent([[margin.left, margin.top], [width - margin.right, height - margin.bottom]])
.on('start brush end', function(event) {
if (!event.selection) {
// No selection - reset
circles.attr('fill', 'steelblue').attr('opacity', 0.6);
return;
}
const [[x0, y0], [x1, y1]] = event.selection;
// Highlight selected points
circles.attr('fill', d => {
const cx = xScale(d.x);
const cy = yScale(d.y);
return cx >= x0 && cx <= x1 && cy >= y0 && cy <= y1
? 'red'
: 'steelblue';
})
.attr('opacity', d => {
const cx = xScale(d.x);
const cy = yScale(d.y);
return cx >= x0 && cx <= x1 && cy >= y0 && cy <= y1
? 1
: 0.2;
});
// Get selected data
const selected = data.filter(d => {
const cx = xScale(d.x);
const cy = yScale(d.y);
return cx >= x0 && cx <= x1 && cy >= y0 && cy <= y1;
});
console.log(`Selected ${selected.length} points`);
});
// Add brush to SVG
svg.append('g')
.attr('class', 'brush')
.call(brush);
// 1D brush (for timeline or axis)
const xBrush = d3.brushX()
.extent([[margin.left, 0], [width - margin.right, 50]])
.on('brush', (event) => {
if (event.selection) {
const [x0, x1] = event.selection.map(xScale.invert);
console.log('Selected range:', x0, x1);
}
});
// Programmatic brush
function selectArea(x0, y0, x1, y1) {
svg.select('.brush')
.call(brush.move, [[x0, y0], [x1, y1]]);
}
// Clear brush
function clearBrush() {
svg.select('.brush')
.call(brush.move, null);
}
// Brush with snap-to-grid
const gridBrush = d3.brush()
.on('end', function(event) {
if (!event.selection) return;
const [[x0, y0], [x1, y1]] = event.selection;
const snapped = [
[Math.round(x0 / 10) * 10, Math.round(y0 / 10) * 10],
[Math.round(x1 / 10) * 10, Math.round(y1 / 10) * 10],
];
d3.select(this).call(brush.move, snapped);
});
Brush Types:
brush() - 2D brush (x and y)brushX() - 1D horizontal brushbrushY() - 1D vertical brushReferences:
docs/d3/brush/function render(data) {
const bars = svg.selectAll('rect')
.data(data, d => d.id)
.join('rect')
.attr('x', (d, i) => i * 50)
.attr('y', d => height - d.value)
.attr('width', 40)
.attr('height', d => d.value)
.attr('fill', 'steelblue');
}
const t = svg.transition().duration(750);
circles.transition(t).attr('r', 20);
lines.transition(t).attr('stroke-width', 3);
svg.on('click', function(event) {
const element = event.target;
if (element.tagName === 'circle') {
console.log('Circle clicked:', d3.select(element).datum());
}
});
# Find selection docs
grep -r "select\|selectAll\|join\|data" /Users/zach/Documents/cc-skills/docs/d3/selection/
# Find transition docs
grep -r "transition\|duration\|ease" /Users/zach/Documents/cc-skills/docs/d3/transition/
# Find interaction docs
grep -r "drag\|zoom\|brush" /Users/zach/Documents/cc-skills/docs/d3/drag/ /Users/zach/Documents/cc-skills/docs/d3/zoom/ /Users/zach/Documents/cc-skills/docs/d3/brush/
# Find timer docs
grep -r "timer\|timeout\|interval" /Users/zach/Documents/cc-skills/docs/d3/timer/
# List interaction modules
ls /Users/zach/Documents/cc-skills/docs/d3/selection/
ls /Users/zach/Documents/cc-skills/docs/d3/transition/
Data not updating: Forgot to use key function
.data(data, d => d.id)Transition not smooth: Wrong easing function
easeCubicInOut for smooth start/endDrag not working: Event propagation stopped
.call(drag) is applied correctlyZoom jumps: Transform state out of sync
zoom.transform to get/set stateBrush not visible: Missing CSS or wrong extent
xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience