news 2026/7/5 12:38:21

D3.js 完整详细使用教程(从入门到实战)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
D3.js 完整详细使用教程(从入门到实战)

一、D3.js 基础介绍

1. 什么是 D3.js

D3(Data-Driven Documents,数据驱动文档)是基于SVG、Canvas、HTML的数据可视化 JS 库,核心思想:绑定数据 → 操作 DOM,把数据映射成图形元素(柱状图、折线图、地图、散点图等)。

2. 优势

  • 高度自定义,无封装死图表,可自由控制每一个图形细节
  • 内置强大比例尺、坐标轴、动画、布局、插值、地理计算工具
  • 兼容现代浏览器,轻量无依赖
  • 生态丰富:图表、力导向图、热力图、树形图、地图可视化

3. 引入 D3

方式 1:CDN(推荐,快速上手)
<!-- D3 v7 稳定最新版 --> <script src="https://d3js.org/d3.v7.min.js"></script>
方式 2:npm 项目
npm install d3
import * as d3 from 'd3'

二、核心核心概念:数据绑定 enter/update/exit(D3 灵魂)

D3 通过select/selectAll选中 DOM,用.data()将数组数据和 DOM 元素一一绑定,分为三种状态:

  1. enter:数据比 DOM 多 → 新增元素(画新图形)
  2. update:数据和 DOM 数量相等 → 更新现有元素(修改位置、颜色、尺寸)
  3. exit:DOM 比数据多 → 删除多余元素

基础绑定示例

<svg width="400" height="200"></svg> <script src="https://d3js.org/d3.v7.min.js"></script> <script> const data = [30, 80, 50, 120, 60]; const svg = d3.select("svg"); // 1. 绑定数据,获取三组状态 const rects = svg.selectAll("rect") .data(data); // exit:多余矩形删除 rects.exit().remove(); // update:已有矩形更新属性 rects.attr("x", (d, i) => i * 70) .attr("y", d => 200 - d) .attr("width", 60) .attr("height", d => d) .fill("#4285f4"); // enter:新增缺失矩形 rects.enter() .append("rect") .attr("x", (d, i) => i * 70) .attr("y", d => 200 - d) .attr("width", 60) .attr("height", d => d) .fill("#4285f4"); </script>

三、D3 基础 API 分类详解

1. 选择器 Select(操作 DOM)

// 单选 d3.select("#box") d3.select(".item") d3.select("svg") // 多选(返回集合) d3.selectAll("rect") d3.selectAll(".circle") // 链式修改属性/样式/文本 d3.select("rect") .attr("width", 100) // SVG属性 .style("fill", "red") // css样式 .text("文字") // 文本内容 .html("<b>富文本</b>")

2. 比例尺 Scale(可视化核心:数据→像素映射)

