梦入琼楼寒有月,行过石树冻无烟

📖 earlier posts 📖

Node.js SSL/ HTTPS

HTTP(Hyper Text Transfer Protocol,超文本传输协议)是一个被互联网广泛使用的协议之一,所有 WWW 文件都必须遵守的标准,但缺点是 HTTP 传输数据是不会被加密的。因此 HTTPS(Hyper Text Transfer Protocol over Secure Socket Layer,安全的超文本传输协议) 的出现解决了这个问题,他主要使用 SSL(Secure Sockets Layer,安全套接层协议)用于对 HTTP 传输协议进行加密,以保证会话过程中的安全性。

由于 SSL 协议中主要通过对称加密和非对称加密两种,在建立链路后,SSL 内容会使用其对称加密的公钥对其进行非对称加密,之后 SSL 会对内容进行对称加密。

OpenSSL

由于 TLS\SSL 是非对称加密,因此在此之前需要准备加密解密和验证的私钥以及数字证书、和服务器、客户端的密钥证书

1
openssl req -x509 -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost'  -keyout localhost-privkey.pem -out localhost-cert.pem

HTTPS

.

1
2
3
4
5
6
7
8
9
10
11
12
13
const https = require('https')
const fs = require('fs')

const options = {
key: fs.readFileSync('./ssl/localhost-privkey.pem'),
cert: fs.readFileSync('./ssl/localhost-cert.pem')
}

https.createServer(options, (req,res) => {
res.writeHead(200)
res.write('<h1>Hello,world!</h1>')
res.end()
}).listen(8210)

之后我们访问 https://localhost 同样会提示证书的问题,因为我们这个是自己生成的并不是以权威机构签名,因此在浏览器中会报错。

Node.js 基础

Node.js 是一个基于 Chrome V8 的 JavaScript 运行环境,这也让 JavaScript 成为与 PHP、Python、Perl、Ruby 等服务端语言对等的脚本语言之一。Node.js 能够在服务端运行 JavaScript 的代码和跨平台环境,因此在 node.js 出现以前,JavaScript 经常作为客户端程序设计语言使用。

ECMAScript 是 JavaScript 最初的名字,他由网景公司的 Brendan Eich 所开发的一种脚本语言标准化规范,历经 Mocha、LiveScript 最终成为 JavaScript,并在 1995年由 Sun 正式联合发布 JavaScript

获取 Node.js 的方法非常简单,我们以 Debian Linux 发行版系为例,通过 https://nodejs.org/zh-cn/ 访问 node.js 的官方站点进行下载你需要的版本(本书版本为 10),解压 tar.xz 文件后进入其 bin 目录,比昂通过 ln 建立软链接到 /usr/bin 中,最后通过其 node -v 来查看是否软链接建立成功

1
2
ln node /usr/bin/node
ln npm /usr/bin/node

JavaScript vs Node.js

JavaScript 他本身是一个客户端程序设计语言,因此针对的也是 DOM、BOM、ECMAScript 组合而成,因此针对的也是前端部分,而 Node.js 他主要通过 ECMAScript、OS、File、Net、Databases 等五个部分,分别实现了 JavaScript 语法基础、操作系统交互、文件系统、网络系统和数据库,这也是后端语言最为常用的几个部分,因此他可以脱离浏览器来运行 JavaScript 的项目。

脚本文件运行

创建名称为 hey.js 的文件后,写入 DOM 语法并通过 node.js 来进行运行

1
2
3
4
console.log("Hello,world!")

$ node hey.js
Hello,world!

交互式环境执行

1
2
3
4
5
$ node
> console.log("Hello,world!")
Hello,world!
undefined
>

构建 Node.js 项目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const http = require('http');

const hostname = '127.0.0.1';
const port = 8210;

const server = http.createServer((req,res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello,world!\n');
});

server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});

至此通过 node index.js 即可运行一个简单的 node 项目,之后点击或访问 console.log 的输出来访问项目视图:http://127.0.0.1:8210/

WebStorm 如果不支持 Node.js 相关语法可以通过在 File -> Setting -> Languages & Frameworks -> JavaScript -> Libraries 内下载 node 语法支持

NPM

NPM(Node Package Manager)Node 包管理器,是 node.js 默认的依赖项之一,通常会随着 node.js 一起被安装,完全由 JavaScript 写成,最初由 lsaac Z.Schlueter 进行开发,2020 年 3 月 16 日 被 Github 收购。

node.js 维护的包管理器允许开发者下载\安装\上传他人\自己编写的第三方包到本地使用\他人使用。

NPM 的包注册(Register)也被称之为 “JavaScript 包注册表”,为了按照名称和版本来解析包,因此 nmp 采用了 CommonJS 包注册规范,这就导致了每个包都会存在一个 Json 格式的元文件。

NPM 名称以 “先到先得” 的原则进行注册,这就不会导致各个模块的名称不会发生错乱,但一旦有人撤回了自己所发布的包,就会导致依赖这个包的项目的对应功能也会出现问题。

你可以通过 nmp -v 的方式来查看版本以及是否正常安装,也可以使用 nmp install npm -g 让其进行升级。

package.json

一个项目中如果存在 package.json 文件,那么可以使用 npm install 命令来自动安装和维护所需要的依赖包,你可以通过使用 npm init 来初始化一个 package.json 文件。

Id Name
name 包名
version 包的版本号采用了语义版本号 X(住版本号).Y(次版本号).Z(补丁版本号) 三位
description 包的描述
main 指定程序的入口文件
scripts 指定了运行脚本命令的 npm 命令行缩写
repository 代码仓库位置
keywords 关键词
author 包作者的名字
license 开源许可证,默认为 (ISC)

package.json 文件可以手写也可以通过 npm init 进行生成(就是回答一些问题后自行进行创建)

