D3.js - 工作示例
D3.js - 工作示例:动画柱状图
Section titled “D3.js - 工作示例:动画柱状图”本章演示如何结合多个 D3.js 概念来创建一个动态柱状图。我们将使用一个示例数据集,加载它,然后渲染一个具有鼠标悬停交互和入场动画的柱状图。
首先,创建一个 population-data.csv 文件(如果上一章中没有的话):
year,population2006,402008,452010,482012,512014,532016,572017,62现在,让我们在一个 HTML 文件中逐步构建这个柱状图。
步骤 1:HTML 结构和基本样式
Section titled “步骤 1:HTML 结构和基本样式”<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>D3 Animated Bar Chart</title> <script src="https://d3js.org/d3.v7.min.js"></script> <style> body { font-family: Arial, sans-serif; } .bar { fill: steelblue; transition: fill 0.2s ease-out; } .bar:hover, .bar.highlight { fill: orange; } .chart-title { font-size: 18px; font-weight: bold; text-anchor: middle; } .axis-label { font-size: 12px; text-anchor: middle; } .axis path, .axis line { fill: none; stroke: #000; shape-rendering: crispEdges; } .axis text { font-family: sans-serif; font-size: 10px; } .tooltip { position: absolute; text-align: center; padding: 8px; font: 12px sans-serif; background: lightsteelblue; border: 0px; border-radius: 8px; pointer-events: none; /* 对于柱子上的鼠标事件至关重要 */ opacity: 0; } </style></head><body> <div id="chart-container"></div> <script> // D3 代码如下 </script></body></html>步骤 2:设置 SVG、边距和尺寸
Section titled “步骤 2:设置 SVG、边距和尺寸”const container = d3.select("#chart-container");
const margin = {top: 60, right: 30, bottom: 70, left: 70};const width = 600 - margin.left - margin.right;const height = 400 - margin.top - margin.bottom;
const svg = container.append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", `translate(${margin.left},${margin.top})`);
// Tooltip divconst tooltip = d3.select("body").append("div") .attr("class", "tooltip");步骤 3:定义比例尺(Scales)
Section titled “步骤 3:定义比例尺(Scales)”X 轴使用 d3.scaleBand()(年份,类别型),Y 轴使用 d3.scaleLinear()(人口,定量型)。
const xScale = d3.scaleBand() .range([0, width]) .padding(0.2); // 柱子之间的间隔
const yScale = d3.scaleLinear() .range([height, 0]); // Y 轴是反向的(0 在顶部)步骤 4:加载和处理数据
Section titled “步骤 4:加载和处理数据”d3.csv("population-data.csv", d => { // 转换数据类型 return { year: d.year, // 将年份保留为字符串用于 scaleBand population: +d.population // 将人口转换为数字 };}).then(data => { // 检查数据 console.log(data);
// 根据数据更新比例尺的域 xScale.domain(data.map(d => d.year)); yScale.domain([0, d3.max(data, d => d.population)]);
// 调用函数绘制图表元素(将在下一步定义) drawChart(data);
}).catch(error => { console.error("加载或处理数据时出错:", error); container.text("未能加载图表数据。");});步骤 5:绘制图表元素(坐标轴和柱子)
Section titled “步骤 5:绘制图表元素(坐标轴和柱子)”function drawChart(data) { // 添加 X 轴 svg.append("g") .attr("class", "axis x-axis") .attr("transform", `translate(0,${height})`) .call(d3.axisBottom(xScale)) .selectAll("text") .style("text-anchor", "end") .attr("dx", "-.8em") .attr("dy", ".15em") .attr("transform", "rotate(-45)");
// 添加 Y 轴 svg.append("g") .attr("class", "axis y-axis") .call(d3.axisLeft(yScale));
// 添加柱子并带有入场动画 svg.selectAll(".bar") .data(data) .join("rect") .attr("class", "bar") .attr("x", d => xScale(d.year)) .attr("width", xScale.bandwidth()) .attr("y", d => yScale(0)) // 为动画效果从 y=0 开始 .attr("height", 0) // 为动画效果从 height=0 开始 .on("mouseover", handleMouseOver) .on("mouseout", handleMouseOut) .transition() .duration(800) .ease(d3.easeBounceOut) // 一个有趣的缓动函数 .delay((d, i) => i * 100) // 交错动画 .attr("y", d => yScale(d.population)) .attr("height", d => height - yScale(d.population));
// 添加图表标题 svg.append("text") .attr("class", "chart-title") .attr("x", width / 2) .attr("y", 0 - (margin.top / 2)) .text("Population Growth Over Years");
// 添加 X 轴标签 svg.append("text") .attr("class", "axis-label") .attr("x", width / 2) .attr("y", height + margin.bottom * 0.8) .text("Year");
// 添加 Y 轴标签 svg.append("text") .attr("class", "axis-label") .attr("transform", "rotate(-90)") .attr("y", 0 - margin.left * 0.8) .attr("x", 0 - (height / 2)) .text("人口 (百万,虚构)");}步骤 6:鼠标事件处理器(Tooltip 和高亮)
Section titled “步骤 6:鼠标事件处理器(Tooltip 和高亮)”function handleMouseOver(event, d) { d3.select(this).classed("highlight", true);
tooltip.transition() .duration(200) .style("opacity", .9); tooltip.html(`年份: ${d.year}<br/>人口: ${d.population}M`) .style("left", (event.pageX + 5) + "px") .style("top", (event.pageY - 28) + "px");}
function handleMouseOut(event, d) { d3.select(this).classed("highlight", false);
tooltip.transition() .duration(500) .style("opacity", 0);}将所有 JavaScript 代码放在 HTML 文件中的 <script> 标签内。在浏览器中打开该文件时,您应该会看到一个柱状图。柱子会动画显示到其位置,并且将鼠标悬停在柱子上会高亮显示该柱子并显示包含其数据的 Tooltip。
关键概念阐述
Section titled “关键概念阐述”- **模块化结构:**分离设置、数据加载和绘图等关注点。
- **边距和 SVG 分组:**使用 D3 的边距惯例进行布局。
- 比例尺(Scales):
d3.scaleBand用于 X 轴的类别型数据,d3.scaleLinear用于 Y 轴的定量型数据。 - **坐标轴(Axes):**使用
d3.axisBottom和d3.axisLeft生成坐标轴。 - **数据加载(
d3.csv):**异步获取和解析 CSV 数据。 - **数据连接(
selection.join):**根据数据创建柱子。 - **过渡和缓动(Transitions and Easings):**使柱子入场时具有动画效果(
.transition()、.duration()、.ease()、.delay())。 - **事件处理(Event Handling):**使用
.on("mouseover", ...)和.on("mouseout", ...)实现交互。 - **Tooltips:**创建一个跟随鼠标的简单 HTML div Tooltip。
此示例提供了一个可靠的模板。您可以通过添加排序、过滤或使用新数据更新图表等功能进一步增强它。