├── .gitignore
├── README.md
├── css
├── consolepanel.css
└── consolepanel.png
├── demo
├── ctopo示例API版.html
├── ctopo示例全配置版.html
├── ctopo示例简写版.html
├── data_big.json
├── data_small.json
├── data_small_api.json
└── 控制台示例.html
├── image
├── ctopobg.png
├── demo.png
└── skin.jpg
└── src
├── consolepanel.js
└── ctopo.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # 这个文件是git的标识忽略文件的配置文件, 标识的文件将不会提交到git
2 |
3 | **/node_modules
4 | .idea/
5 | dist/
6 | npm-debug.log*
7 | .DS_Store*
8 | .project
9 | .vs
10 | net-poc-web.iml
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ctopo
2 | =====
3 |
4 | canvas版的拓扑图(topo)展示工具.
5 |
6 | 起因
7 | -----
8 | 总在使用别人的开源插件来绘制拓扑,总是不能满足公司各个项目的各种需求,痛定思痛自己搞一套吧.
9 |
10 | ps: 感谢[UI设计师yoki](http://www.zcool.com.cn/u/968707)对topo图做的UI设计,很赞!!!
11 |
12 | ps: 因为ff,chrome不支持本地请求json文件, 将整体工程放到本服务器下运行最为适宜.
13 |
14 | 适用场景和环境
15 | -----
16 | (1)适用于监控网络ip节点互联关系的场景
17 | (2)适用于社交网络的群组互联访问关系
18 |
19 | 兼容性
20 | -----
21 | ie9+,firefox,chrome,safari.
22 |
23 | 性能说明
24 | -----
25 | 300+节点毫无压力,顺畅的没有朋友,正常使用尽量控制在300左右.
26 |
27 | 节点的碰撞检测5千节点1ms左右,连线的碰撞检测5千连线1ms左右,(ff,chrome一样)
28 |
29 | 如果需要展示上千节点请参考以下调优方案
30 |
31 | 调优方案参考
32 |
33 | (1)将力导向布局的迭代次数从300次下降到150次,布局时间缩小一倍,例:10s---优化后5s
34 | (2)将布局算法放在后台执行,将前台布局设置为[预设],按java举例,例:10s---优化后2s
35 | (3)将节点的显示标签Label隐藏,下面列举了绘制label的性能影响,例:10s---优化后3s
36 | (4)用空间换时间,将坐标保留,不用每次draw都要重新计算布局
37 |
38 |
39 | ps: 绘制节点标签还是挺吃性能的
40 |
41 | 绘制节点不带label
42 | //ff (浏览器卡死, 建议使用服务器端计算)
43 | //chrome nodes count=1000,edges count=1000,layout time=4838,draw time=9
44 |
45 | //ff nodes count=500,edges count=500,layout time=9543,draw time=19
46 | //chrome nodes count=500,edges count=500,layout time=1272,draw time=6
47 |
48 | //ff nodes count=300,edges count=300,layout time=3445,draw time=11
49 | //chrome nodes count=300,edges count=300,layout time=475,draw time=4
50 |
51 | //ff nodes count=200,edges count=200,layout time=1551,draw time=7
52 | //chrome nodes count=200,edges count=200,layout time=233,draw time=4
53 |
54 | 绘制节点带label
55 | //ff (浏览器卡死, 建议使用服务器端计算)
56 | //chrome nodes count=1000,edges count=1000,layout time=4838,draw time=39
57 |
58 | //ff nodes count=500,edges count=500,layout time=9543,draw time=125
59 | //chrome nodes count=500,edges count=500,layout time=1272,draw time=23
60 |
61 | //ff nodes count=300,edges count=300,layout time=3445,draw time=77
62 | //chrome nodes count=300,edges count=300,layout time=475,draw time=16
63 |
64 | //ff nodes count=200,edges count=200,layout time=1551,draw time=49
65 | //chrome nodes count=200,edges count=200,layout time=233,draw time=10
66 | 缺点
67 | -----
68 | (1)力导向布局算法性能差一点,还需要进一步优化
69 | (2)不支持节点图片和图标
70 | (3)不支持框选操作
71 |
72 | 特性
73 | -----
74 | (1)提供控制面板;
75 | (2)支持鼠标滑轮和拖拽的放大,缩小;
76 | (3)上下左右键盘平移;
77 | (4)拖拽单个节点和屏幕;
78 | (5)支持点击和悬停节点;
79 | (6)支持点击和悬停连线;
80 | (7)支持悬停节点的关联节点高亮;
81 | (8)提供事件回调接口;
82 | (9)支持连线箭头;
83 | (10)支持连线的流动动画;
84 | (11)支持力导向布局定位,即每次刷新页面,坐标不变;
85 |
86 | 实现原理
87 | -----
88 | (1)使用html5的canvas标签来实现的;
89 | (2)布局算法使用力导向布局(库仑斥力公式和胡克定律公式)[来自网络](http://zhenghaoju700.blog.163.com/blog/static/13585951820114153548541/?suggestedreading&wumii);
90 | (3)节点的碰撞检测使用勾股定理测距;
91 | (4)连线的碰撞检测使用反正切计算夹角;
92 |
93 | 界面展示
94 | -----
95 | 
96 | 
97 |
98 | 版权
99 | -----
100 | MIT(随意使用,免费开源)
101 |
102 | 基础实例
103 | -----
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
129 |
130 |
131 |
132 |
133 | api接口
134 | -----
135 | ### 使用方法
136 | //取得ctopo对象, 点出api方法即可
137 | var ctopo = ctopo({..各种基础配置..});
138 | var nodeA = ctopo.node("1108"); //获取节点A
139 | var nodeB = ctopo.node("0724"); //获取节点B
140 | var edge = ctopo.edge("1108","0724"); //获取连线
141 |
142 | ### 属性和方法
143 |
144 |
145 | 属性名 | 描述 |
146 |
147 |
148 | version | 版本 |
149 | option | 配置对象 |
150 | canvas | 画布对象 |
151 | context | 画布上下文对象 |
152 | nodes | 节点数组 |
153 | edges | 连线对象 |
154 |
155 |
156 |
157 |
158 |
159 | 方法名 | 描述 | 参数 | 返回值 |
160 |
161 |
162 |
163 | addEdge(edge,isDrawNow) |
164 | 添加连线 |
165 |
166 | 参数1: edge添加的连线对象
167 | 参数2: isDrawNow是否立刻渲染到屏幕
168 | |
169 | 空 |
170 |
171 |
172 | addNode(node,isDrawNow) |
173 | 添加节点 |
174 |
175 | 参数1: edge添加的节点对象
176 | 参数2: isDrawNow是否立刻渲染到屏幕
177 | |
178 | 空 |
179 |
180 |
181 | draw(option) |
182 | 重新绘制画布, 用法等于ctopo(option) |
183 |
184 | 参数1: option初始的配置对象
185 | |
186 | 空 |
187 |
188 |
189 | drawData(data,isApplyLayout) |
190 | 局部刷新,只刷新数据 |
191 |
192 | 参数1: data格式=optioin.data
193 | 参数2: isApplyLayout是否重新应用布局
194 | |
195 | 成功true,失败false |
196 |
197 |
198 | edge(sid,tid) |
199 |
200 | 取得连线对象
201 | ps:区分方向
202 | |
203 |
204 | 参数1: 开始节点id
205 | 参数2: 结束节点id
206 | |
207 |
208 | 查到: 连线对象
209 | 没查到: null
210 | |
211 |
212 |
213 | edgeArray() |
214 | 取得所有的连线对象数组 |
215 |
216 | 无
217 | |
218 | 连线对象数组 |
219 |
220 |
221 | firstNeighbors(nid) |
222 | 返回与之关联的连线和节点数组对象 |
223 |
224 | 参数1: nid待匹配的节点id
225 | |
226 | 查到:关联数据对象;
227 | 没查到:空数组对象
228 | {
229 | edgeNeighbors:[],
230 | nodeNeighbors:[]
231 | }
232 | |
233 |
234 |
235 | layout(layout) |
236 | 重置切换布局 |
237 |
238 | (可选)参数1: layout==option.layout
239 | |
240 |
241 | 无参数: 返回option.layout
242 | 有参数: 重置布局, 成功true,失败false
243 | |
244 |
245 |
246 | node(id) |
247 | 取得节点对象 |
248 |
249 | 参数1: 节点id
250 | |
251 |
252 | 查到:节点对象
253 | 没查到:null
254 | |
255 |
256 |
257 | nodeLabelsVisible(visible) |
258 | 设置节点标签是否显示 |
259 |
260 | 参数1: visible是否显示标签, 布尔型true,false
261 | |
262 | 空 |
263 |
264 |
265 | edgeLabelsVisible(visible) |
266 | 设置连线标签是否显示 |
267 |
268 | 参数1: visible是否显示标签, 布尔型true,false
269 | |
270 | 空 |
271 |
272 |
273 | edgeArrowsVisible(visible) |
274 | 设置连线箭头是否显示 |
275 |
276 | 参数1: visible是否显示箭头, 布尔型true,false
277 | |
278 | 空 |
279 |
280 |
281 | nodeArray() |
282 | 取得所有节点对象数组 |
283 |
284 | 无
285 | |
286 | 节点对象数组 |
287 |
288 |
289 | nodeTooltipsVisible(visible) |
290 | 设置节点提示框是否显示 |
291 |
292 | 参数1: visible是否显示提示框, 布尔型true,false
293 | |
294 | 空 |
295 |
296 |
297 | edgeTooltipsVisible(visible) |
298 | 设置连线提示框是否显示 |
299 |
300 | 参数1: visible是否显示提示框, 布尔型true,false
301 | |
302 | 空 |
303 |
304 |
305 | edgeAnimateBallsVisible(visible) |
306 | 设置连线动画球是否显示 |
307 |
308 | 参数1: visible是否显示动画球, 布尔型true,false
309 | |
310 | 空 |
311 |
312 |
313 | consolePanelVisible(visible) |
314 | 设置控制台是否显示 |
315 |
316 | 参数1: visible是否显示控制台, 布尔型true,false
317 | |
318 | 空 |
319 |
320 |
321 | removeEdge(sid,tid,isDrawNow) |
322 | 删除连线 |
323 |
324 | 参数1: 开始节点id
325 | 参数2: 结束节点id
326 | 参数3: 是否立刻渲染到屏幕
327 | |
328 | 空 |
329 |
330 |
331 | removeNode(id,isDrawNow) |
332 | 删除节点,与之关联的线也删除 |
333 |
334 | 参数1: 节点id
335 | 参数2: 是否立刻渲染到屏幕
336 | |
337 | 空 |
338 |
339 |
340 | updateEdge(edge,isDrawNow) |
341 | 更新连线 |
342 |
343 | 参数1: 连线对象
344 | 参数3: 是否立刻渲染到屏幕
345 | |
346 | 空 |
347 |
348 |
349 | updateNode(node,isDrawNow) |
350 | 更新节点 |
351 |
352 | 参数1: 节点对象
353 | 参数2: 是否立刻渲染到屏幕
354 | |
355 | 空 |
356 |
357 |
358 | style(style) |
359 | 重置切换样式 |
360 |
361 | (可选)参数1: style==option.style
362 | |
363 |
364 | 无参数: 返回option.style
365 | 有参数: 重置样式, 成功true,失败false
366 | |
367 |
368 |
369 | zoom(scale) |
370 | 设置缩放比例(0-1) |
371 |
372 | (可选)参数1: scale比例0-1,100%=0.5
373 | |
374 |
375 | 无参数: 返回比例值
376 | 有参数: 设置比例值, 成功ture,失败false
377 | |
378 |
379 |
380 |
381 |
382 |
--------------------------------------------------------------------------------
/css/consolepanel.css:
--------------------------------------------------------------------------------
1 | /**
2 | * console_panel为控制台的顶层div
3 | * 以下所有的样式均下层级选择器下,不会污染其他样式文件
4 | */
5 | .console_panel{
6 | position:absolute;
7 | top:5px;
8 | left:5px;
9 | }
10 | /* 方向盘 控制上下左右的样式 */
11 | .console_panel .steerwheel_panel {
12 | position:absolute;
13 | width: 86px;
14 | height: 86px;
15 | overflow: hidden;
16 | background: url(consolepanel.png) no-repeat;
17 | cursor:pointer;
18 | }
19 | .console_panel .steer_default{
20 | background-position: 0px 0px;
21 | }
22 | /* 键盘触发
23 | 37==left
24 | 38==up
25 | 39==right
26 | 40==down
27 | */
28 | .console_panel .steer_37{
29 | background-position: 0px -344px;
30 | }
31 | .console_panel .steer_38{
32 | background-position: 0px -86px;
33 | }
34 | .console_panel .steer_39{
35 | background-position: 0px -172px;
36 | }
37 | .console_panel .steer_40{
38 | background-position: 0px -258px;
39 | }
40 | /* 触发上下左右的div块 */
41 | .console_panel .steer_btn{
42 | position:absolute;
43 | height: 30px;
44 | width: 30px;
45 | }
46 | .console_panel .steer_btn_up{
47 | top: 0;
48 | left: 28px;
49 | }
50 | .console_panel .steer_btn_down{
51 | bottom: 0;
52 | left: 28px;
53 | }
54 | .console_panel .steer_btn_left{
55 | top: 28px;
56 | left: 0px;
57 | }
58 | .console_panel .steer_btn_right{
59 | top: 28px;
60 | right: 0px;
61 | }
62 | /* 放大缩小的面板 */
63 | .console_panel .zoom_panel{
64 | position:absolute;
65 | top:86px;
66 | left:30px;
67 | }
68 | /* 放大缩小按钮*/
69 | .console_panel .zoom {
70 | cursor:pointer;
71 | width: 26px;
72 | height: 26px;
73 | overflow: hidden;
74 | background: url(consolepanel.png) no-repeat;
75 | }
76 | /* 放大 */
77 | .console_panel .zoom_out{
78 | position:absolute;
79 | top:0px;
80 | left:0px;
81 | background-position: 0px -430px;
82 | }
83 | .console_panel .zoom_out:hover{
84 | background-position: 0px -456px;
85 | }
86 | /* 缩小 */
87 | .console_panel .zoom_in{
88 | position:absolute;
89 | top:166px;
90 | left:0px;
91 | background-position: 0px -482px;
92 | }
93 | .console_panel .zoom_in:hover{
94 | background-position: 0px -508px;
95 | }
96 | /* 比例尺条 */
97 | .console_panel .scale_bar{
98 | position:absolute;
99 | top:26px;
100 | left:0px;
101 | height:140px;
102 | width:26px;
103 | }
104 | /* 比例尺的槽 */
105 | .console_panel .scale_ruler {
106 | position:absolute;
107 | top:0px;
108 | left:4px;
109 | z-index: 10;
110 | width: 18px;
111 | height: 140px;
112 | overflow: hidden;
113 | background: url(consolepanel.png) no-repeat -26px -430px;
114 | }
115 | /* 比例尺的滑块 */
116 | .console_panel .scale_slider {
117 | position:absolute;
118 | top:60px;
119 | left:0px;
120 | z-index: 12;
121 | width: 26px;
122 | height: 20px;
123 | overflow: hidden;
124 | background: url(consolepanel.png) no-repeat 0px -534px;
125 | cursor:pointer;
126 | }
127 | /* 比例尺的当前比例(已经选中的比例) */
128 | .console_panel .scale_ruler_current {
129 | position:absolute;
130 | top:70px;
131 | left:4px;
132 | z-index: 11;
133 | width: 18px;
134 | /*height: 140px;*/
135 | height:70px;
136 | overflow: hidden;
137 | background: url(consolepanel.png) no-repeat -44px -500px;
138 | }
--------------------------------------------------------------------------------
/css/consolepanel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tm-roamer/ctopo/5fe44fee1b23e12813227f5e21b86ba277cf33b5/css/consolepanel.png
--------------------------------------------------------------------------------
/demo/ctopo示例API版.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Starter Template for Bootstrap
6 |
7 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
339 |
340 |
--------------------------------------------------------------------------------
/demo/ctopo示例全配置版.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Starter Template for Bootstrap
6 |
7 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
168 |
169 |
170 |
171 |
--------------------------------------------------------------------------------
/demo/ctopo示例简写版.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ctopo示例简写版
6 |
7 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/demo/data_big.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "838644321",
5 | "x": 0,
6 | "y": 0,
7 | "label": "192.168.12.164",
8 | "color": "#00adee",
9 | "size": 20,
10 | "textColor": "#878787",
11 | "textSize": 10,
12 | "customField1": "自定义字段1",
13 | "customField2": [
14 | "自定义字段2_1",
15 | "自定义字段2_2"
16 | ],
17 | "customField3": {
18 | "field3_1": "自定义字段3_1",
19 | "field3_2": "自定义字段3_2"
20 | }
21 | },
22 | {
23 | "id": "-2051156877",
24 | "x": 0,
25 | "y": 0,
26 | "label": "192.168.12.38",
27 | "color": "#00adee",
28 | "size": 20,
29 | "textColor": "#878787",
30 | "textSize": 10,
31 | "customField1": "自定义字段1",
32 | "customField2": [
33 | "自定义字段2_1",
34 | "自定义字段2_2"
35 | ],
36 | "customField3": {
37 | "field3_1": "自定义字段3_1",
38 | "field3_2": "自定义字段3_2"
39 | }
40 | },
41 | {
42 | "id": "838644324",
43 | "x": 0,
44 | "y": 0,
45 | "label": "192.168.12.167",
46 | "color": "#00adee",
47 | "size": 20,
48 | "textColor": "#878787",
49 | "textSize": 10,
50 | "customField1": "自定义字段1",
51 | "customField2": [
52 | "自定义字段2_1",
53 | "自定义字段2_2"
54 | ],
55 | "customField3": {
56 | "field3_1": "自定义字段3_1",
57 | "field3_2": "自定义字段3_2"
58 | }
59 | },
60 | {
61 | "id": "-2051156755",
62 | "x": 0,
63 | "y": 0,
64 | "label": "192.168.12.76",
65 | "color": "#00adee",
66 | "size": 20,
67 | "textColor": "#878787",
68 | "textSize": 10,
69 | "customField1": "自定义字段1",
70 | "customField2": [
71 | "自定义字段2_1",
72 | "自定义字段2_2"
73 | ],
74 | "customField3": {
75 | "field3_1": "自定义字段3_1",
76 | "field3_2": "自定义字段3_2"
77 | }
78 | },
79 | {
80 | "id": "838644171",
81 | "x": 0,
82 | "y": 0,
83 | "label": "192.168.12.119",
84 | "color": "#00adee",
85 | "size": 20,
86 | "textColor": "#878787",
87 | "textSize": 10,
88 | "customField1": "自定义字段1",
89 | "customField2": [
90 | "自定义字段2_1",
91 | "自定义字段2_2"
92 | ],
93 | "customField3": {
94 | "field3_1": "自定义字段3_1",
95 | "field3_2": "自定义字段3_2"
96 | }
97 | },
98 | {
99 | "id": "838645127",
100 | "x": 0,
101 | "y": 0,
102 | "label": "192.168.12.214",
103 | "color": "#00adee",
104 | "size": 20,
105 | "textColor": "#878787",
106 | "textSize": 10,
107 | "customField1": "自定义字段1",
108 | "customField2": [
109 | "自定义字段2_1",
110 | "自定义字段2_2"
111 | ],
112 | "customField3": {
113 | "field3_1": "自定义字段3_1",
114 | "field3_2": "自定义字段3_2"
115 | }
116 | },
117 | {
118 | "id": "838645126",
119 | "x": 0,
120 | "y": 0,
121 | "label": "192.168.12.213",
122 | "color": "#00adee",
123 | "size": 20,
124 | "textColor": "#878787",
125 | "textSize": 10,
126 | "customField1": "自定义字段1",
127 | "customField2": [
128 | "自定义字段2_1",
129 | "自定义字段2_2"
130 | ],
131 | "customField3": {
132 | "field3_1": "自定义字段3_1",
133 | "field3_2": "自定义字段3_2"
134 | }
135 | },
136 | {
137 | "id": "1734948971",
138 | "x": 0,
139 | "y": 0,
140 | "label": "192.168.12.9",
141 | "color": "#00adee",
142 | "size": 20,
143 | "textColor": "#878787",
144 | "textSize": 10,
145 | "customField1": "自定义字段1",
146 | "customField2": [
147 | "自定义字段2_1",
148 | "自定义字段2_2"
149 | ],
150 | "customField3": {
151 | "field3_1": "自定义字段3_1",
152 | "field3_2": "自定义字段3_2"
153 | }
154 | },
155 | {
156 | "id": "-2051156759",
157 | "x": 0,
158 | "y": 0,
159 | "label": "192.168.12.72",
160 | "color": "#00adee",
161 | "size": 20,
162 | "textColor": "#878787",
163 | "textSize": 10,
164 | "customField1": "自定义字段1",
165 | "customField2": [
166 | "自定义字段2_1",
167 | "自定义字段2_2"
168 | ],
169 | "customField3": {
170 | "field3_1": "自定义字段3_1",
171 | "field3_2": "自定义字段3_2"
172 | }
173 | },
174 | {
175 | "id": "838644166",
176 | "x": 0,
177 | "y": 0,
178 | "label": "192.168.12.114",
179 | "color": "#00adee",
180 | "size": 20,
181 | "textColor": "#878787",
182 | "textSize": 10,
183 | "customField1": "自定义字段1",
184 | "customField2": [
185 | "自定义字段2_1",
186 | "自定义字段2_2"
187 | ],
188 | "customField3": {
189 | "field3_1": "自定义字段3_1",
190 | "field3_2": "自定义字段3_2"
191 | }
192 | },
193 | {
194 | "id": "-2051156883",
195 | "x": 0,
196 | "y": 0,
197 | "label": "192.168.12.32",
198 | "color": "#00adee",
199 | "size": 20,
200 | "textColor": "#878787",
201 | "textSize": 10,
202 | "customField1": "自定义字段1",
203 | "customField2": [
204 | "自定义字段2_1",
205 | "自定义字段2_2"
206 | ],
207 | "customField3": {
208 | "field3_1": "自定义字段3_1",
209 | "field3_2": "自定义字段3_2"
210 | }
211 | },
212 | {
213 | "id": "838645129",
214 | "x": 0,
215 | "y": 0,
216 | "label": "192.168.12.216",
217 | "color": "#00adee",
218 | "size": 20,
219 | "textColor": "#878787",
220 | "textSize": 10,
221 | "customField1": "自定义字段1",
222 | "customField2": [
223 | "自定义字段2_1",
224 | "自定义字段2_2"
225 | ],
226 | "customField3": {
227 | "field3_1": "自定义字段3_1",
228 | "field3_2": "自定义字段3_2"
229 | }
230 | },
231 | {
232 | "id": "838644165",
233 | "x": 0,
234 | "y": 0,
235 | "label": "192.168.12.113",
236 | "color": "#00adee",
237 | "size": 20,
238 | "textColor": "#878787",
239 | "textSize": 10,
240 | "customField1": "自定义字段1",
241 | "customField2": [
242 | "自定义字段2_1",
243 | "自定义字段2_2"
244 | ],
245 | "customField3": {
246 | "field3_1": "自定义字段3_1",
247 | "field3_2": "自定义字段3_2"
248 | }
249 | },
250 | {
251 | "id": "-2051156884",
252 | "x": 0,
253 | "y": 0,
254 | "label": "192.168.12.31",
255 | "color": "#00adee",
256 | "size": 20,
257 | "textColor": "#878787",
258 | "textSize": 10,
259 | "customField1": "自定义字段1",
260 | "customField2": [
261 | "自定义字段2_1",
262 | "自定义字段2_2"
263 | ],
264 | "customField3": {
265 | "field3_1": "自定义字段3_1",
266 | "field3_2": "自定义字段3_2"
267 | }
268 | },
269 | {
270 | "id": "838644164",
271 | "x": 0,
272 | "y": 0,
273 | "label": "192.168.12.112",
274 | "color": "#00adee",
275 | "size": 20,
276 | "textColor": "#878787",
277 | "textSize": 10,
278 | "customField1": "自定义字段1",
279 | "customField2": [
280 | "自定义字段2_1",
281 | "自定义字段2_2"
282 | ],
283 | "customField3": {
284 | "field3_1": "自定义字段3_1",
285 | "field3_2": "自定义字段3_2"
286 | }
287 | },
288 | {
289 | "id": "838645128",
290 | "x": 0,
291 | "y": 0,
292 | "label": "192.168.12.215",
293 | "color": "#00adee",
294 | "size": 20,
295 | "textColor": "#878787",
296 | "textSize": 10,
297 | "customField1": "自定义字段1",
298 | "customField2": [
299 | "自定义字段2_1",
300 | "自定义字段2_2"
301 | ],
302 | "customField3": {
303 | "field3_1": "自定义字段3_1",
304 | "field3_2": "自定义字段3_2"
305 | }
306 | },
307 | {
308 | "id": "838644163",
309 | "x": 0,
310 | "y": 0,
311 | "label": "192.168.12.111",
312 | "color": "#00adee",
313 | "size": 20,
314 | "textColor": "#878787",
315 | "textSize": 10,
316 | "customField1": "自定义字段1",
317 | "customField2": [
318 | "自定义字段2_1",
319 | "自定义字段2_2"
320 | ],
321 | "customField3": {
322 | "field3_1": "自定义字段3_1",
323 | "field3_2": "自定义字段3_2"
324 | }
325 | },
326 | {
327 | "id": "838644350",
328 | "x": 0,
329 | "y": 0,
330 | "label": "192.168.12.172",
331 | "color": "#00adee",
332 | "size": 20,
333 | "textColor": "#878787",
334 | "textSize": 10,
335 | "customField1": "自定义字段1",
336 | "customField2": [
337 | "自定义字段2_1",
338 | "自定义字段2_2"
339 | ],
340 | "customField3": {
341 | "field3_1": "自定义字段3_1",
342 | "field3_2": "自定义字段3_2"
343 | }
344 | },
345 | {
346 | "id": "-2007034805",
347 | "x": 0,
348 | "y": 0,
349 | "label": "10.54.14.6",
350 | "color": "#00adee",
351 | "size": 20,
352 | "textColor": "#878787",
353 | "textSize": 10,
354 | "customField1": "自定义字段1",
355 | "customField2": [
356 | "自定义字段2_1",
357 | "自定义字段2_2"
358 | ],
359 | "customField3": {
360 | "field3_1": "自定义字段3_1",
361 | "field3_2": "自定义字段3_2"
362 | }
363 | },
364 | {
365 | "id": "-809256359",
366 | "x": 0,
367 | "y": 0,
368 | "label": "224.0.0.252",
369 | "color": "#00adee",
370 | "size": 20,
371 | "textColor": "#878787",
372 | "textSize": 10,
373 | "customField1": "自定义字段1",
374 | "customField2": [
375 | "自定义字段2_1",
376 | "自定义字段2_2"
377 | ],
378 | "customField3": {
379 | "field3_1": "自定义字段3_1",
380 | "field3_2": "自定义字段3_2"
381 | }
382 | },
383 | {
384 | "id": "-809256360",
385 | "x": 0,
386 | "y": 0,
387 | "label": "224.0.0.251",
388 | "color": "#00adee",
389 | "size": 30,
390 | "textColor": "#878787",
391 | "textSize": 10,
392 | "customField1": "自定义字段1",
393 | "customField2": [
394 | "自定义字段2_1",
395 | "自定义字段2_2"
396 | ],
397 | "customField3": {
398 | "field3_1": "自定义字段3_1",
399 | "field3_2": "自定义字段3_2"
400 | }
401 | },
402 | {
403 | "id": "-2051156912",
404 | "x": 0,
405 | "y": 0,
406 | "label": "192.168.12.24",
407 | "color": "#00adee",
408 | "size": 20,
409 | "textColor": "#878787",
410 | "textSize": 10,
411 | "customField1": "自定义字段1",
412 | "customField2": [
413 | "自定义字段2_1",
414 | "自定义字段2_2"
415 | ],
416 | "customField3": {
417 | "field3_1": "自定义字段3_1",
418 | "field3_2": "自定义字段3_2"
419 | }
420 | },
421 | {
422 | "id": "-2051156911",
423 | "x": 0,
424 | "y": 0,
425 | "label": "192.168.12.25",
426 | "color": "#00adee",
427 | "size": 20,
428 | "textColor": "#878787",
429 | "textSize": 10,
430 | "customField1": "自定义字段1",
431 | "customField2": [
432 | "自定义字段2_1",
433 | "自定义字段2_2"
434 | ],
435 | "customField3": {
436 | "field3_1": "自定义字段3_1",
437 | "field3_2": "自定义字段3_2"
438 | }
439 | },
440 | {
441 | "id": "838645252",
442 | "x": 0,
443 | "y": 0,
444 | "label": "192.168.12.255",
445 | "color": "#00adee",
446 | "size": 20,
447 | "textColor": "#878787",
448 | "textSize": 10,
449 | "customField1": "自定义字段1",
450 | "customField2": [
451 | "自定义字段2_1",
452 | "自定义字段2_2"
453 | ],
454 | "customField3": {
455 | "field3_1": "自定义字段3_1",
456 | "field3_2": "自定义字段3_2"
457 | }
458 | },
459 | {
460 | "id": "838645248",
461 | "x": 0,
462 | "y": 0,
463 | "label": "192.168.12.251",
464 | "color": "#00adee",
465 | "size": 20,
466 | "textColor": "#878787",
467 | "textSize": 10,
468 | "customField1": "自定义字段1",
469 | "customField2": [
470 | "自定义字段2_1",
471 | "自定义字段2_2"
472 | ],
473 | "customField3": {
474 | "field3_1": "自定义字段3_1",
475 | "field3_2": "自定义字段3_2"
476 | }
477 | },
478 | {
479 | "id": "-1072993729",
480 | "x": 0,
481 | "y": 0,
482 | "label": "239.255.255.250",
483 | "color": "#00adee",
484 | "size": 20,
485 | "textColor": "#878787",
486 | "textSize": 10,
487 | "customField1": "自定义字段1",
488 | "customField2": [
489 | "自定义字段2_1",
490 | "自定义字段2_2"
491 | ],
492 | "customField3": {
493 | "field3_1": "自定义字段3_1",
494 | "field3_2": "自定义字段3_2"
495 | }
496 | },
497 | {
498 | "id": "838645159",
499 | "x": 0,
500 | "y": 0,
501 | "label": "192.168.12.225",
502 | "color": "#00adee",
503 | "size": 20,
504 | "textColor": "#878787",
505 | "textSize": 10,
506 | "customField1": "自定义字段1",
507 | "customField2": [
508 | "自定义字段2_1",
509 | "自定义字段2_2"
510 | ],
511 | "customField3": {
512 | "field3_1": "自定义字段3_1",
513 | "field3_2": "自定义字段3_2"
514 | }
515 | },
516 | {
517 | "id": "-2051156789",
518 | "x": 0,
519 | "y": 0,
520 | "label": "192.168.12.63",
521 | "color": "#00adee",
522 | "size": 20,
523 | "textColor": "#878787",
524 | "textSize": 10,
525 | "customField1": "自定义字段1",
526 | "customField2": [
527 | "自定义字段2_1",
528 | "自定义字段2_2"
529 | ],
530 | "customField3": {
531 | "field3_1": "自定义字段3_1",
532 | "field3_2": "自定义字段3_2"
533 | }
534 | },
535 | {
536 | "id": "838645160",
537 | "x": 0,
538 | "y": 0,
539 | "label": "192.168.12.226",
540 | "color": "#00adee",
541 | "size": 20,
542 | "textColor": "#878787",
543 | "textSize": 10,
544 | "customField1": "自定义字段1",
545 | "customField2": [
546 | "自定义字段2_1",
547 | "自定义字段2_2"
548 | ],
549 | "customField3": {
550 | "field3_1": "自定义字段3_1",
551 | "field3_2": "自定义字段3_2"
552 | }
553 | },
554 | {
555 | "id": "1013191670",
556 | "x": 0,
557 | "y": 0,
558 | "label": "0.0.0.0",
559 | "color": "#00adee",
560 | "size": 40,
561 | "textColor": "#878787",
562 | "textSize": 10,
563 | "customField1": "自定义字段1",
564 | "customField2": [
565 | "自定义字段2_1",
566 | "自定义字段2_2"
567 | ],
568 | "customField3": {
569 | "field3_1": "自定义字段3_1",
570 | "field3_2": "自定义字段3_2"
571 | }
572 | },
573 | {
574 | "id": "838644198",
575 | "x": 0,
576 | "y": 0,
577 | "label": "192.168.12.125",
578 | "color": "#00adee",
579 | "size": 20,
580 | "textColor": "#878787",
581 | "textSize": 10,
582 | "customField1": "自定义字段1",
583 | "customField2": [
584 | "自定义字段2_1",
585 | "自定义字段2_2"
586 | ],
587 | "customField3": {
588 | "field3_1": "自定义字段3_1",
589 | "field3_2": "自定义字段3_2"
590 | }
591 | },
592 | {
593 | "id": "838644195",
594 | "x": 0,
595 | "y": 0,
596 | "label": "192.168.12.122",
597 | "color": "#00adee",
598 | "size": 20,
599 | "textColor": "#878787",
600 | "textSize": 10,
601 | "customField1": "自定义字段1",
602 | "customField2": [
603 | "自定义字段2_1",
604 | "自定义字段2_2"
605 | ],
606 | "customField3": {
607 | "field3_1": "自定义字段3_1",
608 | "field3_2": "自定义字段3_2"
609 | }
610 | },
611 | {
612 | "id": "838644318",
613 | "x": 0,
614 | "y": 0,
615 | "label": "192.168.12.161",
616 | "color": "#00adee",
617 | "size": 20,
618 | "textColor": "#878787",
619 | "textSize": 10,
620 | "customField1": "自定义字段1",
621 | "customField2": [
622 | "自定义字段2_1",
623 | "自定义字段2_2"
624 | ],
625 | "customField3": {
626 | "field3_1": "自定义字段3_1",
627 | "field3_2": "自定义字段3_2"
628 | }
629 | },
630 | {
631 | "id": "-2051156699",
632 | "x": 0,
633 | "y": 0,
634 | "label": "192.168.12.90",
635 | "color": "#00adee",
636 | "size": 20,
637 | "textColor": "#878787",
638 | "textSize": 10,
639 | "customField1": "自定义字段1",
640 | "customField2": [
641 | "自定义字段2_1",
642 | "自定义字段2_2"
643 | ],
644 | "customField3": {
645 | "field3_1": "自定义字段3_1",
646 | "field3_2": "自定义字段3_2"
647 | }
648 | },
649 | {
650 | "id": "838644258",
651 | "x": 0,
652 | "y": 0,
653 | "label": "192.168.12.143",
654 | "color": "#00adee",
655 | "size": 20,
656 | "textColor": "#878787",
657 | "textSize": 10,
658 | "customField1": "自定义字段1",
659 | "customField2": [
660 | "自定义字段2_1",
661 | "自定义字段2_2"
662 | ],
663 | "customField3": {
664 | "field3_1": "自定义字段3_1",
665 | "field3_2": "自定义字段3_2"
666 | }
667 | },
668 | {
669 | "id": "1807683160",
670 | "x": 0,
671 | "y": 0,
672 | "label": "111.206.79.144",
673 | "color": "#00adee",
674 | "size": 20,
675 | "textColor": "#878787",
676 | "textSize": 10,
677 | "customField1": "自定义字段1",
678 | "customField2": [
679 | "自定义字段2_1",
680 | "自定义字段2_2"
681 | ],
682 | "customField3": {
683 | "field3_1": "自定义字段3_1",
684 | "field3_2": "自定义字段3_2"
685 | }
686 | },
687 | {
688 | "id": "-2051156939",
689 | "x": 0,
690 | "y": 0,
691 | "label": "192.168.12.18",
692 | "color": "#00adee",
693 | "size": 20,
694 | "textColor": "#878787",
695 | "textSize": 10,
696 | "customField1": "自定义字段1",
697 | "customField2": [
698 | "自定义字段2_1",
699 | "自定义字段2_2"
700 | ],
701 | "customField3": {
702 | "field3_1": "自定义字段3_1",
703 | "field3_2": "自定义字段3_2"
704 | }
705 | },
706 | {
707 | "id": "-2051156696",
708 | "x": 0,
709 | "y": 0,
710 | "label": "192.168.12.93",
711 | "color": "#00adee",
712 | "size": 20,
713 | "textColor": "#878787",
714 | "textSize": 10,
715 | "customField1": "自定义字段1",
716 | "customField2": [
717 | "自定义字段2_1",
718 | "自定义字段2_2"
719 | ],
720 | "customField3": {
721 | "field3_1": "自定义字段3_1",
722 | "field3_2": "自定义字段3_2"
723 | }
724 | },
725 | {
726 | "id": "-2051156693",
727 | "x": 0,
728 | "y": 0,
729 | "label": "192.168.12.96",
730 | "color": "#00adee",
731 | "size": 20,
732 | "textColor": "#878787",
733 | "textSize": 10,
734 | "customField1": "自定义字段1",
735 | "customField2": [
736 | "自定义字段2_1",
737 | "自定义字段2_2"
738 | ],
739 | "customField3": {
740 | "field3_1": "自定义字段3_1",
741 | "field3_2": "自定义字段3_2"
742 | }
743 | },
744 | {
745 | "id": "838644385",
746 | "x": 0,
747 | "y": 0,
748 | "label": "192.168.12.186",
749 | "color": "#00adee",
750 | "size": 20,
751 | "textColor": "#878787",
752 | "textSize": 10,
753 | "customField1": "自定义字段1",
754 | "customField2": [
755 | "自定义字段2_1",
756 | "自定义字段2_2"
757 | ],
758 | "customField3": {
759 | "field3_1": "自定义字段3_1",
760 | "field3_2": "自定义字段3_2"
761 | }
762 | },
763 | {
764 | "id": "-2051156822",
765 | "x": 0,
766 | "y": 0,
767 | "label": "192.168.12.51",
768 | "color": "#00adee",
769 | "size": 20,
770 | "textColor": "#878787",
771 | "textSize": 10,
772 | "customField1": "自定义字段1",
773 | "customField2": [
774 | "自定义字段2_1",
775 | "自定义字段2_2"
776 | ],
777 | "customField3": {
778 | "field3_1": "自定义字段3_1",
779 | "field3_2": "自定义字段3_2"
780 | }
781 | },
782 | {
783 | "id": "201294094",
784 | "x": 0,
785 | "y": 0,
786 | "label": "192.168.110.193",
787 | "color": "#00adee",
788 | "size": 20,
789 | "textColor": "#878787",
790 | "textSize": 10,
791 | "customField1": "自定义字段1",
792 | "customField2": [
793 | "自定义字段2_1",
794 | "自定义字段2_2"
795 | ],
796 | "customField3": {
797 | "field3_1": "自定义字段3_1",
798 | "field3_2": "自定义字段3_2"
799 | }
800 | },
801 | {
802 | "id": "838644412",
803 | "x": 0,
804 | "y": 0,
805 | "label": "192.168.12.192",
806 | "color": "#00adee",
807 | "size": 20,
808 | "textColor": "#878787",
809 | "textSize": 10,
810 | "customField1": "自定义字段1",
811 | "customField2": [
812 | "自定义字段2_1",
813 | "自定义字段2_2"
814 | ],
815 | "customField3": {
816 | "field3_1": "自定义字段3_1",
817 | "field3_2": "自定义字段3_2"
818 | }
819 | },
820 | {
821 | "id": "838644410",
822 | "x": 0,
823 | "y": 0,
824 | "label": "192.168.12.190",
825 | "color": "#00adee",
826 | "size": 20,
827 | "textColor": "#878787",
828 | "textSize": 10,
829 | "customField1": "自定义字段1",
830 | "customField2": [
831 | "自定义字段2_1",
832 | "自定义字段2_2"
833 | ],
834 | "customField3": {
835 | "field3_1": "自定义字段3_1",
836 | "field3_2": "自定义字段3_2"
837 | }
838 | },
839 | {
840 | "id": "201294933",
841 | "x": 0,
842 | "y": 0,
843 | "label": "192.168.110.255",
844 | "color": "#00adee",
845 | "size": 20,
846 | "textColor": "#878787",
847 | "textSize": 10,
848 | "customField1": "自定义字段1",
849 | "customField2": [
850 | "自定义字段2_1",
851 | "自定义字段2_2"
852 | ],
853 | "customField3": {
854 | "field3_1": "自定义字段3_1",
855 | "field3_2": "自定义字段3_2"
856 | }
857 | },
858 | {
859 | "id": "-2050948375",
860 | "x": 0,
861 | "y": 0,
862 | "label": "192.168.19.24",
863 | "color": "#00adee",
864 | "size": 20,
865 | "textColor": "#878787",
866 | "textSize": 10,
867 | "customField1": "自定义字段1",
868 | "customField2": [
869 | "自定义字段2_1",
870 | "自定义字段2_2"
871 | ],
872 | "customField3": {
873 | "field3_1": "自定义字段3_1",
874 | "field3_2": "自定义字段3_2"
875 | }
876 | },
877 | {
878 | "id": "591595283",
879 | "x": 0,
880 | "y": 0,
881 | "label": "228.6.7.8",
882 | "color": "#00adee",
883 | "size": 20,
884 | "textColor": "#878787",
885 | "textSize": 10,
886 | "customField1": "自定义字段1",
887 | "customField2": [
888 | "自定义字段2_1",
889 | "自定义字段2_2"
890 | ],
891 | "customField3": {
892 | "field3_1": "自定义字段3_1",
893 | "field3_2": "自定义字段3_2"
894 | }
895 | },
896 | {
897 | "id": "-910498946",
898 | "x": 0,
899 | "y": 0,
900 | "label": "255.255.255.255",
901 | "color": "#00adee",
902 | "size": 40,
903 | "textColor": "#878787",
904 | "textSize": 10,
905 | "customField1": "自定义字段1",
906 | "customField2": [
907 | "自定义字段2_1",
908 | "自定义字段2_2"
909 | ],
910 | "customField3": {
911 | "field3_1": "自定义字段3_1",
912 | "field3_2": "自定义字段3_2"
913 | }
914 | },
915 | {
916 | "id": "-2051156846",
917 | "x": 0,
918 | "y": 0,
919 | "label": "192.168.12.48",
920 | "color": "#00adee",
921 | "size": 20,
922 | "textColor": "#878787",
923 | "textSize": 10,
924 | "customField1": "自定义字段1",
925 | "customField2": [
926 | "自定义字段2_1",
927 | "自定义字段2_2"
928 | ],
929 | "customField3": {
930 | "field3_1": "自定义字段3_1",
931 | "field3_2": "自定义字段3_2"
932 | }
933 | },
934 | {
935 | "id": "-2051156728",
936 | "x": 0,
937 | "y": 0,
938 | "label": "192.168.12.82",
939 | "color": "#00adee",
940 | "size": 20,
941 | "textColor": "#878787",
942 | "textSize": 10,
943 | "customField1": "自定义字段1",
944 | "customField2": [
945 | "自定义字段2_1",
946 | "自定义字段2_2"
947 | ],
948 | "customField3": {
949 | "field3_1": "自定义字段3_1",
950 | "field3_2": "自定义字段3_2"
951 | }
952 | },
953 | {
954 | "id": "838644232",
955 | "x": 0,
956 | "y": 0,
957 | "label": "192.168.12.138",
958 | "color": "#00adee",
959 | "size": 20,
960 | "textColor": "#878787",
961 | "textSize": 10,
962 | "customField1": "自定义字段1",
963 | "customField2": [
964 | "自定义字段2_1",
965 | "自定义字段2_2"
966 | ],
967 | "customField3": {
968 | "field3_1": "自定义字段3_1",
969 | "field3_2": "自定义字段3_2"
970 | }
971 | },
972 | {
973 | "id": "838644353",
974 | "x": 0,
975 | "y": 0,
976 | "label": "192.168.12.175",
977 | "color": "#00adee",
978 | "size": 20,
979 | "textColor": "#878787",
980 | "textSize": 10,
981 | "customField1": "自定义字段1",
982 | "customField2": [
983 | "自定义字段2_1",
984 | "自定义字段2_2"
985 | ],
986 | "customField3": {
987 | "field3_1": "自定义字段3_1",
988 | "field3_2": "自定义字段3_2"
989 | }
990 | },
991 | {
992 | "id": "838645100",
993 | "x": 0,
994 | "y": 0,
995 | "label": "192.168.12.208",
996 | "color": "#00adee",
997 | "size": 20,
998 | "textColor": "#878787",
999 | "textSize": 10,
1000 | "customField1": "自定义字段1",
1001 | "customField2": [
1002 | "自定义字段2_1",
1003 | "自定义字段2_2"
1004 | ],
1005 | "customField3": {
1006 | "field3_1": "自定义字段3_1",
1007 | "field3_2": "自定义字段3_2"
1008 | }
1009 | },
1010 | {
1011 | "id": "-2051156852",
1012 | "x": 0,
1013 | "y": 0,
1014 | "label": "192.168.12.42",
1015 | "color": "#00adee",
1016 | "size": 20,
1017 | "textColor": "#878787",
1018 | "textSize": 10,
1019 | "customField1": "自定义字段1",
1020 | "customField2": [
1021 | "自定义字段2_1",
1022 | "自定义字段2_2"
1023 | ],
1024 | "customField3": {
1025 | "field3_1": "自定义字段3_1",
1026 | "field3_2": "自定义字段3_2"
1027 | }
1028 | },
1029 | {
1030 | "id": "-2051156850",
1031 | "x": 0,
1032 | "y": 0,
1033 | "label": "192.168.12.44",
1034 | "color": "#00adee",
1035 | "size": 20,
1036 | "textColor": "#878787",
1037 | "textSize": 10,
1038 | "customField1": "自定义字段1",
1039 | "customField2": [
1040 | "自定义字段2_1",
1041 | "自定义字段2_2"
1042 | ],
1043 | "customField3": {
1044 | "field3_1": "自定义字段3_1",
1045 | "field3_2": "自定义字段3_2"
1046 | }
1047 | },
1048 | {
1049 | "id": "838645224",
1050 | "x": 0,
1051 | "y": 0,
1052 | "label": "192.168.12.248",
1053 | "color": "#00adee",
1054 | "size": 20,
1055 | "textColor": "#878787",
1056 | "textSize": 10,
1057 | "customField1": "自定义字段1",
1058 | "customField2": [
1059 | "自定义字段2_1",
1060 | "自定义字段2_2"
1061 | ],
1062 | "customField3": {
1063 | "field3_1": "自定义字段3_1",
1064 | "field3_2": "自定义字段3_2"
1065 | }
1066 | }
1067 | ],
1068 | "edges": [
1069 | {
1070 | "target": "-910498946",
1071 | "source": "1013191670",
1072 | "size": 1,
1073 | "color": "#c2c2c2",
1074 | "customField1": "自定义字段1",
1075 | "customField2": [
1076 | "自定义字段2_1",
1077 | "自定义字段2_2"
1078 | ],
1079 | "customField3": {
1080 | "field3_1": "自定义字段3_1",
1081 | "field3_2": "自定义字段3_2"
1082 | }
1083 | },
1084 | {
1085 | "target": "591595283",
1086 | "source": "838645129",
1087 | "size": 1,
1088 | "color": "#c2c2c2",
1089 | "customField1": "自定义字段1",
1090 | "customField2": [
1091 | "自定义字段2_1",
1092 | "自定义字段2_2"
1093 | ],
1094 | "customField3": {
1095 | "field3_1": "自定义字段3_1",
1096 | "field3_2": "自定义字段3_2"
1097 | }
1098 | },
1099 | {
1100 | "target": "-2051156877",
1101 | "source": "-2050948375",
1102 | "size": 1,
1103 | "color": "#c2c2c2",
1104 | "customField1": "自定义字段1",
1105 | "customField2": [
1106 | "自定义字段2_1",
1107 | "自定义字段2_2"
1108 | ],
1109 | "customField3": {
1110 | "field3_1": "自定义字段3_1",
1111 | "field3_2": "自定义字段3_2"
1112 | }
1113 | },
1114 | {
1115 | "target": "-809256360",
1116 | "source": "-2051156693",
1117 | "size": 1,
1118 | "color": "#c2c2c2",
1119 | "customField1": "自定义字段1",
1120 | "customField2": [
1121 | "自定义字段2_1",
1122 | "自定义字段2_2"
1123 | ],
1124 | "customField3": {
1125 | "field3_1": "自定义字段3_1",
1126 | "field3_2": "自定义字段3_2"
1127 | }
1128 | },
1129 | {
1130 | "target": "-809256360",
1131 | "source": "838644165",
1132 | "size": 1,
1133 | "color": "#c2c2c2",
1134 | "customField1": "自定义字段1",
1135 | "customField2": [
1136 | "自定义字段2_1",
1137 | "自定义字段2_2"
1138 | ],
1139 | "customField3": {
1140 | "field3_1": "自定义字段3_1",
1141 | "field3_2": "自定义字段3_2"
1142 | }
1143 | },
1144 | {
1145 | "target": "-809256360",
1146 | "source": "838644258",
1147 | "size": 1,
1148 | "color": "#c2c2c2",
1149 | "customField1": "自定义字段1",
1150 | "customField2": [
1151 | "自定义字段2_1",
1152 | "自定义字段2_2"
1153 | ],
1154 | "customField3": {
1155 | "field3_1": "自定义字段3_1",
1156 | "field3_2": "自定义字段3_2"
1157 | }
1158 | },
1159 | {
1160 | "target": "-809256360",
1161 | "source": "838644163",
1162 | "size": 1,
1163 | "color": "#c2c2c2",
1164 | "customField1": "自定义字段1",
1165 | "customField2": [
1166 | "自定义字段2_1",
1167 | "自定义字段2_2"
1168 | ],
1169 | "customField3": {
1170 | "field3_1": "自定义字段3_1",
1171 | "field3_2": "自定义字段3_2"
1172 | }
1173 | },
1174 | {
1175 | "target": "-809256360",
1176 | "source": "-2051156728",
1177 | "size": 1,
1178 | "color": "#c2c2c2",
1179 | "customField1": "自定义字段1",
1180 | "customField2": [
1181 | "自定义字段2_1",
1182 | "自定义字段2_2"
1183 | ],
1184 | "customField3": {
1185 | "field3_1": "自定义字段3_1",
1186 | "field3_2": "自定义字段3_2"
1187 | }
1188 | },
1189 | {
1190 | "target": "-809256360",
1191 | "source": "838644195",
1192 | "size": 1,
1193 | "color": "#c2c2c2",
1194 | "customField1": "自定义字段1",
1195 | "customField2": [
1196 | "自定义字段2_1",
1197 | "自定义字段2_2"
1198 | ],
1199 | "customField3": {
1200 | "field3_1": "自定义字段3_1",
1201 | "field3_2": "自定义字段3_2"
1202 | }
1203 | },
1204 | {
1205 | "target": "-809256360",
1206 | "source": "838644232",
1207 | "size": 1,
1208 | "color": "#c2c2c2",
1209 | "customField1": "自定义字段1",
1210 | "customField2": [
1211 | "自定义字段2_1",
1212 | "自定义字段2_2"
1213 | ],
1214 | "customField3": {
1215 | "field3_1": "自定义字段3_1",
1216 | "field3_2": "自定义字段3_2"
1217 | }
1218 | },
1219 | {
1220 | "target": "-809256360",
1221 | "source": "838644321",
1222 | "size": 1,
1223 | "color": "#c2c2c2",
1224 | "customField1": "自定义字段1",
1225 | "customField2": [
1226 | "自定义字段2_1",
1227 | "自定义字段2_2"
1228 | ],
1229 | "customField3": {
1230 | "field3_1": "自定义字段3_1",
1231 | "field3_2": "自定义字段3_2"
1232 | }
1233 | },
1234 | {
1235 | "target": "-809256360",
1236 | "source": "-2051156696",
1237 | "size": 1,
1238 | "color": "#c2c2c2",
1239 | "customField1": "自定义字段1",
1240 | "customField2": [
1241 | "自定义字段2_1",
1242 | "自定义字段2_2"
1243 | ],
1244 | "customField3": {
1245 | "field3_1": "自定义字段3_1",
1246 | "field3_2": "自定义字段3_2"
1247 | }
1248 | },
1249 | {
1250 | "target": "-809256360",
1251 | "source": "-2051156846",
1252 | "size": 1,
1253 | "color": "#c2c2c2",
1254 | "customField1": "自定义字段1",
1255 | "customField2": [
1256 | "自定义字段2_1",
1257 | "自定义字段2_2"
1258 | ],
1259 | "customField3": {
1260 | "field3_1": "自定义字段3_1",
1261 | "field3_2": "自定义字段3_2"
1262 | }
1263 | },
1264 | {
1265 | "target": "-809256360",
1266 | "source": "-2051156852",
1267 | "size": 1,
1268 | "color": "#c2c2c2",
1269 | "customField1": "自定义字段1",
1270 | "customField2": [
1271 | "自定义字段2_1",
1272 | "自定义字段2_2"
1273 | ],
1274 | "customField3": {
1275 | "field3_1": "自定义字段3_1",
1276 | "field3_2": "自定义字段3_2"
1277 | }
1278 | },
1279 | {
1280 | "target": "-809256360",
1281 | "source": "-2051156884",
1282 | "size": 1,
1283 | "color": "#c2c2c2",
1284 | "customField1": "自定义字段1",
1285 | "customField2": [
1286 | "自定义字段2_1",
1287 | "自定义字段2_2"
1288 | ],
1289 | "customField3": {
1290 | "field3_1": "自定义字段3_1",
1291 | "field3_2": "自定义字段3_2"
1292 | }
1293 | },
1294 | {
1295 | "target": "-809256360",
1296 | "source": "838645159",
1297 | "size": 1,
1298 | "color": "#c2c2c2",
1299 | "customField1": "自定义字段1",
1300 | "customField2": [
1301 | "自定义字段2_1",
1302 | "自定义字段2_2"
1303 | ],
1304 | "customField3": {
1305 | "field3_1": "自定义字段3_1",
1306 | "field3_2": "自定义字段3_2"
1307 | }
1308 | },
1309 | {
1310 | "target": "-809256360",
1311 | "source": "838644385",
1312 | "size": 1,
1313 | "color": "#c2c2c2",
1314 | "customField1": "自定义字段1",
1315 | "customField2": [
1316 | "自定义字段2_1",
1317 | "自定义字段2_2"
1318 | ],
1319 | "customField3": {
1320 | "field3_1": "自定义字段3_1",
1321 | "field3_2": "自定义字段3_2"
1322 | }
1323 | },
1324 | {
1325 | "target": "-809256360",
1326 | "source": "-2051156877",
1327 | "size": 1,
1328 | "color": "#c2c2c2",
1329 | "customField1": "自定义字段1",
1330 | "customField2": [
1331 | "自定义字段2_1",
1332 | "自定义字段2_2"
1333 | ],
1334 | "customField3": {
1335 | "field3_1": "自定义字段3_1",
1336 | "field3_2": "自定义字段3_2"
1337 | }
1338 | },
1339 | {
1340 | "target": "-809256360",
1341 | "source": "838645129",
1342 | "size": 1,
1343 | "color": "#c2c2c2",
1344 | "customField1": "自定义字段1",
1345 | "customField2": [
1346 | "自定义字段2_1",
1347 | "自定义字段2_2"
1348 | ],
1349 | "customField3": {
1350 | "field3_1": "自定义字段3_1",
1351 | "field3_2": "自定义字段3_2"
1352 | }
1353 | },
1354 | {
1355 | "target": "-809256360",
1356 | "source": "-2051156850",
1357 | "size": 1,
1358 | "color": "#c2c2c2",
1359 | "customField1": "自定义字段1",
1360 | "customField2": [
1361 | "自定义字段2_1",
1362 | "自定义字段2_2"
1363 | ],
1364 | "customField3": {
1365 | "field3_1": "自定义字段3_1",
1366 | "field3_2": "自定义字段3_2"
1367 | }
1368 | },
1369 | {
1370 | "target": "-809256360",
1371 | "source": "838645100",
1372 | "size": 1,
1373 | "color": "#c2c2c2",
1374 | "customField1": "自定义字段1",
1375 | "customField2": [
1376 | "自定义字段2_1",
1377 | "自定义字段2_2"
1378 | ],
1379 | "customField3": {
1380 | "field3_1": "自定义字段3_1",
1381 | "field3_2": "自定义字段3_2"
1382 | }
1383 | },
1384 | {
1385 | "target": "-809256360",
1386 | "source": "838644410",
1387 | "size": 1,
1388 | "color": "#c2c2c2",
1389 | "customField1": "自定义字段1",
1390 | "customField2": [
1391 | "自定义字段2_1",
1392 | "自定义字段2_2"
1393 | ],
1394 | "customField3": {
1395 | "field3_1": "自定义字段3_1",
1396 | "field3_2": "自定义字段3_2"
1397 | }
1398 | },
1399 | {
1400 | "target": "-809256360",
1401 | "source": "838644198",
1402 | "size": 1,
1403 | "color": "#c2c2c2",
1404 | "customField1": "自定义字段1",
1405 | "customField2": [
1406 | "自定义字段2_1",
1407 | "自定义字段2_2"
1408 | ],
1409 | "customField3": {
1410 | "field3_1": "自定义字段3_1",
1411 | "field3_2": "自定义字段3_2"
1412 | }
1413 | },
1414 | {
1415 | "target": "-809256360",
1416 | "source": "838644166",
1417 | "size": 1,
1418 | "color": "#c2c2c2",
1419 | "customField1": "自定义字段1",
1420 | "customField2": [
1421 | "自定义字段2_1",
1422 | "自定义字段2_2"
1423 | ],
1424 | "customField3": {
1425 | "field3_1": "自定义字段3_1",
1426 | "field3_2": "自定义字段3_2"
1427 | }
1428 | },
1429 | {
1430 | "target": "-809256360",
1431 | "source": "201294094",
1432 | "size": 1,
1433 | "color": "#c2c2c2",
1434 | "customField1": "自定义字段1",
1435 | "customField2": [
1436 | "自定义字段2_1",
1437 | "自定义字段2_2"
1438 | ],
1439 | "customField3": {
1440 | "field3_1": "自定义字段3_1",
1441 | "field3_2": "自定义字段3_2"
1442 | }
1443 | },
1444 | {
1445 | "target": "-809256360",
1446 | "source": "838645160",
1447 | "size": 1,
1448 | "color": "#c2c2c2",
1449 | "customField1": "自定义字段1",
1450 | "customField2": [
1451 | "自定义字段2_1",
1452 | "自定义字段2_2"
1453 | ],
1454 | "customField3": {
1455 | "field3_1": "自定义字段3_1",
1456 | "field3_2": "自定义字段3_2"
1457 | }
1458 | },
1459 | {
1460 | "target": "-809256360",
1461 | "source": "838644171",
1462 | "size": 1,
1463 | "color": "#c2c2c2",
1464 | "customField1": "自定义字段1",
1465 | "customField2": [
1466 | "自定义字段2_1",
1467 | "自定义字段2_2"
1468 | ],
1469 | "customField3": {
1470 | "field3_1": "自定义字段3_1",
1471 | "field3_2": "自定义字段3_2"
1472 | }
1473 | },
1474 | {
1475 | "target": "-809256360",
1476 | "source": "-2051156911",
1477 | "size": 1,
1478 | "color": "#c2c2c2",
1479 | "customField1": "自定义字段1",
1480 | "customField2": [
1481 | "自定义字段2_1",
1482 | "自定义字段2_2"
1483 | ],
1484 | "customField3": {
1485 | "field3_1": "自定义字段3_1",
1486 | "field3_2": "自定义字段3_2"
1487 | }
1488 | },
1489 | {
1490 | "target": "-809256360",
1491 | "source": "1734948971",
1492 | "size": 1,
1493 | "color": "#c2c2c2",
1494 | "customField1": "自定义字段1",
1495 | "customField2": [
1496 | "自定义字段2_1",
1497 | "自定义字段2_2"
1498 | ],
1499 | "customField3": {
1500 | "field3_1": "自定义字段3_1",
1501 | "field3_2": "自定义字段3_2"
1502 | }
1503 | },
1504 | {
1505 | "target": "-809256360",
1506 | "source": "838644164",
1507 | "size": 1,
1508 | "color": "#c2c2c2",
1509 | "customField1": "自定义字段1",
1510 | "customField2": [
1511 | "自定义字段2_1",
1512 | "自定义字段2_2"
1513 | ],
1514 | "customField3": {
1515 | "field3_1": "自定义字段3_1",
1516 | "field3_2": "自定义字段3_2"
1517 | }
1518 | },
1519 | {
1520 | "target": "-809256360",
1521 | "source": "838645126",
1522 | "size": 1,
1523 | "color": "#c2c2c2",
1524 | "customField1": "自定义字段1",
1525 | "customField2": [
1526 | "自定义字段2_1",
1527 | "自定义字段2_2"
1528 | ],
1529 | "customField3": {
1530 | "field3_1": "自定义字段3_1",
1531 | "field3_2": "自定义字段3_2"
1532 | }
1533 | },
1534 | {
1535 | "target": "-809256360",
1536 | "source": "838644350",
1537 | "size": 1,
1538 | "color": "#c2c2c2",
1539 | "customField1": "自定义字段1",
1540 | "customField2": [
1541 | "自定义字段2_1",
1542 | "自定义字段2_2"
1543 | ],
1544 | "customField3": {
1545 | "field3_1": "自定义字段3_1",
1546 | "field3_2": "自定义字段3_2"
1547 | }
1548 | },
1549 | {
1550 | "target": "838645127",
1551 | "source": "-2051156877",
1552 | "size": 1,
1553 | "color": "#c2c2c2",
1554 | "customField1": "自定义字段1",
1555 | "customField2": [
1556 | "自定义字段2_1",
1557 | "自定义字段2_2"
1558 | ],
1559 | "customField3": {
1560 | "field3_1": "自定义字段3_1",
1561 | "field3_2": "自定义字段3_2"
1562 | }
1563 | },
1564 | {
1565 | "target": "838645128",
1566 | "source": "-2051156877",
1567 | "size": 1,
1568 | "color": "#c2c2c2",
1569 | "customField1": "自定义字段1",
1570 | "customField2": [
1571 | "自定义字段2_1",
1572 | "自定义字段2_2"
1573 | ],
1574 | "customField3": {
1575 | "field3_1": "自定义字段3_1",
1576 | "field3_2": "自定义字段3_2"
1577 | }
1578 | },
1579 | {
1580 | "target": "838644353",
1581 | "source": "-2051156884",
1582 | "size": 1,
1583 | "color": "#c2c2c2",
1584 | "customField1": "自定义字段1",
1585 | "customField2": [
1586 | "自定义字段2_1",
1587 | "自定义字段2_2"
1588 | ],
1589 | "customField3": {
1590 | "field3_1": "自定义字段3_1",
1591 | "field3_2": "自定义字段3_2"
1592 | }
1593 | },
1594 | {
1595 | "target": "838644353",
1596 | "source": "-2051156877",
1597 | "size": 1,
1598 | "color": "#c2c2c2",
1599 | "customField1": "自定义字段1",
1600 | "customField2": [
1601 | "自定义字段2_1",
1602 | "自定义字段2_2"
1603 | ],
1604 | "customField3": {
1605 | "field3_1": "自定义字段3_1",
1606 | "field3_2": "自定义字段3_2"
1607 | }
1608 | },
1609 | {
1610 | "target": "838645224",
1611 | "source": "-2051156759",
1612 | "size": 1,
1613 | "color": "#c2c2c2",
1614 | "customField1": "自定义字段1",
1615 | "customField2": [
1616 | "自定义字段2_1",
1617 | "自定义字段2_2"
1618 | ],
1619 | "customField3": {
1620 | "field3_1": "自定义字段3_1",
1621 | "field3_2": "自定义字段3_2"
1622 | }
1623 | },
1624 | {
1625 | "target": "838645252",
1626 | "source": "-2051156877",
1627 | "size": 1,
1628 | "color": "#c2c2c2",
1629 | "customField1": "自定义字段1",
1630 | "customField2": [
1631 | "自定义字段2_1",
1632 | "自定义字段2_2"
1633 | ],
1634 | "customField3": {
1635 | "field3_1": "自定义字段3_1",
1636 | "field3_2": "自定义字段3_2"
1637 | }
1638 | },
1639 | {
1640 | "target": "838644165",
1641 | "source": "-2051156877",
1642 | "size": 1,
1643 | "color": "#c2c2c2",
1644 | "customField1": "自定义字段1",
1645 | "customField2": [
1646 | "自定义字段2_1",
1647 | "自定义字段2_2"
1648 | ],
1649 | "customField3": {
1650 | "field3_1": "自定义字段3_1",
1651 | "field3_2": "自定义字段3_2"
1652 | }
1653 | },
1654 | {
1655 | "target": "201294933",
1656 | "source": "201294094",
1657 | "size": 1,
1658 | "color": "#c2c2c2",
1659 | "customField1": "自定义字段1",
1660 | "customField2": [
1661 | "自定义字段2_1",
1662 | "自定义字段2_2"
1663 | ],
1664 | "customField3": {
1665 | "field3_1": "自定义字段3_1",
1666 | "field3_2": "自定义字段3_2"
1667 | }
1668 | },
1669 | {
1670 | "target": "-2007034805",
1671 | "source": "-2051156877",
1672 | "size": 1,
1673 | "color": "#c2c2c2",
1674 | "customField1": "自定义字段1",
1675 | "customField2": [
1676 | "自定义字段2_1",
1677 | "自定义字段2_2"
1678 | ],
1679 | "customField3": {
1680 | "field3_1": "自定义字段3_1",
1681 | "field3_2": "自定义字段3_2"
1682 | }
1683 | },
1684 | {
1685 | "target": "-1072993729",
1686 | "source": "-2051156912",
1687 | "size": 1,
1688 | "color": "#c2c2c2",
1689 | "customField1": "自定义字段1",
1690 | "customField2": [
1691 | "自定义字段2_1",
1692 | "自定义字段2_2"
1693 | ],
1694 | "customField3": {
1695 | "field3_1": "自定义字段3_1",
1696 | "field3_2": "自定义字段3_2"
1697 | }
1698 | },
1699 | {
1700 | "target": "-1072993729",
1701 | "source": "838644324",
1702 | "size": 1,
1703 | "color": "#c2c2c2",
1704 | "customField1": "自定义字段1",
1705 | "customField2": [
1706 | "自定义字段2_1",
1707 | "自定义字段2_2"
1708 | ],
1709 | "customField3": {
1710 | "field3_1": "自定义字段3_1",
1711 | "field3_2": "自定义字段3_2"
1712 | }
1713 | },
1714 | {
1715 | "target": "-910498946",
1716 | "source": "838644353",
1717 | "size": 1,
1718 | "color": "#c2c2c2",
1719 | "customField1": "自定义字段1",
1720 | "customField2": [
1721 | "自定义字段2_1",
1722 | "自定义字段2_2"
1723 | ],
1724 | "customField3": {
1725 | "field3_1": "自定义字段3_1",
1726 | "field3_2": "自定义字段3_2"
1727 | }
1728 | },
1729 | {
1730 | "target": "1807683160",
1731 | "source": "-2051156759",
1732 | "size": 1,
1733 | "color": "#c2c2c2",
1734 | "customField1": "自定义字段1",
1735 | "customField2": [
1736 | "自定义字段2_1",
1737 | "自定义字段2_2"
1738 | ],
1739 | "customField3": {
1740 | "field3_1": "自定义字段3_1",
1741 | "field3_2": "自定义字段3_2"
1742 | }
1743 | },
1744 | {
1745 | "target": "-809256359",
1746 | "source": "838644318",
1747 | "size": 1,
1748 | "color": "#c2c2c2",
1749 | "customField1": "自定义字段1",
1750 | "customField2": [
1751 | "自定义字段2_1",
1752 | "自定义字段2_2"
1753 | ],
1754 | "customField3": {
1755 | "field3_1": "自定义字段3_1",
1756 | "field3_2": "自定义字段3_2"
1757 | }
1758 | },
1759 | {
1760 | "target": "-2051156789",
1761 | "source": "838644353",
1762 | "size": 1,
1763 | "color": "#c2c2c2",
1764 | "customField1": "自定义字段1",
1765 | "customField2": [
1766 | "自定义字段2_1",
1767 | "自定义字段2_2"
1768 | ],
1769 | "customField3": {
1770 | "field3_1": "自定义字段3_1",
1771 | "field3_2": "自定义字段3_2"
1772 | }
1773 | },
1774 | {
1775 | "target": "838645252",
1776 | "source": "838644353",
1777 | "size": 1,
1778 | "color": "#c2c2c2",
1779 | "customField1": "自定义字段1",
1780 | "customField2": [
1781 | "自定义字段2_1",
1782 | "自定义字段2_2"
1783 | ],
1784 | "customField3": {
1785 | "field3_1": "自定义字段3_1",
1786 | "field3_2": "自定义字段3_2"
1787 | }
1788 | },
1789 | {
1790 | "target": "838645252",
1791 | "source": "838645248",
1792 | "size": 1,
1793 | "color": "#c2c2c2",
1794 | "customField1": "自定义字段1",
1795 | "customField2": [
1796 | "自定义字段2_1",
1797 | "自定义字段2_2"
1798 | ],
1799 | "customField3": {
1800 | "field3_1": "自定义字段3_1",
1801 | "field3_2": "自定义字段3_2"
1802 | }
1803 | },
1804 | {
1805 | "target": "838645252",
1806 | "source": "-2051156755",
1807 | "size": 1,
1808 | "color": "#c2c2c2",
1809 | "customField1": "自定义字段1",
1810 | "customField2": [
1811 | "自定义字段2_1",
1812 | "自定义字段2_2"
1813 | ],
1814 | "customField3": {
1815 | "field3_1": "自定义字段3_1",
1816 | "field3_2": "自定义字段3_2"
1817 | }
1818 | },
1819 | {
1820 | "target": "838645252",
1821 | "source": "-2051156822",
1822 | "size": 1,
1823 | "color": "#c2c2c2",
1824 | "customField1": "自定义字段1",
1825 | "customField2": [
1826 | "自定义字段2_1",
1827 | "自定义字段2_2"
1828 | ],
1829 | "customField3": {
1830 | "field3_1": "自定义字段3_1",
1831 | "field3_2": "自定义字段3_2"
1832 | }
1833 | },
1834 | {
1835 | "target": "838645252",
1836 | "source": "-2051156939",
1837 | "size": 1,
1838 | "color": "#c2c2c2",
1839 | "customField1": "自定义字段1",
1840 | "customField2": [
1841 | "自定义字段2_1",
1842 | "自定义字段2_2"
1843 | ],
1844 | "customField3": {
1845 | "field3_1": "自定义字段3_1",
1846 | "field3_2": "自定义字段3_2"
1847 | }
1848 | },
1849 | {
1850 | "target": "838645252",
1851 | "source": "-2051156883",
1852 | "size": 1,
1853 | "color": "#c2c2c2",
1854 | "customField1": "自定义字段1",
1855 | "customField2": [
1856 | "自定义字段2_1",
1857 | "自定义字段2_2"
1858 | ],
1859 | "customField3": {
1860 | "field3_1": "自定义字段3_1",
1861 | "field3_2": "自定义字段3_2"
1862 | }
1863 | },
1864 | {
1865 | "target": "838645252",
1866 | "source": "-2051156699",
1867 | "size": 1,
1868 | "color": "#c2c2c2",
1869 | "customField1": "自定义字段1",
1870 | "customField2": [
1871 | "自定义字段2_1",
1872 | "自定义字段2_2"
1873 | ],
1874 | "customField3": {
1875 | "field3_1": "自定义字段3_1",
1876 | "field3_2": "自定义字段3_2"
1877 | }
1878 | },
1879 | {
1880 | "target": "838645252",
1881 | "source": "838644412",
1882 | "size": 1,
1883 | "color": "#c2c2c2",
1884 | "customField1": "自定义字段1",
1885 | "customField2": [
1886 | "自定义字段2_1",
1887 | "自定义字段2_2"
1888 | ],
1889 | "customField3": {
1890 | "field3_1": "自定义字段3_1",
1891 | "field3_2": "自定义字段3_2"
1892 | }
1893 | }
1894 | ]
1895 | }
--------------------------------------------------------------------------------
/demo/data_small.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "838644321",
5 | "label": "192.168.12.164"
6 | },
7 | {
8 | "id": "-2051156877",
9 | "label": "192.168.12.38"
10 | },
11 | {
12 | "id": "838644324",
13 | "label": "192.168.12.167"
14 | },
15 | {
16 | "id": "-2051156755",
17 | "label": "192.168.12.76"
18 | },
19 | {
20 | "id": "838644171",
21 | "label": "192.168.12.119"
22 | },
23 | {
24 | "id": "838645127",
25 | "label": "192.168.12.214"
26 | },
27 | {
28 | "id": "838645126",
29 | "label": "192.168.12.213"
30 | },
31 | {
32 | "id": "1734948971",
33 | "label": "192.168.12.9"
34 | },
35 | {
36 | "id": "-2051156759",
37 | "label": "192.168.12.72"
38 | },
39 | {
40 | "id": "838644166",
41 | "label": "192.168.12.114"
42 | },
43 | {
44 | "id": "-2051156883",
45 | "label": "192.168.12.32"
46 | },
47 | {
48 | "id": "838645129",
49 | "label": "192.168.12.216"
50 | },
51 | {
52 | "id": "838644165",
53 | "label": "192.168.12.113"
54 | },
55 | {
56 | "id": "-2051156884",
57 | "label": "192.168.12.31"
58 | },
59 | {
60 | "id": "838644164",
61 | "label": "192.168.12.112"
62 | },
63 | {
64 | "id": "838645128",
65 | "label": "192.168.12.215"
66 | },
67 | {
68 | "id": "838644163",
69 | "label": "192.168.12.111"
70 | },
71 | {
72 | "id": "838644350",
73 | "label": "192.168.12.172"
74 | },
75 | {
76 | "id": "-2007034805",
77 | "label": "10.54.14.6"
78 | },
79 | {
80 | "id": "-809256359",
81 | "label": "224.0.0.252"
82 | },
83 | {
84 | "id": "-809256360",
85 | "label": "224.0.0.251"
86 | },
87 | {
88 | "id": "-2051156912",
89 | "label": "192.168.12.24"
90 | },
91 | {
92 | "id": "-2051156911",
93 | "label": "192.168.12.25"
94 | },
95 | {
96 | "id": "838645252",
97 | "label": "192.168.12.255"
98 | },
99 | {
100 | "id": "838645248",
101 | "label": "192.168.12.251"
102 | },
103 | {
104 | "id": "-1072993729",
105 | "label": "239.255.255.250"
106 | },
107 | {
108 | "id": "838645159",
109 | "label": "192.168.12.225"
110 | },
111 | {
112 | "id": "-2051156789",
113 | "label": "192.168.12.63"
114 | },
115 | {
116 | "id": "838645160",
117 | "label": "192.168.12.226"
118 | },
119 | {
120 | "id": "1013191670",
121 | "label": "0.0.0.0"
122 | },
123 | {
124 | "id": "838644198",
125 | "label": "192.168.12.125"
126 | },
127 | {
128 | "id": "838644195",
129 | "label": "192.168.12.122"
130 | },
131 | {
132 | "id": "838644318",
133 | "label": "192.168.12.161"
134 | },
135 | {
136 | "id": "-2051156699",
137 | "label": "192.168.12.90"
138 | },
139 | {
140 | "id": "838644258",
141 | "label": "192.168.12.143"
142 | },
143 | {
144 | "id": "1807683160",
145 | "label": "111.206.79.144"
146 | },
147 | {
148 | "id": "-2051156939",
149 | "label": "192.168.12.18"
150 | },
151 | {
152 | "id": "-2051156696",
153 | "label": "192.168.12.93"
154 | },
155 | {
156 | "id": "-2051156693",
157 | "label": "192.168.12.96"
158 | },
159 | {
160 | "id": "838644385",
161 | "label": "192.168.12.186"
162 | },
163 | {
164 | "id": "-2051156822",
165 | "label": "192.168.12.51"
166 | },
167 | {
168 | "id": "201294094",
169 | "label": "192.168.110.193"
170 | },
171 | {
172 | "id": "838644412",
173 | "label": "192.168.12.192"
174 | },
175 | {
176 | "id": "838644410",
177 | "label": "192.168.12.190"
178 | },
179 | {
180 | "id": "201294933",
181 | "label": "192.168.110.255"
182 | },
183 | {
184 | "id": "-2050948375",
185 | "label": "192.168.19.24"
186 | },
187 | {
188 | "id": "591595283",
189 | "label": "228.6.7.8"
190 | },
191 | {
192 | "id": "-910498946",
193 | "label": "255.255.255.255"
194 | },
195 | {
196 | "id": "-2051156846",
197 | "label": "192.168.12.48"
198 | },
199 | {
200 | "id": "-2051156728",
201 | "label": "192.168.12.82"
202 | },
203 | {
204 | "id": "838644232",
205 | "label": "192.168.12.138"
206 | },
207 | {
208 | "id": "838644353",
209 | "label": "192.168.12.175"
210 | },
211 | {
212 | "id": "838645100",
213 | "label": "192.168.12.208"
214 | },
215 | {
216 | "id": "-2051156852",
217 | "label": "192.168.12.42"
218 | },
219 | {
220 | "id": "-2051156850",
221 | "label": "192.168.12.44"
222 | },
223 | {
224 | "id": "838645224",
225 | "label": "192.168.12.248"
226 | }
227 | ],
228 | "edges": [
229 | {
230 | "target": "-910498946",
231 | "source": "1013191670"
232 | },
233 | {
234 | "target": "591595283",
235 | "source": "838645129"
236 | },
237 | {
238 | "target": "-2051156877",
239 | "source": "-2050948375"
240 | },
241 | {
242 | "target": "-809256360",
243 | "source": "-2051156693"
244 | },
245 | {
246 | "target": "-809256360",
247 | "source": "838644165"
248 | },
249 | {
250 | "target": "-809256360",
251 | "source": "838644258"
252 | },
253 | {
254 | "target": "-809256360",
255 | "source": "838644163"
256 | },
257 | {
258 | "target": "-809256360",
259 | "source": "-2051156728"
260 | },
261 | {
262 | "target": "-809256360",
263 | "source": "838644195"
264 | },
265 | {
266 | "target": "-809256360",
267 | "source": "838644232"
268 | },
269 | {
270 | "target": "-809256360",
271 | "source": "838644321"
272 | },
273 | {
274 | "target": "-809256360",
275 | "source": "-2051156696"
276 | },
277 | {
278 | "target": "-809256360",
279 | "source": "-2051156846"
280 | },
281 | {
282 | "target": "-809256360",
283 | "source": "-2051156852"
284 | },
285 | {
286 | "target": "-809256360",
287 | "source": "-2051156884"
288 | },
289 | {
290 | "target": "-809256360",
291 | "source": "838645159"
292 | },
293 | {
294 | "target": "-809256360",
295 | "source": "838644385"
296 | },
297 | {
298 | "target": "-809256360",
299 | "source": "-2051156877"
300 | },
301 | {
302 | "target": "-809256360",
303 | "source": "838645129"
304 | },
305 | {
306 | "target": "-809256360",
307 | "source": "-2051156850"
308 | },
309 | {
310 | "target": "-809256360",
311 | "source": "838645100"
312 | },
313 | {
314 | "target": "-809256360",
315 | "source": "838644410"
316 | },
317 | {
318 | "target": "-809256360",
319 | "source": "838644198"
320 | },
321 | {
322 | "target": "-809256360",
323 | "source": "838644166"
324 | },
325 | {
326 | "target": "-809256360",
327 | "source": "201294094"
328 | },
329 | {
330 | "target": "-809256360",
331 | "source": "838645160"
332 | },
333 | {
334 | "target": "-809256360",
335 | "source": "838644171"
336 | },
337 | {
338 | "target": "-809256360",
339 | "source": "-2051156911"
340 | },
341 | {
342 | "target": "-809256360",
343 | "source": "1734948971"
344 | },
345 | {
346 | "target": "-809256360",
347 | "source": "838644164"
348 | },
349 | {
350 | "target": "-809256360",
351 | "source": "838645126"
352 | },
353 | {
354 | "target": "-809256360",
355 | "source": "838644350"
356 | },
357 | {
358 | "target": "838645127",
359 | "source": "-2051156877"
360 | },
361 | {
362 | "target": "838645128",
363 | "source": "-2051156877"
364 | },
365 | {
366 | "target": "838644353",
367 | "source": "-2051156884"
368 | },
369 | {
370 | "target": "838644353",
371 | "source": "-2051156877"
372 | },
373 | {
374 | "target": "838645224",
375 | "source": "-2051156759"
376 | },
377 | {
378 | "target": "838645252",
379 | "source": "-2051156877"
380 | },
381 | {
382 | "target": "838644165",
383 | "source": "-2051156877"
384 | },
385 | {
386 | "target": "201294933",
387 | "source": "201294094"
388 | },
389 | {
390 | "target": "-2007034805",
391 | "source": "-2051156877"
392 | },
393 | {
394 | "target": "-1072993729",
395 | "source": "-2051156912"
396 | },
397 | {
398 | "target": "-1072993729",
399 | "source": "838644324"
400 | },
401 | {
402 | "target": "-910498946",
403 | "source": "838644353"
404 | },
405 | {
406 | "target": "1807683160",
407 | "source": "-2051156759"
408 | },
409 | {
410 | "target": "-809256359",
411 | "source": "838644318"
412 | },
413 | {
414 | "target": "-2051156789",
415 | "source": "838644353"
416 | },
417 | {
418 | "target": "838645252",
419 | "source": "838644353"
420 | },
421 | {
422 | "target": "838645252",
423 | "source": "838645248"
424 | },
425 | {
426 | "target": "838645252",
427 | "source": "-2051156755"
428 | },
429 | {
430 | "target": "838645252",
431 | "source": "-2051156822"
432 | },
433 | {
434 | "target": "838645252",
435 | "source": "-2051156939"
436 | },
437 | {
438 | "target": "838645252",
439 | "source": "-2051156883"
440 | },
441 | {
442 | "target": "838645252",
443 | "source": "-2051156699"
444 | },
445 | {
446 | "target": "838645252",
447 | "source": "838644412"
448 | }
449 | ]
450 | }
--------------------------------------------------------------------------------
/demo/data_small_api.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "1",
5 | "label": "1",
6 | "tooltip": "1",
7 | "x": 300,
8 | "y": 100
9 | },
10 | {
11 | "id": "2",
12 | "label": "2",
13 | "tooltip": "2",
14 | "x": 400,
15 | "y": 200
16 | },
17 | {
18 | "id": "3",
19 | "label": "3",
20 | "tooltip": "3",
21 | "x": 100,
22 | "y": 100
23 | },
24 | {
25 | "id": "4",
26 | "label": "4",
27 | "tooltip": "4",
28 | "x": 200,
29 | "y": 101
30 | }
31 | ],
32 | "edges": [
33 |
34 | {
35 | "source": "3",
36 | "target": "4",
37 | "label": "3-4",
38 | "tooltip": "3-4"
39 | }
40 | ]
41 | }
--------------------------------------------------------------------------------
/demo/控制台示例.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | canvas demo
6 |
7 |
14 |
15 |
16 |
17 |
18 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/image/ctopobg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tm-roamer/ctopo/5fe44fee1b23e12813227f5e21b86ba277cf33b5/image/ctopobg.png
--------------------------------------------------------------------------------
/image/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tm-roamer/ctopo/5fe44fee1b23e12813227f5e21b86ba277cf33b5/image/demo.png
--------------------------------------------------------------------------------
/image/skin.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tm-roamer/ctopo/5fe44fee1b23e12813227f5e21b86ba277cf33b5/image/skin.jpg
--------------------------------------------------------------------------------
/src/consolepanel.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 控制台小工具
3 | * 作者:田明
4 | * 使用方式:
5 | * var consolePanel = new ConsolePanel({
6 | * //上下左右回调
7 | * steerwheel:function(direct){
8 | * //direct==event.keyCode
9 | * },
10 | * //放大,缩小回调
11 | * scale:function(scale){
12 | * //scale=比例
13 | * }
14 | * });
15 | */
16 | function ConsolePanel(){
17 | var self = this;
18 | this.console_panel="console_panel"; //div的名称
19 | this.zoom="zoom";
20 | this.steerwheel_panel="steerwheel_panel";
21 | this.steer_btn="steer_btn";
22 | this.scale_bar="scale_bar";
23 | this.scale_slider="scale_slider";
24 | this.scale_ruler_current="scale_ruler_current";
25 | //当前缩放比例和上一次的缩放比例,用于监听change
26 | this.scale=.5;
27 | this.prevScale=.5;
28 |
29 | //预置的高度
30 | var steerwheelHeight = 86, //方向盘的高度
31 | zoomBtnHeight=26, //放大,缩小按钮的高度
32 | scaleBarHeight = 140, //比例尺总高度
33 | scaleSliderHeight = 20, //滑块高度
34 | scaleBarBackgroundPositionX = -44, //比例尺图片精灵定位的起点x
35 | scaleBarBackgroundPositionY = -430; //比例尺图片精灵定位的起点y
36 |
37 | //切换上下左右的图片
38 | this.setSteerWheelClass = function(className){
39 | var steerwheel_panel = document.querySelector("."+this.steerwheel_panel);
40 | steerwheel_panel.setAttribute("class",this.steerwheel_panel+" "+className);
41 | };
42 | //设置初始化参数
43 | this.setOption = function(opt){
44 | self.opt=opt||{};
45 | };
46 | //调整滑块和当前比例的位置
47 | this.setScale = function(num){
48 | var ratioHeight,
49 | ratioTop,
50 | sliderTop,
51 | slider = self.scale_slider, //滑块
52 | ruler_current = self.scale_ruler_current; //当前比例
53 | //设置滑块和当前比例
54 | self.scale = num>1?1:(num<0.1?0:num);
55 | ratioHeight = parseInt(scaleBarHeight*self.scale); //取得当前比例高度
56 | ratioTop = parseInt(scaleBarHeight-ratioHeight); //取得当前比例top位置
57 | sliderTop = (ratioTop-scaleSliderHeight/2); //滑块的top位置
58 | sliderTop = sliderTop<0?0:(sliderTop>=130?120:sliderTop); //ps: 比例尺图片有阴影,所以补充运算
59 | var position = scaleBarBackgroundPositionX+"px "+(scaleBarBackgroundPositionY-ratioTop)+"px";
60 | var slider = document.querySelector("."+slider);
61 | slider.style.top=sliderTop+"px";
62 | var ruler_current = document.querySelector("."+ruler_current);
63 | ruler_current.style.height=ratioHeight+"px";
64 | ruler_current.style.top=ratioTop+"px";
65 | ruler_current.style.backgroundPosition=position;
66 | //配置的回调
67 | if( self.opt.scale ){
68 | var n = new Number(self.scale).toFixed(1)*1;
69 | //只有值发生改变才会触发回调
70 | if( n != self.prevScale ){
71 | //这里设定放大,缩小的值域
72 | self.opt.scale(self.prevScale,n);
73 | }
74 | self.prevScale = n;
75 | }
76 | };
77 | //上下左右的hover监听(兼职监听keydown)
78 | function selectedBtn(e){
79 | if( e.keyCode ){ //为键盘
80 | var direct = e.keyCode;
81 | if( direct == 37 || direct == 38 || direct==39 || direct == 40){
82 | //配置的回调
83 | if( self.opt.steerwheel ){
84 | self.opt.steerwheel(direct);
85 | }
86 | }
87 | }else{ //为mouseenter
88 | var direct = parseInt(e.target.getAttribute("data-direct"));
89 | }
90 | self.setSteerWheelClass("steer_"+direct);
91 | }
92 | function selectedClickBtn(e){
93 | //配置的回调
94 | var direct = parseInt(e.target.getAttribute("data-direct"));
95 | if( self.opt.steerwheel ){
96 | self.opt.steerwheel(direct);
97 | }
98 | }
99 | function cancelSelectedBtn(e){
100 | self.setSteerWheelClass("steer_default");
101 | }
102 | //滑块的滑轮监听(向上是放大,向下是缩小)
103 | function scrollSlider(e){
104 | var num = self.scale;
105 | if(e.wheelDelta){
106 | num = e.wheelDelta>0?num+0.1:num-0.1; //ie,chrome向上滚 > 0
107 | }else if(e.detail){
108 | num = e.detail>0?num-0.1:num+0.1; //firefox向下滚 > 0
109 | }
110 | self.setScale(num);
111 | }
112 | //放大缩小的点击事件
113 | function zoomBtn(e){
114 | var type = e.target.getAttribute("data-type");
115 | if( type=="out" ){
116 | self.scale=self.scale+0.1;
117 | }else if( type=="in" ){
118 | self.scale=self.scale-0.1;
119 | }
120 | self.setScale(self.scale);
121 | }
122 | //比例尺滑块的拖拽监听
123 | function enableDragDrapSlider(e){
124 | var slider = e.target.getAttribute("class");
125 | if( slider == self.scale_slider ){
126 | e.target.setAttribute("data-drag","true");
127 | }
128 | }
129 | function runDragDrapSlider(e){
130 | var console_panel = document.querySelector("."+self.console_panel),
131 | slider = document.querySelector("."+self.scale_slider);
132 | if( eval( slider.getAttribute("data-drag") ) ){
133 | var offsetTop = console_panel.offsetTop+steerwheelHeight+zoomBtnHeight; //预设变量
134 | if( offsetTop<=e.pageY && e.pageY <=offsetTop+scaleBarHeight ){
135 | var num = ( scaleBarHeight-(e.pageY-offsetTop) )/scaleBarHeight;
136 | //console.log(b);
137 | self.setScale(num);
138 | }
139 | }
140 | }
141 | function disableDragDrapSlider(e){
142 | var slider = document.querySelector("."+self.scale_slider);
143 | slider.setAttribute("data-drag","false");
144 | }
145 |
146 | //初始化监听
147 | this.initEvent = function(){
148 | //上下左右的hover监听(click)
149 | var steerBtnArray = document.querySelectorAll("."+self.steer_btn);
150 | if( steerBtnArray ){
151 | for(var i=0; i'+
233 | ''+
234 | '
'+
235 | '
'+
236 | '
'+
237 | '
'+
238 | '
'+
239 | ''+
240 | ''+
241 | '
'+
242 | '
'+
243 | '
'+
244 | '
'+
245 | '
'+
246 | '
'+
247 | '
'+
248 | '
';
249 | return console_panel;
250 | };
251 | }
--------------------------------------------------------------------------------
/src/ctopo.js:
--------------------------------------------------------------------------------
1 | /**
2 | * canvas版topo图
3 | * 作者:田明
4 | * github: https://github.com/tm-roamer/ctopo
5 | */
6 | function ctopo(opt){
7 | //状态机常量
8 | const STATE_IMAGE_LOAD=10, //加载图片
9 | STATE_INIT_LOAD=20, //正在初始化
10 | STATE_INIT_OVER=30; //初始化完成
11 | //默认配置对象
12 | var defaultOpt = {
13 | id:"", //说明: canvas标签的id, 写法: canvas , #canvas
14 | width:"auto", //说明: canvas的宽度, 写法: 500,500px,50%,auto,默认auto
15 | height:"auto", //说明: canvas的高度, 写法: 500,500px,50%,auto,默认auto
16 | isOnAnimateBall:false, //说明: 是否启动连线的动画球 写法: true,false, 默认:false
17 | isShowConsolePanel:true, //说明: 是否显示控制台, 写法: true,false, 默认true
18 | isHoverNodeLight:true, //说明: 是否悬停节点高亮, 写法: true,false, 默认true
19 | isShowNodeLabel:true, //说明: 是否显示节点文字, 写法: true,false, 默认true
20 | isShowNodeTooltip:false, //说明: 是否显示节点提示框, 写法: true,false, 默认false
21 | isShowEdgeLabel:false, //说明: 是否显示连线文字, 写法: true,false, 默认false
22 | isShowEdgeTooltip:false, //说明: 是否显示连线提示框, 写法: true,false, 默认false
23 | isShowEdgeArrow:false, //说明: 是否显示连线箭头, 写法: true,false, 默认false
24 | style:{ //说明: 全局样式
25 | global:{
26 | backgroundColor:"#ffffff", //说明: 支持fillstyle所有原生写法, 例: rgba(255,255,255,1),默认#ffffff
27 | backgroundImage:null, //说明: 背景网格线, 默认为null
28 | backgourndImageOpacity:0.3, //说明: 背景网格线透明度, 配置了backgroundImage此字段才有效, 默认0.3
29 | //备注: backgroundColor灰,白,0.5美观;backgroundColor黑色,蓝色,0.2美观
30 | tooltipBackgroundColor:"rgba(0, 0, 0, 0.7)", //说明: 悬停提示的背景色样式,默认rgba(0, 0, 0, 0.7)
31 | tooltipColor:"rgb(255, 255, 255)" //说明: 悬停提示的文字样式,默认rgb(255, 255, 255)
32 | },
33 | node:{
34 | color:"#00adee", //说明: 节点颜色, 支持fillstyle所有原生写法, 优先级低于节点自带属性, 默认#00adee
35 | size:20, //说明: 节点直径, 优先级低于节点自带属性, 默认20px,
36 | textColor:"#878787", //说明: 节点Label颜色, 优先级低于节点自带属性, 默认#878787
37 | textSize:10 //说明: 节点Label字体大小, 优先级低于节点自带属性, 默认10px
38 | },
39 | edge:{
40 | color:"#c2c2c2", //说明: 连线颜色, 支持fillstyle所有原生写法, 优先级低于节点自带属性, 默认#c2c2c2
41 | size:1, //说明: 连线宽度, 优先级低于节点自带属性, 默认1px
42 | textColor:"#878787", //说明: 节点Label颜色, 优先级低于节点自带属性, 默认#878787
43 | textSize:10 //说明: 节点Label字体大小, 优先级低于节点自带属性, 默认10px
44 | }
45 | },
46 | layout:{
47 | name:"force", //说明: 布局方式,支持force力导向和preset预设, 默认force
48 | param:{
49 | isScale : false, //说明: 默认第一次应用布局,是否自适应屏幕宽高,等比例缩放,默认false
50 | isRandom : false, //说明: 默认初始位置是随机Random还是定位location,默认false
51 | initAreaW : 100, //说明: 初始分布是的初始宽,默认100px
52 | initAreaH : 56, //说明: 初始分布是的初始高,默认56px
53 | energy:0.5, //说明: 能量值范围0.3-1, 默认0.5
54 | iterations :150, //说明: 力导向的迭代次数,默认150
55 | ejectFactor : 2, //说明: 默认斥力常量,默认2
56 | ejectRange : 250, //说明: 最大斥力范围,超过的不计算节点间斥力,默认2
57 | ejectFadeRange : 30, //说明: 节点簇的减弱范围,此范围内ejectFactor-1,默认30
58 | condenseFactor : 1, //说明: 默认引力常量,默认1
59 | maxtx : 3, //说明: 每次迭代的x方向最大移动距离,默认3
60 | maxty : 1.68 //说明: 每次迭代的y方向最大移动距离,默认1.68
61 | }
62 | },
63 | data:{ //说明: 数据格式
64 | /*
65 | nodes:[
66 | {
67 | id:"节点id", //说明: 必填项, 节点id, 必须唯一
68 | x:0, //说明: 节点x坐标,
69 | y:0, //说明: 节点y坐标,
70 | label:"显示标签", //说明: 显示标签, 默认null
71 | tooltip:"悬停文字", //说明: 悬停文字, 默认null
72 | color:"#00adee", //说明: 节点颜色, 支持fillstyle所有原生写法, 优先级高于全局样式, 不配置默认等于全局样式
73 | size:20, //说明: 节点直径, 优先级高于全局样式, 不配置默认等于全局样式
74 | textColor:"#878787", //说明: 节点Label颜色, 优先级高于全局样式, 不配置默认等于全局样式
75 | textSize:10 //说明: 节点Label字体大小, 优先级高于全局样式, 不配置默认等于全局样式
76 | //保留字段 //说明: 保留字段不可使用
77 | // originalColor=color,保存初始颜色,方便颜色变换
78 | // dispx,dispy,布局运算时的偏移量
79 | //支持自定义字段 //说明:
80 | // (1)不支持函数形式的自定义字段,基本类型,对象,数组都行
81 | // (2)不可以和保留字段重名
82 | customField1:"fieldl",
83 | customField2:["field2_1","field2_2"],
84 | customField3:{field3_1:"field3_1",field3_2:"field3_2"}
85 | }
86 | ],
87 | edges:[
88 | {
89 | source:"开始节点id", //说明: 必填项, 开始节点id
90 | target:"结束节点id" //说明: 必填项, 结束节点id
91 | label:"显示标签", //说明: 显示标签, 默认null
92 | tooltip:"悬停文字", //说明: 悬停文字, 默认null
93 | color:"#c2c2c2", //说明: 连线颜色, 支持fillstyle所有原生写法, 优先级高于全局样式, 不配置默认等于全局样式
94 | size:1 //说明: 连线宽度, 优先级高于全局样式, 不配置默认等于全局样式
95 | textColor:"#878787", //说明: 节点Label颜色, 优先级高于全局样式, 不配置默认等于全局样式
96 | textSize:10 //说明: 节点Label字体大小, 优先级高于全局样式, 不配置默认等于全局样式
97 | //保留字段 //说明: 保留字段不可使用
98 | // originalColor=color,保存初始颜色,方便颜色变换
99 | // sourceIndex,targetIndex=开始和结束节点在nodes数组的索引值
100 | //支持自定义字段 //说明:
101 | // (1)不支持函数形式的自定义字段,,基本类型,对象,数组都行
102 | // (2)不可以和保留字段重名
103 | customField1:"fieldl",
104 | customField2:["field2_1","field2_2"],
105 | customField3:{field3_1:"field3_1",field3_2:"field3_2"}
106 | }
107 | ]
108 | */
109 | },
110 | event:{ //说明: 监听回调
111 | steerwheel:null, //说明: 上下左右的平移回调
112 | scale:null, //说明: 放大缩小的回调
113 | clickNode:null, //说明: 点击节点的回调
114 | hoverNode:null, //说明: 悬停节点的回调
115 | leaveNode:null, //说明: 离开节点的回调
116 | clickEdge:null, //说明: 点击连线的回调
117 | hoverEdge:null, //说明: 悬停连线的回调
118 | leaveEdge:null //说明: 离开连线的回调
119 | }
120 | },
121 | //topo对象
122 | tp = {
123 | version:"v1.0.0",
124 | /**
125 | * 属性合并
126 | */
127 | extendMerge:function(){
128 | var res={};
129 | for(var i=0; i 全局样式
314 | node.originalColor = node.color;
315 | node.x=node.x ? node.x : 0;
316 | node.y=node.y ? node.y : 0;
317 | node.label = node.label ? node.label : null;
318 | node.tooltip=node.tooltip ? node.tooltip : null;
319 | return node;
320 | }
321 |
322 | /**
323 | * 设置单个连线的字段的初始工作(私有方法)
324 | */
325 | function setEdge(edge,edgeStyle){
326 | edge.size = edge.size ? edge.size : edgeStyle.size;
327 | edge.color = edge.color? edge.color: edgeStyle.color; //edge对象样式 > 全局样式
328 | edge.textSize = edge.textSize ? edge.textSize : edgeStyle.textSize;
329 | edge.textColor = edge.textColor? edge.textColor: edgeStyle.textColor;
330 | edge.originalColor = edge.color;
331 | edge.sourceIndex = 0;
332 | edge.targetIndex = 0;
333 | edge.label = edge.label ? edge.label : null;
334 | edge.tooltip = edge.tooltip ? edge.tooltip : null;
335 | return edge;
336 | }
337 |
338 | /**
339 | * 初始化对象(私有方法)
340 | */
341 | function initObject(){
342 | ConPanel.prototype= new ConsolePanel();//初始化控制台对象
343 | conPanel = new ConPanel();
344 | tooltip = new Tooltip(); //悬停提示框对象
345 | utils = new Utils(); //初始化工具对象
346 | layout = new Layout(); //初始化布局对象
347 | event = new Event(); //初始化监听对象
348 | render = new Render(); //初始化绘制对象
349 | animate = new Animate(); //初始化动画对象
350 | api = new API(); //初始化API接口
351 | tp.extendCopy(tp,api); //属性copy
352 | }
353 |
354 | //控制台对象(私有对象)------------------------------------------------
355 | function ConPanel(){
356 | //不存在的时候才添加
357 | var console_panel = document.querySelector("."+this.console_panel);
358 | if( !console_panel ){
359 | document.body.insertBefore(this.getDom(),document.body.firstChild); //插入html
360 | }
361 | this.setOption({steerwheel:steerwheelCallBack,scale:scaleCallBack});
362 | this.initEvent(); //加入监听
363 | if( tp.option.isShowConsolePanel ){
364 | this.show(); //显示
365 | }else{
366 | this.hide(); //隐藏
367 | //this.removeEvent(); //移除监听
368 | }
369 | //上下左右平移的回调
370 | function steerwheelCallBack(keyCode){
371 | animate.destory();
372 | //逻辑处理
373 | utils.moveSteerWheel(tp.nodes,keyCode);
374 | //初始完成后才进行绘制
375 | if( appState == STATE_INIT_OVER ){
376 | render.draw();
377 | animate.init(tp.nodes,tp.edges);
378 | }
379 | //设置回调
380 | if( tp.option.event.steerwheel ){
381 | tp.option.event.steerwheel(keyCode);
382 | }
383 | }
384 | //放大缩小的回调
385 | function scaleCallBack(prevScale,scale){
386 | animate.destory();
387 | //逻辑处理
388 | utils.inOutScale(tp.nodes,tp.canvas.width,tp.canvas.height,prevScale,scale);
389 | //初始完成后才进行绘制
390 | if( appState == STATE_INIT_OVER ){
391 | render.draw();
392 | animate.init(tp.nodes,tp.edges);
393 | }
394 | //设置回调
395 | if( tp.option.event.scale ){
396 | tp.option.event.scale(prevScale,scale);
397 | }
398 | }
399 | }
400 | //悬停提示框对象(私有对象)------------------------------------------------
401 | function Tooltip(){
402 | var name="ctopo_tooltip";
403 | //不存在的时候才添加
404 | var tip = document.querySelector("."+name);
405 | if( !tip ){
406 | document.body.insertBefore(getDom(),document.body.firstChild); //插入html
407 | }
408 | this.chooseDisplay = function(node,edge,ex,ey){
409 | if( tp.option.isShowNodeTooltip ){
410 | if( node ){
411 | if( node.tooltip ){
412 | show(node,ex,ey);
413 | return; //因为node悬停优先级高于edge
414 | }
415 | }else{
416 | hide();
417 | }
418 | }
419 | if( tp.option.isShowEdgeTooltip ){
420 | if( edge ){
421 | if( edge.tooltip ){
422 | show(edge,ex,ey);
423 | }
424 | }else{
425 | hide();
426 | }
427 | }
428 | }
429 | this.remove=function(){
430 | var tip = document.querySelector("."+name);
431 | document.body.removeChild(tip);
432 | };
433 | function getDom(){
434 | var tip = document.createElement("div");
435 | tip.setAttribute("class",name);
436 | tip.style.cssText='position: absolute;'+
437 | 'left: 0px;'+
438 | 'top: 0px;'+
439 | 'background-color: '+tp.option.style.global.tooltipBackgroundColor+'; '+
440 | 'color: '+tp.option.style.global.tooltipColor+'; '+
441 | 'visibility: hidden; '+
442 | 'border-radius: 4px; '+
443 | 'white-space: nowrap; transition: left 0.4s ease 0s, top 0.4s ease 0s; '+
444 | 'text-decoration: none; '+
445 | 'font-family: Arial,Verdana,sans-serif; '+
446 | 'font-size: 12px; '+
447 | 'line-height: 18px; '+
448 | 'font-style: normal; '+
449 | 'font-weight: normal;'+
450 | 'padding: 5px;';
451 | return tip;
452 | }
453 | function show(element,ex,ey){
454 | var tip = document.querySelector("."+name);
455 | var coord = computePosition(element,ex,ey);
456 | tip.style.top=coord.top+"px";
457 | tip.style.left=coord.left+"px";
458 | tip.innerHTML=element.tooltip;
459 | tip.style.visibility="visible";
460 | }
461 | function hide(){
462 | var tip = document.querySelector("."+name);
463 | tip.style.visibility="hidden";
464 | }
465 | function computePosition(element,ex,ey){
466 | var coord={},
467 | textH=28,
468 | textW=computeTextWidth(element.tooltip)+10, //+pading
469 | canvasW=tp.canvas.width,
470 | canvasH=tp.canvas.height;
471 | //边界判断
472 | coord.top = ey <= textH/2 ? 0 : ( ey>=canvasH-textH/2 ? canvasH-textH/2 : ey-textH/2);
473 | coord.left = ex >= canvasW-element.size-textW ? (ex-element.size/2-textW) : ex+element.size/2;
474 | return coord;
475 | }
476 | function computeTextWidth(text){
477 | return tp.context.measureText(text).width; //文字宽
478 | }
479 | }
480 | //工具对象(私有对象)-----------------------------------------------
481 | function Utils(){
482 | this.relationEdges = [];
483 | this.relationNodes = [];
484 | this.notRelationNodes = [];
485 |
486 | //函数节流
487 | this.throttle = function(method,context,time){
488 | var arg = arguments;
489 | clearTimeout(method.tId);
490 | method.tId=setTimeout(function(){
491 | method.call(context,arg[3],arg[4]);
492 | },time);
493 | }
494 |
495 | //节点的碰撞检测 ff,chrome:5000节点1ms
496 | this.collideNode=function(nodes,ex,ey){
497 | var obj = null;
498 | for(var i=0; i= 1 ){
696 | return bool;
697 | }
698 | for (var i = 0; i < nodes.length; i++) {
699 | var node = nodes[i];
700 | if( node.x <=0 || node.x >= canvasW || node.y <=0 || node.y >= canvasH ){
701 | bool = true;
702 | break;
703 | }
704 | }
705 | return bool;
706 | }
707 | /**
708 | * 计算箭头平移位置
709 | */
710 | this.computeArrowTranslate = function (dx,dy,radian,nodeE){
711 | //计算偏移
712 | var ox = 0,
713 | oy = 0,
714 | arrawW=6,
715 | arrawHalfW=arrawW/2,
716 | x = nodeE.x,
717 | y = nodeE.y,
718 | radius = nodeE.size/2;
719 | if( dx == 0 ){
720 | y = dy > 0 ? y-radius-arrawHalfW : y+radius+arrawHalfW ;
721 | }else if( dy == 0 ){
722 | x = dx > 0 ? x-radius-arrawHalfW : x+radius+arrawHalfW ;
723 | }else{
724 | ox = Math.ceil( (radius+arrawHalfW)*Math.cos( Math.abs(radian) ) );
725 | oy = Math.ceil( (radius+arrawHalfW)*Math.sin( Math.abs(radian) ) );
726 | x = dx > 0 ? x-ox : x+ox ;
727 | y = dy > 0 ? y-oy : y+oy ;
728 | }
729 | return {x:x,y:y};
730 | }
731 | /**
732 | * 计算箭头旋转角度
733 | */
734 | this.computeArrowRotate = function (dx,dy,angle){
735 | //第一步,取得夹角,x轴正方向为0度的顺时针0-360范围
736 | if( dx==0 || dy==0 ){
737 | if( dx < 0 ){
738 | angle = 180;
739 | }
740 | if( dy < 0 ){
741 | angle = 270;
742 | }
743 | }else{
744 | if( dx > 0 && dy > 0){ //4
745 | //angle = angle;
746 | }else if( dx > 0 && dy < 0 ){ //1
747 | angle = 360 - Math.abs(angle);
748 | }else if( dx < 0 && dy > 0 ){ //3
749 | angle = 180 - Math.abs(angle);
750 | }else if( dx < 0 && dy < 0 ){ //2
751 | angle = 180 + Math.abs(angle);
752 | }
753 | }
754 | //第二步,转换成context.rotate适用的的y轴正方向为0度的0-360范围
755 | angle = angle+90 >= 360 ? (angle+90)%360 : angle+90;
756 | //第三步,角度变弧度
757 | return angle*Math.PI/180;
758 | }
759 | }
760 | //布局对象(私有对象)------------------------------------------------
761 | function Layout(){
762 | this.run=function(layout){
763 | try{
764 | utils.createNodeIndex(tp.nodes,tp.edges); //(优化)建立索引机制,方便用线查点
765 | if( layout.name === "force" ){
766 | this.force(layout.param);
767 | }
768 | }catch(e){
769 | throw new Error("ctopo layout error");
770 | }
771 | }
772 | this.force=function(param){
773 | var nodes = tp.nodes,
774 | edges = tp.edges,
775 | canvasW = tp.canvas.width,
776 | canvasH = tp.canvas.height,
777 | canvasArea = canvasW*canvasH, //画布的面积
778 | k = Math.sqrt( canvasArea / nodes.length )*param.energy, //求出每个节点的能量
779 | isScale = param.isScale, //默认第一次应用布局,是否自适应屏幕宽高,等比例缩放
780 | isRandom = param.isRandom, //默认初始位置是随机Random还是定位location
781 | initAreaW = param.initAreaW, //随机分布是的初始宽
782 | initAreaH = param.initAreaH, //随机分布是的初始高
783 | iterations = param.iterations, //力导向的迭代次数
784 | ejectFactor = param.ejectFactor, //默认斥力常量
785 | ejectRange = param.ejectRange, //最大斥力范围,超过的不计算节点间斥力
786 | ejectFadeRange = param.ejectFadeRange, //节点簇的减弱范围,此范围内斥力-1
787 | condenseFactor = param.condenseFactor, //默认引力常量
788 | maxtx = param.maxtx, //每次迭代的x方向最大移动距离
789 | maxty = param.maxty; //每次迭代的y方向最大移动距离
790 |
791 | //随机初始位置 or 定位摆放
792 | isRandom ? randomCoord(nodes) : locationCoord(nodes);
793 | //反复迭代
794 | repeatLayout(nodes,edges);
795 |
796 | //检测屏幕是否需要缩放
797 | if( isScale ){
798 | while( utils.testIsScale(nodes,canvasW,canvasH,conPanel.scale) ){
799 | //(优化)自动缩放比例适应屏幕
800 | utils.setZoom(conPanel.scale-0.1);
801 | }
802 | }
803 | //1. 随机分布初始节点位置
804 | function randomCoord(nodes){
805 | var startX, startY;
806 | for (var i=0;i b ? 1 : ( a < b ? -1 : 0 ) });
820 | for (var i=0;i 0 && diff < ejectRange ) {
848 | //离得近的节点,斥力减弱,形成簇
849 | eject =(diff < ejectFadeRange )?ejectFactor-1:ejectFactor;
850 | //f=g*m1*m2/r*r 库仑定律
851 | node.dispx = node.dispx + diffx / diff * k * k / diff * eject;
852 | node.dispy = node.dispy + diffy / diff * k * k / diff * eject;
853 | }
854 | }
855 | }
856 | //4. 计算每次迭代每条边的引力对两端节点所产生的单位位移(一般为负值)
857 | for (var a = 0; a < edges.length; a++) {
858 | var nodeS = nodes[ edges[a].sourceIndex ],
859 | nodeE = nodes[ edges[a].targetIndex ],
860 | diffx = nodeS.x - nodeE.x,
861 | diffy = nodeS.y - nodeE.y;
862 | diff = Math.sqrt(diffx * diffx + diffy * diffy); //勾股定理斜线距离
863 | //f=-g*d 胡克定律
864 | nodeS.dispx = nodeS.dispx - diffx * diff / k * condenseFactor;
865 | nodeS.dispy = nodeS.dispy - diffy * diff / k * condenseFactor;
866 | nodeE.dispx = nodeE.dispx + diffx * diff / k * condenseFactor;
867 | nodeE.dispy = nodeE.dispy + diffy * diff / k * condenseFactor;
868 | }
869 | //5. 重置每个节点的坐标set x,y
870 | for (var v = 0; v < nodes.length; v++) {
871 | var node = nodes[v],
872 | disppx = parseInt( Math.floor( node.dispx ) ),
873 | disppy = parseInt( Math.floor( node.dispy ) );
874 | disppx = (disppx < -maxtx ? -maxtx : ( disppx > maxtx ? maxtx : disppx ) );
875 | disppy = (disppy < -maxty ? -maxty : ( disppy > maxty ? maxty : disppy ) );
876 | node.x = node.x + disppx;
877 | node.y = node.y + disppy;
878 | }
879 | return nodes;
880 | }
881 | }
882 | }
883 | //监听对象(私有对象)------------------------------------------------
884 | function Event(){
885 | var self = this;
886 | self.fps = Math.floor(1000/200), //约等于5毫秒
887 | self.fpsCount = 0; //计算触发帧率
888 | self.dragPrevX = 0;
889 | self.dragPrevY = 0;
890 | self.dragCurrentX = 0;
891 | self.dragCurrentY = 0;
892 | this.init=function(){
893 | var canvas = tp.canvas;
894 | //监听拖拽
895 | canvas.addEventListener("mousedown",this.canvasMouseDown,false); //摁下
896 | canvas.addEventListener("mousemove",this.canvasMouseMove,false); //移动
897 | canvas.addEventListener("mouseup",this.canvasMouseUp,false); //抬起
898 | canvas.addEventListener("click",this.canvasClick,false); //点击 mouseup之后触发
899 | }
900 | this.canvasMouseDown = function (e){
901 | //切换手型
902 | tp.canvas.style.cursor="pointer";
903 | //启动拖拽
904 | self.isDragDrap = true;
905 | //判断是node还是canvas
906 | utils.collideNode(tp.nodes,e.pageX,e.pageY);
907 | //保存坐标
908 | self.dragPrevX = e.pageX;
909 | self.dragPrevY = e.pageY;
910 | self.dragCurrentX = e.pageX;
911 | self.dragCurrentY = e.pageY;
912 | }
913 | this.canvasMouseMove = function (e){
914 | //ff:200节点 50ms, chrome:200节点 25ms
915 | if( self.fpsCount++ >= self.fps ){
916 | //判断是否拖拽
917 | if( self.isDragDrap ){
918 | animate.destory();
919 | //保存坐标
920 | self.dragCurrentX = e.pageX;
921 | self.dragCurrentY = e.pageY;
922 | //计算位移
923 | var dx = self.dragCurrentX - self.dragPrevX;
924 | var dy = self.dragCurrentY - self.dragPrevY;
925 | //当前坐标变成上一次的坐标
926 | self.dragPrevX = self.dragCurrentX;
927 | self.dragPrevY = self.dragCurrentY;
928 |
929 | //判断mouseDown是节点还是屏幕
930 | if( self.collideNode ){
931 | //修改节点坐标
932 | self.collideNode.x = self.collideNode.x + dx;
933 | self.collideNode.y = self.collideNode.y + dy;
934 | }else{
935 | //nodes整体平移
936 | var nodes = tp.nodes;
937 | for(var i=0; i 全局样式
1104 | context.arc(node.x,node.y,parseInt(node.size/2),0,(Math.PI/180)*360,false);
1105 | context.fill();
1106 | context.closePath();
1107 | //绘制节点label
1108 | if( node.label && tp.option.isShowNodeLabel ){
1109 | drawNodeLabel(node);
1110 | }
1111 | }
1112 |
1113 | function drawNodeLabel(node){
1114 | var textWidth = context.measureText(node.label).width; //文字宽
1115 | context.fillStyle=node.textColor;
1116 | context.font=node.textSize+"px serif";
1117 | context.fillText(node.label,node.x-(textWidth/2),node.y+node.size/2+node.textSize);
1118 | }
1119 |
1120 | function drawEdge(edge){
1121 | var nodeS = tp.nodes[edge.sourceIndex];
1122 | var nodeE = tp.nodes[edge.targetIndex];
1123 | context.beginPath();
1124 | context.strokeStyle=edge.color;
1125 | context.lineWidth=edge.size;
1126 | context.moveTo(nodeS.x,nodeS.y);
1127 | context.lineTo(nodeE.x,nodeE.y);
1128 | context.stroke();
1129 | context.closePath();
1130 | //绘制连线label
1131 | if( edge.label && tp.option.isShowEdgeLabel ){
1132 | drawEdgeLabel(nodeS,nodeE,edge);
1133 | }
1134 | //绘制箭头
1135 | if( tp.option.isShowEdgeArrow ){
1136 | drawArrow(nodeS,nodeE);
1137 | }
1138 | }
1139 |
1140 | function drawEdgeLabel(nodeS,nodeE,edge){
1141 | var textWidth = context.measureText(edge.label).width, //文字宽
1142 | textX = nodeS.x + (nodeE.x-nodeS.x)/2 - textWidth/2 ,
1143 | textY = nodeS.y + (nodeE.y-nodeS.y)/2 - textWidth/2 ;
1144 | context.fillStyle=edge.textColor;
1145 | context.font=edge.textSize+"px serif";
1146 | context.fillText(edge.label,Math.abs(textX),Math.abs(textY)+edge.textSize );
1147 | }
1148 |
1149 | /**
1150 | * 绘制箭头(6px*6px)
1151 | */
1152 | function drawArrow(nodeS,nodeE){
1153 | //计算箭头位置
1154 | var dx = nodeE.x - nodeS.x,
1155 | dy = nodeE.y - nodeS.y,
1156 | radian = Math.atan(dy/dx),
1157 | angle = radian *180 / Math.PI,
1158 | coord = utils.computeArrowTranslate(dx,dy,radian,nodeE),
1159 | angleRadian = utils.computeArrowRotate(dx,dy,angle);
1160 |
1161 | //将画布保存到栈中
1162 | context.save();
1163 | context.setTransform(1,0,0,1,0,0); //恢复初始值
1164 | context.translate(coord.x,coord.y); //平移
1165 | context.rotate( angleRadian ); //旋转
1166 | context.beginPath();
1167 | context.fillStyle=nodeE.color;
1168 | //定位到箭头中心点
1169 | context.moveTo(0,-3);
1170 | context.lineTo(3,3);
1171 | context.lineTo(-3,3);
1172 | context.lineTo(0,-3);
1173 | context.fill();
1174 | context.closePath();
1175 | context.restore();
1176 | }
1177 | }
1178 | //动画对象(私有对象)------------------------------------------------
1179 | function Animate(){
1180 | var context = tp.context,//画布的上下文对象
1181 | speed = 1; //小球的速度
1182 | this.animateBalls =[]; //连线动画小球数组
1183 | this.init = function(nodes,edges){
1184 | if( tp.option.isOnAnimateBall ){
1185 | //初始化动画小球的各项参数
1186 | var nodeS,nodeE,edge,dx,dy,diff,moves,xuint,yuint;
1187 | this.animateBalls.splice(0,this.animateBalls.length); //干掉所有计算数据
1188 | for(var i=0; i 1 ? 1 : ( scale < 0 ? 0 : scale );
1493 | scale = new Number(scale).toFixed(1)*1;
1494 | utils.setZoom(scale);
1495 | render.draw(); //绘制
1496 | }
1497 | return bool;
1498 | }else{
1499 | return conPanel.scale;
1500 | }
1501 | }
1502 | }
1503 | //构建初始配置
1504 | tp.option = tp.extendMerge(defaultOpt,opt?opt:{});
1505 | //初始化函数
1506 | init();
1507 | return tp;
1508 | }
--------------------------------------------------------------------------------