从而可以快速体验工作空间以及脚本外壳的使用,因此你需要在初始化后的 CommonJS 包中添加或修改一出信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"name": "demo",
"version": "1.0.0",
"description": "node demo",
"main": "index.js",
"scripts": {
"test": "node index.js"
},
"repository": {
"type": "git",
"url": "https://gitee.com/sif_one/node-demo"
},
"keywords": [
"null"
],
"author": "sun likun",
"license": "ISC"
}

有了 package.json 文件后,可以直接通过 npm install 进行安装所需要的包(在 dependencires 字段中写了),也可以通过指定安装依赖并写入字段的方法进行完善

install express --save``` 参数将所需依赖写入 package.json 文件中的 ```dependencies``` 属性中。
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

同样的还提供了写入 ```devDependencies``` 属性中的参数 ```--save-dev```

在 ```package.json``` 中,如果在 ```script``` 字段内定义 js 脚本文件,即可通过 ``` npm run test``` 进行运行,此时也就同等于 ```node index.js```

当然也可以通过使用 ```rpm run``` 来查看当前项目所有的 NPM 脚本命令,因为执行该命令时会执行指定的脚本命令,因此只要是 Shell 可以运行的都可以写在 NPM 脚本中。

在大的项目中, npm run 会在 ```node_modules/.bin``` 子目录中加入变量,因此在该目录中的所有脚本都可以字节使用脚本的名称而不需要写成绝对路径。

### 常用命令

| Id | Name | Info |
| --- | --- | --- |
| 1 | ```npm install express``` | 安装某一个模块 |
| | | ```-g``` 全局安装这个模块(也就是说直接在 ```/user/local/bin``` 内直接关联) |
| 2 | ```npm uninstall name``` | 卸载安装的所有模块 |
| | | ```--save``` 以 ```dependencires``` 为目标进行卸载 |
| | | ```--save-dev``` 从 ```devDependencies``` 为目标进行卸载 |
| | | ```--no-save``` 不会将 ```package.json``` 中进行删除依赖 |
| 3 | ```npm update express``` | 更新模块 |
| 4 | ```npm outdated``` | 检查模块版本 |
| 5 | ```npm list``` |查看本地安装的模块 |
| | | ```-g ``` 查看全局安装的模块 |
| 6 | ```npm search express``` | 搜索模块 |
| 7 | ```npm cache clean``` | 清除 NPM 本地缓存 |
| 8 | ```npm start/restart ``` | 启动/重启模块 |

## REPL
REPL(Read Eval Print Loop,交互式解释器),也有另一种解释是:“读取-求值-输出-循环(Read-Eva-Print Loop,REPL)”,表示一个交互式顶层构件,也就是说提供一个简单的交互式编程环境。

Node 也自带了这种交互式解释器,同样可以完成读取、执行、输出、循环的任务,通常你可以直接在终端输入 ```node``` 来进入 node.js 的交互式环境:

```js
1 + 1
2
x = 1
1
y = 1
1
x + y
2
console.log("Hello,world!")
Hello,world!
undefined

var x = 0
undefined
do {
... x++;
... console.log("echo: " + x);
... } while (x < 10);
echo: 1
echo: 2
echo: 3
echo: 4
echo: 5
echo: 6
echo: 7
echo: 8
echo: 9
echo: 10
undefined

交互


REPL 是由node.js所支持的可运行在终端内的控制台窗口即交互式命令环境,按照官方的说法叫“简单的交互式编程环境”,通常就比如你在终端内输入 R,进入R的REPL环境一样,node同样支持。

提示


Node REPL与其他的REPL环境内不同的特点在于,他支持tap键进行补全,以及提示:

1
2
> console.log
[Function: log]

点命令

node.js与 python以及r等 repl交互式环境不同的之处通过点命令可以体现的琳琳尽至,就比如:

python

