5.2 KiB
5.2 KiB
Common D3 Patterns - Code Templates
Bar Chart Template
const data = [{label: 'A', value: 30}, {label: 'B', value: 80}, {label: 'C', value: 45}];
const xScale = d3.scaleBand()
.domain(data.map(d => d.label))
.range([0, width])
.padding(0.1);
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([height, 0]);
svg.selectAll('rect')
.data(data)
.join('rect')
.attr('x', d => xScale(d.label))
.attr('y', d => yScale(d.value))
.attr('width', xScale.bandwidth())
.attr('height', d => height - yScale(d.value))
.attr('fill', 'steelblue');
svg.append('g').attr('transform', `translate(0, ${height})`).call(d3.axisBottom(xScale));
svg.append('g').call(d3.axisLeft(yScale));
Line Chart Template
const parseDate = d3.timeParse('%Y-%m-%d');
data.forEach(d => { d.date = parseDate(d.date); });
const xScale = d3.scaleTime()
.domain(d3.extent(data, d => d.date))
.range([0, width]);
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([height, 0]);
const line = d3.line()
.x(d => xScale(d.date))
.y(d => yScale(d.value));
svg.append('path')
.datum(data)
.attr('d', line)
.attr('fill', 'none')
.attr('stroke', 'steelblue')
.attr('stroke-width', 2);
svg.append('g').attr('transform', `translate(0, ${height})`).call(d3.axisBottom(xScale));
svg.append('g').call(d3.axisLeft(yScale));
Scatter Plot Template
const xScale = d3.scaleLinear()
.domain(d3.extent(data, d => d.x))
.range([0, width]);
const yScale = d3.scaleLinear()
.domain(d3.extent(data, d => d.y))
.range([height, 0]);
const colorScale = d3.scaleOrdinal(d3.schemeCategory10);
svg.selectAll('circle')
.data(data)
.join('circle')
.attr('cx', d => xScale(d.x))
.attr('cy', d => yScale(d.y))
.attr('r', 5)
.attr('fill', d => colorScale(d.category))
.attr('opacity', 0.7);
Network Graph Template
const simulation = d3.forceSimulation(nodes)
.force('link', d3.forceLink(links).id(d => d.id))
.force('charge', d3.forceManyBody().strength(-100))
.force('center', d3.forceCenter(width / 2, height / 2));
const link = svg.selectAll('line')
.data(links)
.join('line')
.attr('stroke', '#999');
const node = svg.selectAll('circle')
.data(nodes)
.join('circle')
.attr('r', 10)
.attr('fill', d => d3.schemeCategory10[d.group]);
simulation.on('tick', () => {
link.attr('x1', d => d.source.x).attr('y1', d => d.source.y)
.attr('x2', d => d.target.x).attr('y2', d => d.target.y);
node.attr('cx', d => d.x).attr('cy', d => d.y);
});
Treemap Template
const root = d3.hierarchy(data)
.sum(d => d.value)
.sort((a, b) => b.value - a.value);
const treemap = d3.treemap().size([width, height]).padding(1);
treemap(root);
svg.selectAll('rect')
.data(root.leaves())
.join('rect')
.attr('x', d => d.x0)
.attr('y', d => d.y0)
.attr('width', d => d.x1 - d.x0)
.attr('height', d => d.y1 - d.y0)
.attr('fill', d => d3.interpolateBlues(d.value / root.value));
svg.selectAll('text')
.data(root.leaves())
.join('text')
.attr('x', d => d.x0 + 5)
.attr('y', d => d.y0 + 15)
.text(d => d.data.name);
Geographic Map Template
d3.json('world.geojson').then(geojson => {
const projection = d3.geoMercator()
.fitExtent([[0, 0], [width, height]], geojson);
const path = d3.geoPath().projection(projection);
svg.selectAll('path')
.data(geojson.features)
.join('path')
.attr('d', path)
.attr('fill', '#ccc')
.attr('stroke', '#fff');
});
Zoom and Pan Pattern
const zoom = d3.zoom()
.scaleExtent([0.5, 5])
.on('zoom', (event) => g.attr('transform', event.transform));
svg.call(zoom);
const g = svg.append('g');
g.selectAll('circle').data(data).join('circle')...
Brush Selection Pattern
const brush = d3.brush()
.extent([[0, 0], [width, height]])
.on('end', (event) => {
if (!event.selection) return;
const [[x0, y0], [x1, y1]] = event.selection;
const selected = data.filter(d => {
const x = xScale(d.x), y = yScale(d.y);
return x >= x0 && x <= x1 && y >= y0 && y <= y1;
});
updateChart(selected);
});
svg.append('g').attr('class', 'brush').call(brush);
Transition Pattern
function update(newData) {
yScale.domain([0, d3.max(newData, d => d.value)]);
svg.selectAll('rect')
.data(newData)
.join('rect')
.transition().duration(750).ease(d3.easeCubicOut)
.attr('y', d => yScale(d.value))
.attr('height', d => height - yScale(d.value));
svg.select('.y-axis').transition().duration(750).call(d3.axisLeft(yScale));
}
Tooltip Pattern
const tooltip = d3.select('body').append('div')
.attr('class', 'tooltip')
.style('position', 'absolute')
.style('visibility', 'hidden');
svg.selectAll('circle')
.data(data)
.join('circle')
.attr('r', 5)
.on('mouseover', (event, d) => {
tooltip.style('visibility', 'visible').html(`Value: ${d.value}`);
})
.on('mousemove', (event) => {
tooltip.style('top', (event.pageY - 10) + 'px')
.style('left', (event.pageX + 10) + 'px');
})
.on('mouseout', () => tooltip.style('visibility', 'hidden'));