0%

使用D3库封装函数画折线图

使用D3库封装函数画折线图

最近很忙,但是很懒,还有一堆课设没有总结,好久没写东西了。之前做了点日志整理的可视化,把可视化的部分写下来。内容上传到github,虽然这东西难度不高,而且参考教程,但是不得不感谢D3开源项目的伟大。短时内就完成了绘图功能。

这里是我封装函数的我的github项目地址,封装了一个js函数,并且写了一个java程序进行内容的输出,需要给一个ArrayList即可。


前言

D3是一个JavaScript的开源项目
官网地址:https://d3js.org/
github地址:https://github.com/d3/d3
中文文档地址:https://github.com/d3/d3/wiki/CN-Home

  使用D3可视化库可以非常方便得进行各种图形的绘制,而且是基于浏览器使用的svg,对IE9以前的大多数浏览器都有非常好的支持性,具体的浏览器可以去查看上面的官方文档。
使用D3可视化库看的教程在这里咯--->{% post_link JavaScript/D3可视化库教程整理 %}


HTML部分

当然首先需要在html文件中包含D3的库文件

1
2
3
4
5
6
7
8
9
10
11
12
13
<html>
<head>
<title></title>
<meta charset="UTF-8">
</head>
<link rel="stylesheet" type="text/css" href="css/style.css">
<body>
</body>
<!--<div id="container"></div>-->
<script src="https://d3js.org/d3.v4.js"></script>
<!-- 绘图的内容在index.js中,使用D3可视化库进行实现 -->
<script src="js/index.js"></script>
</html>

css部分

css也基本没有改动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#container{
background: #ddd;
width: 500px;
height: 250px;
}

path{
fill:none;
stroke:#9D94D7;
stroke-width:3;
}

text{
fill:black;
}

.domain,.tick{
stroke:gray;
stroke-width:1;

}

JavaScript函数部分

这个是参考的一个慕课网上的绘制折线图的教程,然后稍作修改之后完成的。大体没有变化。

教程的地址可以戳这里,打开之后的第二章

下面将会讲解在demo文件夹中的内容,简单的html和css内容也就不过多赘述了.主要解释js的部分.

设置长宽等

在这里设置变量,也就是折线图的长宽

1
2
3
4
5
6
//width,height
var width = 500,
height = 250,
margin = {left:50,top:30,right:20,bottom:20},
g_width = width - margin.left -margin.right,
g_height = height -margin.top - margin.bottom;

创建svg和container对象

1
2
3
4
5
6
7
8
9
//svg
var container = d3.select('body')
.append('div');
var svg = container.append('svg')
.attr('width',width)
.attr('height',height);
var g = svg.append('g').attr('transform','translate('+margin.left+','+margin.top+')');
var scale_x = d3.scaleLinear().domain([0,xRange]).range([0,g_width])
var scale_y = d3.scaleLinear().domain([0,yRange]).range([g_height,0])

画线

在折线图中画出线

1
2
3
4
5
6
7
8
9
10
11
12
13
//画线
var line_generator = d3.line()
.x(function(d,i){return scale_x(i)/scaleN;})//0,1,2,3...
.y(function(d){return scale_y(d);})//1,3,5
// .curve(d3.curveCardinal.tension(1)) //也是不拟合方式
// .curve(d3.curveCardinal) //曲线拟合方式
g.append('path').attr('d',line_generator(data))

var x_axis =d3.axisBottom(scale_x),
y_axis =d3.axisLeft(scale_y);
//.text中内容是显示在坐标轴的内容,transform内容表示旋转,dxdy表示离坐标轴的距离
g.append('g').call(x_axis).attr('transform','translate(0,'+g_height+')').append('text').text(xText).attr('dx','40em').attr('dy','-1em')
g.append('g').call(y_axis).append('text').text(yText).attr('text-anchor','start').attr('dx','1em').attr('dy','1em')

打印坐标信息

