D3入门到创建图表【完整代码示例】

372 阅读6分钟

pexels-pixabay-533509.jpg

简介

  • D3.js 是一个 JavaScript 库,用于在 Web 上创建图表、地图等可视化效果。
  • D3 是 Data-Driven Documents(数据驱动文档) 的简称
  • 它使用可缩放矢量图形 (SVG)、HTML5 级联样式表(CSS) 标准
  • 与许多其他提供现成图表的数据可视化库不同,D3 为您提供了很大的创作自由,因为您可以完全控制您创建的可视化。D3 还使用 HTML、CSS、SVG 和 JavaScript 等 Web 技术。

特点:

  • D3 速度极快
  • 它鼓励代码可重用性
  • 它支持大型数据集,并提供了一种加载和转换数据的简单方法
  • 它非常适合创建具有丰富交互的可视化效果

你得知道的 SVG

数据可视化系列之 SVG 手册 - 掘金 (juejin.cn)

因为 D3 是基于 SVG 的

SVG,指可缩放矢量图形(Scalable Vector Graphics),是用于描述二维矢量图形的一种图形格式,是由万维网联盟制定的开放标准。SVG 使用 XML 格式来定义图形,绝大部分浏览器都支持 SVG,可将 SVG 文本直接嵌入 HTML 中显示。 SVG 有如下特点:

  • SVG 绘制的是矢量图,因此对图像进行放大不会失真。
  • 基于 XML,可以为每个元素添加 JavaScript 事件处理器。
  • 每个图形均视为对象,更改对象的属性,图形也会改变。

环境准备

CDN 地址

d3.v7.js

NPM

yarn add d3

使用

import * as d3 from "d3";
// or
import { select, selectAll } from "d3";

元素选择

采用任何 CSS 选择器并返回与指定选择器匹配的元素。如果没有元素与选择器匹配,它将返回一个空选择。

  • d3.select() :匹配它找到的第一个元素。
  • d3.selectAll() : 选择与选择器匹配的所有元素

选择 DOM 并设置属性

d3.select("#d3_p").style("color", "blue");

d3.selectAll(".d3_p").style("color", "blue");

属性方法介绍

方法解释示例
.attr()更新所选元素属性d3.select("p").attr("name", "fred")
.classed()在选定元素上赋值或取消赋值指定的 CSS 类名d3.select("p").classed("radio", true);
.style()更新 style 属性d3.select("p").style("color", "blue");
.property()用于设置元素属性d3.select('input').property('value', 'hello world')
.text()更新选定的元素文本内容d3.select('h1').text('Learning d3.js')
.html()将内部 HTML 设置为所有选定元素的指定值d3.select('div').html('h1>learning d3.js')
.append()追加一个新元素作为所选元素的最后一个子元素d3.select("div").append("p")
.insert()追加一个新元素作为所选元素的最后一个子元素d3.select("div").insert("p", "h1")
.remove()追加一个新元素作为所选元素的最后一个子元素d3.select("div").remove("p")

上面的实例中,第二个参数是一个常量,第二个参数还可以是一个函数,例如:

d3.selectAll("circle").attr('cx', ((d, i) => i * 100))

数据驱动

  • D3 是数据驱动的,它是支持 数组, CSV, XML, TSV, JSON 等数据格式,
  • 数据原来:本地、服务器、第三方 API
  • 使用.data()方法向选中的元素中添加数据
let fruits = ['Apple', 'Orange', 'Mango']

d3.selectAll(".d3_fruit").data(fruits).text((d) => d)

// html

<p class="d3_fruit"></p>

输出结果

我们只能等到一个输出

<p class="d3_fruit">Apple</p>

image.png

join 函数

绑定 data 数据到空的选择器上, 内部自动创建空元素

select 在线实例

import { useEffect, useRef } from 'react';
import { select, selectAll } from 'd3';

function App() {
  let d3Dom = useRef<HTMLDivElement | null>(null);
  useEffect(() => {
    console.log(d3Dom.current!.id, '..');
    let data = ['Apple', 'Orange', 'Mango'];
    select(d3Dom.current)
      .selectAll('div')
      .data(data)
      .join('div')
      .text((e: string) => e);
    // selectAll('p')
    //   .selectAll('div')
    //   .data(data)
    //   .join('div')
    //   .text((e: string) => e);
  });
  return (
    <>
      <p>select , selectAll</p>
      <div id="d3" ref={d3Dom}></div>
    </>
  );
}

export default App;

数据加载方式

支持数据格式: :jsoncsvxmltsvtext 加载数据函数:d3.json(url)d3.csv(url)d3.xml(url)d3.tsv(url)d3.text(url)

import { useEffect, useRef } from "react";
import { select, json } from "d3";

function LoadData() {
  const d3Dom = useRef<HTMLDivElement | null>(null);
  const loadData = async () => {
    const data = await json<string>("./data.json");
    select(d3Dom.current)
      .selectAll("div")
      .data(data!)
      .join("div")
      .text((e: string) => e);
  };
  useEffect(() => {
    loadData();
  });
  return (
    <>
      <p>LoadData</p>
      <div id="d3_1" ref={d3Dom}></div>
    </>
  );
}

export default LoadData;

image.png

Scale(比例 or 缩放)

