vue+d3实现横向树状图(节点使用矩形图展示信息)

树状图展示如下:

​​​​​​​​​​​​

项目步骤见代码标注: 

<template>
  <div class="history" ref="history" style="height: 99%">
    <history-view-title :title="history_view_title"></history-view-title>
    <main class="container">
      <div id="myInput">
        <div style="flex:1; display:flex;">
          <div class="paramInput">
            <span
              style="font:italic bold 13px Georgia, serif;">batch_size:</span>
            <input type="text" class="styleInput" v-model="batchSize"
              placeholder="请输入样本大小" />
          </div>
          <div class="paramInput">
            <span style="font:italic bold 13px Georgia, serif;">epoch:</span>
            <input type="text" class="styleInput" v-model="epoch"
              placeholder="请输入循环次数" />
          </div>
        </div>
        <div
          style="flex:1; display:flex;align-items: center;margin-bottom: 5%;">
          <div class="paramInput">
            <span
              style="font:italic bold 13px Georgia, serif;">pruning_ratio:</span>
            <input type="text" class="styleInput" list="rateList"
              v-model="pruningRatio" placeholder="请输入剪枝率" />
            <!-- <datalist id="rateList" style="text-align: center;">
              <option>0.1</option>
              <option>0.2</option>
              <option>0.3</option>
            </datalist> -->
          </div>
          <div class="paramButtom" style="flex:1">
            <button type="submit" @click="addNode"
              style="flex:2;font:italic bold 16px Georgia, serif;">submit</button>
          </div>
        </div>

      </div>
      <div id="tree_history"><svg id="mainsvg"></svg></div>
    </main>
  </div>
</template>

