d3.v3实现分层分类可拖拽图表效果代码

代码语言:html

所属分类:图表

代码描述:d3.v3实现分层分类可拖拽图表效果代码

代码标签: d3.v3 分层 分类 拖拽 图表

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开

<!DOCTYPE html>
<html lang="en" >

<head>
  <meta charset="UTF-8">
  

  
<style>
.nodebox {
    fill: #fff;
    stroke: steelblue;
    stroke-width: 3.5px;
}
#node_PHYSICAL .nodebox {
    stroke: beige;
}
#node_PHYSICAL_HARD .nodebox {
    stroke: tan;
}
#node_PHYSICAL_SOFT .nodebox {
    stroke: yellow;
}
#node_BIOTA .nodebox {
    stroke: darkgreen;
}
#node_BIOTA_SPONGES .nodebox {
    stroke: orange;
}
#node_BIOTA_ALGAE .nodebox {
    stroke: green;
}
#node_BIOTA_ALGAE_CANOPY_ECK .nodebox {
    stroke: lightgreen;
}
#node_BIOTA_ALGAE_CRUSTOSE .nodebox {
    stroke: purple;
}
.nodeTitle {
    font: 8px sans-serif;
    font-weight: bold;
}
.nodeText {
    font: 8px sans-serif;
}
.f1Text {
    fill: orange;
    font-weight: bold;
}
.link {
    fill: none;
    stroke: #ccc;
    stroke-width: 3.5px;
}
</style>


  
  
</head>

<body translate="no">
  <div>This is not my code, just a port of <a href="" target="_blank">this</a> to codepen.</div>
<div>Drag to rearrange these nodes, then click
    <button onclick="return refresh();">refresh data</button> Some nodes will be replaced.
</div>

<script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/d3.v3.js"></script>
      <script  type="module">

