美文网首页js css html
d3绘制树形图, 初始化居中,可拖拽,点击某个节点也会以这个节点

d3绘制树形图, 初始化居中,可拖拽,点击某个节点也会以这个节点

作者: littlesunn | 来源:发表于2022-07-02 03:56 被阅读0次
    1656706432578.gif

    svg 绘制树形图, 功能点有:
    1.初始化居中
    2.可拖拽
    3.点击某个节点也会以这个节点居中
    4.悬浮某个节点出现tooltip
    5.path动画描边

    当你学了svg和d3的一些基础之后再来看这个案例,其中包含的知识点非常多,如果能全部掌握,相信你对d3的了解更上一层楼啦
    当然你可以不学,直接复制粘贴,也是可以用的~

    npm i d3 --save

    <template>
      <div id="tree" :class="['tree-container']">
      </div>
    </template>
    <script>
    import * as d3 from "d3"
    const curTranslate = {
      x: 0,
      y: 0
    }
    export default {
      data() {
        return {
          dataset: {
            cname: "主节点",
            children: [{
              cname: "子节点1",
              children: [{
                cname: "孙子点1"
              }, {
                cname: "孙子点2"
              }]
            }, {
              cname: "子节点2",
            }, {
              cname: "子节点3",
            }],
          },
          gsize: null,
        }
      },
      mounted() {
        this.initTree();
      },
      methods: {
        initTree() {
          var timer;
          let treeContainer = document.getElementById("tree");
          var width = treeContainer.clientWidth;
          var height = treeContainer.clientHeight;
          var preTranslate = [0, 0];
          var svg = d3
            .select("#tree")
            .append("svg")
            .attr("width", width + "px")
            .attr("height", height + "px")
    
          var treeGroup = svg
            .append("g")
            .attr("transition", ".2s")
          let zoom = d3.zoom() // 拖拽与缩放
            .scaleExtent([.1, 10])
            .on("zoom", zoomed);
          svg.call(zoom);
    
          function zoomed(event) {
            let transofrm = d3.zoomTransform(this)
            treeGroup.attr("transform", transofrm.translate(curTranslate.x, curTranslate.y));
          }
          var hierarchyData = d3.hierarchy(this.dataset).sum(function (d) {
            return d.value;
          });
    
          var tree = d3
            .tree()
            .nodeSize([width / 6, height / 6.5])  // 以node为参考缩放,size ()以容器大小缩放,node节点过多会叠加
            .separation(function (a, b) { //定义邻居节点的距离
              return 1
            });
          var treeData = tree(hierarchyData); //获取布 局数据
          var toolsWrap = svg.append("g").attr("id", 'toolsWrap').attr("style", "display: none;position:relative");
    
          toolsWrap.append('rect').attr('width', 50).attr('height', 15).attr('fill', "#000").on("click", function () {
            toolsWrap.attr("transform", `translate(0, 0)`).attr("style", "display: none;")
          })
          toolsWrap.append('rect').attr('width', 50).attr('height', 15).attr('fill', "red").attr("x", 50)
          toolsWrap.append('rect').attr('width', 50).attr('height', 15).attr('fill', "green").attr("x", 100)
    
          var nodes = treeData.descendants(); // 节点数据
          var links = treeData.links(); //连线数据
          treeGroup.append("g")
            .selectAll("path") // 创建节点之间的连线
            .data(links)
            .enter()
            .append("path")
            .attr("d", function (d) {
              var start = { x: d.source.y, y: d.source.x };
              var end = { x: d.target.y, y: d.target.x };
              return `M${start.y},${start.x}L${start.y},${end.x - 30}L${end.y},${end.x - 30}L${end.y},${end.x}`
            })
            .attr("fill", "none")
            .attr("stroke", "#999")
            .attr("stroke-width", 2)
            .attr("stroke-dasharray", function (d) {
              return this.getTotalLength()
            })
            .attr("stroke-dashoffset", function (d) {
              return this.getTotalLength()
            })
            .transition()
            .duration(1500)
            .attr("stroke-dashoffset", 0).delay(function (d) {
              return d.source.depth * 800
            });
    
          var gs = treeGroup.append("g") //生成每个节点
            .selectAll("g")
            .data(nodes)
            .enter()
            .append("g")
            .attr("style", "cursor: pointer;")
            .attr("transform", function (d) {
              var cx = d.x;
              var cy = d.y;
              return `translate(${cx},${cy})`;
            })
            .on("click", function (e) {
              let point = this.getBoundingClientRect(); // 被点击节点的dom信息
              let gsize = treeGroup.node().getBoundingClientRect(); // 整个group的信息
              preTranslate = treeGroup.attr('transform').match(/translate\(.*\)/g)[0].replace("translate(", "").replace(")", "").split(".");
    
              d3.zoomTransform(this).x = (gsize.x + width / 2 - (point.x + point.width / 2) + gsize.width / 2)
              d3.zoomTransform(this).y = (gsize.y + height / 2 - point.y)
              curTranslate.x = 0
              curTranslate.y = 0
    
              let curScale = d3.zoomTransform(this).k;
              treeGroup
                .transition()
                .attr('transform', `translate(${gsize.x + width / 2 - (point.x + point.width / 2) + gsize.width / 2}, ${gsize.y + height / 2 - point.y}) scale(${curScale})`) // 将被点击的节点居中显示
            })
            .on("mouseenter", function (d, level) {
              if (timer) {
                clearTimeout(timer)
              }
              let curScale = d3.zoomTransform(svg).k || 0;
              toolsWrap.attr("transform", `translate(0,0) scale(${curScale})`).attr("style", "display: block;")
              this.appendChild(toolsWrap.node())
              let bbox = this.getBBox(); // bbox 不会因为放大而宽高变化
              console.log(bbox);
              toolsWrap.attr("transform", `translate(${bbox.x}, ${bbox.height - 4}) scale(${curScale})`).attr("style", "display: block;")
            })
            .on("mousemove", function (d, v) {
              // let point = this.getBoundingClientRect();
            }).on("mouseleave", function () {
              timer = setTimeout(() => {
                toolsWrap.attr("transform", `translate(0, 0)`).attr("style", "display: none;")
              }, 1000)
            })
    
          gs.append("circle") // 创建节点的圆点
            .attr("r", 4)
            .attr("fill", "#bae637")
            .attr("stroke", "#000")
            .attr("stroke-width", 1)
    
          gs.append("text") // 创建节点对应的文本内容
            .attr("y", 20)
            .attr("dy", 10)
            .attr("fill", function (d) {
              return "orange"
            })
            .attr("font-size", 18)
            .attr("style", (d) => {
              return !d.data.mainNode ? `transform : scale(${.8})` : ""
            })
            .text(function (d) {
              return d.data.cname;
            }).attr("x", function (d) {
              let bbox = this.getBBox(); // 居中对齐
              return -bbox.width / 2
            }).each(function (d) {
              d3.select(this.parentNode)
    
                .insert("rect")
                .attr("width", this.getBBox().width + 20)
                .attr("height", this.getBBox().height + 5)
                .attr("fill", d => {
                  return "#000"
                })
                .attr("stroke", d => {
                  return "#333"
                })
                .attr("x", function (d) {
                  let bbox = this.getBBox(); //居中对齐
                  return -bbox.width / 2
                })
                .attr("y", 9).each(function (d) {
                  this.parentNode.insertBefore(this, this.parentNode.children[0])
                })
                .attr("style", !d.data.mainNode ? `transform: scale(${.8})` : "")
            })
          let gsize = treeGroup.node().getBoundingClientRect();
          curTranslate.x = width / 2;
          curTranslate.y = height / 2 - gsize.height / 2;
          treeGroup.attr("transform", `translate(${curTranslate.x}, ${curTranslate.y})`)
        },
        update() {  // 重绘
          d3.selectAll("svg").remove()
          this.initTree();
        },
      },
    }
    </script>
    <style scoped lang="scss" >
    .tree-container {
      position: relative;
      width: 800px;
      height: 600px;
      user-select: none;
      border: 1px solid;
      background: linear- gradient(to top, transparent 12px, #d9d9d9 12px),
        linear-gradient(to left, transparent 12px, #d9d9d9 13px);
      background-size: 13px 13px;
    }
    </style>
    

    相关文章

      网友评论

        本文标题:d3绘制树形图, 初始化居中,可拖拽,点击某个节点也会以这个节点

        本文链接:https://www.haomeiwen.com/subject/byvhbrtx.html