简单描述,如果我们要展示一个地区跟人口相关的一个柱状图表,我们要展示的人口数量在(2w-200W)之间,我们不能让图表上显示 200w 个像素点

scale 需要设置 domainrange , (domain 代表 缩放后的范围,range 代表真实数据的范围

语法

// scaleLinear(domain, range)

d3.scaleLinear([0, 100], ["red", "blue"]);

d3.scaleLinear().domain([10, 500]).range([2000000, 16000000]); // 将 2w-160w ,限制在了10-500像素点显示

scale 有很多种类

  • Linear scales :- 用于定量数据
  • Time scales :- 用于时间序列数据
  • Pow scales :- 用于定量数据(范围很广)
  • Log scales :- 用于定量数据(范围很广)
  • Symlog scales :- 用于定量数据(范围很广)
  • Ordinal scales :- 用于分类或序数数据
  • Band scales :- 用于作为位置编码的分类或有序数据
  • Point scales :- 用于作为位置编码的分类或有序数据
  • Sequential scales :- 用于将定量数据作为顺序颜色编码
  • Diverging scales :- 将定量数据作为发散颜色编码
  • Quantile scales :- 用于作为离散编码的定量数据
  • Quantize scales :- 将定量数据作为离散编码
  • Threshold scales :- 将定量数据作为离散编码

用 D3 创建一个 柱状图

image.png

  1. 定义 x,y 轴的范围
const width = 960,
  height = 500;
const x_scale = d3.scaleBand().range([0, width]);
const y_scale = d3.scaleLinear().range([height, 0]);
  1. 选择 DOM
const svg = d3.select("#d3_demo").attr("width", width).attr("height", height);
  1. 获取数据
d3.json(
  "https://raw.githubusercontent.com/iamspruce/intro-d3/main/nigeria-states.json"
).then(({ data }) => {
  data.forEach((d) => (d.Population = +d.info.Population));
});
  1. 设置 domain
x_scale.domain(data.map((d) => d.Name);
y_scale.domain([0, d3.max(data, (d) => d.Population)]);

  1. 展示区域设置
svg
  .selectAll("rect")
  .data(data)
  .join("rect")
  .attr("class", "bar")
  .attr("x", (d) => x_scale(d.Name)) // 设置每个 react 在x轴上的位置
  .attr("y", (d) => y_scale(d.Population))
  .attr("width", x_scale.bandwidth()) // 设置每一个 rect的 宽
  .attr("height", (d) => height - y_scale(d.Population));

Axis 轴线

image.png

给图表上添加轴线,让图表更可读

创建不同方向上的轴线

  • d3.axisTop
  • d3.axisBottom
  • d3.axisLeft
  • d3.axisRight

代码示例

let svg = d3.select('#d3_demo8').attr('width', 200).attr('height', 200)
let scale = d3.scaleLinear().domain([0, 100]).range([0, 200])

let bottom_axis = d3.axisBottom(scale)

svg.append('g').call(bottom_axis)

// html
<svg id="d3_demo"></svg>

给上面图表添加坐标

边缘设定

// 定义一个 边缘对象
const margin = { top: 20, right: 30, bottom: 55, left: 70 };
// 定义宽高变量
const width = document.querySelector("body").clientWidth;
const height = 500;
// 设置 svg的宽高
const svg = d3.select("#d3_demo").attr("viewBox", [0, 0, width, height]);
const x_scale = d3
  .scaleBand()
  .range([margin.left, width - margin.right]) // 结合nargin , 设置 x轴的范围
  .padding(0.1);

const y_scale = d3.scaleLinear().range([height - margin.bottom, margin.top]); // 结合nargin , 设置 Y 轴的范围

设置轴线

let x_axis = d3.axisBottom(x_scale);

let y_axis = d3.axisLeft(y_scale);

// 最加 x 轴线
svg
  .append("g")
  .attr("transform", `translate(0,${height - margin.bottom})`)
  .call(x_axis)
  .selectAll("text")
  .style("text-anchor", "end")
  .attr("dx", "-.8em")
  .attr("dy", ".15em")
  .attr("transform", "rotate(-65)");

// 最加 y 轴线
svg.append("g").attr("transform", `translate(${margin.left},0)`).call(y_axis);

给 D3 设置 css

.attr("class", "bar")
.bar {
  fill: green;
}

完整代码:

所有实例代码入口

最近好像 stackblitz.com 对一些 cdn 访问有了限制,可能代码不能在线运行,但是不影响下载,下载后可以直接运行,查看运行结果

交互性

监听事件

为 SVG 元素添加事件监听器。

svg.selectAll("rect").on("mouseover", function (event, d) {
  d3.select(this).style("fill", "red");
});

更新数据

根据新的数据更新图表。

const newData = [40, 50, 20, 70, 30];
svg
  .selectAll("rect")
  .data(newData)
  .transition()
  .duration(1000)
  .attr("y", (d, i) => 250 - d * 2);

结语

D3.js 是一个功能丰富、灵活的数据可视化库。通过本教程,你应该已经对 D3.js 有了深入的了解,并且能够开始在项目中应用 D3.js 技术。随着实践的深入,你将能够发掘 D3.js 更多的潜力,创造出令人惊叹的视觉效果。