欢迎访问优讯网!
您当前的位置:首页 > 爱编程

D3.js 实现带伸缩时间轴拓扑图的示例代码

时间:2020-01-21 08:25:49  来源:优讯网  作者:小卡司  浏览次数:
这篇文章主要介绍了D3.js 实现带伸缩时间轴拓扑图的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

效果图:

基于d3-v5, 依赖dagre-d3, 直接上代码:

 
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
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
<!DOCTYPE html>
<html lang="en">
 
<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <meta http-equiv="X-UA-Compatible" content="ie=edge">
 <title>Document</title>
 <style>
  svg {
   border: 1px solid darkcyan;
  }
 
  /* 拓扑图--start */
  /* 节点状态颜色 */
  g.type-current>circle {
   fill: #FFAC27;
  }
 
  g.type-success>circle {
   fill: #9270CA;
  }
 
  g.type-fail>circle {
   fill: #67C23A;
  }
 
  g.type-done>circle {
   fill: #E8684A;
  }
 
  /* 拓扑图--end */
 
  /* 坐标轴-start */
  .axis path,
  .axis line {
   fill: none;
   stroke: #DCDCDC;
   shape-rendering: crispEdges;
  }
 
  .axis text {
   font-family: sans-serif;
   font-size: 12px;
   fill: #999999;
  }
 
  .axis .x2-axis text {
   font-size: 14px;
   font-weight: 400;
   fill: #333;
  }
 
  .axis .x2-axis .tick {
   stroke-width: 2px;
  }
 
 
  /* 坐标轴-end */
 </style>
</head>
<script src=" http://d3js.org/d3.v5.min.js "></script>
<script src="https://cdn.bootcss.com/dagre-d3/0.6.3/dagre-d3.js"></script>
 
<body>
</body>
<script>
 
 let nodeInfo = [{
  id: 0,
  label: "",
  status: 'success',
  date: 1575129600000
 }, {
  id: 1,
  label: "",
  status: 'fail',
  date: 1578376890000
 }, {
  id: 2,
  label: '',
  status: 'success',
  date: 1578376890000
 }, {
  id: 3,
  label: '',
  status: 'fail',
  date: 1578895290000
 }, {
  id: 4,
  label: '',
  status: 'current',
  date: 1578895290000
 }, {
  id: 5,
  label: '',
  status: 'done',
  date: 1579327290000
 }, {
  id: 6,
  label: '',
  status: 'done',
  date: 1579932090000
 }, {
  id: 7,
  label: '',
  status: 'done',
  date: 1581487290000
 }, {
  id: 8,
  label: '',
  status: 'success',
  date: 1583461994000
 }]
 let lineInfo = [
  { from: 0, to: 1 },
  { from: 0, to: 2 },
  { from: 0, to: 3 },
  { from: 2, to: 4 },
  { from: 2, to: 5 },
  { from: 3, to: 6 },
  { from: 6, to: 7 },
  { from: 6, to: 8 },
 ]
 
 let nodeMap = new Map() //节点信息map
 let nodeDomMap = new Map() //节点dom--map
 let timeArr = [] //存储时间
 
 const width = 1200
 const height = 400
 const padding = { top: 0, bottom: 40, left: 40, right: 40 }
 
 // 节点信息转化为map
 nodeInfo.forEach(item => {
  nodeMap.set(item.id, item);
  timeArr.push(item.date)
 })
 let max = new Date(d3.max(timeArr))
 let min = new Date(d3.min(timeArr))
 maxY = max.getFullYear()
 maxM = max.getMonth()
 minY = min.getFullYear()
 minM = min.getMonth()
 
 // 创建画布 svg
 let svg = d3.select("body").append("svg")
  .attr("id", "svg-canvas")
  .attr("preserveAspectRatio", "xMidYMid meet")
  .attr("viewBox", `0 0 ${width} ${height}`)
 
 // 初始化元素
 let background = svg.append("rect").attr("class", "bg")
 let view = svg.append("g").attr("class", "view")
 let grid = svg.append("g").attr("class", "grid")
 let axis = svg.append("g").attr("class", "axis")
 let separateLine = svg.append("line").attr("class", "separate-line")
 
 // 绘制箭头以供引用
 d3.select("#svg-canvas").append("defs").append("marker")
  .attr("id", "triangle").attr("viewBox", "0 0 10 10")
  .attr("refX", "17").attr("refY", "5")
  .attr("markerWidth", "6").attr("markerHeight", "6")
  .attr("orient", "auto").append("path")
  .attr("d", "M 0 0 L 10 5 L 0 10 z").style("fill", "#bbbbbb")
 
 // 添加背景板 rect
 background.attr("fill", "#FAFAFA")
  .attr("x", 0).attr("y", 0)
  .attr("width", width).attr("height", height - padding.bottom)
 const monthNum = d3.timeMonth.count(min, max) // 区间月份数量
 
 // 确定比例尺
 let xScale = d3.scaleTime()
  .domain([new Date(minY, minM, 1), new Date(maxY, ++maxM, 1)])
  .range([0, width - padding.left - padding.right])
 
 // 坐标轴文本格式化
 let formatDay = d3.axisBottom(xScale).tickFormat((d, i) => {
  const date = new Date(d)
  const day = date.getDate()
  return `${day === 1 ? "" : day}`
// 如果是1号, 不显示刻度,直接由xAxis2显示年月

 })
 let formatMonth = d3.axisBottom(xScale).ticks(d3.timeMonth.every(1))