1
2
3
4
Python 3.9.2 (default, Feb 20 2021, 18:40:11) 
[GCC 10.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> exit()

node

1
2
3
4
Welcome to Node.js v15.10.0.
Type ".help" for more information.
> .exit
>
No.x Info 快捷键
.help 显示帮助
.editor 启用编辑模式,可以编写多行的js代码,在editor模式下可通过使用ctrl+D来运行 code
.break 当输入多行表达式的时候,使用break可以中止进一步输入 ctrl+c
.clear 将 REPL上下文设置空占位符,通常等价式为 Linux内的create ,主要用于清除多行表达式
.load 加载 js文件
.save 保存 repl 环境下输入的所有内容
.exit 退出 node.js repl 交互式环境

Jquery mouseover 与 mouseout

淡出与淡退
在 Jquery中,最为常用的是鼠标悬停mouseover以及鼠标移除mouseout,分别可以实现当鼠标悬停时触发的效果以及鼠标移除时所做的动作,本文我们通过配合fadeToggle来实现当鼠标悬停以及鼠标移除时所做的淡出、淡退的效果:

1
2
3
4
5
6
7
8
9
10
<script>
$(document).ready(function () {
$("#issue").mouseover(function () {
$("#issuer").fadeToggle(1000);
});
$("#issue").mouseout(function () {
$("#issuer").fadeToggle(1000);
});
});
</script>

当然如果你觉得消失的太快,也可以设置一下消失方法的停留时间,即mouseout方法下添加setTimeout

1
2
3
4
5
6
7
8
9
10
$(document).ready(function () {
$("#issue").mouseover(function () {
$("#issuer").fadeToggle(1000);
});
$("#issue").mouseout(function () {
setTimeout(function() {
$("#issuer").fadeToggle(1000);
},3000)
});
});

Ejs 基本语法

Ejs 的基础语法与 Jpa 语法类似,所以有Java或java script的读者会感觉非常的亲近,所以本章我们主要介绍Ejs的基础语法,分别为 输出、循环、判断等三个大类,也是常用的一些语法进行演示,在本文中,我们主要使用 ejs-cli 来编写 .ejs转换为.html的形式。

在 Ejs 中,快速编辑和渲染以及简单的木板标签和浏览器端和服务器端是他的特点之一,而常用标签主要有:

  1. <% 流程控制标签
  2. <%= 原文输出HTML内容标签
  3. <%- 输出标签
  4. <%- include() 引入其他模板路径标签

变量

<% and <%=

在本文中我们主要通过使用 <%来进行声明一个变量,之后我们可以他哦难过使用 <%=来进行引用变量:

1
2
3
4
5
<% title="Hello,world" %>

<h1>
Title: <%= title %>
</h1>

<% and <%-

除了之前通过使用原文输出内容标签所输出的并不支持嵌套html语法,我们通过使用<%-输出标签即可完成变量中包含html语法:

1
2
<% var title="<h1>hello,world</h1>" %>
<%-title %>

其输出的将会是 “hello,world”

循环

1
2
3
<% for(var i=0; i<10; i++) { %>
<li>输出 <%-i%> 次</li>
<% } %>

其具体流程与变量一样,通过使用<%标签来定义 i 变量,并输出9次,之后通过使用<%-即输出标签来输出循环次数,之后我们就可以通过使用 ejs-cli 将其输出为.html即可完成循环。

判断


除了变量与循环标签之外,<%还支持判断语法,在上一段代码中,我们加入以下代码即可完成判断的操作:

1
2
3
4
5
<% if(i >= 9) { %>
<p>输出完成</p>
<% } else {%>
<p>输出失败</p>
<%} %>

当变量i输出次数大于且等于9(>=9)的情况下,输出“输出完成”,否则则输出“输出失败”等字样。

模板嵌套


模板嵌套是在 ejs中稍微特别的一种,通过使用木板嵌套可以使得两个不同的.ejs文件内信息互相引用等(以文件形式):

index.ejs

1
<% include ./include %>

include.ejs

1
<p>Hello,world</p>

Ejs 安装与简介

Ejs,是一个纯 JavaScript所开发的一种高校式JavaScript模板引擎,其中“E”来代表可嵌入、高效、优雅、简单。可以帮助普通的JavaScript 代码来生成 HTML页面,EJS由于缓存和快速开发、用于调试以及语法简单和社区活跃备受开发者所喜爱。

安装

全局安装与浏览器支持安装

安装 EJS的方法分为两种,一种为通过使用 npm install ejs来安装,而另一种也可以通过ejs 仓库内的发行版来进行直接的引用,即全局安装和浏览器支持版本等。这里我们附上 EJS 仓库URL:https://github.com/mde/ejs,你可通过使用 https://github.com/mde/ejs/releases/tag/v3.1.6 来下载 EJS 3.1 版本。

全局安装

在一般的情况下,会使用npm install ejs来进行安装ejs,但本文中使用ejs-cli,即npm install ejs-cli,ejs与ejs-cli的区别就是 ejs-cli更加的简洁实在,没有 ejs那样的繁琐。通过使用ejs和ejs-cli的一段时间,将 .ejs 输出转换为 .html,ejs 基本上全部出错,而 ejs-cli则是一路绿灯,所以本文使用 ejs-cli 作为演示。

安装过程中的报错解决
package.json 没有的问题

如果在执行npm install ejs-cli的时候出现有类似与 package.json没有的问题,我们可以使用 npm init来执行生成package.json文件。

这个是会他会提示你进行配置,基本上我们可以直接全部使用回车键来进行跳过,也可以使用npm init -y来使用默认配置。

npm ERR! code ETIMEDOUT \ npm ERR! errno ETIMEDOUT

如果在安装中出现这种错误,我们可以使用npm install -g ejs-cli进行全局安装来解决此问题。

全局安装与本地安装的区别
在默认的情况下我们会使用本地安装,通过使用本地安装会在当前目录下生成一个 node_modules的目录,而全局安装则是将模块安装到全局,不会在项目的 node_modules目录下保存模块包。

输出

ejs-cli


当安装完成之后,我们可以通过使用 ejs-cli -h来查看ejs-cli的安装是否成功,如果没有成功可以查看npm是否安装失败来最后判断问题,当安装完成之后,我们可以在本地新建项目,且建立 index.ejs 文件,并写入(在此之前先建立一个/ejs目录用于存储ejs转换html后的文件):

1
2
3
4
5
<% title="Hello,world" %>

<h1>
Title: <%= title %>
</h1>

最后我们可以通过使用 ejs-cli -f index.ejs -o ejs/来在 /ejs 目录下生成 index.html文件文件。

浏览器支持


除了通过使用 ejs-cli 的方式来将 .ejs文件转换成 .html的方式,处于这种方式开发团队表示很繁琐,所以推出了另一种通过直接引入就可以写 ejs 语法的js扩展,再次我们需要新建一个index.html文件并写入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>hello</title>
</head>
<body>
<p id="title"></p>
<script src="js/ejs.js"></script>
<script>
var html = ejs.render('<%="Hello,world!" %>','');
document.getElementById('title').innerHTML = html;
</script>
</body>
</html>

D3 雷达图

HTML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>D3.js</title>
<link rel="stylesheet" type="text/less" href="less/style.less">
<script src="js/less.js"></script>
<script src="https://d3js.org/d3.v4.0.0-alpha.50.min.js"></script>
</head>
<body>
<script src="js/style.js"></script>
<div id="chart"></div>
</body>
<script>
</script>
</html>

JS

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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
var padding = {
width: 400,
height: 400
}

var svg = d3.select("body")
.selectAll("svg")
.append("svg")
.attr("width", padding.width)
.attr("height",padding.height)
/*
* config
* w: width 300
* h: height 300
* maxValue 最大值为 100
* levels 分别在五个外圈中标注文字
* ExtrExtraWidthX 额外宽度为 300
*/
var config = {
w: padding.width,
h: padding.height,
maxValue: 100,
levels: 5,
ExtraWidthX: 300
}

d3.json("./data.json", function(error, data) {
if (error) throw error;
RadarChart.draw("#chart", data, config);
});

var RadarChart = {
draw: function (id,d,options) {
var cfg = {
radius: 5,
w: 600,
h: 600,
factor: 1,
factorLegend: .85,
levels: 3,
maxValue: 0,
radians: 2 * Math.PI,
opacityArea: 0.5,
ToRight: 5,
TranslateX: 80,
TranslateY: 30,
ExtraWidthX: 100,
ExtraWidthY: 100,
color: d3.scaleOrdinal()
.range(["#037ef3"], "#bbd4ff")
}

if ('undefined' !== typeof options) {
for (var i in options) {
if ('undefined' !== typeof options[i]) {
cfg[i] = options[i]
}
}
}
cfg.maxValue = 100

var allAxis = (d[0].map(function (i, j) {
return i.area
}))
var total = allAxis.length
var radius = cfg.factor * Math.min(cfg.w/2, cfg.h/2)
var Format = d3.format('%')

d3.select(id).select("svg")
.remove()

var g = d3.select(id)
.append("svg")
.attr("width",cfg.w+cfg.ExtraWidthX)
.attr("height",cfg.h+cfg.ExtraWidthY)
.append("g")
.attr("transform", "translate(" + cfg.TranslateX + "," + cfg.TranslateY + ")")

var tooltip

/*
绘制圆形线段
*/
for(var j=0;j<cfg.levels;j++) {
var levelFactor = cfg.factor * radius * ((j + 1) / cfg.levels)
g.selectAll(".levels")
.data(allAxis)
.enter("")
.append("attr:line")
.attr("x1", function (d, i) {
return levelFactor * (1 - cfg.factor * Math.sin(i * cfg.radians / total))
})
.attr("y1", function (d, i) {
return levelFactor * (1 - cfg.factor * Math.cos(i * cfg.radians / total))
})
.attr("x2", function (d, i) {
return levelFactor*(1 - cfg.factor * Math.sin((i + 1) * cfg.radians / total))
})
.attr("y2", function (d, i) {
return levelFactor* (1 - cfg.factor * Math.cos((i + 1) * cfg.radians / total))
})
.attr("class", "line")
.style("stroke", "#037ef3")
.style("stroke-opacity", "0.75")
.style("stroke-width", "0.3px")
.attr("transform", "translate(" + (cfg.w / 2 - levelFactor) + "," + (cfg.h / 2 - levelFactor) + ")")
}
/*
* 绘制每个外线的百分比文本
*/
for (var j=0;j<cfg.levels;j++) {
var leveFactor = cfg.factor*radius*((j+1)/cfg.levels)
g.selectAll(".levels")
.data([1])
.enter()
.append("svg:text")
.attr("x", function (d) {
return leveFactor*(1-cfg.factor*Math.sin(0))
})
.attr("y", function (d) {
return leveFactor*(1-cfg.factor*Math.cos(0))
})
.attr("class","legend")
.style("font-weight","100")
.style("font-size","10px")
.attr("transform", "translate(" + (cfg.w/2-leveFactor + cfg.ToRight) + "," + (cfg.h/2-leveFactor) + ")")
.attr("fill", "#202020")
.text((j+1)*100/cfg.levels)
}
/*
* 绘制坐标系
*/
series = 0
var axis = g.selectAll(".axis")
.data(allAxis)
.enter()
.append("g")
.attr("class","axis")

axis.append("line")
.attr("x1", cfg.w/2)
.attr("y1", cfg.h/2)
.attr("x2", function (d,i) {
return cfg.w/2*(1-cfg.factor*Math.sin(i*cfg.radians/total))
})
.attr("y2", function (d,i) {
return cfg.h/2*(1-cfg.factor*Math.cos(i*cfg.radians/total))
})
.attr("class","line")
.style("stroke","#738796")
.style("stroke-width","1px")

/*
* 绘制文字
*/
axis.append("text")
.attr("class","legend")
.text(function (d) {
return d
})
.style("font-family","sans-serif")
.style("fill","#202020")
.style("font-size","11px")
.attr("text-anchor","middle")
.attr("dy","1.5em")
.attr("transform", function (d,i) {
return "translate(0,-10)"
})
.attr("x", function (d,i) {
return cfg.w/2*(1-cfg.factorLegend*Math.sin(i*cfg.radians/total))-60*Math.sin((i*cfg.radians/total))
})
.attr("y", function (d,i) {
return cfg.h/2*(1-Math.cos(i*cfg.radians/total))-20*Math.cos(i*cfg.radians/total)
})
/*
* 绘制数据
* 与添加鼠标事件
*/
d.forEach(function (y,x) {
dataValues = []
g.selectAll(".nodes")
.data(y, function (j,i) {
dataValues.push([
cfg.w/2*(1-(parseFloat(Math.max(j.value, 0))/cfg.maxValue)*cfg.factor*Math.sin(i*cfg.radians/total)),
cfg.h/2*(1-(parseFloat(Math.max(j.value, 0))/cfg.maxValue)*cfg.factor*Math.cos(i*cfg.radians/total))
])
})
dataValues.push(dataValues[0])
g.selectAll(".area")
.data([dataValues])
.enter()
.append("polygon")
.attr("class","radar-chart-serie"+series)
.style("stroke",cfg.color(series))
.style("stroke-width","2px")
.attr("points", function (d) {
var str="";
for (var pti=0;pti<d.length;pti++) {
str=str+d[pti][0]+ "," + d[pti][1]+ " "
}
return str
})
.style("fill", function (j,i) {
return cfg.color(series)
})
.style("fill-opacity",cfg.opacityArea)
.on("mouseover", function (d) {
z = "polygon." + d3.select(this).attr("class")
g.selectAll("polygon")
.transition(200)
.style("fill-opacity", 0.1)
g.selectAll(z)
.transition(200)
.style("fill-opacity",.7)
})
.on("mouseout", function () {
g.selectAll("polygon")
.transition(200)
.style("fill-opacity", cfg.opacityArea)
})
series++
})
series=0
/*
* 绘制边界点
*/
var tooltip = d3.select("body")
.append("div")
.attr("class","toolTip")
d.forEach(function(y, x){
g.selectAll(".nodes")
.data(y)
.enter()
.append("svg:circle")
.attr("class","radar-chart-serie"+series)
.attr("r",cfg.radius)
.attr("alt",function (j) {
return Math.max(j.value,0)
})
.attr("cx", function(j, i) {
dataValues.push([
cfg.w / 2 * (1 - (parseFloat(Math.max(j.value, 0)) / cfg.maxValue) * cfg.factor * Math.sin(i * cfg.radians / total)),
cfg.h / 2 * (1 - (parseFloat(Math.max(j.value, 0)) / cfg.maxValue) * cfg.factor * Math.cos(i * cfg.radians / total))
])
return cfg.w / 2 * (1 - (Math.max(j.value, 0) / cfg.maxValue) * cfg.factor * Math.sin(i * cfg.radians / total))
})
.attr("cy", function (j,i) {
return cfg.h/2*(1-(Math.max(j.value,0)/cfg.maxValue)*cfg.factor*Math.cos(i*cfg.radians/total))
})
.attr("data-id", function (j) {
return j.area
})
.style("fill","#fff")
.style("stroke-width","2px")
.style("stroke",cfg.color(series))
.style("fill-opacity",.9)
.on("mouseover", function (d) {
tooltip
.style("left",d3.event.pageX - 40 + "px")
.style("top", d3.event.pageY -80 + "px")
.style("display","inline-block")
.html((d.area) + "<br><span>" + (d.value) + "</span>")
})
.on("mouseout", function (d) {
tooltip.style("display","none")
series++
})
})
}
}

JSON

1
2
3
4
5
6
7
8
9
10
[
[
{"area": "端口协议漏洞 ", "value": 80},
{"area": "客户端涉及端口漏洞", "value": 50},
{"area": "端口威胁情报 ", "value": 90},
{"area": "协议逻辑漏洞", "value": 90},
{"area": "端口服务漏洞", "value": 60},
{"area": "协议底层漏洞", "value": 20}
]
]

CSS

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
#chart {
margin-top: 10%;
margin-left: 10%;
}