鼠标悬停是匆忙加上去的。有些边角效果貌似不好

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 打印坐标位置信息
var wenzi = svg.append('text')
.attr('font-family', 'sans-serif')
.attr('font-size', '11px')
.attr('fill', 'red')
.attr('class','move2')
// 使用circles对象进行画圆
var yuan = svg.append('circle')
.attr('r', 1)
.attr('class','move1');
//鼠标悬停
svg.on('mousemove',function(event){
var event = event || window.event;
yuan.attr('style','display:block');
wenzi.attr('style','display:block');
yuan.attr('cx',event.offsetX);
yuan.attr('cy',event.offsetY);
if(event.offsetX <=450){
wenzi.text(function(d) {
return ((event.offsetX-50)/g_width*xRange).toFixed(2) + "," + ((height-event.offsetY-20)/g_height*yRange).toFixed(2);
})
wenzi.attr('x', event.offsetX+7)
wenzi.attr('y', event.offsetY+7)
}else{
wenzi.text(function(d) {
return ((event.offsetX-50)/g_width*xRange).toFixed(2) + "," + ((height-event.offsetY-20)/g_height*yRange).toFixed(2);
})
wenzi.attr('x', event.offsetX-50)
wenzi.attr('y', event.offsetY+7)
}
})

封装成函数的调用方式

知道函数调用方式就好了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//创建数据,因为按照x的xRange范围来,还可以通过scaleN限制粗度
var data = [1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,
1,1,1,1,50,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1, 1]
var xRange=60,
yRange=100,
name='<p>进程AEMgr</p>',
xText='时间(60分)',
yText='CPU使用率(%)',
scaleN=1; //每个x单位有几个值
//函数调用
draw(name,xRange,yRange,xText,yText,data,scaleN);

使用Java来进行JavaScript文件的输出

demo.java

调用的demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.io.IOException;
import java.util.ArrayList;

public class demo {
public static void main(String []args) throws IOException{
ArrayList data = new ArrayList();
//我随便创的一个直线数据
for( int i = 0; i< 60; i++){
data.add(i);
}
System.out.println(data);

SaveJs.initIndex("test/js/index.js");
SaveJs.drawLineChart("test/js/index.js", "进程AEMgr", 60, 100, "时间(60分)", "CPU使用率(%)", data, 1);
SaveJs.drawLineChart("test/js/index.js", "进程AEMgr", 60, 100, "时间(60分)", "CPU使用率(%)", data, 3);
}
}

SaveJs.java

