当前位置: 移动技术网 > IT编程>开发语言>JavaScript > D3.js 实现带伸缩时间轴拓扑图的示例代码

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

2020年03月09日  | 移动技术网IT编程  | 我要评论
效果图: 基于d3-v5, 依赖dagre-d3, 直接上代码: <!doctype html> <html lang="en">

效果图:


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

<!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>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网