效果图
数据集examples-multiple.tsv
x y1 y2
1.0 0.001 0.63
3.0 0.003 0.84
4.0 0.024 0.56
4.5 0.054 0.22
4.6 0.062 0.15
5.0 0.100 0.08
6.0 0.176 0.20
8.0 0.198 0.71
9.0 0.199 0.65
example2-5.ftl
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<script src="../js/d3.js"></script>
<title>双数据集图表-示例2-5</title>
</head>
<body onload="makeDemo2()">
<svg id="demo2" width="600" height="300" style="background: lightgrey">
</svg>
</body>
<script>
function makeDemo2() {
d3.tsv("../tsv/examples-multiple.tsv")
.then(function (data) {
// svg区域的长度和宽度
var pxX = 600, pxY = 300;
// 线性比例尺将数据值从它们的自然定义域按比例缩放到图表的像素范围内,除此之外,还有对数比例尺和幂比例尺
// 比例尺是函数对象
var scX = d3.scaleLinear()
// domin()方法和range()方法所规定的参数都是一个双元素数组。
// d3.extend()方法,接收一个对象数组,返回由数组中最大值和最小值组成的双元素数组
.domain(d3.extent(data, d => d["x"]))
.range([0, pxX]);
// 数据集有三列且值域各不相同,因此我们需要分别与之对应的三个比例尺对象。
var scY1 = d3.scaleLinear()
.domain(d3.extent(data, d => d["y1"]))
// 对于垂直方向的轴,我们在定义比例尺对象时反转输出范围,从而符合SVG坐标系在垂直方向的反转规则
.range([pxY, 0])
var scY2 = d3.scaleLinear()
.domain(d3.extent(data, d => d["y2"]))
.range([pxY, 0]);
// 为第一个数据集添加符号并放入<svg>元素
d3.select("svg")
// 在添加任何图形元素之前,我们先放一个<g>元素再设置一个id。
// SVG的<g>元素提供了一个逻辑分组,方便我们能够一起引用第一个数据集的所有符号,且区别于第二个数据集的符号
.append("g").attr("id", "ds1")
// 创建一个空的占位符集合。<circle>元素将作为<g>元素的子元素被创建出来
.selectAll("circle")
.data(data).enter().append("circle")
// 为每个<circle>元素添加固定样式
.attr("r", 5).attr("fill", "green")
// 访问函数将合适的列分配到水平轴
.attr("cx", d => scX(d["x"]))
// 访问函数为第一个数据集选择了合适的列,并进行恰当的缩放
.attr("cy", d => scY1(d["y1"]));
// 第二个数据集
d3.select("svg")
.append("g").attr("id", "ds2")
.attr("fill", "blue")
.selectAll("circle")
.data(data).enter().append("circle")
.attr("r", 5)
.attr("cx", d => scX(d["x"]))
.attr("cy", d => scY2(d["y2"]));
// d3.line()工厂函数返回一个函数对象,它通过一个给定的数据集来生成一个能够适配SVG<path>元素中d属性的字符串
var lineMaker = d3.line()
// 直线生成器函数需要访问函数来挑出每个数据点的水平和垂直坐标
.x(d => scX(d["x"]))
.y(d => scY1(d["y1"]));
// 根据<g>元素的id属性来选择第一个数据集。ID选择器字符串由“#”和属性值组成
d3.select("#ds1")
// 添加一个<path>元素作为第一个数据集<g>组的子元素
.append("path")
.attr("fill", "none").attr("stroke", "red")
// 其d属性是通过调用数据集上的直线生成器函数来设置的。
.attr("d", lineMaker(data));
// 我们并非从头开始创建一个新的直线生成器函数,而是通过设置新的访问函数复用现有的直线生成器
lineMaker.y(d => scY2(d["y2"]));
// 向SVG树中的合适位置添加第二个数据集的<path>元素并填充颜色
d3.select("#ds2")
.append("path")
.attr("fill", "none").attr("stroke", "cyan")
.attr("d", lineMaker(data));
// 由于第二个数据集符号的填充样式是在其父元素上定义的(而并非单个<circle>元素本身上),
// 因此可以在单个操作中进行修改。取消这一行的注释,那么第二个数据集的所有圆圈就都会变为红色。
// 因为只有外观选项继承自父类,所以不可能以这种方式更改所有圆的半径或是将圆改为矩形。
// 若要这么做需要单独修改每个元素。
d3.select("#ds2").attr("fill", "red");
})
}
</script>
</html>