.toolTip {
pointer-events: none;
position: absolute;
display: none;
min-width: 50px;
height: auto;
background: #ffffff;
padding: 5px 14px 6px 14px;
border-radius: 3px;
text-align: center;
line-height: 1.3;
color: #5B6770;
font-size: 10px;
box-shadow: 1px 1px 1px rgb(208, 208, 208);
}
.toolTip:after {
content: "";
width: 0;
height: 0;
border-left: 12px solid transparent;
border-right: 0px solid transparent;
border-top: 12px solid white;
position: absolute;
bottom: -10px;
left: 50%;
margin-left: -12px;
}
.toolTip span {
font-weight: bold;
color: #037ef3;
}

D3 路径生成器(lines)

D3 的路径生成器在拥有指定坐标的情况下可以生成路径,可以通过使用d3.line()来进行创建一个线生成器。

在 d3 version 3 版本中, 创建路径生成器需要使用 d3.svg.line() 来进行创建,在 v4 中将此变得更加语义化

d3.line()


首先我们使用d3.line()来创建一个默认的线(line)生成器,之后定义一个坐标数组并通过pathData调用lineGenerator传入数组points,之后添加路径(path)

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
var svg = d3.select('#demo')
.append("svg")
.attr("width",700)
.attr("height",700)

/*
* d3.line()
* 使用默认的配置创建一个 线(line)生成器
* points
* 定义一个坐标数组
*/
var lineGenerator = d3.line()
var points = [
[1, 1],
[100, 100],
[200, 30],
[300, 50],
[400, 40],
[500, 80]
]

