DevToolBox免费
博客

D3.js 完全指南:Web 数据驱动可视化

22 min read作者 DevToolBox Team
TL;DRD3.js 是最强大的 Web 数据可视化库,通过将数据绑定到 DOM 元素来创建动态交互式图表。使用 select/selectAll 选择元素,用 data/enter/exit/join 绑定数据,用 scaleLinear/scaleBand/scaleTime 映射数据到像素,用 transition 添加动画。在 React 中通过 useRef + useEffect 集成 D3。支持 SVG 柱状图、折线图、饼图、地图和力导向图。
关键要点
  • D3 通过 select/selectAll 操作 DOM,用 data() 将数组绑定到元素
  • enter/exit/join 模式处理新增、更新和删除的数据元素
  • 比例尺(scales)是核心:scaleLinear 用于连续数据,scaleBand 用于类别
  • 使用 transition() 和 duration() 添加平滑动画效果
  • 在 React 中使用 useRef 获取容器引用,useEffect 中运行 D3 代码
  • SVG viewBox 属性实现响应式图表,配合 ResizeObserver 动态调整
  • D3-geo 支持 GeoJSON 地图,d3-force 支持力导向网络图

1. 什么是 D3.js

D3.js(Data-Driven Documents)是一个用于创建数据驱动的交互式可视化的 JavaScript 库。由 Mike Bostock 于 2011 年创建,它不是一个图表库——而是一个低级别的可视化工具集,让你完全控制从数据到像素的每一步转换。D3 使用 SVG、HTML 和 CSS 渲染可视化,在所有现代浏览器中都能运行。

与 Chart.js 或 Recharts 等高级图表库不同,D3 不提供预制的图表组件。它提供的是构建任何可视化所需的基本工具:DOM 操作、数据绑定、比例尺、形状生成器、布局算法和过渡动画。这种灵活性使 D3 成为 New York Times、Observable 和数千个数据可视化项目的首选。

2. 安装与设置

通过 npm 安装

npm install d3
# or
yarn add d3

# TypeScript types
npm install @types/d3 --save-dev

导入方式

// Import everything
import * as d3 from "d3";

// Import only what you need (smaller bundle)
import { select, selectAll } from "d3-selection";
import { scaleLinear, scaleBand } from "d3-scale";
import { axisBottom, axisLeft } from "d3-axis";
import { line, area, pie, arc } from "d3-shape";
import { transition } from "d3-transition";

CDN 方式

<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
<script>
  // d3 is available as a global
  d3.select("body").append("h1").text("Hello D3!");
</script>

3. 选择集:select 与 selectAll

选择集是 D3 的核心。d3.select() 选择第一个匹配的 DOM 元素,d3.selectAll() 选择所有匹配元素。选择集支持链式调用来修改属性、样式和内容。

// Select a single element
const svg = d3.select("#chart");

// Select all matching elements
const circles = d3.selectAll("circle");

// Chain attribute and style modifications
d3.selectAll("rect")
  .attr("width", 50)
  .attr("height", 30)
  .attr("fill", "#3b82f6")
  .style("opacity", 0.8);

// Append new elements
d3.select("#chart")
  .append("svg")
  .attr("width", 600)
  .attr("height", 400)
  .append("circle")
  .attr("cx", 100)
  .attr("cy", 100)
  .attr("r", 40)
  .attr("fill", "tomato");

4. 数据绑定:data / enter / exit / join

数据绑定是 D3 最强大的特性。data() 方法将数组与 DOM 元素关联。当数据项多于元素时,enter() 处理新增项;当元素多于数据时,exit() 处理删除项;update 选择集处理已有元素的更新。

经典 enter/exit 模式

const data = [10, 20, 35, 50, 25];

// Select all rects (initially none exist)
const bars = svg.selectAll("rect")
  .data(data);

// Enter: create new elements for new data
bars.enter()
  .append("rect")
  .attr("x", (d, i) => i * 60)
  .attr("y", (d) => 200 - d * 3)
  .attr("width", 50)
  .attr("height", (d) => d * 3)
  .attr("fill", "#3b82f6");

// Exit: remove elements without data
bars.exit().remove();

现代 join 方法(D3 v5+)

const data = [10, 20, 35, 50, 25];

// join() handles enter, update, and exit in one call
svg.selectAll("rect")
  .data(data)
  .join("rect")
  .attr("x", (d, i) => i * 60)
  .attr("y", (d) => 200 - d * 3)
  .attr("width", 50)
  .attr("height", (d) => d * 3)
  .attr("fill", "#3b82f6");