.tickPadding(6).tickSizeInner(20).tickFormat((d, i) => {

  const date = new Date(d)
  const mon = date.getMonth() + 1
  const year = date.getFullYear()
  return `${year} - ${mon > 9 ? mon : "0" + mon}`
 })
 axis.attr('transform', `translate(${padding.left},${height - padding.bottom})`)
 let xAxisDay = axis.append("g")
  .attr("class", "x-axis").call(formatDay)
 let xAxisMonth = axis.append("g")
  .attr("class", "x2-axis").call(formatMonth)
 
 
 // 绘制x网格
 const lineGroup = grid.attr("transform", `translate(${padding.left},0)`)
  .selectAll("g")
  .data(xScale.ticks(monthNum))
  .enter().append("g")
 lineGroup.append("line")
  .attr("x1", d => { return xScale(new Date(d)) })
  .attr("x2", d => { return xScale(new Date(d)) })
  .attr("y1", padding.top)
  .attr("y2", height - padding.bottom)
  .attr("class", "grid-line")
  .style("stroke", "#DCDCDC")
  .style("stroke-dasharray", 6)
 
 // 添加坐标轴与拓扑图分隔线
 separateLine.style("stroke", "#DCDCDC")
  .style("stroke-width", 2)
  .attr("x1", 0)
  .attr("x2", width)
  .attr("y1", height - padding.bottom)
  .attr("y2", height - padding.bottom)
 
 // 绘制流程图 节点--箭头
 let g = new dagreD3.graphlib.Graph()
  .setGraph({})
  .setDefaultEdgeLabel(function () { return {}; });
 g.graph().rankdir = "LR"; // 控制水平显示
 g.graph().marginx = 0;
 g.graph().marginy = 50;
 
 nodeInfo && nodeInfo.map((item, i) => {
  g.setNode(item.id, {
   label: item.label,
   class: "type-" + item.status,
   style: "stroke-width: 2px; stroke: #fff",
   shape: "circle",
   id: item.id
  });
 
 })
 
 lineInfo && lineInfo.map((item, i) => {
  g.setEdge(item.from, item.to,
   {
    arrowheadStyle: "stroke:none; fill: none", // 箭头头部样式
    style: "stroke:none; fill: none" //线条样式
   })
 
 })
 
 let render = new dagreD3.render();
 render(view.attr("transform", `translate(${padding.left},0)`), g);
 
 // 重新定位节点x坐标
 const nodesArr = d3.select(".nodes").selectAll(".node")._groups[0]
 nodesArr.forEach((item) => {
  let dom = d3.select(item)._groups[0][0]
  let id = Number(dom.id)
  let date = nodeMap.get(id).date
  const x = xScale(new Date(date));
  const y = dom.transform.animVal[0].matrix.f
  d3.select(item).attr("transform", `translate(${x},${y})`)
  nodeDomMap.set(Number(item.id), item)
 })
 
 // 重新绘制箭头
 lineInfo && lineInfo.map((item, i) => {
  let fromDom = nodeDomMap.get(Number(item.from))
  let toDom = nodeDomMap.get(Number(item.to))
  const [x1, y1, x2, y2] = [
   fromDom.transform.animVal[0].matrix.e,
   fromDom.transform.animVal[0].matrix.f,
   toDom.transform.animVal[0].matrix.e,
   toDom.transform.animVal[0].matrix.f,
  ]
  d3.select(".edgePaths").append("g")
   .append("line")
   .attr("class", `to-${item.to}`) // 设置唯一的class方便修改路径
   .attr("stroke-width", "2")
   .attr("stroke", "#bbbbbb")
   .style("stroke-dasharray", 8)
   .attr("marker-end", "url(#triangle)")
   .attr("x1", x1).attr("y1", y1)
   .attr("x2", x2).attr("y2", y2)
 
 })
 
 // 设置zoom参数
 let zoom = d3.zoom()
  .scaleExtent([1, 10])
  .translateExtent([[0, 0], [width, height]]) //移动的范围
  .extent([[0, 0], [width, height]])//视窗 (左上方,右下方)
 
 svg.call(zoom.on("zoom", reRender.bind(this)));
 
 
 // 每次缩放重定位渲染拓扑图
 function reRender() {
  const t = d3.event.transform.rescaleX(xScale) //获得缩放后的比例尺
  xAxisDay.call(formatDay.scale(t))  //重新设置x坐标轴的scale
  xAxisMonth.call(formatMonth.scale(t))  //重新设置x坐标轴的scale
 
  const view = d3.select(".output")
  const axis = d3.select(".axis-month")
  const grid = d3.selectAll(".grid-line")
 
  // 重新绘制节点
  nodesArr.forEach((item) => {
   let dom = d3.select(item)._groups[0][0]
   let id = Number(dom.id)
   let date = nodeMap.get(id).date
   const x = t(new Date(date));
   const y = dom.transform.animVal[0].matrix.f
   d3.select(item).attr("transform", `translate(${x},${y})`)
   nodeDomMap.set(Number(item.id), item)
  })
 
  // 重新绘制箭头
  lineInfo && lineInfo.map((item, i) => {
   let fromDom = nodeDomMap.get(Number(item.from))
   let toDom = nodeDomMap.get(Number(item.to))
   const [x1, y1, x2, y2] = [
    fromDom.transform.animVal[0].matrix.e,
    fromDom.transform.animVal[0].matrix.f,
    toDom.transform.animVal[0].matrix.e,
    toDom.transform.animVal[0].matrix.f,
   ]
   d3.select(`.to-${item.to}`)
    .attr("x1", x1).attr("y1", y1)
    .attr("x2", x2).attr("y2", y2)
 
  })
 
  //重新绘制x网格
  svg.selectAll(".grid-line")
   .attr("x1", d => { return t(new Date(d)) })
   .attr("x2", d => { return t(new Date(d)) })
 }
 
 
 
</script>
 
</html>

以上就是本文的全部内容,希望对大家的学习有所帮助

来顶一下
返回首页
返回首页

原文链接:https://www.jb51.net/article/178901.htm


推荐资讯
如何下载旧版centos iso镜像 如何下载迷你mini版的centos镜像
如何下载旧版centos i
计算机的正确使用姿势 电脑痴如何正确的使用电脑
计算机的正确使用姿势
好用的后台管理的前端框架模版H-ui H-ui框架模版分享
好用的后台管理的前端
微信电脑多开方法 无需辅助电脑版微信双开方法分享
微信电脑多开方法 无
相关文章
    无相关信息
栏目更新
栏目热门