封装好的调用函数,代码中函数的参数属性。注意:调用draw方法前必须要调用init方法初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package file;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
/**
* 保存index.js文件
* @author xuan
* 2017-7-17
*/
public class SaveJs {
/**
* 创建index.js文件并写入draw函数
* @throws IOException
*/
public static boolean initIndex(String fileName) throws IOException{
File file = new File(fileName);
//判断目标文件所在的目录是否存在
if(!file.getParentFile().exists()){
System.out.println("不存在"+fileName+"的目录,进行创建");
if(!file.getParentFile().mkdirs()){
System.out.println("创建目标文件目录失败");
return false;
}
}
//如果不存在则创建目标文件
try {
if(!file.exists()){
if(!file.createNewFile()){
return false;
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//调用writefunc写入js的函数内容
writefunc(fileName);
return true;
}
/**
* 写入js的函数部分
* @throws IOException
*/
private static void writefunc(String fileName) throws IOException{
FileWriter fw = new FileWriter(fileName,false);
fw.write("function draw(name,xRange,yRange,xText,yText,data,scaleN){\n");
fw.write(" document.writeln(name);\n");
fw.write(" var width = 500, height = 250,margin = {left:50,top:30,right:20,bottom:20},g_width = width - margin.left -margin.right,g_height = height -margin.top - margin.bottom;\n");
fw.write(" var container = d3.select('body').append('container');\n");
fw.write(" var svg = container.append('svg').attr('width',width).attr('height',height);\n");
fw.write("var g = svg.append('g').attr('transform','translate('+margin.left+','+margin.top+')');\n");
fw.write("var scale_x = d3.scaleLinear().domain([0,xRange]).range([0,g_width])\n");
fw.write(" var scale_y = d3.scaleLinear().domain([0,yRange]).range([g_height,0])\n");
fw.write("var line_generator = d3.line()\n");
fw.write(".x(function(d,i){return scale_x(i)/scaleN;})\n");
fw.write(".y(function(d){return scale_y(d);})\n");
fw.write("g.append('path').attr('d',line_generator(data))\n");
fw.write(" var x_axis =d3.axisBottom(scale_x),y_axis =d3.axisLeft(scale_y);\n");
fw.write("g.append('g').call(x_axis).attr('transform','translate(0,'+g_height+')').append('text').text(xText).attr('dx','40em').attr('dy','-1em')\n");
fw.write("g.append('g').call(y_axis).append('text').text(yText).attr('text-anchor','start').attr('dx','1em').attr('dy','1em')\n");
fw.write("\n\n");//此处反花括号挪到最下
//xuan加入鼠标悬停功能2017-7-17
fw.write(" var wenzi = svg.append('text').attr('font-family', 'sans-serif').attr('font-size', '11px')");
fw.write(".attr('fill', 'red').attr('class','move2')\n");
fw.write("var yuan = svg.append('circle').attr('r', 1).attr('class','move1');\n");
fw.write("svg.on('mousemove',function(event){\n");
fw.write("var event = event || window.event;\n");
fw.write("yuan.attr('style','display:block');\n");
fw.write("wenzi.attr('style','display:block'); \n");
fw.write("yuan.attr('cx',event.offsetX)\n");
fw.write("yuan.attr('cy',event.offsetY);\n");
fw.write("if(event.offsetX <=450){\n");
fw.write("wenzi.text(function(d) {\n");
fw.write("return ((event.offsetX-50)/g_width*xRange).toFixed(2) + \",\" + ((height-event.offsetY-20)/g_height*yRange).toFixed(2);\n");
fw.write("})\n");
fw.write("wenzi.attr('x', event.offsetX+7)\n");
fw.write("wenzi.attr('y', event.offsetY+7)\n");
fw.write("}else{\n");
fw.write("wenzi.text(function(d) {\n");
fw.write("return ((event.offsetX-50)/g_width*xRange).toFixed(2) + \",\" + ((height-event.offsetY-20)/g_height*yRange).toFixed(2);\n");
fw.write("})\n");
fw.write("wenzi.attr('x', event.offsetX-50)\n");
fw.write("wenzi.attr('y', event.offsetY+7)\n");
fw.write("}})\n");
fw.write("}\n");
fw.close();
}
/**
* 调用绘制函数前必须使用initIndex方法初始化文件
* @param fileName 传入文件名
* @param name 网页显示的名字
* @param xRange x轴的最大值
* @param yRange y轴的最大值
* @param xText x轴说明
* @param yText y轴说明
* @param data ArrayList数据
* @param scaleN 一个x单位有多少个数据
* @throws IOException
*/
public static void drawLineChart(String fileName,String name, int xRange, int yRange,String xText,String yText,ArrayList data,int scaleN) throws IOException{
//使用true选项打开追加模式
FileWriter fw = new FileWriter(fileName,true);
fw.write("var data = "+data+"\n\n");
fw.write("var xRange="+xRange+",yRange="+yRange+",name='<p>"+name);
fw.write("</p>',xText='"+xText+"',yText='"+yText+"',scaleN="+scaleN+";\n");
fw.write("draw(name,xRange,yRange,xText,yText,data,scaleN);\n");
fw.close();
}
}

后记

通过以上方法就可以绘制出对应的折线图.相对简单,差不多就到这里吧.有点不懂的话就去看下视频教程。

听说好看的人都关注了我的公众号《泫言》