/*
* pathData
* -> 通过pathData 调用 lineGenerator来传入数组(points)
* svg
* 选择 svg 元素,添加路径(path),为其绑定数据(pathData)与样式
*/
var pathData = lineGenerator(points)
d3.select('svg')
.append("path")
.attr('d',pathData)
.attr("fill","none")
.attr("stroke","red")

Scale

当然,纯粹的默认路径生成器是满足不了我们的需求的,因为他的路径是根据数据来决定的,因此我们需要来使用一个线性比例尺

线性比例尺(scaleLinear)是一个适用与连续定义数据的比例尺,他很好的保留了比例的差异。每一个 range(y) 中的值都可以被表示为一个函数,其中 domain 对应了 x内的值。

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
var svg = d3.select('#demo')
.append("svg")
.attr("width",700)
.attr("height",700)

/*
分别创建一个 xScale 与 yScale 比例尺
*/
var xScale = d3.scaleLinear()
.domain([0,6])
.range([0,600])
var yScale = d3.scaleLinear()
.domain([0,80])
.range([150,0])

/*
定义访问器
*/
var lineGenerator = d3.line()
.x(function (d,i) {
return xScale(i)
})
.y(function (d) {
return yScale(d.value)
})
/*
* d3.line()
* 使用默认的配置创建一个 线(line)生成器
* points
* 定义一个坐标数组
*/
var points = [
{value: 10},
{value: 50},
{value: 10},
{value: 1}
]



/*
* pathData
* -> 通过pathData 调用 lineGenerator来传入数组(points)
* svg
* 选择 svg 元素,添加路径(path),为其绑定数据(pathData)与样式
*/
var pathData = lineGenerator(points)
svg.append("g")
d3.select('g')
.append("path")
.attr('d',pathData)
.attr("fill","none")
.attr("stroke","red")

curve


在 D3 version 4 中新加了一种参数为curve,curve可以在一系列点之间进行差值,最终形成一条连续的线,下述我们使用了d3.curceCardinalcardinal 三次曲线。

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
var svg = d3.select('#demo')
.append("svg")
.attr("width",700)
.attr("height",700)

svg.append('path')
.attr("fill","none")
.attr("stroke","#999")