<script>
import $ from 'jquery';
import HistoryViewTitle from '@/components/title/Title'
import * as d3 from "d3";
export default {
  name: 'History',
  components: {
    HistoryViewTitle
  },
  props: {},
  data () {
    return {
      history_view_title: 'Explore History',
      // id:"treeMap"+Math.ceil(Math.random()*1000),
      treeData: {
        "id": 0,
        "name": 'pruning',
        "children": [
          {
            "id": 1,
            "name": 'pruning1',
            "batch_size": 256,
            "epoch": 30,
            "pruning_ratio": 0.9,
          },
          {
            "id": 2,
            "name": 'pruning2',
            "batch_size": 512,
            "epoch": 100,
            "pruning_ratio": 0.8,
          },
          {
            "id": 3,
            "name": 'pruning3',
            "batch_size": 128,
            "epoch": 20,
            "pruning_ratio": 0.7,
          },
          {
            "id": 4,
            "name": 'pruning5',
            "batch_size": 64,
            "epoch": 100,
            "pruning_ratio": 0.9,
          },
          // {
          //   "id":this.id,
          //   "name":'pruning'+this.id,
          //   "batch_size": this.batchSize,
          //   "epoch": this.epoch,
          //   "pruning_ratio":this.pruningRatio,
          // }
        ]
      },
      id: parseInt(100 * Math.random()),
      batchSize: '',
      epoch: '',
      pruningRatio: ''
    }
  },
  computed: {},
  watch: {},
  created () { },
  mounted () {
    this.initTree()

  },
  methods: {

    initTree () {
      let that = this
      // 1、 选中页面给页面添加svg标签;设置Svg绘制区域的宽和高;添加g元素(svg的group分组标签元素)并设置位置。

      // Set the dimensions and margins of the diagram
      //获得画布
      var svg = d3.select("#mainsvg")
      //定义比例尺
      var width = 100;
      var height = 800;
      var rectWidth = 60;
      var rectHeight = 20;
      //用于控制表格边框线的位置
      var margin = { top: 10, right: 10, bottom: 10, left: 50 };
      let data = {}//源数据
      if (!this.treeData) {
        this.treeData = data
      } else {
        // 清空画布
        d3.select("#mainsvg").select('.body').remove();
      }

      // 2、生成树状布局,设置树图布局容器尺寸。
      //读取数据
      //使用hierarchy讲数据转换成d3中自己的数据格式
      //sum方法让非叶子结点也有value属性,他的value值为它下面所有叶子结点的value之和
      var root = d3.hierarchy(this.treeData)
        .sum(function (d) { return d.value; });
      root.x0 = height / 2;
      root.y0 = 0;
      root.descendants().forEach((d, i) => {
        d.id = i;
      });

      //separation表示同层中节点的占比情况,同一层(相同父节点)占比1:1,同层(不一样父节点)占比1:2
      //不同层,每个节点占比1:1,但深度越深,高度越小
      var tree = d3.tree().size([height, width - margin.left - margin.right - 10])
        // .nodeSize([-100,30])
        .separation(function (a, b) {
          return (a.parent == b.parent ? 3 : 4) / a.depth;
        })
        .nodeSize([22, 26])(root)

      var g = svg.append('g')
        .attr('class', 'body')
        // .call(zoom)
        .attr('transform', function (d) {
          if (tree.links().length == 0) {
            return `translate(${margin.left + 10},${(height - margin.top - margin.bottom) / 2})`
          }
          return `translate(${margin.left + 10},0)`
        })

      //设置缩放功能
      const zoom = d3.zoom()
        .scaleExtent([1 / 8, 8])
        .on("zoom", function(){
          g.attr("transform", d3.zoomTransform(this))
        })
      svg.call(zoom)


      //3、绘制连线、节点以及文本
      //获取links线段
      // console.log(tree.links())
      //定义link连接函数
      var link = d3.linkHorizontal()
        .x(function (d) { return d.y })
        .y(function (d) { return d.x })
      var links = g.selectAll('.link')
        .data(tree.links())
        .enter()
        .append('path')
        // .join('path')
        .attr('class', 'link')
        .attr('fill', 'none')
        .attr('stroke', 'skyblue')
        .attr('stroke-width', 1.5)
        .attr("d", function (d) {
          if (d.source.depth == 0) {
            var start = { x: d.source.x, y: d.source.y };
            var end = { x: d.target.x, y: d.target.y + 100 }
          } else {
            var start = { x: d.source.x, y: d.source.y + 100 };
            var end = { x: d.target.x, y: d.target.y + 150 }
          }
          // var start = {x: d.source.x, y: d.source.y};
          // var end = {x: d.target.x, y: d.target.y}
          return link({ source: start, target: end })
        })
      // 边框终点的形状
      // .attr('stroke-linecap',"square")
      //对角线生成器,并旋转90度.这里由于是横向显示,所以把x和y的位置反过来写
      // .attr('d',d3.linkHorizontal().x(d => d.y).y(d => d.x))

      // 自定义节点填充样式
      var color = d3.scaleOrdinal(d3.schemeCategory10);
      const fill = (d) => {
        if (d.depth === 0) return color(d.data.name);
        while (d.depth > 1) {
          d = d.parent;
        }
        // console.log("d.data.name", d.data.name);
        return color(d.data.name);
      };

      //获取node节点
      // console.log(tree.descendants())
      var node = g.selectAll('.node').data(tree.descendants())
      var nodes = tree.descendants()


      //动态计算树的高度/宽度,node.x指的是纵坐标
      let left = tree
      let right = tree
      root.eachBefore(node => {
        if (node.x < left.x) left = node;
        if (node.x > right.x) right = node;
      })

      //计算树的高度
      const heightTree = right.x - left.x + margin.top + margin.bottom
      const transition = svg.transition()
        .duration(0.5)
        .attr("viewBox", [margin.left, left.x - margin.top, width + margin.left + margin.right, height])

      var nodeEnter = node.enter()
        .append('g')
        .attr('class', 'node')
        .attr("transform", d => `translate(${d.y},${d.x})`)
        .attr("fill-opacity", 1)
        .attr("stroke-opacity", 1)
        .attr("cursor", "pointer")
        .attr("pointer-events", "all")


      //绘制节点
      nodeEnter.append("circle")
        .attr("r", 5)
        .attr("fill", d => d.data.childrenTemp ? "red" : "#999")
        .attr("stroke-width", 1)
        .attr('transform', function (d) {
          if (d.depth == 0) {
            return `translate(6,0)`
          } else if (d.depth == 1) {
            return `translate(${d.y + 70 * d.depth},0)`
          } else {
            return `translate(${d.y + 60 * d.depth},0)`
          }
        })
        .on("click", function (event, node) {
          console.log(event)
          let data = event.data
          // console.log(node)
          // console.log(event.data)
          //节点展开状态
          if (data.children) {
            data.childrenTemp = data.children
            data.children = null
          } else {
            //节点折叠状态
            data.children = data.childrenTemp
            data.childrenTemp = null
          }
          that.initTree()
          // d3.select("#mainsvg").select('body').remove();
        });

      nodeEnter.append('rect')
        .attr('transform', function (d) {
          if (d.depth == 0) {
            return `translate(${-(9 * d.data.name.length)},0)`
          }

        })
        .attr("x", function (d) {
          if (d.depth == 0) {
            return d.y - 16;
          }
          else if (d.children) {
            return d.y + d.depth * 24
          }
          else return d.y + 55 * d.depth + 20
        })
        .attr("y", '-0.6em')
        .attr("width", function (d) {
          // console.log(d)
          if (d.depth == 0) {
            return 11 * d.data.name.length
            // return 10*Math.max(d.data.name.length,d.data.pruning_ratio.length)
          }
          return 12 * d.data.name.length + 2
        })
        .attr("height", function (d) {
          if (d.depth == 0) {
            return rectHeight * 2.4
          }
          return rectHeight * 3
        })
        .attr("fill", function (d) {
          return d.children ? "whitesmoke" : '#9fd3c7'
        })
        .style("opacity", 0.6)
        .attr("stroke", function (d) {
          return d.children ? '#248888' : "whitesmoke"
        })
        .attr('stroke-width', 2)
      // .on("mouseover",d=>addVisible(d))
      // .on("mouseout", d=>addVisible(d))

      //进行字体的渲染
      //显示id
      nodeEnter.append('text')
        .attr('font-size', '0.7em')
        // 用三元运算符去判断,它如果有子节点的话,就是true,显示形式就用end,会显示在圆点的左边。这样的话根结点就在左边,子节点在右边。
        .attr('text-anchor', d => d._children ? 'end' : 'start')
        .attr('transform', function (d) {
          if (d.depth == 0) {
            return `translate(${-(10 * d.data.name.length) + 20},0)`
          }
          if (d.children) {
            return `translate(${-(6.5 * d.data.name.length)},0)`
          }
        })
        .attr('x', function (d) {
          if (d.depth == 0) {
            return d.y - 10;
          }
          if (d.children) {
            return d.y - 10 + d.depth * 76
          } else {
            return d.y + 55 * d.depth + 30
          }
        })
        .attr("dy", "0.31em")
        .html(d => "id:" + d.data.id)
        // .text(d=>{
        //   return "name:"+d.data.name
        // })
        .style("fill", "blue")
        .on("mouseover", function (d) {
          d3.select(this)
            .style('fill', 'orange')
        })
        .on("mouseout", function () {
          d3.select(this)
            .style('fill', 'blue')
        })
        .on("click", function (event, node) {
          let data = node.data
          // console.log(data)
          //节点展开状态
          if (data.children) {
            data.childrenTemp = data.children
            data.children = null
          } else {
            //节点折叠状态
            data.children = data.childrenTemp
            data.childrenTemp = null
          }
          that.initTree()
        })


      nodeEnter.append('line')
        .attr("x1", d => {
          if (d.depth == 0) {
            return d.y - 12 * d.data.name.length + 5
          }
          return null
        })
        .attr("y1", d => {
          if (d.depth == 0) {
            return d.x + 10
          }
          return null
        })
        .attr("x2", d => {
          if (d.depth == 0) {
            return d.y - 2
          }
          return null
        })
        .attr("y2", d => {
          if (d.depth == 0) {
            return d.x + 10
          }
          return null
        })
        .attr("stroke", "#5a6fc0")
        .attr("stroke-width", "3")
        .attr("opacity", 0.8)


      //显示name
      nodeEnter.append('text')
        .attr('font-size', '0.6em')
        .attr('transform', function (d) {
          if (d.depth == 0) {
            return `translate(${-(12 * d.data.name.length) + 20},22)`
          } else {
            return `translate(${-(d.data.name.length) + 8},12)`
          }
          // if(d.children){
          //   return `translate(${-(6.5*d.data.name.length)},10)`
          // }
        })
        .attr('x', function (d) {
          if (d.depth == 0) {
            return d.y - 10;
          }
          if (d.children) {
            return d.y - 10 + d.depth * 76
          } else {
            return d.y + 55 * d.depth + 30
          }
        })
        .attr("dy", "0.31em")
        // .html(d=>"name:"+d.data.name)
        .text(d => {
          return "name:" + d.data.name
        })
        .on("mouseover", function (d) {
          d3.select(this)
            .style('fill', 'orange')
        })
        .on("mouseout", function () {
          d3.select(this)
            .style('fill', 'black')
        })
        .on("click", function (event, node) {
          let data = node.data
          // console.log(data)
          //节点展开状态
          if (data.children) {
            data.childrenTemp = data.children
            data.children = null
          } else {
            //节点折叠状态
            data.children = data.childrenTemp
            data.childrenTemp = null
          }
          that.initTree()
        })

      //显示样本大小
      nodeEnter.append('text')
        .attr('font-size', '0.6em')
        .attr('transform', function (d) {
          if (d.depth == 0) {
            return `translate(${-(7 * d.data.name.length) + 20},22)`
          } else {
            return `translate(${-(d.data.name.length) + 8},22)`
          }
        })
        .attr('x', function (d) {
          if (d.depth == 0) {
            return d.y - 10;
          }
          if (d.children) {
            return d.y - 10 + d.depth * 76
          } else {
            return d.y + 55 * d.depth + 30
          }
        })
        .attr("dy", "0.31em")
        .text(d => {
          if (d.depth == 0) {
            return null
          }
          return "batch_size:" + d.data.batch_size
        })
        .on("mouseover", function (d) {
          d3.select(this)
            .style('fill', 'orange')
        })
        .on("mouseout", function () {
          d3.select(this)
            .style('fill', 'black')
        })
        .on("click", function (event, node) {
          let data = node.data
          // console.log(data)
          //节点展开状态
          if (data.children) {
            data.childrenTemp = data.children
            data.children = null
          } else {
            //节点折叠状态
            data.children = data.childrenTemp
            data.childrenTemp = null
          }
          that.initTree()
        })


      //显示循环次数
      nodeEnter.append('text')
        .attr('font-size', '0.6em')
        .attr('transform', function (d) {
          if (d.depth == 0) {
            return `translate(${-(7 * d.data.name.length) + 20},32)`
          } else {
            return `translate(${-(d.data.name.length) + 8},32)`
          }
          // if(d.children){
          //   return `translate(${-(6.5*d.data.name.length)},10)`
          // }
        })
        .attr('x', function (d) {
          if (d.depth == 0) {
            return d.y - 10;
          }
          if (d.children) {
            return d.y - 10 + d.depth * 76
          } else {
            return d.y + 55 * d.depth + 30
          }
        })
        .attr("dy", "0.31em")
        // .html(d=>"name:"+d.data.name)
        .text(d => {
          if (d.depth == 0) {
            return null
          }
          return "epoch:" + d.data.epoch
        })
        .on("mouseover", function (d) {
          d3.select(this)
            .style('fill', 'orange')
        })
        .on("mouseout", function () {
          d3.select(this)
            .style('fill', 'black')
        })
        .on("click", function (event, node) {
          let data = node.data
          // console.log(data)
          //节点展开状态
          if (data.children) {
            data.childrenTemp = data.children
            data.children = null
          } else {
            //节点折叠状态
            data.children = data.childrenTemp
            data.childrenTemp = null
          }
          that.initTree()
        })


      //显示剪枝率
      nodeEnter.append('text')
        .attr('font-size', '0.6em')
        .attr('transform', function (d) {
          if (d.depth == 0) {
            return `translate(${-(7 * d.data.name.length) + 20},42)`
          } else {
            return `translate(${-(d.data.name.length) + 8},42)`
          }
          // if(d.children){
          //   return `translate(${-(6.5*d.data.name.length)},10)`
          // }
        })
        .attr('x', function (d) {
          if (d.depth == 0) {
            return d.y - 10;
          }
          if (d.children) {
            return d.y - 10 + d.depth * 76
          } else {
            return d.y + 55 * d.depth + 30
          }
        })
        .attr("dy", "0.31em")
        // .html(d=>"name:"+d.data.name)
        .text(d => {
          if (d.depth == 0) {
            return null
          }
          return "pruning_ratio:" + d.data.pruning_ratio
        })
        .on("mouseover", function (d) {
          d3.select(this)
            .style('fill', 'orange')
        })
        .on("mouseout", function () {
          d3.select(this)
            .style('fill', 'black')
        })
        .on("click", function (event, node) {
          let data = node.data
          // console.log(data)
          //节点展开状态
          if (data.children) {
            data.childrenTemp = data.children
            data.children = null
          } else {
            //节点折叠状态
            data.children = data.childrenTemp
            data.childrenTemp = null
          }
          that.initTree()
        })
    },
    addNode () {
      var that = this
      var arrayObj = { id: this.id, name: "pruning" + this.id, epoch: this.epoch, pruning_ratio: this.pruningRatio, batch_size: this.batchSize }
      var data = this.treeData.children
      data.push(arrayObj)
      // console.log(data.push(arrayObj))
      data["id"] = this.id
      data["name"] = "pruning" + this.id
      data["epoch"] = this.epoch
      data["pruning_ratio"] = this.pruningRatio
      data["batch_size"] = this.batchSize
      // console.log(data)
      that.treeData.children = data
      this.initTree()
      this.epoch = ""
      this.batchSize = ""
      this.pruningRatio = ""
    }
  },


}
</script>

