参考:
【D3.js】基础教程-优快云博客
https://blog.youkuaiyun.com/weixin_39085822/article/details/119755130
D3 by Observable | The JavaScript library for bespoke data visualization
https://d3js.org/
d3的优点是比较底层,代码简洁,操作灵活(相比echarts)。
安装
去github下载最新包:https://github.com/d3/d3/releases
也可以引用:(目前最新是v7.9)
<!-- 压缩版本 -->
<script src="https://d3js.org/d3.v7.min.js"></script>
<!-- 完整版本 -->
<script src="https://d3js.org/d3.v7.js"></script>
基础知识
选择元素
// 选择所有指定元素的第一个
var body = d3.select("body"); //选择文档中的body元素
var svg = body.select("svg"); //选择body中的svg元素
// 选择所有指定元素
var rects = svg.selectAll("rect"); //选择svg中所有的rect元素
// 根据id选择元素,id前面要加#
var p2 = body.select("#pear-id");
// 根据class选择元素,class名称前加.
var p = body.selectAll(".myclass");
绑定数据
datum()
是绑定一个数据,data()
是绑定一组数据。
// 选中body下所有的p元素
var body = d3.select("body");
var p = body.selectAll("p");
// datum()
var str = "World";
// 把数据绑定到选择集上
p.datum(str);
// 把选择集的每个p元素的text属性(文字内容)都改成str
// function(d, i)是一个无名函数,d是数据,i是index
// 因为datum只绑了一个数据,所以所有的d都是World
p.text(function(d, i){
return "第 "+ i + " 个元素绑定的数据是 " + d;
});
// data()
var dataset = ["I like dog","I like cat","I like snake"];
p.data(dataset)
.text(function(d, i){
return d;
}); // 这样3个p元素会依次显示对应的字符串
插入和删除元素
// 在末尾增加元素
body.append("p")
.text("append p element");
// 在某个元素之前增加元素,需要id
body.insert("p","#pear-id") // 在pear-id元素前增加一个p元素
.text("insert p element"); // 指定p元素的内容
// 删除元素需要指定id
var p = body.select("#pear-id");
p.remove();
绘图
绑定数据的水平条形图
代码:
<svg width="1000" height="1000">
</svg>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
var dataset = [ 250 , 210 , 170 , 130 , 90 ]; //数据(表示矩形的宽度)
var rectHeight = 25; //每个矩形所占的像素高度(包括空白)
const svg = d3.select("svg"); // 选择svg元素
svg.selectAll("rect") // 选择所有矩形
.data(dataset) // 绑定数组
.enter()
.append("rect") // 添加足够数量的矩形元素
.attr("x", 20) // 矩形的x坐标设为20,左上顶点的坐标
.attr("y", function(d, i){ // d代表数据,i是索引
return i * rectHeight;
})
.attr("width",function(d){ // d其实是dataset里每个数据
return d;
})
.attr("height",rectHeight - 2)
.attr("fill","steelblue"); // 颜色
</script>
关于enter()
的说明:
d3在处理数据集和选择集(待绑定的元素)数量不符合的情况时提出了3个概念,分别是enter
、update
和exit
。
假设选择集是3个p元素,
- 当数据集也是3个数据时,这3个p元素称为
update
,update
可以更新属性值; - 当数据集有5个元素时,会有2个数据绑定不上,此时d3会建立两个空元素与数据对应,这俩空的就是
enter
。所以enter()
后可以跟append
去添加元素; - 当数据集只有1个元素时,另外俩元素没有数据绑定,被称为
exit
,此时可以修改属性,但是大部分时候都是要删除它。进入的方法跟.enter()
一样,就是比如.exit().remove()
。
比例尺
d3中引入比例尺可以更容易地放缩图形,不必写死像素数目。在数学中,x的范围是定义域,y的范围是值域,d3中对应的是domain和range,我们需要指定这俩。
- 线性比例尺
就是将domain等比例地映射到range。
var dataset = [1.2, 2.3, 0.9, 1.5, 3.3];
var min = d3.min(dataset); // 最小值
var max = d3.max(dataset); // 最大值
var scaleLinear = d3.scaleLinear()
.domain([min, max])
.range([0, 300]);
scaleLinear(0.9); //返回 0. 闭区间的
scaleLinear(2.3); //返回 175
scaleLinear(3.3); //返回 300
上面水平条形图可以用scaleLinear()修改,效果是一样的。
- 序数比例尺
domain和range是离散的情况,使用序数一一对应。
var index = [0, 1, 2, 3, 4];
var color = ["red", "blue", "green", "yellow", "black"];
var scaleOrdinal = d3.scaleOrdinal()
.domain(index)
.range(color);
scaleOrdinal (0); //返回 red
scaleOrdinal (2); //返回 green
坐标轴
直接用实例。效果如下:
<svg width="500" height="300"></svg>
<script>
// 1. 定义 SVG 画布的宽度和高度
const width = 500;
const height = 300;
const margin = { top: 20, right: 20, bottom: 30, left: 40 };
// 2. 创建一个线性比例尺,用于将数据映射到 SVG 画布的宽度
const xScale = d3.scaleLinear()
.domain([0, 10]) // 数据范围
.range([margin.left, width - margin.right]); // 画布范围
// 3. 创建一个比例尺,用于将数据映射到 SVG 画布的高度
const yScale = d3.scaleLinear()
.domain([0, 100]) // 数据范围
.range([height - margin.bottom, margin.top]); // 画布范围
// 4. 创建 X 轴生成器
const xAxis = d3.axisBottom(xScale);
// 5. 创建 Y 轴生成器
const yAxis = d3.axisLeft(yScale);
const svg = d3.select("svg");
// 6. 选择 SVG 元素并添加 X 轴
svg.append("g")
.attr("transform", `translate(0, ${height - margin.bottom})`) // 将 X 轴移动到画布底部
.call(xAxis);
// 7. 选择 SVG 元素并添加 Y 轴
svg.append("g")
.attr("transform", `translate(${margin.left}, 0)`) // 将 Y 轴移动到画布左侧
.call(yAxis);
</script>
说明:
- d3提供4种坐标轴生成器:
d3.axisBottom(scale)
是刻度向下的,d3.axisLeft()
是刻度向左的,d3.axisTop()
和d3.axisRight()
同理 - 坐标轴默认是从(0, 0)点开始绘制的,所以需要移动
.attr("transform", "translate(x, y)")
表示平移svg元素,x
和y
表示在x轴和y轴移动的距离call()
是将当前选择集作为参数传递给一个函数,所以svg.append("g").call(xAxis)
就是将x轴生成器应用到g元素上(等价于xAxis(svg.append(g))
)- 如果要修改axis的格式,可以在
<style>
里定义,然后引用,比如下面这种:
<style>
.axis path,
.axis line{
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
</style>
...
<script>
//添加y轴
svg.append("g")
.attr("class","axis") // 这句
.attr("transform", `translate(${margin.left}, 0)`)
.call(yAxis);
</script>