// join() with enter/update/exit callbacks
svg.selectAll("rect")
  .data(data)
  .join(
    (enter) => enter.append("rect")
      .attr("fill", "green"),
    (update) => update
      .attr("fill", "blue"),
    (exit) => exit
      .attr("fill", "red")
      .transition().duration(500)
      .attr("opacity", 0)
      .remove()
  );

5. 比例尺(Scales)

比例尺将数据值(domain)映射到视觉值(range),如像素位置、颜色或大小。它们是构建图表的核心工具。

线性比例尺 (scaleLinear)

// Map data 0-100 to pixels 0-500
const xScale = d3.scaleLinear()
  .domain([0, 100])     // data range
  .range([0, 500]);     // pixel range

xScale(50);   // 250
xScale(100);  // 500
xScale.invert(250);  // 50 (reverse lookup)

序数比例尺 (scaleBand)

// Map categories to equal-width bands
const xScale = d3.scaleBand()
  .domain(["Mon", "Tue", "Wed", "Thu", "Fri"])
  .range([0, 500])
  .padding(0.2);  // gap between bands

xScale("Wed");           // 200 (pixel position)
xScale.bandwidth();      // 80 (width of each band)

时间比例尺与对数比例尺

// Time scale
const timeScale = d3.scaleTime()
  .domain([new Date("2025-01-01"), new Date("2025-12-31")])
  .range([0, 800]);

// Log scale (useful for data spanning orders of magnitude)
const logScale = d3.scaleLog()
  .domain([1, 10000])
  .range([0, 500]);

// Color scale
const colorScale = d3.scaleOrdinal(d3.schemeCategory10)
  .domain(["A", "B", "C", "D"]);

6. 坐标轴

D3 坐标轴生成器自动从比例尺创建 SVG 刻度线、标签和参考线。使用 axisBottom、axisLeft、axisTop 和 axisRight 设置方向。

const margin = { top: 20, right: 20, bottom: 40, left: 50 };
const width = 600 - margin.left - margin.right;
const height = 400 - margin.top - margin.bottom;

const svg = d3.select("#chart")
  .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 + ")");

const xScale = d3.scaleLinear()
  .domain([0, 100]).range([0, width]);
const yScale = d3.scaleLinear()
  .domain([0, 500]).range([height, 0]);

// Add bottom axis
svg.append("g")
  .attr("transform", "translate(0," + height + ")")
  .call(d3.axisBottom(xScale).ticks(10));

// Add left axis
svg.append("g")
  .call(d3.axisLeft(yScale)
    .ticks(5)
    .tickFormat(d => "\$" + d));

7. SVG 基础

D3 主要使用 SVG 进行渲染。理解核心 SVG 元素对于使用 D3 至关重要。SVG 坐标系原点在左上角,y 轴向下递增。

<!-- Core SVG elements used in D3 -->
<svg width="400" height="300">
  <!-- Rectangle: x, y, width, height -->
  <rect x="10" y="10" width="80" height="50"
    fill="#3b82f6" rx="4" />

  <!-- Circle: cx, cy, r -->
  <circle cx="200" cy="100" r="30"
    fill="#ef4444" stroke="#000" stroke-width="2" />

  <!-- Line: x1, y1, x2, y2 -->
  <line x1="10" y1="200" x2="390" y2="200"
    stroke="#94a3b8" stroke-width="1" />

  <!-- Path: the most powerful SVG element -->
  <path d="M10 250 L100 200 L200 230 L300 180"
    fill="none" stroke="#10b981" stroke-width="2" />

  <!-- Text -->
  <text x="200" y="290" text-anchor="middle"
    font-size="14">Label</text>
  <!-- Group: transform applies to all children -->
  <g transform="translate(50, 50)">
    <rect width="20" height="20" fill="#8b5cf6" />
  </g>
</svg>

8. 柱状图

柱状图是最常见的 D3 图表类型。下面是一个完整的垂直柱状图示例,包含比例尺、坐标轴和数据标签。

const data = [
  { label: "React", value: 42 },
  { label: "Vue", value: 30 },
  { label: "Angular", value: 22 },
  { label: "Svelte", value: 18 },
  { label: "Solid", value: 12 },
];

const margin = { top: 20, right: 20, bottom: 40, left: 50 };
const width = 500 - margin.left - margin.right;
const height = 300 - margin.top - margin.bottom;