<style scoped lang="less">
.container {
  width: 100%;
  height: calc(~'100% - 30px');
  border-left: 2px solid #dddddd;
  border-right: 2px solid #dddddd;
  border-bottom: 2px solid #dddddd;
}
#myInput {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: left;
}
.paramInput {
  flex: 1;
  margin: 10px;
  /* display:flex;
    justify-content: center;
    align-items: center; */
}
.styleInput {
  background-color: #9fd3c7;
  border: none;
  /* flex:0.5; */
  text-align: center;
  font: italic bold 13px Georgia, serif;
}
.paramButtom {
  margin-left: 15%;
  margin-top: 4%;
}
#tree_history {
  /* border:1px solid red; */
  height: 100%;
  width: auto;
  justify-content: center;
  margin: -5px 10px 7px 10px;
  padding: 0px 10px;
}
#mainsvg {
  width: 100%;
  height: 100%;
}
</style>

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
你可以使用Vue.jsD3.js来实现组织架构横向树状图,并且可以自定义节点的样式。 首先,你需要安装Vue.jsD3.js。可以使用npm或yarn来安装它们: ``` npm install vue d3 ``` 或者 ``` yarn add vue d3 ``` 接下来,在你的Vue组件中,可以使用D3.js来绘制横向树状图。以下是一个简单的示例: ```html <template> <div id="chart"></div> </template> <script> import * as d3 from 'd3'; export default { mounted() { this.drawChart(); }, methods: { drawChart() { // 获取SVG容器 const svg = d3.select('#chart') .append('svg') .attr('width', 600) .attr('height', 400); // 构造树状图数据 const data = { name: 'CEO', children: [ { name: 'Manager' }, { name: 'Manager' }, { name: 'Manager' } ] }; // 创建树状图布局 const layout = d3.tree().size([400, 400]); // 转换数据为层次结构 const root = d3.hierarchy(data); const treeData = layout(root); // 创建节点和连线 const nodes = svg.selectAll('.node') .data(treeData.descendants()) .enter() .append('g') .attr('class', 'node') .attr('transform', d => `translate(${d.y},${d.x})`); nodes.append('circle') .attr('r', 10) .style('fill', 'steelblue') // 在这里可以自定义节点的样式 nodes.append('text') .attr('dy', 4) .attr('x', d => d.children ? -8 : 8) .style('text-anchor', d => d.children ? 'end' : 'start') .text(d => d.data.name); } } }; </script> <style> .node circle { fill: steelblue; } </style> ``` 上述代码中,使用D3.js创建了一个SVG容器,并在其中绘制了树状图。你可以通过自定义节点的样式,例如修改圆的颜色、大小等来实现标签的自定义。 这只是一个简单的示例,你可以根据你的需求进一步自定义和扩展。希望对你有帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值