function graph(d3) {
  //step 0, new graph() ,import "https://d3js.org/d3.v3.min.js" to get d3

  var svg;

  //step 1, custom the config
  this.config = {
    bg_size: {
      width: 800,
      height: 600 },

    edge_def_width: 5,
    edge_show_arrow: true,
    node_draggable: true,
    show_performance_bar: false };


  var self = this;
  var cluster = d3.layout.cluster().size([self.config.bg_size.height, self.config.bg_size.width - 160]);


  /// step 2, custom the actions
  var showTitleAction;
  var showSubheadAction;
  var showPathDesc;

  this.showTitle = function (f) {
    showTitleAction = f;
  };

  this.showSubhead = function (f) {
    showSubheadAction = f;
  };

  this.showPathDesc = function (f) {
    showPathDesc = f;
  };


  /// final step , bind some data

  this.bind = function (data) {

    /**
    忽略连通图中的回路,产生一棵树。
    这棵树符合cluster.nodes(tree)的调用要求(参见:https://github.com/mbostock/d3/wiki/Cluster-Layout)    
    */
    var conv2tree = function (data) {
      var root = self.getRoot(data);
      var hasParentFlag = {}; //保证每个节点只有一个父节点,以便形成树状结构
      hasParentFlag[root.id] = true; //根节点不允许作为子节点
      self.traverseEdge(data, function (source, target) {//遍历每条边,即所有节点间关系
        if (!hasParentFlag[target.id] && source.id != target.id) {//首次被遍历到的target,作为source的子节点,后续将不被其它节点作为子节点
          if (!source.children) {
            source.children = [];
          }
          source.children.push(target);
          hasParentFlag[target.id] = true;
        }
      });
      return root;
    };


    /**
            通过cluster.nodes(tree),为tree的每个节点计算x,y,depth等属性以便定位              
    */
    var buildNodes = function (tree) {
      return cluster.nodes(tree);
    };

    /**
    建立节点之间各条边。
    如果直接调用cluster.links(nodes),其只支持树状结构,回路会被丢弃,借此把所有边补充完整。
    */
    var buildLinks = function (data) {
      var result = [];
      self.traverseEdge(data, function (source, target, ref) {
        result.push({
          'source': source,
          'target': target,
          'ref': ref });

      });
      return result;
    };

    /**
    更新数据时保留原有节点的位置信息
    */
    var merge = function (nodes, links) {

      var oldData = [];
      if (self.nodes) {//原nodes存在,输出oldData
        self.nodes.forEach(function (d) {
          oldData[d.id] = d;
        });
      }
      if (oldData) {//用oldData里的数据覆盖现nodes里的数据
        nodes.forEach(function (d) {
          if (oldData[d.id]) {
            d.x = oldData[d.id].x;
            d.y = oldData[d.id].y;
          }
        });
      }


      self.nodes = nodes;
      self.links = links;

    };

    //1)连通图->树 参见:https://github.com/mbostock/d3/wiki/Cluster-Layout)       
    //1)temporarily convert a connectivity to a tree        
    var tree = conv2tree(data);
    //2)根据树状结构计算节点位置.
    //2)caculate for nodes' coords with <code>cluster.nodes(tree);</code>
    var nodes = buildNodes(tree);
    //3)因为连通图是网状而非树状,将所有边补充完整
    //3)fill in all the edges(links) of the connectivity
    var links = buildLinks(data);
    //4)与原有的数据做一次merge,保留位置等信息
    //4)do merge to keep info like node's position
    merge(nodes, links);
    //5)重绘
    //5)redraw      
    self.redraw();
  };


  /// call redraw() if necessary (reconfig,recostom the actions, etc. )
  this.redraw = function () {
    var fontSize = 8;
    var lineSpace = 2;
    var boxHeight = 50;
    var boxWidth = 85;

    var width = self.config.bg_size.width;
    var height = self.config.bg_size.height;

    var yscale_performancebar = d3.scale.linear().
    domain([0, 1]).
    rangeRound([boxHeight / 2, -boxHeight / 2]);


    var diagonal = d3.svg.diagonal().
    projection(function (d) {
      return [d.y - boxWidth / 2, d.x];
    });


    var _clear = function () {
      d3.select("svg").remove();

      svg = d3.select("body").append("svg").
      attr("width", width).
      attr("height", height).
      append("g").
      attr("transform", "translate(80,0)");

      svg.append("svg:defs").selectAll("marker").
      data(["suit"]).
      enter().append("svg:marker").
      attr("id", "idArrow").
      attr("viewBox", "0 -5 10 10").
      attr("refX", 15).
      attr("refY", -1.5).
      attr("markerWidth", 6).
      attr("markerHeight", 6).
      attr("orient", "auto").
      append("svg:path").
      attr("d", "M0,-5L10,0L0,5");
    };

    var _redrawEdges = function () {


      var linksWithArrow = self.links;

      //to show arrow at the end of the path with fixed size, we have to copy each path with .stroke-width=1 
      if (self.config.edge_show_arrow) {
        linksWithArrow = [];
        self.links.forEach(function (d) {

          var fake = {};

          for (var prop in d) {
            fake[prop] = d[prop];
          }

          fake.faked = true; //copy each path with .faked=true as flag

          linksWithArrow.push(fake);
          linksWithArrow.push(d);


        });
      }



      var path = svg.selectAll(".link").data(linksWithArrow);


      // when new path arrives
      path.enter().insert("path", ":first-child").
      attr("marker-end", function (d) {
        if (d.faked) return "url(#idArrow)";
      }).
      attr("id", function (d) {
        if (!d.faked) return "link" + d.ref.from + "-" + d.ref.to;
      }).
      attr("class", function (d) {
        return "link" + " link-" + d.ref.from + " link-" + d.ref.to;
      }).
      attr("d", diagonal).
      transition().
      duration(1000).
      style("stroke-width", function (d) {
        if (d.faked) {
          return 1;
        }
        if (d.ref.edge_width) return Math.max(1, boxHeight / 2 * d.ref.edge_width); //won't become invisible if too thin
        else return self.config.edge_def_width; //default value
      });

      // when path changes
      path.attr("d", diagonal);

      // when path's removed
      path.exit().remove();




    };


    _clear();

    _redrawEdges();

    ///show description on each path(edge)
    if (showPathDesc) {
      svg.selectAll(".abc").data(self.links).enter().append("text").append("textPath").
      attr("xlink:xlink:href", function (d) {
        return "#link" + d.ref.from + "-" + d.ref.to;
      }) //why not .attr("xlink:href",...)? this's a hack, see https://groups.google.com/forum/?fromgroups=#!topic/d3-js/vLgbiM4ki1g                       
      .attr("startOffset", "50%").
      text(showPathDesc);
    }

    ///show each node with text

    var existingNodes = svg.selectAll(".node").data(self.nodes); //选中所有节点

    //矩形
    //draw rectangle
    var newNodes = existingNodes.enter().append("g");

    newNodes.attr("class", "node").
    attr("id", function (d) {
      return "node-" + d.id;
    }).
    attr("transform", function (d) {
      return "translate(" + d.y + "," + d.x + ")";
    })
    //.append("rect") //make nodes as rectangles OR:
    .append("circle").attr('r', 50) //make nodes as circles
    .attr('class', 'nodebox').
    attr("x", -boxWidth / 2).
    attr("y", -boxHeight / 2).
    attr("width", boxWidth).
    attr("height", boxHeight);





    if (self.config.node_draggable) {
      newNodes.call(d3.behavior.drag().origin(Object).on("drag", function (d) {

        //拖动时移动节点
        //translate the node
        function translate(x, y) {
          return {
            'x': x,
            'y': y };

        }
        var coord = eval(d3.select(this).attr("transform"));
        d3.select(this).
        attr("transform", "translate(" + (coord.x + d3.event.dx) + "," + (coord.y + d3.event.dy) + ")");


        //拖动时重绘边                        
        //update node's coord ,then redraw affected edges
        d.x = d.x + d3.event.dy;
        d.y = d.y + d3.event.dx;

        _redrawEdges();


      }));
    }



    //红色柱状性能指示图
    //show performance bar
    if (self.config.show_performance_bar) {
      newNodes.append("rect").
      attr('class.........完整代码请登录后点击上方下载按钮下载查看

网友评论0