数据可视化工具 D3.js 介绍
D3.js简略介绍
D3.js 是一个用于在网页上创建交互式数据的可视化 JavaScript 库。它提供了一组用于处理数据、创建图表和动画的函数,使得开发人员可以轻松地创建动态的、交互式的数据可视化。 D3.js 的主要特点包括:
- 强大的数据处理能力:D3.js 提供了丰富的数据处理函数,如选择、过滤、聚合等,使得开发人员可以轻松地处理数据。
- 可视化效果丰富:D3.js 提供了丰富的可视化图表类型
- 动画效果丰富:D3.js 提供了丰富的动画效果,如缓动、缓动函数等,使得开发人员可以轻松地创建动态的动画效果。
- 跨平台支持:D3.js 支持多种平台,如浏览器、Node.js 等,使得开发人员可以轻松地使用 D3.js 在各种环境中创建可视化。
- 社区活跃:D3.js 有广大社区,提供了丰富的资源,如文档、教程、示例等,使得开发人员可以轻松地找到需要的资源。
D3.js的安装
D3.js 可以通过 npm 安装,使用命令如下:
npm install d3如果是使用TypeScript进行开发的话 则需要下载额外的类型声明文件
npm install @types/d3在此之后 可以在项目中使用D3.js了 在程序import中引入d3.js
import * as d3 from "d3"之后就可以通过类似d3.select()等方法来操作DOM元素了
D3.js的入门知识
一般情况下 D3.js 往往配合SVG元素使用,SVG是一种用于描述二维矢量图形的XML标记语言,它被用于创建动态的、交互式的图形。 首先需要创建一个SVG元素,然后使用D3.js提供的函数来操作SVG元素,如选择、过滤、聚合等。
<svg class="diagram"/><script> d3.select(svg).attr("style","background:gray")</script>然后我们通过D3.js来选中这个SVG元素,并使用一些函数来操作它。
D3.js核心函数
d3.select()attr("data-name",value)data(data_list)append("element")enter()d3.select()可以通过id class等选择器选中一个组件 比如
d3.select("svg.diagram") //选中类名为diagram的svg元素attr可以操作元素的属性 也就是html元素写在开头括号里面的值 例如
<div className={"divClass"}/>此div中的className就是属性的一种
SVG元素的基本组件以及语法
Rect组件(矩形)
<rect x="x-coordinate" <!-- 矩形左上角的 x 坐标 --> y="y-coordinate" <!-- 矩形左上角的 y 坐标 --> width="width-value" <!-- 矩形的宽度 --> height="height-value" <!-- 矩形的高度 --> rx="rx-value" <!-- 矩形的圆角半径(水平方向) --> ry="ry-value" <!-- 矩形的圆角半径(垂直方向) --> fill="fill-color" <!-- 矩形的填充颜色 --> stroke="stroke-color" <!-- 矩形的描边颜色 --> stroke-width="width-value" <!-- 矩形的描边宽度 -->/>rect.attr('x',30).attr('y',30) //设置矩形的起始坐标为(30,30).attr('width',300).attr('height',300) //设置矩形的宽高分别为300.attr('fill','red') //设置矩形填充颜色为红色.attr('stroke','blue') //设置矩形的描边颜色为蓝色.attr('stroke-width',5) //设置矩形的描边宽度为5Circle组件(圆形)
<circle cx="x-coordinate" <!-- 圆心的 x 坐标 --> cy="y-coordinate" <!-- 圆心的 y 坐标 --> r="radius" <!-- 圆的半径 --> fill="fill-color" <!-- 圆的填充颜色 --> stroke="stroke-color" <!-- 圆的描边颜色 --> stroke-width="width" <!-- 圆的描边宽度 -->/>circle.attr('cx',30).attr('cy',30) //设置圆心为(30,30).attr('r',20) //设置半径为20.attr('fill','red') //设置填充颜色为红色Line组件(线段)
<line x1="x1-coordinate" <!-- 起点的 x 坐标 --> y1="y1-coordinate" <!-- 起点的 y 坐标 --> x2="x2-coordinate" <!-- 终点的 x 坐标 --> y2="y2-coordinate" <!-- 终点的 y 坐标 --> stroke="stroke-color" <!-- 直线的颜色 --> stroke-width="width" <!-- 直线的宽度 -->/>line.attr('x1',30).attr('y1',30) //设置Line的起始坐标为(30,30).attr('x2',300).attr('y2',200) //设置Line的终点坐标为(300,200).attr('stroke','red').attr('stroke-width',3) //设置Line颜色为红色 粗细为3Path组件(路径)
<path d="path-data" <!-- 定义路径的路径数据 --> fill="fill-color" <!-- 路径的填充颜色 --> stroke="stroke-color" <!-- 路径的描边颜色 --> stroke-width="width" <!-- 路径的描边宽度 -->/>d 属性定义了路径的路径数据,即路径命令序列。 路径数据由一系列的路径命令组成,每个路径命令以字母开头,后面跟随一组数字参数。 常用的路径命令包括:
- M(移动到)
- L(直线到)
- H(水平线到)
- V(垂直线到)
- C(三次贝塞尔曲线)
- S(光滑曲线)
- Q(二次贝塞尔曲线)
- T(光滑二次贝塞尔曲线)
- A(圆弧)
- Z(闭合路径)等。
path.attr('d','M100 100 L200 200 L300 100 L400 400')//设置线段移动轨迹 (100,100) -> (200,200) -> (300,100) -> (400,400).attr('stroke','blue').attr('stroke-width',5).attr('fill','none')Text组件(文本)
<text x="x-coordinate" <!-- 文本左上角的 x 坐标 --> y="y-coordinate" <!-- 文本左上角的 y 坐标 --> font-family="font" <!-- 字体名称 --> font-size="size" <!-- 字体大小 --> fill="fill-color" <!-- 文本颜色 --> text-anchor="anchor" <!-- 文本锚点 -->> Text content <!-- 文本内容 --></text>const text=d3.select(svg).append('text')text.attr('x',200).attr('y',200) //设置文本的起始坐标为(200,200).text('Hello World') //设置文本内容为"Hello World".attr('font-family','sans-serif') //设置字体为sans-serif.attr('fill','black') //设置文本颜色为黑色.attr('font-size',40) //设置字体大小为40D3的链式函数调用
const svg=d3.select(".diagram")svg.attr("width",600).attr("height",400)//设置svg的属性为宽600高400.attr("style","background:red") //设置svg的style 背景颜色为红色D3.js通过一连串链式调用的函数来操作SVG元素,体现了函数式编程的思想.
而且在attr函数里面 第二个参数可以不是一个值而是一个函数 这个函数接受两个参数 第一个参数是数据本身 第二个参数是当前元素的索引 返回一个值作为attr的值 如
attr("height",(d,i)=>i*100)比如我们现在有一个数组 我们来用这个data模拟一个最简单的柱状图
const data=[12,10,19,10,4,3,4,5,6,7]然后让我们把这个数组作为数据绑定到rect元素上
const rects=d3.select(svg).selectAll('rect').data(data).enter().append('rect')然后我们根据数据来设置rect的宽高 x属性是根据索引来设置的 每一个rect的x坐标都是50的倍数 y坐标为20 因此每一个柱的起始坐标都是(50*i,20) 宽度恒定为40 高度根据数据乘以10来设定
rects.attr('x',(d,i)=>i*50).attr('y',20).attr('width',40).attr('height',(d)=>10*d)在设定x坐标和高度的时候 我们就希望让每一个rect的x坐标和高度根据数据动态变化 这时候就可以使用attr的第二个参数作为函数的形式来实现 这个函数的第一个参数d就是数据本身 i就是当前元素的索引
const data=[12,10,19,10,4,3,4,5,6,7]const rects=d3.select(svg).selectAll('rect').data(data).enter().append('rect')rects.attr('x',(d,i)=>i*50).attr('y',20).attr('width',40).attr('height',(d)=>10*d)数据绑定中 enter update exit 三个部分的区别解释
在数据绑定中,D3.js会将数据数组中的每个元素与DOM元素进行匹配,这通常被称为数据绑定。在绑定过程中,D3.js会将数据数组中的元素映射到DOM元素上,以便我们可以根据数据的变化动态更新DOM元素
enter update exit的图解(图片来源于 https://www.bookstack.cn/read/d3wiki/enterexit.md )

enter()
enter()函数用于处理新数据项,这些数据项在数据绑定之前并不存在于DOM中。当数据数组中的元素数量增加时,D3.js会调用enter()函数来创建新的DOM元素,并将这些元素与新增的数据项进行绑定。
update()
update()函数用于处理已存在的DOM元素,这些元素在数据绑定之后存在于DOM中。当数据数组中的元素数量减少时,D3.js会调用update()函数来更新现有的DOM元素,使其与新数据项保持一致。
exit()
exit()函数用于处理不再存在于数据数组中的DOM元素。当数据数组中的元素数量减少时,D3.js会调用exit()函数来删除不再需要的DOM元素。
D3的比例尺函数
介绍
比例尺函数是D3.js中用于将数据映射到可视化元素的一种重要工具。 它能够将数据的范围映射到可视化元素的范围,例如将数据的值映射到图形的大小、颜色或位置等。比例尺函数可以分为线性比例尺、对数比例尺、时间比例尺等不同类型,可以根据数据的特点选择合适的比例尺函数。
- 线性比例尺: 用于将线性数据映射到线性空间,例如将数据的值映射到图形的大小或位置。 例如,将数据的值映射到柱状图的宽度或高度。
const xScale=d3.scaleLinear().domain([0,100]).range([100,900])//将100~900像素之间映射到0~100的值- 对数比例尺: 用于将对数数据映射到线性空间,例如将数据的值映射到图形的大小或位置。 例如,将数据的值映射到散点图的大小。
const yScale=d3.scaleLog().domain([1,1000]).range([100,900])- 还有其他的各种各样的比例尺 在需要用到的时候再进行介绍
Range与Domain
- Range:表示映射后的范围,即在视觉上要映射到的目标范围。例如,将数据映射到像素坐标,则Range就是像素坐标的范围。
- Domain:表示映射前的范围,即原始数据的取值范围。 例如,我们可以通过设置Domain和Range来确定比例尺的映射方式。例如,我们可以将Domain设置为[0,1],Range设置为[0,100],这样,将数据映射到100个像素的宽度上,其中0映射到0像素,1映射到100像素。
const xScale=d3.scaleLinear().domain([0,1]).range([0,100])xScale(0.5) //返回50xScale(1) //返回100学完比例尺之后 我们再回到刚刚讲到的柱状图例子中
rects.attr('x',(d,i)=>i*50).attr('y',20).attr('width',40).attr('height',(d)=>10*d)在这里我们是通过索引设置x的值和height的值的 但是我们会发现每次都要这样运算会十分麻烦 因此我们可以使用比例尺来简化这个图像
const xScale=d3.scaleLinear().domain([0,data.length-1]).range([50,500])const yScale=d3.scaleLinear().domain([0,Math.max(...data)]).range([0,300])rects.attr('x',(d,i)=>xScale(i)) //通过X比例尺计算X坐标.attr('y',20).attr('width',40).attr('height',(d)=>yScale(d)) //通过Y比例尺计算Y坐标但是在这里我们又发现了一个问题 按正常阅读习惯来说的话 柱状图应该是从下往上长的 但是因为(0,0)点在左上角(如图)
现在是反过来的 为了实现这个效果 我们需要将比例尺的范围反过来 利用比例尺实现这个功能也十分简单 我们只需要让range的顺序反过来即可
const yScale=d3.scaleLinear().domain([0,Math.max(...data)]).range([300,0])//调转range的参数顺序 使其反向映射这样的话 0对应的是300 max值对应0 恰好就可以将图像反过来了
坐标轴
- 创建比例尺
d3.axisBottom(scale)//通过比例尺创建一个刻度位于底部的水平坐标轴d3.axisTop(scale)//通过比例尺创建一个刻度位于顶部的水平坐标轴d3.axisLeft(scale)// 通过比例尺创建一个刻度位于左侧的垂直坐标轴d3.axisRight(scale)// 通过比例尺创建一个刻度位于右侧的垂直坐标轴- 将比例尺添加到svg上 创建比例尺之后 我们再根据比例尺 创建X Y坐标轴并绘制在svg上面 这里我们使用d3的call函数
const xAxis = d3.axisBottom(xScale)const yAxis = d3.axisLeft(yScale)d3.select(svg).append('g').attr('transform','translate(0,350)').call(xAxis)d3.select(svg).append('g').attr('transform','translate(100,50)').call(yAxis)‘g’元素是一个svg里面的容器元素,也就是group,用于将某一系列的元素进行分组,用于组合其他元素,如线、文本等。 因为坐标轴是由多个元素组合而成的 我们可以使用g元素把组成坐标轴的所有元素放进一个分组里面 d3的坐标轴非常智能 它可以根据具体比例和数据大小 自动推断刻度的距离
实战:使用D3绘制一个简单的折线图
const data=[12,10,19,10,4,3,4,5,6,7]1.创建一个svg元素
const svg = d3.select("#chart").append("svg").attr("width",600).attr("height",600)2.创建比例尺
经过分析发现 我们的数据量为10 通过合理的分析我们决定设置svg宽度为600 高度为400 然后让我们创建x y比例尺
const xScale=d3.scaleLinear().domain([0,data.length-1]).range([50,550])const yScale=d3.scaleLinear().domain([0,Math.max(...data)]).range([350,50])这里为了使图像更加美观 我们让x的range为50~550之间 也就是左右留出50px的边距 y轴同理
3.创建坐标轴
const xScale=d3.scaleLinear().domain([0,data.length-1]).range([50,550])const yScale=d3.scaleLinear().domain([0,Math.max(...data)]).range([350,50])const xAxis=d3.axisBottom(xScale).ticks(10)const yAxis=d3.axisLeft(yScale).ticks(5)d3.select(svg).append('g').attr('transform','translate(0,350)').call(xAxis)//设置x轴的transform坐标偏移 使其位于(0,350)d3.select(svg).append('g').attr('transform','translate(50,0)').call(yAxis)//设置y轴的transform坐标偏移 使其位于(50,0)4.绘制线段
坐标轴绘制完成后 我们再通过path元素来绘制一条线
svg.append("path").attr("d",d3.line()(data.map((d,i)=>[xScale(i),yScale(d)])))//d就是这一条折线经过的所有点的x y坐标.attr("fill","none")//设置线的fill为none 就是不填充线下面的空间.attr("stroke","red").attr("stroke-width",3)//设置线的颜色为红色 宽度为35.绘制数据点
线绘制完成后 我们接下来绘制所有的点
svg.selectAll("points").data(data).enter().append("circle").attr("class","point").attr("cx",(_,i)=>xScale(i)).attr("cy",(d)=>yScale(d))//设置点的x y坐标.attr("r",4).attr("fill","red")//设置点的半径为4 颜色为红色6.绘制数据文字
最后 我们在点的上方绘制数据对应的值
svg.selectAll("data-text").data(data).enter().append("text").attr("class","data-text").attr("text-anchor","middle")//设置文字居中显示.attr("x",(_,i)=>xScale(i)).attr("y",(d)=>yScale(d)-10)//设置各个数据文字的坐标 注意yScale(d)-10的作用是让数据文字出现在点的上方.text((d)=>d)