d3.v3实现分层分类可拖拽图表效果代码
代码语言:html
所属分类:图表
代码描述: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