/*
* 定义访问器
* curve
* 三次cardinal曲线
*/
var lineGenerator = d3.line()
.x(function (d,i) {
return xScale(i)
})
.y(function (d) {
return yScale(d.value)
})
.curve(d3.curveCardinal)
/*
分别创建一个 xScale 与 yScale 比例尺
*/
var xScale = d3.scaleLinear()
.domain([0,6])
.range([0,600])
var yScale = d3.scaleLinear()
.domain([0,80])
.range([150,0])

/*
* d3.line()
* 使用默认的配置创建一个 线(line)生成器
* points
* 定义一个坐标数组
*/
var points = [
{value: 10},
{value: 50},
{value: 10},
{value: 1}
]


/*
* pathData
* -> 通过pathData 调用 lineGenerator来传入数组(points)
* svg
* 选择 svg 元素,添加路径(path),为其绑定数据(pathData)与样式
*/
var pathData = lineGenerator(points)

d3.select('path')
.attr('d', pathData)

D3 坐标轴与刻度(Axes)

坐标轴(Axes),通常在一些学术图标中进行使用,很多情况下的图表数据都是经过 比例尺(Scale) 来计算出的大小与坐标位置,可以通过使用刻度尺来进行标注,主要作用是辅助读者进行阅读,可以通过下图来感受到明显的差距:

参数与坐标轴

ID DA FA
Axes 设置比例尺提供人类观感友好的标尺刻度
d3.axisTop 创建一个刻度尺在的坐标轴生成器
d3.axisRight 创建一个刻度尺在的坐标轴生成器
d3.axisBottom 创建一个刻度尺在的坐标轴
d3.axisLeft 创建一个刻度尺在的坐标轴
axis 为指定的选择器生成一个坐标轴
axis.scale 设置坐标轴的比例尺
axis.ticks 自定义刻度的显示方式及格式化刻度
axis.tickArguments 自定义如何生成刻度或格式化刻度
axis.tickValues 指定固定的刻度值
axis.tickFormat 指定固定的刻度单位
axis.tickSize 设置刻度大小
axis.tickSizelnner 设置内刻度大小
axis.tickSizeOuter 设置外侧,刻度大小
axis.tickPadding 设置刻度和刻度文本之间的间距

d3 version 3 和 d3 version 4 的区别就是在 v3 中除了需要绘制比例尺和刻度尺之外,还需要写入其CSS样式,极大的方便了开发者在绘制中的时间。

需要注意的是,在 d3 version4 中将 v3 scale.linear 改为 scaleLinaer。除此之外,还将 orient,替换成了**d3.axisBottom(xScale)**。

bottom and left


通过使用 d3.axisBottom以及d3.axisLeat来构建一个坐标轴,其坐标轴构建完成后一定是关联起来的而并非是分开的,因此我们通过使用线性比例尺来通过center中的数据来决定刻度的最大值和最小值。

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
var padding = {
width: 700,
height: 700,
xAxisWidth: 500,
yAxisWidth: 500,
right: 30,
bottom: 30,
left: 130
}
var center= [
[1,1]
]
/*
* 选择 id 为 demo 的元素
* 设置 width.height 为 700
*/
var svg = d3.select("#demo")
.append("svg")
.attr("width",padding.width)
.attr("height", padding.height)

/*
分别定义 x/y 轴比例尺 xScale 、 yScale
并根据数据来决定比例尺数值中的数据
*/
var xScale = d3.scaleLinear()
.domain([0, 1 * d3.max(center, function (d) {
return d[1]
})])
.range([0, padding.xAxisWidth])

var yScale = d3.scaleLinear()
.domain([0,1 * d3.max(center, function (d) {
return d[1]
})])
.range([0, padding.yAxisWidth])

/*
* 分别选择 svg 并添加 g 元素
* 分别计算出 xScale 和 yScale 位置
* 将坐标轴位置定义为 Bottom、Left等,分别为 x 轴和 y 轴方向。
*/
d3.select("svg")
.append("g")
.attr("transform","translate(" + padding.left + "," + (padding.height - padding.bottom) + ")")
.call(d3.axisBottom(xScale))

d3.select("svg")
.append("g")
.attr("transform", "translate(" + padding.left + "," + (padding.height - padding.bottom - padding.yAxisWidth) + ")")
.call(d3.axisLeft(yScale))

bottom


如果你觉得上述的 code 太复杂,只想要一个坐标轴,那我们可以单独使用axisBottom来绘制一个刻度为底部方向的坐标轴。

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
var padding = {
width: 700,
height: 700,
xAxisWidth: 500,
yAxisWidth: 500,
right: 30,
bottom: 30,
left: 130
}
var center= [
[1,1]
]
/*
* 选择 id 为 demo 的元素
* 设置 width.height 为 700
*/
var svg = d3.select("#demo")
.append("svg")
.attr("width",padding.width)
.attr("height", padding.height)

/*
分别定义 x/y 轴比例尺 xScale
并根据数据来决定比例尺数值中的数据
*/
var xScale = d3.scaleLinear()
.domain([0, 1 * d3.max(center, function (d) {
return d[1]
})])
.range([0, padding.xAxisWidth])

/*
* 定义坐标轴
* ticks
* 自定义刻度的显示方式及格式化刻度
*/
var xAxis = d3.axisBottom(xScale)

d3.select("svg")
.append("g")
.attr("transform","translate(" + padding.left + "," + (padding.height - padding.bottom) + ")")
.call(xAxis)

刻度

数值

ticks


假设没有使用 ticks参数的比例尺刻度为0.0、0.1、0.2、0.3、0.4、0.5、0.6、0.7、0.8、0.9、1.0,那么通过ticks则会格式化输出为0.0、0.2、0.4、0.6、0.8、1.0,即自定义刻度的显示方式并格式化刻度