const svg = d3.select("#bar-chart")
  .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 + ")");

const x = d3.scaleBand()
  .domain(data.map((d) => d.label))
  .range([0, width])
  .padding(0.2);

const y = d3.scaleLinear()
  .domain([0, d3.max(data, (d) => d.value)])
  .range([height, 0]);

// Axes
svg.append("g")
  .attr("transform", "translate(0," + height + ")")
  .call(d3.axisBottom(x));
svg.append("g").call(d3.axisLeft(y));

// Bars
svg.selectAll("rect")
  .data(data)
  .join("rect")
  .attr("x", (d) => x(d.label))
  .attr("y", (d) => y(d.value))
  .attr("width", x.bandwidth())
  .attr("height", (d) => height - y(d.value))
  .attr("fill", "#3b82f6")
  .attr("rx", 4);

9. 折线图

折线图使用 d3.line() 生成器将数据点转换为 SVG 路径。适合展示随时间变化的趋势数据。

const data = [
  { date: new Date("2025-01"), value: 120 },
  { date: new Date("2025-02"), value: 180 },
  { date: new Date("2025-03"), value: 150 },
  { date: new Date("2025-04"), value: 220 },
  { date: new Date("2025-05"), value: 280 },
  { date: new Date("2025-06"), value: 250 },
];

const x = d3.scaleTime()
  .domain(d3.extent(data, (d) => d.date))
  .range([0, width]);

const y = d3.scaleLinear()
  .domain([0, d3.max(data, (d) => d.value)])
  .range([height, 0]);

// Line generator
const lineGen = d3.line()
  .x((d) => x(d.date))
  .y((d) => y(d.value))
  .curve(d3.curveMonotoneX);  // smooth curve

// Draw the line
svg.append("path")
  .datum(data)
  .attr("fill", "none")
  .attr("stroke", "#3b82f6")
  .attr("stroke-width", 2.5)
  .attr("d", lineGen);

// Add data points
svg.selectAll("circle")
  .data(data)
  .join("circle")
  .attr("cx", (d) => x(d.date))
  .attr("cy", (d) => y(d.value))
  .attr("r", 4)
  .attr("fill", "#3b82f6");

10. 饼图和环形图

饼图使用 d3.pie() 计算角度,d3.arc() 生成弧形路径。设置 innerRadius > 0 即可变为环形图。

const data = [
  { label: "Desktop", value: 55 },
  { label: "Mobile", value: 35 },
  { label: "Tablet", value: 10 },
];

const radius = 150;
const color = d3.scaleOrdinal(d3.schemeCategory10);

// Pie layout computes start/end angles
const pieLayout = d3.pie()
  .value((d) => d.value)
  .sort(null);

// Arc generator
const arcGen = d3.arc()
  .innerRadius(60)    // 0 for pie, >0 for donut
  .outerRadius(radius);

const g = svg.append("g")
  .attr("transform",
    "translate(" + (width / 2) + "," + (height / 2) + ")");

// Draw arcs
g.selectAll("path")
  .data(pieLayout(data))
  .join("path")
  .attr("d", arcGen)
  .attr("fill", (d, i) => color(i))
  .attr("stroke", "#fff")
  .attr("stroke-width", 2);

// Add labels
g.selectAll("text")
  .data(pieLayout(data))
  .join("text")
  .attr("transform", (d) =>
    "translate(" + arcGen.centroid(d) + ")")
  .attr("text-anchor", "middle")
  .attr("font-size", "12px")
  .text((d) => d.data.label);

11. 过渡与动画

D3 的 transition() 方法在属性变化之间创建平滑的动画效果。支持自定义持续时间、延迟、缓动函数和链式过渡。

// Basic transition
svg.selectAll("rect")
  .data(data)
  .join("rect")
  .attr("x", (d) => x(d.label))
  .attr("y", height)              // start from bottom
  .attr("width", x.bandwidth())
  .attr("height", 0)              // start with zero height
  .attr("fill", "#3b82f6")
  .transition()                   // begin transition
  .duration(800)                  // 800ms
  .delay((d, i) => i * 100)       // stagger bars
  .ease(d3.easeCubicOut)          // easing function
  .attr("y", (d) => y(d.value))   // animate to final y
  .attr("height", (d) => height - y(d.value));

