D3.js基本面积图
D3.js (Data-Driven Documents) 是一个用于操作文档的JavaScript库,它可以通过使用HTML, SVG 和 CSS等技术在网页上动态生成数据可视化。本教程将带你了解如何使用D3.js创建一个基本的面积图。
准备工作
在开始之前,请下载最新版的D3.js。你可以直接从这里下载或者使用CDN方式加入页面中。
<script src="https://d3js.org/d3.v5.min.js"></script>
数据集定义
首先,我们需要定义好我们要展示的数据。下面我们来看一下我们将要用到的数据集:
var dataset = [
{ name: "苹果", value: 50 },
{ name: "橙子", value: 30 },
{ name: "香蕉", value: 70 },
{ name: "核桃", value: 20 },
{ name: "芒果", value: 60 },
{ name: "梨子", value: 100 },
{ name: "菠萝", value: 80 },
{ name: "葡萄", value: 90 },
{ name: "草莓", value: 35 },
{ name: "西瓜", value: 75 },
{ name: "桃子", value: 55 },
{ name: "樱桃", value: 25 }
];
该数据集包含12条数据,每条数据由水果的名称和对应的值组成。
设置画布
接下来,我们需要定义好我们将要使用的画布大小、内边距等属性。在本例中,我们将会创建一个宽度为600像素,高度为400像素,且四周留有30像素的空白的画布:
var padding = 30;
var svgWidth = 600;
var svgHeight = 400;
定义比例尺
根据数据集中的数值范围,我们需要创建与之对应的比例尺。在这个例子中,我们将使用scaleBand()和scaleLinear()两个函数分别创建x轴和y轴的比例尺:
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.range([padding, svgWidth - padding])
.padding(0)
.paddingInner(1)
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function (d) { return d.value; })])
.range([svgHeight - padding, padding]);
这里我们使用domain()方法来设置比例尺的输入域(即数据范围),并使用range()方法来确定输出范围(在画布上的位置)。
创建面积图
现在,我们需要生成面积图。在这个例子中,我们将使用d3.area()来创建一个面积图:
var area = d3.area()
.x(function (d, i) { return xScale(i) + xScale.bandwidth() / 2; })
.y0(svgHeight - padding)
.y1(function (d) { return yScale(d.value); })
.curve(d3.curveCardinal);
该函数创建的面积图会使用dataset中的值,将x轴和y轴上的各个点用曲线连接起来。在这里,我们通过.curve()方法指定了折线的形状。
添加SVG元素
接下来,我们需要向网页中添加一个SVG元素,并将面积图加入其中:
var svg = d3.select("body")
.append("svg")
.attr("width", svgWidth)
.attr("height", svgHeight)
.style('border', '1px solid #999999')
svg.append("path")
.datum(dataset)
.attr("class", "area")
.attr("d", area)
.attr("fill", "#69b3a2");
上述代码块首先使用.d3.select()方法选择了文档中的<body>
元素,并在其中添加了一个SVG元素。接下来,我们使用svg.append()
方法向SVG中添加一个路径元素(path)来展示面积图。我们需要将数据集(dataset)传递给d3.area()
函数的.datum()方法,然后将它的返回值(d)传递给该路径元素的.d属性中。
为了让年月标签对齐,我们可以在x函数中将每个点的位置计算为:(i + 0.5) * xScale.bandwidth()
最后,我们还需要为这个路径设置好颜色和样式:
.attr("fill", "#69b3a2")
坐标轴设置
最后一步,我们需要添加坐标轴和数字标签,以便更好地显示数据。
var xAxis = d3.axisBottom(xScale)
.tickFormat(function (d, i) { return dataset[i].name; });
var yAxis = d3.axisLeft(yScale);
svg.append("g")
.attr("class", "x-axis")
.attr("transform", "translate(0," + (svgHeight - padding) + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y-axis")
.attr("transform", "translate(" + padding + ",0)")
.call(yAxis);
在这里,我们创建了两个坐标轴,并通过调用g元素上的.call()方法向画布上添加了这些坐标轴。我们还使用.transform()方法将坐标轴移动到正确的位置,并使用.tickFormat()函数向坐标轴上添加年月标签。
完整代码
// 基本面积图
var dataset = [
{ name: "苹果", value: 50 },
{ name: "橙子", value: 30 },
{ name: "香蕉", value: 70 },
{ name: "核桃", value: 20 },
{ name: "芒果", value: 60 },
{ name: "梨子", value: 100 },
{ name: "菠萝", value: 80 },
{ name: "葡萄", value: 90 },
{ name: "草莓", value: 35 },
{ name: "西瓜", value: 75 },
{ name: "桃子", value: 55 },
{ name: "樱桃", value: 25 }
];
var padding = 30;
var svgWidth = 600;
var svgHeight = 400;
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.range([padding, svgWidth - padding])
.padding(0)
.paddingInner(1)
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function (d) { return d.value; })])
.range([svgHeight - padding, padding]);
var area = d3.area()
.x(function (d, i) { return xScale(i) + xScale.bandwidth() / 2; })
.y0(svgHeight - padding)
.y1(function (d) { return yScale(d.value); })
.curve(d3.curveCardinal);
var svg = d3.select("body")
.append("svg")
.attr("width", svgWidth)
.attr("height", svgHeight)
.style('border', '1px solid #999999')
svg.append("path")
.datum(dataset)
.attr("class", "area")
.attr("d", area)
.attr("fill", "#69b3a2");
var xAxis = d3.axisBottom(xScale)
.tickFormat(function (d, i) { return dataset[i].name; });
var yAxis = d3.axisLeft(yScale);
svg.append("g")
.attr("class", "x-axis")
.attr("transform", "translate(0," + (svgHeight - padding) + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y-axis")
.attr("transform", "translate(" + padding + ",0)")
.call(yAxis);