ticks 方法也可以被看做是 axis.tickArguments的简写,如下述 code 的例子为ticks(5),则可以被看做是axis.tickArguments([10])

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
var padding = {
width: 700,
height: 700,
xAxisWidth: 500,
yAxisWidth: 500,
right: 30,
bottom: 30,
left: 130
}
var center= [
[1,1]
]
/*
* 选择 id 为 demo 的元素
* 设置 width.height 为 700
*/
var svg = d3.select("#demo")
.append("svg")
.attr("width",padding.width)
.attr("height", padding.height)

/*
分别定义 x/y 轴比例尺 xScale 、 yScale
并根据数据来决定比例尺数值中的数据
*/
var xScale = d3.scaleLinear()
.domain([0, 1 * d3.max(center, function (d) {
return d[1]
})])
.range([0, padding.xAxisWidth])

var yScale = d3.scaleLinear()
.domain([0,1 * d3.max(center, function (d) {
return d[1]
})])
.range([0, padding.yAxisWidth])

/*
* 定义坐标轴
* ticks
* 自定义刻度的显示方式及格式化刻度
*/
var xAxis = d3.axisBottom(xScale)
.ticks(5)

var yAxis = d3.axisLeft(yScale)
.ticks(5)
/*
* 分别选择 svg 并添加 g 元素
* 分别计算出 xScale 和 yScale 位置
* 将坐标轴位置定义为 Bottom、Left等,分别为 x 轴和 y 轴方向。
*/
d3.select("svg")
.append("g")
.attr("transform","translate(" + padding.left + "," + (padding.height - padding.bottom) + ")")
.call(xAxis)


d3.select("svg")
.append("g")
.attr("transform", "translate(" + padding.left + "," + (padding.height - padding.bottom - padding.yAxisWidth) + ")")
.call(yAxis)

tickValues

tickValues 为指定刻度数值,如原始数值为1,2,3,4,5,6,7,8,9,10,当tickValues 内参数值为[0, 2, 4, 6, 8, 10]时,则刻度尺将会是数组内的数据。

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
var padding = {
width: 700,
height: 700,
xAxisWidth: 500,
yAxisWidth: 500,
right: 30,
bottom: 30,
left: 130
}
var center= [
[1,1]
]
/*
* 选择 id 为 demo 的元素
* 设置 width.height 为 700
*/
var svg = d3.select("#demo")
.append("svg")
.attr("width",padding.width)
.attr("height", padding.height)

/*
分别定义 x/y 轴比例尺 xScale
并根据数据来决定比例尺数值中的数据
*/
var xScale = d3.scaleLinear()
.domain([0, 1 * d3.max(center, function (d) {
return d[1]
})])
.range([0, padding.xAxisWidth])

/*
* 定义坐标轴
* ticks
* 自定义刻度的显示方式及格式化刻度
*/
var xAxis = d3.axisBottom(xScale)
/*
tickValue([value])
如果数据为 0~1 的话,那么通过使用 tickValues 则会输出
-> 0, 0.2, 0.4, 0.6, 0.8, 1
*/
.tickValues([0, 0.2, 0.4, 0.6, 0.8, 1])

d3.select("svg")
.append("g")
.attr("transform","translate(" + padding.left + "," + (padding.height - padding.bottom) + ")")
.call(xAxis)

tickFormat

如果指定了 tickFormat 则设置刻度的文字标签的格式化方法,可以通过使用 tickFormat 来为刻度添加单位,如:

1
2
3
var xAxis = d3.axisBottom(xScale)
.ticks(2)
.tickFormat(d => d+ " day")

长度

tickSize


tickSize 主要用于设置刻度外侧(outer)和内侧(inner)的高度,但需要注意的是在 d3 version3 版本中 tickSize 可以分别设置内侧和外侧的高度,但在 v4中被取消了。

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
var padding = {
width: 700,
height: 700,
xAxisWidth: 500,
yAxisWidth: 500,
right: 30,
bottom: 30,
left: 130
}
var center= [
[1,1]
]
/*
* 选择 id 为 demo 的元素
* 设置 width.height 为 700
*/
var svg = d3.select("#demo")
.append("svg")
.attr("width",padding.width)
.attr("height", padding.height)

/*
分别定义 x/y 轴比例尺 xScale
并根据数据来决定比例尺数值中的数据
*/
var xScale = d3.scaleLinear()
.domain([0, 1 * d3.max(center, function (d) {
return d[1]
})])
.range([0, padding.xAxisWidth])

/*
* 定义坐标轴
* ticks
* 自定义刻度的显示方式及格式化刻度
*/
var xAxis = d3.axisBottom(xScale)
/*
* tickSize
* 设置刻度外侧(outer)和内侧(inner)的高度
* -> 需要注意的是在 d3 version3 版本中 tickSize 可以分别设置内侧和外侧的高度,但在 v4中被取消了
*/
.tickSize(3)

d3.select("svg")
.append("g")
.attr("transform","translate(" + padding.left + "," + (padding.height - padding.bottom) + ")")
.call(xAxis)

tickSizeInner & tickSizeOuter


tickSizeInner 与 tickSizeOuter 分别可以来进行对刻度的外侧(outer)和内侧(inner)来对其设置高度

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
var padding = {
width: 700,
height: 700,
xAxisWidth: 500,
yAxisWidth: 500,
right: 30,
bottom: 30,
left: 130
}
var center= [
[1,1]
]
/*
* 选择 id 为 demo 的元素
* 设置 width.height 为 700
*/
var svg = d3.select("#demo")
.append("svg")
.attr("width",padding.width)
.attr("height", padding.height)

/*
分别定义 x/y 轴比例尺 xScale
并根据数据来决定比例尺数值中的数据
*/
var xScale = d3.scaleLinear()
.domain([0, 1 * d3.max(center, function (d) {
return d[1]
})])
.range([0, padding.xAxisWidth])

/*
* 定义坐标轴
* ticks
* 自定义刻度的显示方式及格式化刻度
*/
var xAxis = d3.axisBottom(xScale)
/*
* tickSizeInner & tickSizeOuter
* 设置刻度外侧(outer)和内侧(inner)的高度
*/
.tickSizeInner(2)
.tickSizeOuter(5)