// Chained transitions
d3.select("circle")
  .transition()
  .duration(500)
  .attr("r", 50)
  .attr("fill", "red")
  .transition()                   // chain another
  .duration(500)
  .attr("r", 20)
  .attr("fill", "blue");

12. 事件处理

D3 使用 .on() 方法监听 DOM 事件。回调函数接收事件对象和绑定的数据,可以创建悬停高亮、工具提示和点击交互。

// Hover highlight
svg.selectAll("rect")
  .on("mouseenter", function (event, d) {
    d3.select(this)
      .attr("fill", "#2563eb")
      .attr("opacity", 0.8);
  })
  .on("mouseleave", function (event, d) {
    d3.select(this)
      .attr("fill", "#3b82f6")
      .attr("opacity", 1);
  });

// Tooltip
const tooltip = d3.select("body")
  .append("div")
  .style("position", "absolute")
  .style("background", "#1e293b")
  .style("color", "#f1f5f9")
  .style("padding", "8px 12px")
  .style("border-radius", "6px")
  .style("font-size", "13px")
  .style("pointer-events", "none")
  .style("opacity", 0);

svg.selectAll("circle")
  .on("mouseover", function (event, d) {
    tooltip
      .style("opacity", 1)
      .html("Value: " + d.value)
      .style("left", (event.pageX + 10) + "px")
      .style("top", (event.pageY - 10) + "px");
  })
  .on("mouseout", function () {
    tooltip.style("opacity", 0);
  });

13. 响应式图表

使用 SVG 的 viewBox 属性让图表自动缩放,配合 ResizeObserver 实现真正的响应式设计。

// Method 1: viewBox (simplest)
const svg = d3.select("#chart")
  .append("svg")
  .attr("viewBox", "0 0 600 400")
  .style("width", "100%")
  .style("height", "auto");
// Chart scales to container automatically

// Method 2: ResizeObserver (dynamic redraw)
function createChart(container) {
  const observer = new ResizeObserver((entries) => {
    const { width } = entries[0].contentRect;
    const height = width * 0.6;  // aspect ratio

    // Clear and redraw
    d3.select(container).selectAll("*").remove();
    const svg = d3.select(container)
      .append("svg")
      .attr("width", width)
      .attr("height", height);

    // Recalculate scales with new dimensions
    const x = d3.scaleLinear()
      .domain([0, 100])
      .range([40, width - 20]);
    // ... draw chart with new dimensions
  });
  observer.observe(container);
}

14. D3 与 React 集成

在 React 中使用 D3 的推荐方式是用 useRef 获取容器引用,在 useEffect 中运行 D3 代码。React 管理组件生命周期,D3 负责 SVG 渲染。

import React, { useRef, useEffect } from "react";
import * as d3 from "d3";

function BarChart({ data }) {
  const svgRef = useRef(null);

  useEffect(() => {
    if (!data || !svgRef.current) return;

    const svg = d3.select(svgRef.current);
    svg.selectAll("*").remove();  // clear previous

    const width = 500, height = 300;
    const margin = { top: 20, right: 20,
      bottom: 30, left: 40 };
    const innerW = width - margin.left - margin.right;
    const innerH = height - margin.top - margin.bottom;

    const g = svg
      .attr("width", width)
      .attr("height", height)
      .append("g")
      .attr("transform",
        "translate(" + margin.left + ","
        + margin.top + ")");

    const x = d3.scaleBand()
      .domain(data.map((d) => d.name))
      .range([0, innerW]).padding(0.2);

    const y = d3.scaleLinear()
      .domain([0, d3.max(data, (d) => d.val)])
      .range([innerH, 0]);

    g.append("g")
      .attr("transform", "translate(0," + innerH + ")")
      .call(d3.axisBottom(x));
    g.append("g").call(d3.axisLeft(y));

    g.selectAll("rect")
      .data(data)
      .join("rect")
      .attr("x", (d) => x(d.name))
      .attr("y", (d) => y(d.val))
      .attr("width", x.bandwidth())
      .attr("height", (d) => innerH - y(d.val))
      .attr("fill", "#3b82f6");
  }, [data]);

  return <svg ref={svgRef} />;
}

方法二:React 渲染 SVG + D3 工具