原始数据范围(域domain)→ 画布像素范围(范围range

(1)线性比例尺 scaleLinear(柱状图 / 折线图)

数值均匀映射

// 数据0~200 映射到画布高度0~180 const yScale = d3.scaleLinear() .domain([0, 200]) // 原始数据区间 .range([180, 0]); // 画布区间(svg y轴向下,倒置) console.log(yScale(100)); // 90
(2)序数比例尺 scaleOrdinal(分类颜色、分类 X 轴)

离散分类数据映射

const colorScale = d3.scaleOrdinal() .domain(["苹果", "香蕉", "橙子"]) .range(["red", "yellow", "orange"]); colorScale("香蕉"); // yellow
(3)band 分段比例尺 scaleBand(柱状图 X 轴均分)

自动均分宽度、设置内边距

const xData = ["1月","2月","3月","4月"] const xScale = d3.scaleBand() .domain(xData) .range([20, 380]) .padding(0.1); // 柱子间距 xScale.bandWidth() // 单根柱子宽度
其他常用比例尺
  • scaleLog:对数比例尺(跨度极大数据)
  • scaleTime:时间比例尺(时间轴折线图)
  • scalePow:幂等比例尺
  • scaleQuantize:分阶比例尺(热力图分级)

3. 坐标轴 Axis(自动生成 x/y 轴)

依赖比例尺,自动生成刻度、轴线、文字

// 生成x轴渲染函数 const xAxis = d3.axisBottom(xScale); // 底部x轴,刻度文字朝下 const yAxis = d3.axisLeft(yScale); // 左侧y轴 // 在svg中渲染坐标轴 svg.append("g") .attr("transform", "translate(0, 200)") // 下移放到底部 .call(xAxis); svg.append("g") .attr("transform", "translate(20, 0)") .call(yAxis);

transform="translate(x,y)":SVG 平移,解决坐标轴偏移问题

4. 动画 Transition

.transition()实现平滑过渡

rects.enter() .append("rect") .attr("height", 0) // 初始高度0 .transition() // 开启动画 .duration(800) // 动画时长ms .delay((d,i)=>i*100) // 每个柱子延迟错开 .attr("height", d=>d)

5. 数据处理工具 d3-array

const data = [10,50,30,90]; d3.max(data) // 最大值 90 d3.min(data) // 最小值 10 d3.sum(data) // 总和 d3.mean(data) // 平均值 d3.range(0,10) // [0,1,2...9]

四、实战 1:完整柱状图(可直接复制运行)

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>D3 柱状图完整示例</title> <style> svg { border: 1px solid #eee; } </style> </head> <body> <svg width="500" height="300"></svg> <script src="https://d3js.org/d3.v7.min.js"></script> <script> // 1. 数据源 const dataset = [ {month:"1月",value:80}, {month:"2月",value:120}, {month:"3月",value:60}, {month:"4月",value:150}, {month:"5月",value:90} ] const width = 500; const height = 300; const margin = {top:20, right:20, bottom:40, left:40}; // 边距 // 画布 = 总宽高 - 边距 const innerW = width - margin.left - margin.right; const innerH = height - margin.top - margin.bottom; // 2. 创建svg画布,整体右移下移留出边距 const svg = d3.select("svg") .attr("width", width) .attr("height", height) .append("g") .attr("transform", `translate(${margin.left},${margin.top})`); // 3. 创建比例尺 // X轴 band比例尺 const xScale = d3.scaleBand() .domain(dataset.map(d=>d.month)) .range([0, innerW]) .padding(0.15); // Y轴线性比例尺 const yScale = d3.scaleLinear() .domain([0, d3.max(dataset, d=>d.value)]) .range([innerH, 0]); // 4. 坐标轴 const xAxis = d3.axisBottom(xScale); const yAxis = d3.axisLeft(yScale); // 渲染X轴(放在画布底部) svg.append("g") .attr("transform", `translate(0,${innerH})`) .call(xAxis); // 渲染Y轴 svg.append("g") .call(yAxis); // 5. 绘制柱状图 enter/update/exit const bars = svg.selectAll("rect") .data(dataset); bars.exit().remove(); bars.attr("x", d=>xScale(d.month)) .attr("y", d=>yScale(d.value)) .attr("width", xScale.bandwidth()) .attr("height", d=>innerH - yScale(d.value)) .fill("#3498db"); bars.enter() .append("rect") .attr("x", d=>xScale(d.month)) .attr("width", xScale.bandwidth()) .attr("y", innerH) // 初始在底部 .transition().duration(1000) .attr("y", d=>yScale(d.value)) .attr("height", d=>innerH - yScale(d.value)) .fill("#3498db"); </script> </body> </html>

五、实战 2:折线图(时间轴示例)

<svg width="600" height="300"></svg> <script src="https://d3js.org/d3.v7.min.js"></script> <script> const data = [ {date:new Date("2025-01-01"), val:20}, {date:new Date("2025-02-01"), val:45}, {date:new Date("2025-03-01"), val:32}, {date:new Date("2025-04-01"), val:60}, {date:new Date("2025-05-01"), val:48} ] const margin = {t:20, r:20, b:30, l:40}; const w = 600 - margin.l - margin.r; const h = 300 - margin.t - margin.b; const svg = d3.select("svg") .append("g") .attr("transform",`translate(${margin.l},${margin.t})`); // 时间比例尺 const x = d3.scaleTime() .domain(d3.extent(data, d=>d.date)) .range([0,w]); const y = d3.scaleLinear() .domain([0, d3.max(data, d=>d.val)]) .range([h,0]); // 折线生成器 line const line = d3.line() .x(d=>x(d.date)) .y(d=>y(d.val)) .curve(d3.curveMonotoneX); // 平滑曲线 // 绘制折线path svg.append("path") .datum(data) // 单组数据绑定用datum .attr("fill","none") .attr("stroke","#e74c3c") .attr("stroke-width",2) .attr("d", line); // 坐标轴 svg.append("g").attr("transform",`translate(0,${h})`).call(d3.axisBottom(x)); svg.append("g").call(d3.axisLeft(y)); // 圆点标记 svg.selectAll("circle") .data(data) .enter() .append("circle") .attr("cx",d=>x(d.date)) .attr("cy",d=>y(d.val)) .attr("r",4) .fill("#e74c3c"); </script>

六、常用布局 Layout(复杂图表)

D3 内置布局函数,专门处理复杂图形数据转换,只需要传入数据,返回图形坐标:

  1. d3.pie():饼图 / 环形图布局
  2. d3.forceSimulation():力导向网络图
  3. d3.tree() / d3.hierarchy:树形图、组织架构图
  4. d3.arc():饼图扇形生成器
  5. d3.map/geo:地图地理投影

饼图极简示例

const pieData = [10,20,30,40]; const pie = d3.pie(); // 饼图布局 const arcs = pie(pieData); // 转换为扇形坐标数据 const arc = d3.arc() .innerRadius(0) // 内半径,大于0就是环形图 .outerRadius(100); svg.selectAll("path") .data(arcs) .enter() .append("path") .attr("d", arc) .fill((d,i)=>d3.schemeSet2[i]);

七、交互:鼠标事件、tooltip

1. 鼠标监听

bars.on("mouseover", function(event, d){ d3.select(this).style("fill","#ff6600"); }) .on("mouseout", function(){ d3.select(this).style("fill","#3498db"); }) .on("click", (e,d)=>{ console.log("点击数据", d) })

2. Tooltip 悬浮提示

.tooltip { position:absolute; padding:6px 10px; background:#222; color:#fff; border-radius:4px; pointer-events:none; opacity:0; }
<div class="tooltip"></div>
const tooltip = d3.select(".tooltip"); bars.on("mouseover", (e,d)=>{ tooltip.html(`数值:${d.value}`) .style("left", e.pageX + "px") .style("top", e.pageY - 20 + "px") .style("opacity",1); }).on("mouseout", ()=>{ tooltip.style("opacity",0) })

八、D3 分层开发规范(工程化推荐)

  1. 边距模式 margin convention:统一预留上下左右边距,避免坐标轴被截断(上面示例全部使用)
  2. 分离数据、比例尺、坐标轴、图形渲染逻辑
  3. 图表封装成函数,支持传入配置(宽高、颜色、数据)
  4. 数据更新只调用一次渲染函数,依赖 enter/update/exit 自动 diff
  5. 样式分离,尽量用 class 代替 inline style

九、常见踩坑问题

  1. SVG y 轴向下,比例尺 range 必须倒置 [maxH, 0],否则柱子倒置
  2. .data()绑定数组,单条数据用.datum()
  3. 坐标轴需要.call(axis)才能渲染
  4. 折线 path 使用fill:none,否则会填充闭合区域
  5. 动画 transition 只能写在修改属性前
  6. 页面缩放模糊:svg 设置宽高,不要用 css 拉伸

十、官方资源 & 拓展学习

  1. 官方文档:https://d3js.org/
  2. 官方示例图库:https://observablehq.com/@d3/gallery(海量可编辑可视化案例)
  3. D3 API 完整手册:https://github.com/d3/d3/blob/main/API.md
  4. 进阶方向:地图可视化、3D 结合、Canvas 渲染大数据、交互式大屏

十一、扩展练习路线

  1. 基础:柱状图 → 横向柱状图 → 折线图 → 散点图
  2. 中级:饼图 / 环形图、堆叠柱状图、面积图、Tooltip 交互
  3. 高级:树形图、力导向网络图、中国地图、热力图、动态实时更新图表
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/5 12:36:23

ScaleTail:自托管服务一键接入 Tailscale 网络

文章目录ScaleTail&#xff1a;自托管服务一键接入 Tailscale 网络它做了什么安装使用Funnel 和 Serve 的区别项目结构为什么用这个ScaleTail&#xff1a;自托管服务一键接入 Tailscale 网络 ScaleTail 是一个开源项目&#xff0c;提供现成的 Docker Compose 配置&#xff0c;让…

作者头像 李华
网站建设 2026/6/27 4:56:43

OpenAI芯片Jalapeño亮相,能否打破英伟达算力垄断?

OpenAI芯片Jalapeo惊艳亮相OpenAI的芯片终于与大家见面了&#xff0c;只是亮相的场合有点让人意外&#xff1a;萨姆奥尔特曼与博通总裁兼CEO陈福阳一起捧着一块标准的12英寸晶圆&#xff0c;底座上印着芯片的名字&#xff1a;Jalapeo。Jalapeo名字背后的含义Jalapeo原意是墨西哥…

作者头像 李华
网站建设 2026/6/27 4:54:02

自己开店怎么弄扫码点餐,扫码点餐小程序,门店盈利翻倍的秘密武器

目录 低成本易操作轻松上手 杜绝错单漏单减少损耗 提升效率节省人工 自动锁客数据运营 稳步经营细水长流 老板们别再用老方法亏钱了 开实体店的朋友都知道&#xff0c;高峰期店员忙得脚打后脑勺&#xff0c;漏单错单家常便饭&#xff0c;工资涨得比利润快&#xff0c;最后…

作者头像 李华