d3.select("svg")
.append("g")
.attr("transform","translate(" + padding.left + "," + (padding.height - padding.bottom) + ")")
.call(xAxis)

D3 简介


D3 version 4 版本由 Mike Bostock 在 2016年 7 月 28号正式发布了此版本,与 version 3 不同的是 D3 目前是模块化,并由许多小库组成的,可以独立的进行使用,而且每个库都有自己的存储库和发行周期。

这也和D3 version 3 版本不同,在 D3 中开发者可能会从之前从0开始到现在的通过各种库来调用几个库来进行完成一个图表的绘制。

在此基础上虽然 D3 version 4 是模块化开发的,但是与之前其他的Highcharts、Echarts、Chart框架不同,在模块化的基础上保证了代码的可扩展性和自定义等。

需要注意的是,D3 是鼓励使用者来使用现成图表来进行绘制数据的,您只需要修改下数据和参数即可,官方认为这样可以帮助读者学习。

D3 的最好学习方法就是浏览他们的图库,这些例子可以照样重新利用来减少大量的从零学习的成本。

虽然你可能感觉这是 CV工程师所做的,但这没有关系,这些例子不仅仅是一个模板,而是一个学习的工具,通过改变这些参数来看看发生什么,比被动阅读更加的可以帮助理解。
—— Mike Bostock

模块化开发的好处还不仅仅只是方便开发,他还有一个对项目开发中较为友好的处理。在 d3 version3 版本中,的版本容量为148KB,而在 d3 version 4 中,容量则为12.8KB

这也归功与模块化开发,将功能分开,各模块单独发展、单独使用,这里就不会考虑容量的问题。

D3 version 4 是各个独立模块间协同工作的,而不是一个单独的库。可以根据需要而夹杂独立的模块,默认的完整资源包包含了大约 30 子模块:
<script src="https://d3js.org/d3.v4.0.0-alpha.50.min.js"></script>

如果需要按照项目需要而单独加载模块,比如使用一个颜色生成器,则需要单独引入:
<script src="https://d3js.org/d3-scale-chromatic.v0.3.min.js"></script>

D3 序数比例尺

序数比例尺(Ordinal Scale)所定义的定义域都是离散且不会连续的,即为序数比例尺,通常是在场景中最为常用的比例尺之一,如通过输入一个离散值来获取得到另一个离散的值,我们就可以通过使用序数比例尺来实现。

ID DA FA
d3.scale.ordinal() 定义序数比例尺
ordinal(y) 输入定义域离散值返回值域内的一个离散值
ordinal.domain() 设定或获取定义域
ordinal.range() 设定获取值域
ordinal.rangePoints(间隔(interval) [间距(padding)]) 代替 range() 定义值域,并接受一个连续区间,之后根据定义与中离散值的数量将其分段。之后的出的分段值用于值域中的离散值
ordinal.rangeRoundPoints ,与 ordinal.rangePoints()作用一样,但是结果会被取整
ordinal.rangeBands(间隔 (interval) [内边距 (padding) ]) 代替 range()定义值域,与rangePoints类似,但分段方法不同
ordinal.rangeRoundBands() ,与rangeBands类似,但结果会被取整
ordinal.rangeBand() 返回使用 rangeBands() 设定后的每一段参数
ordinal.rangeExtend() 返回一个数组,数组中存放着最大值以及一个最小值

rangePoints()

1
2
3
4
5
6
7
8
var ordinal = d3.scale.ordinal()
.domain([1,2,3])
.range([10,20,30])

console.log(ordinal(1))
console.log(ordinal(2))
console.log(ordinal(3))
console.log(ordinal(4))

在上述的code中,当定义域内数据大于值域时,那么多出的定义域值将会重新轮回一遍值域为止。但此方法来定义值域或许有些麻烦,所以我们可以通过使用rangePoints()来解决:

1
2
3
4
5
6
var ordinal = d3.scale.ordinal()
.domain([1,2,3,4,5])
.rangePoints([0,1000])

console.log(ordinal.range())
console.log(ordinal(2))

通常,我们使用rangePoints(interval,padding),其中interval[0,200],而padding是一个缺省值,之后计算得出step是根据定义域与数值计算得到的值,因为我们并没有设置padding,所以间距默认为50,而计算出的值就是圆的对应点。:

1
2
3
4
5
var ordinal = d3.scale.ordinal()
.domain([1,2,3,4,5])
.rangePoints([0,100],5)

console.log(ordinal.range())

padding那边距设置为5,那么当step * padding / 2 时,那么将会的出第一个圆的位置坐标,在下述 code 中:

1
2
3
4
5
var ordinal = d3.scale.ordinal()
.domain([1,2,3,4,5])
.rangePoints([0,200],5)

console.log(ordinal.range())

rangePoints替换为rangeRoundPoints()来四舍五入取整的数据,此时如果上述 code 输出55.55555555555,那么将会被四舍五入56。就如四舍五入后的结果为56、78、100、122、144,那么我们从55.55555555555556 - 77.77777777777777可以得知,step22.222222222222214,那么通过step * padding / 2 将会得出第一个结果的坐标。

RangeBands()

rangeBands()与rangePoints同样是代替 range()定义值域,与rangePoints类似,其分段方法为outerPadding、padding、outerPadding其中outerPadding主要是边界的空白参数(默认为0)。

1
2
3
4
5
var ordinal = d3.scale.ordinal()
.domain([1,2,3,4,5])
.rangeBands([0,200],2,3)

console.log(ordinal.range())

在上述的 code 中,我们首先定义了interval[0,200],将paddingouterPadding分别设置为2和3,那么输出的信息将会是最终是圆的位置坐标。那么最终经过计算,他的outerPaddingPadding分别22.22222222222223、22.222222222222214。通常从字面上意思来进行理解outherPadding就是圆开头与结尾处即两边的空白距离,就为outerPadding

📖 more posts 📖