当前位置 主页 > 服务器问题 > Linux/apache问题 >

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

    栏目:Linux/apache问题 时间:2020-01-28 21:33

    效果图:


    基于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>
    
易波叶平:【ELK】elastalert 日志告警 肖哥哥:Zabbix监控虚拟机服务-告警与自动恢复 高德技术:高德地图驾车导航内存优化原理与实战 月图灵:postgres模糊匹配大杀器 theMine:Mybatis执行流程学习之手写mybatis雏形 MCTW:从手写三层循环到标准实现,矩阵相乘运行效率提高三万六千 Grey Zeng:apijson简单使用 美味的糯米:命令模式与go-redis command设计 晨星1032:AWS IoT Greengrass是什么?V1和V2版本及其差异 努力的老刘:Canal:同步mysql增量数据工具,一篇详解核心知识点 云服务器规格选型 远程桌面连接win系统发生身份验证错误,要求的函数不受支持解决办法 win8系统发生身份验证错误,要求的函数不受支持解决办法 linux远程拷贝到另一个机器文件? linux远程工具有哪些,常用的远程连接Linux工具有哪些? Linux远程管理常见工具PuTTY和SecureCRT对比 linux远程复制文件夹命令进行远程拷贝文件的 scp命令 linux远程复制 linux远程复制拷贝命令使用实例 windows上好用的ssh工具,Windows下连接Linux的三款好用的ssh工具 windows下ssh工具 windows如何ssh远程登录Linux设置免密登录和别名 windows ssh远程登录,windows如何使用ssh登录ubuntu Visual Studio Code(VS Code)查询PostgreSQL拓展安装教程图解 java 如何判断是否是26个英文字母 Vscode上使用SQL的方法 SpringBoot快速迁移至Quarkus的方法步骤 详解Flutter 调用 Android Native 的方法 xftp xshell,Linux下Xshell的安装和 Xftp的安装 mac下怎么安装go语言 介绍python的函数装饰器