function BarChartReact({ data }) {
  const width = 500, height = 300;
  const margin = { top: 20, right: 20,
    bottom: 30, left: 40 };

  const x = d3.scaleBand()
    .domain(data.map((d) => d.name))
    .range([margin.left, width - margin.right])
    .padding(0.2);

  const y = d3.scaleLinear()
    .domain([0, d3.max(data, (d) => d.val)])
    .range([height - margin.bottom, margin.top]);

  return (
    <svg width={width} height={height}>
      {data.map((d, i) => (
        <rect
          key={i}
          x={x(d.name)}
          y={y(d.val)}
          width={x.bandwidth()}
          height={y(0) - y(d.val)}
          fill="#3b82f6"
        />
      ))}
    </svg>
  );
}

15. GeoJSON 与地图

D3 内置强大的地理投影和路径生成器,可以将 GeoJSON/TopoJSON 数据渲染为交互式地图。支持数十种投影方式。

// Load and render a world map
const projection = d3.geoNaturalEarth1()
  .scale(150)
  .translate([width / 2, height / 2]);

const pathGen = d3.geoPath().projection(projection);

// Load GeoJSON data
d3.json("https://unpkg.com/world-atlas@2"
  + "/countries-110m.json")
  .then((world) => {
    const countries = topojson.feature(
      world, world.objects.countries
    );

    svg.selectAll("path")
      .data(countries.features)
      .join("path")
      .attr("d", pathGen)
      .attr("fill", "#cbd5e1")
      .attr("stroke", "#64748b")
      .attr("stroke-width", 0.5);
  });

// Choropleth: color by data value
const colorScale = d3.scaleSequential()
  .domain([0, 100])
  .interpolator(d3.interpolateBlues);

svg.selectAll("path")
  .attr("fill", (d) =>
    colorScale(dataMap.get(d.id) || 0));

16. 力导向图

力导向图使用物理模拟来布局网络节点。d3-force 模块提供多种力:链接力、电荷力、中心力和碰撞力。

const nodes = [
  { id: "A" }, { id: "B" }, { id: "C" },
  { id: "D" }, { id: "E" },
];
const links = [
  { source: "A", target: "B" },
  { source: "A", target: "C" },
  { source: "B", target: "D" },
  { source: "C", target: "E" },
];

const simulation = d3.forceSimulation(nodes)
  .force("link", d3.forceLink(links)
    .id((d) => d.id).distance(100))
  .force("charge", d3.forceManyBody()
    .strength(-200))
  .force("center", d3.forceCenter(
    width / 2, height / 2))
  .force("collision", d3.forceCollide(20));

// Draw links
const link = svg.selectAll("line")
  .data(links)
  .join("line")
  .attr("stroke", "#94a3b8")
  .attr("stroke-width", 2);

// Draw nodes
const node = svg.selectAll("circle")
  .data(nodes)
  .join("circle")
  .attr("r", 15)
  .attr("fill", "#3b82f6")
  .call(d3.drag()
    .on("start", dragStart)
    .on("drag", dragging)
    .on("end", dragEnd));

// Update positions on each tick
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);
});

function dragStart(event, d) {
  if (!event.active)
    simulation.alphaTarget(0.3).restart();
  d.fx = d.x; d.fy = d.y;
}
function dragging(event, d) {
  d.fx = event.x; d.fy = event.y;
}
function dragEnd(event, d) {
  if (!event.active)
    simulation.alphaTarget(0);
  d.fx = null; d.fy = null;
}

17. D3.js 最佳实践

  • 使用 margin 约定:定义 margin 对象(top/right/bottom/left),从 SVG 尺寸中减去 margin 计算内部绘图区域。所有图表都应遵循此模式。
  • 优先使用 join() 而非 enter/exit:D3 v5+ 的 join() 方法更简洁,自动处理进入、更新和退出选择集,减少样板代码。
  • 按需导入模块:不要导入整个 d3 库。只导入你需要的模块(如 d3-scale、d3-selection),可以显著减小打包体积。
  • 使用 viewBox 实现响应式:设置 SVG 的 viewBox 属性而非固定 width/height,让图表自动适应容器大小。
  • 为 key 使用数据标识:在 data() 中传入 key 函数(如 d => d.id),确保数据更新时元素正确匹配,而不是依赖索引。
  • 清理定时器和事件监听:在 React useEffect 的清理函数中停止 simulation、取消 transition 和移除事件监听,防止内存泄漏。
  • 合理使用过渡动画:动画应增强理解而非分散注意力。持续时间保持在 200-800ms 之间,避免在大量元素上同时触发复杂动画。
  • 为可访问性添加 ARIA 属性:为 SVG 添加 role="img"、aria-label 描述,为数据点添加 title 元素,确保屏幕阅读器可以理解图表内容。

