1. 概要
本文主要使用的时d3.js作为绘制折线图的JavaScript库,其官方网站为d3js.org。这里引用了官方的两句话来介绍d3.js:
The JavaScript library for bespoke data visualization
Create custom dynamic visualizations with unparalleled flexibility
当前的已发布的d3.js库最新版本为v7,官方提供有两个版本,一个是完整版,一个压缩版本,分别为d3.v7.js
以及d3.v7.min.js
。
使用d3.js必须在html中引用其js文件,在html头部添加即可。
<script src="https://d3js.org/d3.v7.js"></script>
注意:若要使用d3.js离线版本,需将上述js文件下载,放在项目指定文件夹并正确引用。
离线文件笔者以下载好,可在此下载:链接
2. 确定数据
提示:这里仅展示简单一维数组
接下来我们将使用下面的一维数组来绘制折线图。
const data = [3,4,6,7,8,3,9,1,5,3,8,6,4,9,3,2,6,7,5,8,9];
3. 添加画布
3.1 创建svg标签
svg是基于XML语法的图像格式,全称:Scalable Vector Graphics,即可缩放矢量图。svg可以绘制基本图形,如:直线、圆形、矩形、多边形、路径等。
使用d3画图必须要先创建一个画布,一般为svg,这里可以理解为在该svg容器中进行图表的绘制与展示。
定义一个宽度为600、高度为300的svg标签,设置id为mainsvg
,以便后续通过id来查找该svg:
<svg width="600" height="300" id="mainsvg" class="svgs"></svg>
3.2 给画布添加属性
首先需要使用d3选中上述标签元素,在d3中有两种选择器,分别是
- d3.select():默认选择所有指定元素中的第一个,该选择器一次只能选择一个标签元素。如:通过标签的id选择。
- d3.selectAll():选择一批相同的类型的元素,可以一次选择多个标签。如:通过标签class选择。
这里我们绘制的是一个图形,所以直接使用d3.select()。选中标签后需要设置d3绘图的实际区域,该区域要比上述svg标签指定的宽度和长度要小,需要设置合适的大小。
下面写法有很多种,仅供参考:
// svg的id
var svg_id = "#mainsvg";
// 选择svg标签
const svg = d3.select(svg_id);
// 获取svg标签的宽度和长度属性用于后续计算内部宽度和长度
const width = +svg.attr('width');
const height = +svg.attr('height');
// 设置margin内部边框
const margin = {top : 60, right : 30, bottom : 60, left : 150};
// 设置内部宽度和长度,实际绘图区域
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
4. 设置比例尺
比例尺指的是实际画图中坐标轴的长度大小,以及每一个坐标单位对应的实际像素大小。绘制折线图使用到的是线性比例尺,通常线性比例尺有两个配置参数,分别为domain和range:
- scaleLinear() 线性比例尺
- domain() 比例尺需要映射的长度,比如数据的长度范围为10000映射到100px
- range() 比例尺实际长度,在浏览器中实际的像素长度,如100px
分别定义两个比例尺,作为x轴和y轴的缩放比例尺
const xScale = d3.scaleLinear() // 线性比例尺
.domain([0, data.length-1]) // 映射长度
.range([0, innerWidth]); // 实际占用长度
const yScale = d3.scaleLinear()
.domain([0, d3.max(data)])
.range([innerHeight, 0]);
注意:在定义y轴的比例尺时,range中属性的范围是由大到小。
5. 绘制轴线
5.1 添加group标签
在画布中添加一个group标签,可以设置id属性。将此group的移动合适的位置:
由于在浏览器页面中原点的位置位于左上角,x轴为水平向右,y轴为垂直向下,这点与数学中的坐标轴是不一样的。不将group进行移动的话,图形会默认紧靠着浏览器的左上角绘制,会造成展示效果不美观,部分内容显示不全。
所以需要对group进行水平和向下移动:
- 设置
transform
属性,translate(${margin.left}, ${margin.top})
将group向右和向下平移
const g = svg.append('g').attr('id', 'maingroup')
.attr('transform', `translate(${margin.left}, ${margin.top})`);
5.2 在group添加轴线
这里主要的步骤为三步:
- 为比例尺设置轴线及刻度
- 在group中调用坐标轴函数
- 将坐标轴进行水平或垂直方向移动(非必须,根据实际情况而定)
使用到的函数及api如下:
- axisBottom() 参数为比例尺,添加坐标轴,将刻度设置在坐标轴下方
- axisLeft() 参数为比例尺,添加坐标轴,将刻度设置在轴线左方
- call() 调用定义好的的坐标轴函数
前文提到浏览器中坐标轴原点位左上角,采用默认方式,则x轴位于页面上方,为与常用数学坐标轴保持相同形式将x轴平移innerHeight的距离。这里可以按照自己的需求设置坐标轴位置。
const xAxis = d3.axisBottom(xScale);
g.append('g').call(xAxis).attr('transform', `translate(0, ${innerHeight})`);
const yAxis = d3.axisLeft(yScale);
g.append('g').call(yAxis);
6. 绘制曲线
使用d3提供的line函数,线生成器来生成线段。并设置x、y坐标访问器
var lineGenerator = d3.line()
.x(function(d,i){return xScale(i);})
.y(function(d){return yScale(d);});
7. 添加图表标题
为group添加text文本,并设置文本位置
g.append('text').text('折线图-test') // 添加文本信息
.attr('font-size', '1em') // 设置字体大小
.attr('transform', `translate(${innerWidth / 2}, ${innerHeight + 40})`) // 将文本框移动到图表下方中间位置
.attr('text-anchor', 'middle'); // 文本居中对齐
8. 绘制图表
根据给定的data数据生成线条,设置线条的相关属性。
d3.select("g")
.append("path")
.attr("d",lineGenerator(data)) // 调用生成器
.attr('fill', 'none') // 设置填充
.attr('stroke-width', 1) // 设置线条的宽度
.attr('stroke', 'green'); // 设置线条的颜色
9. 完整代码
绘制效果图:
![](https://i-blog.csdnimg.cn/blog_migrate/9813b9be95dedf76964c939c3cf6b735.png)
完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="d3.v7.js"></script>
</head>
<body>
<svg width="600" height="300" id="mainsvg" class="svgs"></svg>
<script type="module">
const data = [3,4,6,7,8,3,9,1,5,3,8,6,4,9,3,2,6,7,5,8,9];
var svg_id = "#mainsvg";
const svg = d3.select(svg_id);
const width = +svg.attr('width');
const height = +svg.attr('height');
const margin = {top : 60, right : 30, bottom : 60, left : 150};
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
const xScale = d3.scaleLinear()
.domain([0, data.length-1])
.range([0, innerWidth]);
const yScale = d3.scaleLinear()
.domain([0, d3.max(data)])
.range([innerHeight, 0]);
const g = svg.append('g').attr('id', 'maingroup')
.attr('transform', `translate(${margin.left}, ${margin.top})`);
const xAxis = d3.axisBottom(xScale);
g.append('g').call(xAxis).attr('transform', `translate(0, ${innerHeight})`);
const yAxis = d3.axisLeft(yScale);
g.append('g').call(yAxis);
var lineGenerator = d3.line()
.x(function(d,i){return xScale(i);})
.y(function(d){return yScale(d);});
// .curve(d3.curveCardinal);
g.append('text').text('折线图-test')
.attr('font-size', '1em')
.attr('transform', `translate(${innerWidth / 2}, ${innerHeight + 40})`)
.attr('text-anchor', 'middle');
d3.select("g")
.append("path")
.attr("d",lineGenerator(data))
.attr('fill', 'none')
.attr('stroke-width', 1)
.attr('stroke', 'green');
</script>
</body>