文章目录
D3.js是什么?
D3 stands for Data-Driven Documents. D3.js 是一个用于根据数据操作文档的 JavaScript 库。 D3.js 是一个在大量网站中使用的动态、交互式、在线数据可视化框架。
学习D3.js的基础
- HTML
- DOM
- CSS
- SVG 可伸缩矢量图
- Javascript
D3.js 选择DOM
D3.js 数据操作大致两步,首先选择绑定DOM元素,然后对选择的DOM元素进行操纵。因此元素选择是第一步。
两种选择方法
d3.select() 的选择
1.按元素名称选择
以下示例演示了使用 d3.select() 按标签名称选择第一个匹配元素。
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<p>First paragraph</p>
<p>Second paragraph</p>
<script>
d3.select("p").style("color", "green");
</script>
</body>
</html>
程序运行结果:只有第一个段落文本变为绿色
2.按元素id选择
select()参数为"#id",如下代码将第二个段落选择并将其文本颜色设置为绿色
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<p id="p1">First paragraph</p>
<p id="p2">Second paragraph</p>
<script>
d3.select("#p2").style("color", "green");
</script>
</body>
</html>
d3.selectAll() 的选择
d3.selectAll() 方法根据指定的 CSS 选择器返回 HTML 文档中所有匹配的元素。如下代码 d3.selectAll(“p”) 返回所有 p 标签元素, .style(“color”,“green”) 使其字体颜色为绿色
<p>First paragraph</p>
<p>Second paragraph</p>
<script>
d3.selectAll("p").style("color", "green");
</script>
程序运行结果如图
按照CSS类名选择所有元素
以下示例代码展示了按照CSS类名称选择元素并将其文本颜色设为绿色,selectAll()的参数为".className"
<!doctype html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
.myclass{
color:red
}
</style>
</head>
<body>
<p class="myclass ">First paragraph</p>
<p>Second paragraph</p>
<p class="myclass ">Third paragraph</p>
<script>
d3.selectAll(".myclass ").style('color','green');
</script>
</body>
</html>
程序运行结果
选择嵌套元素
select() 和 selectAll() 可用于选择嵌套元素,示例如下:
<table>
<tr>
<td>
One
</td>
<td>
Two
</td>
</tr>
<tr>
<td>
Three
</td>
<td>
Four
</td>
</tr>
</table>
<script>
d3.select("tr").selectAll("td").style('background-color','yellow');
</script>
select() 方法将第一个tr标签元素选择,selectAll将第一个tr标签下所有的td标签元素选择,style()方法将背景颜色置为黄色。
在调用 select() 方法后立即调用 selectAll() 方法 称为 Method Chaining 函数链
程序运行结果如图:
D3.js 操纵DOM
D3包括以下DOM操作方法
text() 方法
text()方法添加或修改被选中元素的文本内容,以下示例展示了向一个DOM元素中添加文本信息
在这个例子中,仅 div标签中的p标签被选中,text()方法将该标签文本设置为"This is paragraph"
<div>
<p></p>
</div>
<p></p>
<script>
d3.select("p").text("This is paragraph.")
</script>
append() 方法
append() 方法创建一个新的 DOM 元素并将其添加到被选中的元素的末尾,d3.select(“body”) 返回 body 元素,.append(“p”) 创建一个新的 p 元素并将其添加到 body 末尾之前
例如下面这段示例代码执行后,查看运行结果的网页页面元素信息可看到,p标签个数为3个
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<p>First paragraph</p>
<p>Second paragraph</p>
<script>
d3.select("body").append("p");
</script>
</body>
</html>
程序运行结果
你可以使用 text() 方法向新元素添加文本,如下所示
<p>First paragraph</p>
<p>Second paragraph</p>
<script>
d3.select("body").append("p").text("Third paragraph.");
</script>
执行结果页面审查信息如下图:
insert() 方法
insert()与append()是d3中用于新增元素的方法,它们的用法完全相同,作用也完全相同,唯一的差别在于 insert() 可以指定将新元素插入在什么位置,而 append() 只能将元素添加在末尾
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<div style="border:1px solid">
<p>First paragraph.</p>
</div>
<script>
d3.select("div").insert("p").text("Second paragraph.");
</script>
</body>
</html>
运行结果页面审查信息:
remove() 方法
remove() 方法用于删除选中的 DOM 元素
下面这个例子中,有两个p标签元素,select()选中第一个,remove() 方法将其从文档删除
<p>First paragraph</p>
<p>Second paragraph</p>
<script>
d3.select("p").remove();
</script>
html() 方法
html() 方法用于设置被选中元素内部的 html,示例程序如下:
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<p>First paragraph</p>
<script>
d3.select("p").html("<span>This is new inner HTML</span>");
</script>
</body>
</html>
上述程序将 p 标签内的内容整体替换为 html() 方法中参数内容,也就是"<span>This is new inner HTML</span>" 替换 First paragraph 执行程序后审查页面信息如下图:
attr() 方法
attr() 方法在选定的 DOM 元素上应用属性, 如下示例将p标签选中并将其文本颜色设置为红色
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
.error {
color: red
}
</style>
</head>
<body>
<p>Error: This is dummy error.</p>
<script>
d3.select("p").attr("class","error");
</script>
</body>
</html>
程序运行结果:
property() 方法
某些元素的属性不能通过 attr() 方法设置,例如复选框或单选按钮的选中属性。 在这种情况下,使用 property() 方法在选定的 DOM 元素上应用属性。
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<p>D3</label><input type="checkbox" />
<p>jQuery</label><input type="checkbox" />
<script>
d3.select("input").property("checked",true);
</script>
</body>
</html>
例如上面的程序 select() 选中第一个 checkbox ,并将选中设为 true,程序执行结果如图:
style() 方法
使用 d3.selection.style() 方法将具有指定名称和值的样式属性应用于所选元素。
<p>Error: This is dummy error.</p>
<script>
d3.select("p").style("color", "red")
</script>
程序执行结果:
classed() 方法
使用 d3.selection.classed() 方法设置 class 属性或修改 classList 属性到被选元素。
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
.error {
color: red
}
</style>
</head>
<body>
<p>This is error.</p>
<script>
d3.select("p").classed('error', true);
</script>
</body>
</html>
我们可以使用 classed() 方法将 css 的类class 应用或删除到我们的选择中。 在此示例中,d3.select(“p”) 选择 <p> 元素,然后 classed(‘error’, true) 将类 error 应用于所选的 <p> 元素。 分类方法中的第二个属性是布尔值。 如果传递为 true,它会将类添加到所选元素,如果为 false,则会从所选元素中删除该类。
D3 方法链
在前面的部分中,我们编写了用点“连接”彼此的 D3 函数。 你可能会好奇 这称为 chain syntax “链式语法”。 如果你熟悉 JQuery,那么您可能对以下内容很熟悉。
$("#myDiv").text("Some text").attr("style", "color:red")
D3 使用类似的技巧,方法使用句点 . 链接在一起
d3.select("body").append("p").text("Hello World!");
第一个方法的输出作为输入传递给链中的下一个方法,可将其视为过滤通道。 第一个方法过滤掉部分内容并将过滤后的内容的引用提供给下一个方法,下一个方法进一步过滤并将引用传递给下一个方法等等。
现在我们可以写一个不使用方法链的D3代码如下:
var bodyElement = d3.select("body");
var paragraph = bodyElement.append("p");
paragraph.text("Hello World!");
但方法链是一个简洁的实现,与上面代码相同实现的方法链代码如下:
d3.select("body").append("p").text("Hello World!");
上述例子中,
d3.select("body")
使用方法链后,在 DOM 中,D3 的 select() 方法选择 body 元素并将选择的引用返回给链中的下一个方法,即 append()。
append("p")
从 append() 接收到的过滤内容来看,append() 方法仅适用于它接收到的引用元素。 在本例中,它是 body 元素。 现在,它创建一个新的 <p> 元素并将其附加到它接收到的元素,并将这个新元素返回给链中的下一个方法
text("Hello World!")
text() 方法从前一个方法接收段落元素并添加提供给它的文本
这里值得一提的是,可以通过在新行上编写每个方法,以更易读的格式编写链方法,这肯定会提高可读性。如下:
d3.select("body")
.append("p")
.text("Third paragraph");
D3 数据 function
在上面操纵DOM部分,我们了解了 D3 中不同的 DOM 操作方法,例如 append()、style()、text() 等。这些函数中的每一个都可以接受一个常量值或一个 function 作为参数。 这个function是数据的函数。 因此,对于绑定到 DOM 的每个数据值,将调用这些方法中的每一个。 考虑以下 text() 函数。
.text(function(d) {
return d;
});
在这个函数中,我们可以应用任何逻辑来操作数据。 这些是匿名函数,这意味着没有与该函数相关联的名称。
除了数据(或 d)参数外,还有两个其他参数可供我们使用。
.text(function (d, i) {
console.log(d); // the data element
console.log(i); // the index element
console.log(this); // the current DOM object
return d;
});
考虑下面这个示例程序:
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<p></p>
<p></p>
<p></p>
<script>
var data = [100, 200, 300];
var paragraph = d3.select("body")
.selectAll("p")
.data(data)
.text(function (d, i) {
console.log("d: " + d);
console.log("i: " + i);
console.log("this: " + this);
return d;
});
</script>
</body>
</html>
注意,我们在上面调用了 .data(data) 函数。 data() 函数为所选元素提供数据,在我们的例子中是数组数据。 下面会在数据绑定部分再讨论 data() 函数。
参数 d 给出数据元素,i 给出数组下标信息,this 是当前DOM元素的引用,本例中是 段落元素,程序执行控制台打印的 log 信息如下图:
D3 动态属性
在操作 DOM 元素的同时,我们可能还想向我们的元素添加某些特性或属性。 有时,我们可能希望这些属性绑定到我们的数据或由我们的数据驱动。
数据函数可用于根据我们的数据或业务逻辑动态设置属性。 例如,如果我们想根据段落的内容为段落着色,可以在样式属性函数中执行此操作。
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<p>Error: This is error.</p>
<p>Warning:This is warning.</p>
<script>
d3.selectAll("p").style("color", function(d, i) {
var text = this.innerText;
if (text.indexOf("Error") >= 0) {
return "red";
} else if (text.indexOf("Warning") >= 0) {
return "yellow";
}
});
</script>
</body>
</html>
在上面的示例中,d3.selectAll(“p”) 选择所有 <p> 元素,.style () 方法根据给定函数的返回语句将颜色属性应用于所选元素。 在这个函数中,我们使用了一些逻辑来检查当前 <p> 元素的文本是否包含诸如“Error”或“Warning”之类的关键字。 如果它包含关键字“Error”,我们返回红色,或者如果它包含关键字“Warning”,我们返回黄色。
程序执行结果如图:
D3 数据绑定
这部分介绍如何绑定数据到 DOM 元素上 和基于你的数据创建新的 DOM 元素,D3 包含以下几种重要的数据绑定方法
data() 方法
data() 函数用于将指定的数组数据连接到选定的 DOM 元素并返回更新后的选择。 D3 支持不同类型的数据,如 Array、CSV、TSV、JSON、XML 等。
可以将两种类型的值传递给 data() 函数,数组元素(数值或对象) 或 数据函数。
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<p>D3 Tutorials</p>
<script>
var myData = ["Hello World!"];
var p = d3.select("body")
.selectAll("p")
.data(myData)
.text(function (d) {
return d;
});
</script>
</body>
</html>
在上面的示例中,我们有一个段落元素 <p>D3 Tutorials</p>。 我们创建了一个名为“myData”的数组,其中包含我们要绑定到 <p> 元素的单个字符串“Hello World”。
这个过程如何工作如下:
d3.select("body")
选中 HTML 的 body 元素
.selectAll("p")
返回所有的段落元素, 本例只有一个
.data(myData)
data() 函数将我们的数组数据“myData”绑定到先前选择返回的对象。 由于我们的选择具有单个 p 元素,data() 函数会将数组中的第一个值绑定到 <p> 元素
.text(function(d, i) { return d; });
此文本函数将数据作为文本添加到我们的每个选择元素中。 每个数组值都作为第一个参数 (d) 传递给文本函数
注意,需要将数组传递给 data() 函数。 如果提供常量值给 data() 函数,它不会执行任何操作。例如下面这个程序不会显示任何信息,因为 data() 要求提供数组
下面这个示例程序展示如何将数据加入到多个DOM元素中。
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<p> </p>
<p> </p>
<p> </p>
<script>
var myData = ["Hello World!", "Hello D3","Hello JavaScript"];
var p = d3.select("body")
.selectAll("p")
.data(myData)
.text(function (d, i) {
return d;
});
</script>
</body>
</html>
程序运行结果:
上面这个例子中,myData 数组元素个数 = <p> 标签个数 = 3,所以每个字符串各自绑定到每个段落标签上,我们来看另一个例子,
<body>
<p>D3 Tutorials </p>
<script>
var myData = [1, 2, 3, 4, 5];
var p = d3.select("body")
.selectAll("p")
.data(myData)
.text(function (d, i) {
return d;
});
</script>
</body>
上面这个例子,数组元素个数 不等于 DOM 元素个数,我们不知道数据集中的数据元素和 DOM 元素的数量,d3 提供了 enter() 函数来处理这种情况。
enter() 方法
当数组数据元素个数不等于 DOM 元素个数时,仅使用 select() 和 selectAll() 方法无法得到期待的选择,
enter() 方法动态创建对应于数据值数量的占位符引用。 enter() 的输出可以提供给 append() 方法,append() 将创建相应 DOM 元素。
在下面这个示例程序中,enter() 方法会创建6个占位符,紧接着 append() 方法动态创建 6 个 <span> 元素
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<script>
var data = [4, 1, 6, 2, 8, 9];
var body = d3.select("body")
.selectAll("span")
.data(data)
.enter()
.append("span")
.text(function(d) { return d + " "; });
</script>
</body>
</html>
程序执行结果页面审查信息如下图:
我们看看上面这个程序执行过程:
d3.select("body")
选中 HTML 的 body 部分
.selectAll("span")
由于在 body 中没有 <span> 元素所以返回空数组
.data(data)
我们将 data 数组提供给 data() 方法,由于数组有6个元素,所以这行代码之后的代码会运行 6 次
.enter()
enter() 方法会检查对应于每个数组元素的 <span> 元素, 由于没有找到任何 <span> 元素 ,它会为每个数组元素创建
.append()
在上面已经创建的 <span> 标签后追加创建
exit() 方法
exit() 方法用于去除结点,在下面的代码中,所有\
元素都将被删除。 使用 exit(),元素进入退出阶段。 这意味着所有退出的元素都存储在一些不可见的地方,准备好在给出命令时被删除。 该命令是 remove()。 remove() 将从 DOM 中删除所有“退出”节点。
<body>
<p>D3 Tutorials</p>
<p></p>
<p></p>
<script>
var myData = ["Hello World!"];
var p = d3.select("body")
.selectAll("p")
.data(myData)
.text(function (d, i) {
return d;
})
.exit()
.remove();
</script>
</body>
在上面的示例中,HTML 包含三个 <p> 元素,而数据数组仅包含一个数据值。 因此, .exit().remove() 删除了额外的 <p> 元素。
datum() 方法
datum() 函数用于不需要更新的静态可视化。 它将数据直接绑定到元素。
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<p>D3 Tutorials</p>
<script>
d3.select("body")
.select("p")
.datum(100)
.text(function (d, i) {
return d;
});
</script>
</body>
</html>
D3 数据加载
D3 提供如下几种方法从外部文件加载不同类型的数据
d3.csv()
可以使用 d3.csv() 方法加载 csv 文件或 csv 数据
d3.csv(url[, row, callback]);
第一个参数是 .csv 文件的 url,或者 webapi,或者将返回 csv 数据的 webservice。 第二个可选参数是一个转换函数,它允许我们改变表示方式。 第三个可选参数是一个回调函数,一旦加载了 .csv 文件/数据,就会执行该函数。 它将解析的数据对象作为参数传递给回调函数。
例如创建一个如下的csv文件 employees.csv
Name, Age
John, 30
Jane, 32
然后把这个csv文件存放到和 html 文件相同文件夹下面。运行如下 html 文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" path1tent=
"width=device-width,initial-scale=1.0">
</head>
<body>
<script src=
"https://d3js.org/d3.v4.min.js">
</script>
<script>
d3.csv("employees.csv", function(data) {
for (var i = 0; i < data.length; i++) {
console.log(data[i].Name);
console.log(data[i].Age);
}
});
</script>
</body>
</html>
可在控制台看到如下打印信息:
将 function 里面的代码修改如下:
d3.csv("employees.csv", function(data) {
console.log(data);
});
重新执行查看控制台:
可以看到,d3.csv() 将数据作为 object 对象返回。 该对象是从我们的 csv 文件加载的一组对象,其中每个对象代表 csv 文件的一行。
d3.json() 方法
JSON 数据可以是单个或一组JSON对象
单个JSON对象示例:
var nameObj = {
"name": "John",
"age": 30,
"city": "New York"
};
一组JSON对象示例:
var nameArray = [{
"name": "John",
"age": 30,
"city": "New York"
},
{
"name": "Jane",
"age": 20,
"city": "San Francisco"
}];
JSON和CSV方法类似,d3.json() 方法将 JSON 文件作为输入并将其转换为对象数组, 语法如下
d3.json(input[, init]);
第一个参数是 .json 文件的 url,第二个参数是一个回调函数,一旦加载 .json 文件就会执行。 它将解析的数据对象作为参数传递给回调函数。
现在我们创建一个JSON文件,内容如下:
{
"name":"Jane",
"age":20,
"city":"San Francisco"
}
将其放在 html 相同文件夹下,然后打开 html 页面查看控制台如图:
d3.tsv()方法
语法
d3.tsv(url, callback);
我们创建一个如下的employees.tsv文件
Name Age
John 30
Jane 32
然后运行如下html代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" path1tent=
"width=device-width,initial-scale=1.0">
</head>
<body>
<script src=
"https://d3js.org/d3.v4.min.js">
</script>
<script>
d3.tsv("employees.tsv", function(data) {
for (var i = 0; i < data.length; i++) {
console.log(data[i].Name);
console.log(data[i].Age);
}
});
</script>
</body>
</html>
可以查看控制台输入如图:
d3.xml() 方法
d3.xml() 方法接受 xml 文件的 url 并返回一个 xml 对象。
语法
d3.xml(url, callback);
我们首先创建一个 employees.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<root>
<row>
<Name>John</Name>
<Age>30</Age>
</row>
<row>
<Name>Jane</Name>
<Age>32</Age>
</row>
</root>
然后执行如下 html 代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" path1tent=
"width=device-width,initial-scale=1.0">
</head>
<body>
<script src=
"https://d3js.org/d3.v4.min.js">
</script>
<script>
d3.xml("employees.xml", function(data) {
console.log(data);
});
</script>
</body>
</html>
可以看到控制台输出如图:
绑定加载的数据
我们首先创建一个 users.json 文件
[{
"name": "Jon",
"age": 30,
"location": "The Wall"
},
{
"name": "Arya",
"age": 12,
"location": "Braavos"
},
{
"name": "Cersei",
"age": 42,
"location": "Kings Landing"
},
{
"name": "Tyrion",
"age": 40,
"location": "Kings Landing "
}]
然后让我们使用 d3.json() 方法加载上面的 json 数据并将其与 DOM 元素绑定。
d3.json("users.json", function(error, data) {
d3.select("body")
.selectAll("p")
.data(data)
.enter()
.append("p")
.text(function(d) {
return d.name + ", " + d.location;
});
});
然后运行如下代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" path1tent=
"width=device-width,initial-scale=1.0">
</head>
<body>
<script src=
"https://d3js.org/d3.v4.min.js">
</script>
<script>
d3.json("users.json", function(error, data) {
d3.select("body")
.selectAll("p")
.data(data)
.enter()
.append("p")
.text(function(d) {
return d.name + ", " + d.location;
});
});
</script>
</body>
</html>
我们在浏览器可以看到如下图
![在这里插入图片描述](https://img-blog.csdnimg.cn/fb0a7973a3674f55a10b4e9f4760c4f5.png
我们把这段代码的过程分析一下
d3.json("users.json", function(error, data) {
该行代码将users.json文件的数据加载,返回格式化的数据对象传递到回调函数的data,也就是第二个参数
d3.select("body")
我们有了数据对象后,我们想把这些内容输出到我们的页面,这里选择输出到 body 部分
.selectAll("p")
我们进一步选择将数据输出到 body 的所有段落部分
.data(data)
我们将加载的数据对象传递给 data()函数,绑定数据到段落,该 data() 函数将我们数据集中的数据值传递给链中的下一个方法。
.enter()
enter() 函数从 data() 接收值。 在我们的例子中,有四个值。 但是由于我们还没有对与这些数据值对应的
元素的引用,因此 enter() 返回对新元素的空占位符引用。
.append("p")
我们现在有了对元素的引用。 append() 方法将这些元素添加到 DOM。
.text(function(d) { return d.name + ", " + d.location; });
最后是 text() 方法。 大多数 d3 函数接受函数作为参数。 在我们的例子中,我们已经将一个匿名函数传递给文本方法,该方法从我们的数据对象返回名称和位置的串联。 调用 text() 并将其绑定到具有相应数据值的每个页面元素。
出错处理
从外部源加载数据时,D3 返回一个名为“error”的参数。 可以使用此参数来检查我们的数据是否已成功加载
d3.json("users.json", function(error, data) {
if (error) {
return console.warn(error);
}
d3.select("body")
.selectAll("p")
.data(data)
.enter()
.append("p")
.text(function(d) {
return d.name + ", " + d.location;
});
});
如果加载数据时出现错误,说数据格式不正确; D3 将返回一个错误对象。 我们可以检查错误并据此做出决定。
创建一个简单的SVG图表
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
svg rect {
fill: orange;
}
svg text {
fill:white;
font: 10px sans-serif;
text-anchor: end;
}
</style>
</head>
<body>
<script>
var data = [5, 10, 12];
var width = 200,
scaleFactor = 10,
barHeight = 20;
var graph = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", barHeight * data.length);
var bar = graph.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function(d, i) {
return "translate(0," + i * barHeight + ")";
});
bar.append("rect")
.attr("width", function(d) {
return d * scaleFactor;
})
.attr("height", barHeight - 1);
bar.append("text")
.attr("x", function(d) { return (d*scaleFactor); })
.attr("y", barHeight / 2)
.attr("dy", ".35em")
.text(function(d) { return d; });
</script>
</body>
</html>
比例尺
比例尺就是把一组输入域映射到输出域的函数。映射就是两个数据集之间元素相互对应的关系。比如输入是1,输出是100,输入是5,输出是10000,那么这其中的映射关系就是你所定义的比例尺。
D3中有各种比例尺函数,有连续性的,有非连续性的
d3.scaleLinear() 线性比例尺
使用d3.scaleLinear()
创造一个线性比例尺,而domain()
是输入域,range()
是输出域,相当于将domain中的数据集映射到range的数据集中。
let scale = d3.scaleLinear().domain([1,5]).range([0,100])
映射关系:
接下来,我们来研究这个比例尺的输入和输出。
scale(1) // 输出:0
scale(4) // 输出:75
scale(5) // 输出:100
刚才的输入都是使用了domain区域里的数据,那么使用区域外的数据会得出什么结果呢?
scale(-1) // 输出:-50
scale(10) // 输出:225
所以这只是定义了一个映射规则,映射的输入值并不局限于domain()
中的输入域
d3.scaleBand() 序数比例尺
d3.scaleBand()
并不是一个连续性的比例尺,domain()
中使用一个数组,不过range()
需要是一个连续域。
let scale = d3.scaleBand().domain([1,2,3,4]).range([0,100])
映射关系
看一下输入与输出
scale(1) // 输出:0
scale(2) // 输出:25
scale(4) // 输出:75
当输入不是domain()
中的数据集时
scale(0) // 输出:undefined
scale(10) // 输出:undefined
由此可见,d3.scaleBand()
只针对domain()
中的数据集映射相应的值。
d3.scaleOrdinal() 序数比例尺
d3.scaleOrdinal()
的输入域和输出域都使用离散的数据
let scale = d3.scaleOrdinal().domain(['jack', 'rose', 'john']).range([10, 20, 30])
映射关系
输入与输出:
scale('jack') // 输出:10
scale('rose') // 输出:20
scale('john') // 输出:30
当输入不是domain()中的数据集时
scale('tom') // 输出:10
scale('trump') // 输出:20
输入不相关的数据依然可以输出值。所以在使用时,要注意输入数据的正确性。
我们从上面的映射关系中可以看出,domain()
和range()
的数据是一一对应的,如果两边的值不一样呢?下面两张图说明这个问题:
domain()
的值按照顺序循环依次对应range()
的值。
d3.scaleQuantize() 量化比例尺
d3.scaleQuantize()
也属于连续性比例尺。定义域是连续的,而输出域是离散的。
let scale = d3.scaleQuantize().domain([0, 10]).range(['small', 'medium', 'long'])
映射关系
输入与输出
scale(1) // 输出:small
scale(5.5) // 输出:medium
scale(8) // 输出:long
而对于domain()
域外的情况
scale(-10) // 输出:small
scale(10) // 输出:long
大概就是对于domain()
域的两侧的延展
d3.scaleTime() 时间比例尺
d3.scaleTime()
类似于d3.scaleLinear()
线性比例尺,只不过输入域变成了一个时间轴
let scale = d3.scaleTime()
.domain([new Date(2017, 0, 1, 0), new Date(2017, 0, 1, 2)])
.range([0,100])
输入与输出
scale(new Date(2017, 0, 1, 0)) // 输出:0
scale(new Date(2017, 0, 1, 1)) // 输出:50
时间比例尺较多用在根据时间顺序变化的数据上。另外有一个d3.scaleUtc()
是依据世界标准时间(UTC)来计算的。
我们用比例尺重新画一下上面的条形图
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>scaleLinear</title>
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<svg width="100%" height="300"></svg>
</body>
<script>
var data = [7.3,5.3,3.3,2.3,1.3];
//定义比例尺
var scaleLinear = d3.scaleLinear().domain([0,d3.max(data)]).range([0,800]);
var g = d3.select('svg')
.append('g')
.attr('transform','translate(30,30)');
var rectHeight = 30;
g.selectAll('rect')
.data(data)
.enter()
.append('rect')
.attr('x',0)
.attr('y',function (d,i) {
return rectHeight*i;
})
.attr('width',function (d,i) {
return scaleLinear(d);//在这里的得到 映射的宽度
})
.attr('height',rectHeight - 5)
.attr('fill','pink');
</script>
</html>
效果如下图
坐标轴
D3 提供下面几种典型的函数来画坐标轴
d3.axisTop()
− 创建水平刻度向上的坐标轴d3.axisRight()
−创建垂直刻度向右的坐标轴d3.axisBottom()
− 创建水平刻度向下的坐标轴d3.axisLeft()
−创建垂直刻度向左的坐标轴
一个示例
- 定义变量
var width = 400, height = 400;
var data = [100, 150, 200, 250, 280, 300];
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
- 创建比例尺
var xscale = d3.scaleLinear()
.domain([0, d3.max(data)])
.range([0, width - 100]);
var yscale = d3.scaleLinear()
.domain([0, d3.max(data)])
.range([height/2, 0]);
- 添加比例尺到 x, y 坐标轴
var x_axis = d3.axisBottom()
.scale(xscale);
var y_axis = d3.axisLeft()
.scale(yscale);
上面这种写法和下面这种效果是一样的
var x_axis = d3.axisBottom(xscale);
var y_axis = d3.axisLeft(yscale);
- 添加分组元素并应用转换
svg.append("g")
.attr("transform", "translate(50, 10)")
.call(y_axis);
var xAxisTranslate = height/2 + 10;
svg.append("g")
.attr("transform", "translate(50, " + xAxisTranslate +")")
.call(x_axis)
完整示例程序
<html>
<head>
<script type = "text/javascript" src = "https://d3js.org/d3.v4.min.js"></script>
<style>
svg text {
fill: purple;
font: 12px sans-serif;
text-anchor: end;
}
</style>
</head>
<body>
<script>
var width = 400, height = 400;
var data = [100, 120, 140, 160, 180, 200];
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var xscale = d3.scaleLinear()
.domain([0, d3.max(data)])
.range([0, width - 100]);
var yscale = d3.scaleLinear()
.domain([0, d3.max(data)])
.range([height/2, 0]);
var x_axis = d3.axisBottom().scale(xscale);
var y_axis = d3.axisLeft().scale(yscale);
svg.append("g")
.attr("transform", "translate(50, 10)")
.call(y_axis);
var xAxisTranslate = height/2 + 10;
svg.append("g")
.attr("transform", "translate(50, " + xAxisTranslate +")")
.call(x_axis)
</script>
</body>
</html>
运行结果如图
条形图
示例程序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<svg width="500" height="400"></svg>
</body>
<script>
var data = [90,75,12,36,54,88,24,66];
var margin = 30;//上下左右边距
var svg = d3.select('svg');
var width = svg.attr('width');
var height = svg.attr('height');
//创建一个矩形分组
var g = svg.append('g').attr('transform','translate('+ margin +','+ margin +')');
//定义x轴比例尺
var scaleX = d3.scaleBand()
.domain(d3.range(data.length))//[0, 1, 2, 3, 4, 5, 6, 7]
.rangeRound([0,width - margin*2]);//左右边距30
//定义Y轴比例尺
var scaleY = d3.scaleLinear().domain([0,d3.max(data)]).range([height - margin*2,0]);//上边距30 这里还得注意 range后面跟的参数 0放在第二位 因为y轴正方向是反的
//绘制x y 轴
var axisX = d3.axisBottom(scaleX);
var axisY = d3.axisLeft(scaleY);
g.append('g').attr('transform','translate(0,'+ (height - margin*2) +')').call(axisX);
g.append('g').attr('transform','translate(0,0)').call(axisY);
var rectP = 30;//柱状图间距
var gs = g.selectAll('rect').data(data).enter().append('g');//分组
//绘制矩形
gs.append('rect')
.attr('x',function (d,i) {
return scaleX(i)+rectP/2;
}).attr('y',function (d,i) {
return scaleY(d);
}).attr('width',function () {
return scaleX.step() - rectP;
}).attr('height',function (d,i) {
return height - margin*2 - scaleY(d);
}).attr('fill','pink');
//绘制文字
gs.append('text')
.attr('x',function (d,i) {
return scaleX(i) + rectP/2;
})
.attr('y',function (d,i) {
return scaleY(d);
})
.text(function (d,i) {
return d;
});
</script>
</html>
运行结果
动画
事件交互
饼图
示例程序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>pie</title>
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<svg width="500" height="400"></svg>
</body>
<script>
//准备数据
var margin = 30;//边距
var data = [56,21,11,85,42,66];//绘制饼图所需数据
var svg = d3.select('svg');
var width = svg.attr('width');
var height = svg.attr('height');
//创建一个分组 并设置偏移
var g = svg.append('g').attr('transform','translate('+ margin +','+ margin +')');
//为了让饼图不同区域显示不同颜色 我们先创建一个颜色比例尺 用之前我们讲到的序数比例尺
var scaleColor = d3.scaleOrdinal()
.domain(d3.range(data.length))
.range(d3.schemeCategory10);
//创建一个饼图
var pie = d3.pie();
//创建一个弧形生成器
var arc = d3.arc()
.innerRadius(0)//设置环的内半径.
.outerRadius(100)//设置环的外半径.
//除了内半径与外半径 还可以设置很多 如下
// arc.cornerRadius - 设置拐角半径.
// arc.startAngle - 设置起始角度.
// arc.endAngle - 设置终止角度.
// arc.padAngle - 设置相邻两个环之间的间隙角度.
// arc.padRadius - 设置半径间隔.
// arc.context - 设置渲染上下文.
//利用pie 转换数据
var pieData = pie(data);
console.log(pieData);//看看转换成了啥
//创建扇形分组
var gs = g.selectAll('.g')
.data(pieData)
.enter()
.append('g')
.attr('transform','translate('+ width/2 +','+ height/2 +')');
//绘制扇形
gs.append('path')
.attr('d',function (d,i) {
return arc(d);//给弧形生成器添加数据
})
.attr('fill',function (d,i) {
return scaleColor(i);//给扇形填充颜色
})
//绘制文字
gs.append('text')
.attr('transform',function (d,i) {
return 'translate('+ arc.centroid(d) +')';//位置设置在扇形中心处
})
.attr("text-anchor","middle")
.text(function (d,i) {
return d.data;
})
</script>
</html>
运行结果