18. 常用 D3 模块速查

模块用途关键 API
d3-selection选择和操作 DOM 元素select, selectAll, append, attr, style, on
d3-scale将数据映射到视觉通道scaleLinear, scaleBand, scaleTime, scaleOrdinal, scaleLog
d3-axis生成坐标轴axisBottom, axisLeft, axisTop, axisRight
d3-shape形状生成器line, area, arc, pie, stack, symbol
d3-transition动画过渡transition, duration, delay, ease
d3-array数组统计工具min, max, extent, mean, sum, group, rollup
d3-fetch数据加载json, csv, tsv, text, xml
d3-geo地理投影和路径geoPath, geoMercator, geoNaturalEarth1, geoAlbersUsa
d3-force力导向模拟forceSimulation, forceLink, forceManyBody, forceCenter
d3-zoom缩放和平移zoom, zoomTransform, zoomIdentity
d3-drag拖拽交互drag, dragDisable, dragEnable
d3-color颜色操作rgb, hsl, lab, interpolateRgb, schemeCategory10

常见问题

D3.js 是什么?用来做什么?

D3.js(Data-Driven Documents)是一个 JavaScript 可视化库,用于在浏览器中使用 SVG、HTML 和 CSS 创建动态交互式数据可视化。它将数据绑定到 DOM 元素并应用数据驱动的变换,用于创建图表、地图、图形和仪表板。

如何安装 D3.js?

通过 npm install d3 安装并使用 import * as d3 from "d3" 导入。也可以按需导入单独模块如 d3-scale 或 d3-selection。或者从 CDN 加载 script 标签。

D3 的 select 和 selectAll 有什么区别?

d3.select() 选择第一个匹配的 DOM 元素,d3.selectAll() 选择所有匹配元素。两者都接受 CSS 选择器,返回的选择集支持链式调用来修改属性、样式和绑定数据。

D3 数据绑定的 enter/update/exit 是什么?

enter() 处理新数据项(需要创建新元素),update 选择集处理已有元素的数据更新,exit() 处理不再有对应数据的元素(需要移除)。D3 v7 的 join() 方法将三者合一,简化了代码。

D3 比例尺是什么?为什么重要?

比例尺将数据值(domain)映射到视觉值(range)。scaleLinear 映射连续数值,scaleBand 映射类别,scaleTime 映射日期,scaleLog 使用对数映射。它们是将原始数据转换为适配图表尺寸的坐标的核心工具。

如何在 React 中使用 D3?

推荐使用 useRef 获取 SVG 或 div 容器的引用,然后在 useEffect 中运行 D3 代码操作该容器。React 管理组件生命周期,D3 负责 SVG 渲染。对于简单图表,也可以只用 D3 的比例尺工具,由 React 直接渲染 SVG 元素。

如何创建响应式 D3 图表?

使用 SVG viewBox 属性代替固定 width/height,设置 viewBox="0 0 width height" 并用 CSS 设置宽度 100%。或用 ResizeObserver 检测容器大小变化并重绘。

D3 能创建地图吗?

是的。使用 d3-geo 投影(geoMercator、geoNaturalEarth1 等)将经纬度转换为屏幕坐标,加载 GeoJSON/TopoJSON 数据,用 geoPath 渲染为 SVG 路径。支持等值线地图、点地图和自定义投影。

总结

D3.js 是 Web 数据可视化的黄金标准。掌握选择集、数据绑定、比例尺和过渡动画四个核心概念后,你就能构建任何类型的可视化。从柱状图开始,逐步挑战折线图、饼图、地图和力导向图。在 React 中使用 useRef + useEffect 模式集成。

𝕏 Twitterin LinkedIn
这篇文章有帮助吗?

保持更新

获取每周开发技巧和新工具通知。

无垃圾邮件,随时退订。

试试这些相关工具

{ }JSON Formatter

相关文章

Three.js 完全指南:Web 3D 图形

掌握 Three.js 场景、相机、几何体、材质、光照、纹理、动画、3D 模型加载与 React Three Fiber。

RxJS 完全指南:JavaScript 响应式编程

掌握 RxJS Observable、Subject、操作符、管道、错误处理、多播、调度器与弹珠测试。

JavaScript Promises 和 Async/Await 完全指南

掌握 JavaScript Promises 和 async/await:创建、链式调用、Promise.all、错误处理和并发策略。