<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>D3 layout tree</title> <style type="text/css"> #body { width: 1200px; margin: 0 auto; position: relative; } rect { fill: none; pointer-events: all; } pre { font-size: 18px; } line { stroke: #0E0; stroke-width: 1.5px; } .string, .regexp { color: #f39; } .keyword { color: #00c; } .comment { color: #777; font-style: oblique; } .number { color: #369; } .class, .special { color: #1181B8; } a:link, a:visited { color: steelblue; text-decoration: none; } a:hover { color: #666; } .node circle { cursor: pointer; fill: #fff; stroke: steelblue; stroke-width: 1.5px; } .node text { font-size: 12px; } path.link { fill: none; stroke: #ccc; stroke-width: 2.0px; } </style> </head> <body> <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> <div id="body"> </div> <script type="text/javascript"> var json_data = { "name": " ", "children": [ { "name": "Interactive tools", "free": true, "description": "Interactive authoring tools", "children": [ { "name": "Browser-based", "description": "Web-based 'cloud' applications for authoring data visualisations", "free": true, "children": [ { "name": "Datawrapper", "description": "An open-source platform for publishing charts on the web. Cloud-based or self-hosted.", "url": "https://datawrapper.de/", "free": true }, { "name": "Google Sheets", "description": "Spreadsheet in the cloud with charting", "free": true }, { "name": "plotly", "description": "Cloud-based interactive tool for creating data visualisations", "url": "https://plot.ly/", "free": true }, { "name": "RAW", "description": "Open-source interactive tool for creating and exporting D3-like charts", "url": "http://raw.densitydesign.org/", "free": true } ] }, { "name": "Desktop", "children": [ { "name": "Tableau Desktop", "description": "Powerful tool for data analytics and visualisation", "url": "http://www.tableausoftware.com/products/desktop" }, { "name": "Tableau Public", "description": "Free version of Tableau Desktop where charts are public", "url": "http://www.tableausoftware.com/products/public", "free": true } ] } ] }, { "name": "Coding", "description": "Code-based data visualisation creation", "free": true, "children": [ { "name": "JavaScript", "description": "The language behind most (all?) browser-based data visualisations", "free": true, "children": [ { "name": "Charting libraries", "description": "Off-the-shelf pre-designed charts. Easy to use but less flexible.", "free": true, "children": [ { "name": "Google Charts", "description": "A good selection of charts including bar, line, scatter, geo, pie, donut, org etc.", "url": "https://developers.google.com/chart/", "free": true }, { "name": "HighCharts", "description": "A well maintained commercial library of commonly used chart types", "url": "http://www.highcharts.com/" }, { "name": "InfoVis", "description": "A lovely selection of charts including bar, pie, sunburst, icicle, network, trees etc.", "url": "http://philogb.github.io/jit/", "free": true }, { "name": "Mapping", "description": "Libraries for visualising geographic data", "free": true, "children": [ { "name": "Kartograph", "description": "Lovely vector based mapping library with good browser support", "url": "http://kartograph.org/", "free": true }, { "name": "Leaflet", "description": "Tile-based mapping library", "url": "http://leafletjs.com/", "free": true } ] }, { "name": "MetricsGraphics.js", "description": "Beautiful line, scatter and histogram charts built on top of D3", "url": "http://metricsgraphicsjs.org/", "free": true }, { "name": "NVD3", "description": "A general purpose charting library built on top of D3", "url": "http://nvd3.org/", "free": true }, { "name": "Sigma", "description": "Library for visualising networks", "url": "http://sigmajs.org/", "free": true } ] }, { "name": "Custom coded", "description": "For maximum flexibility, custom coding is the way to go. These libraries will lend a hand.", "free": true, "children": [ { "name": "D3", "description": "The jewel in the crown of web-based data visualisation. A library packed full of components for building any data visualisation you can imagine.", "url": "http://d3js.org/", "free": true }, { "name": "Ractive", "description": "Relatively new, Ractive helps you make your HTML and SVG interactive", "url": "http://www.ractivejs.org/", "free": true }, { "name": "Raphaël", "description": "A general purpose drawing library with good browser support", "url": "http://raphaeljs.com/", "free": true }, { "name": "Snap.svg", "description": "A modern version of Raphaël that supports modern browsers", "url": "http://snapsvg.io/", "free": true }, { "name": "Variance", "description": "A declarative, mark-up based data visualisation library", "url": "https://variancecharts.com/" }, { "name": "Vega", "description": "A declarative language for specifying data visualistions", "url": "http://trifacta.github.io/vega/", "free": true } ] } ] }, { "name": "Other", "description": "Non-JavaScript languages for producing web-based data visualisations", "free": true, "children": [ { "name": "Python", "description": "Python's a very popular language in data science and is a pleasant language to learn and use", "free": true, "children": [ { "name": "Bokeh", "description": "A powerful tool for producing interactive plots, dashboards and data applications", "url": "http://bokeh.pydata.org/", "free": true } ] }, { "name": "R", "description": "Very popular language for data science", "free": true, "children": [ { "name": "Shiny", "description": "A platform for producing web applications using R", "url": "http://shiny.rstudio.com/", "free": true } ] } ] } ] } ] } var m = [20, 120, 20, 20], w = 1280 - m[1] - m[3], h = 800 - m[0] - m[2], i = 0, root; var tree = d3.layout.tree() .size([h, w]); var diagonal = d3.svg.diagonal() .projection(function(d) { return [d.y, d.x]; }); var vis = d3.select("#body").append("svg:svg") .attr("width", w + m[1] + m[3]) .attr("height", h + m[0] + m[2]) .append("svg:g") .attr("transform", "translate(" + m[3] + "," + m[0] + ")"); root = json_data; root.x0 = h / 2; root.y0 = 0; function toggleAll(d) { if (d.children) { d.children.forEach(toggleAll); toggle(d); } } // Initialize the display to show a few nodes. // root.children.forEach(toggleAll); // toggle(root.children[1]); // toggle(root.children[1].children[2]); // toggle(root.children[9]); // toggle(root.children[9].children[0]); update(root); function update(source) { var duration = d3.event && d3.event.altKey ? 5000 : 500; // Compute the new tree layout. var nodes = tree.nodes(root).reverse(); // Normalize for fixed-depth. nodes.forEach(function(d) { d.y = d.depth * 180; }); // Update the nodes… var node = vis.selectAll("g.node") .data(nodes, function(d) { return d.id || (d.id = ++i); }); // Enter any new nodes at the parent's previous position. var nodeEnter = node.enter().append("svg:g") .attr("class", "node") .attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; }) .on("click", function(d) { toggle(d); update(d); }); nodeEnter.append("svg:circle") .attr("r", 1e-6) .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; }); nodeEnter.append('a') .attr('xlink:href', function(d) { return d.url; }) .append("svg:text") .attr("x", function(d) { return d.children || d._children ? -10 : 10; }) .attr("dy", ".35em") .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; }) .text(function(d) { return d.name; }) .style('fill', function(d) { return d.free ? 'black' : '#999'; }) .style("fill-opacity", 1e-6); nodeEnter.append("svg:title") .text(function(d) { return d.description; }); // Transition nodes to their new position. var nodeUpdate = node.transition() .duration(duration) .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; }); nodeUpdate.select("circle") .attr("r", 6) .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; }); nodeUpdate.select("text") .style("fill-opacity", 1); // Transition exiting nodes to the parent's new position. var nodeExit = node.exit().transition() .duration(duration) .attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; }) .remove(); nodeExit.select("circle") .attr("r", 1e-6); nodeExit.select("text") .style("fill-opacity", 1e-6); // Update the links… var link = vis.selectAll("path.link") .data(tree.links(nodes), function(d) { return d.target.id; }); // Enter any new links at the parent's previous position. link.enter().insert("svg:path", "g") .attr("class", "link") .attr("d", function(d) { var o = {x: source.x0, y: source.y0}; return diagonal({source: o, target: o}); }) .transition() .duration(duration) .attr("d", diagonal); // Transition links to their new position. link.transition() .duration(duration) .attr("d", diagonal); // Transition exiting nodes to the parent's new position. link.exit().transition() .duration(duration) .attr("d", function(d) { var o = {x: source.x, y: source.y}; return diagonal({source: o, target: o}); }) .remove(); // Stash the old positions for transition. nodes.forEach(function(d) { d.x0 = d.x; d.y0 = d.y; }); } // Toggle children. function toggle(d) { if (d.children) { d._children = d.children; d.children = null; } else { d.children = d._children; d._children = null; } } </script> </body> </html> 提示:你可以先修改部分代码再运行。 下面这个貌似要拿到本地看 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>think tree</title> <style type="text/css"> html { overflow: hidden; touch-action: none; content-zooming: none; } body { margin: 0; padding: 0; background: #eee; position: absolute; width: 100%; height: 100%; } #screen { position:absolute; width: 100%; height: 100%; background: #ddd; overflow: hidden; user-select: none; cursor: default; } #tree { display: none; } </style> </head> <body> <div id="screen"></div> <q id="tree">I think <q>I have <q>not <q>yet spoken.</q> <q>many opinions.</q> <q>enough soul <q>to understand them.</q> </q> </q> <q>seen <q>two of them die. <q>They were full of horror, <q>but silent.</q> </q> </q> <q>bright minds <q>express astonishment.</q> </q> <q>that all of them <q>without exception <q>suggested escape.</q> </q> </q> </q> <q>said that <q>it existed.</q> <q>we were not alone.</q> <q>there is no choice.</q> </q> </q> <q>I am <q>speaking <q>here of <q>difficult places to retreat.</q> <q>men enclined <q>to be in harmony <q>with themselves</q> </q> </q> <q>decency.</q> </q> </q> <q>convinced <q>that this ignorance <q>explains everything, <q>and that in darkness <q>is my light.</q> </q> </q> </q> <q>that human existance <q>is an utter absurdity <q>for anyone without faith <q>in immortality.</q> </q> </q> </q> </q> <q>going to <q>introduce hope <q>in a strange form.</q> </q> <q>escape by the leap.</q> <q>keep to myself.</q> </q> </q> <q>I know <q>what <q>I cannot deny.</q> <q>links them.</q> <q>they <q>have in common.</q> <q>need.</q> <q>are not.</q> </q> </q> <q>that <q>the absurd cannot be settled.</q> <q>one can compromise.</q> <q>it is impossible for me to know.</q> </q> <q>it implies <q>obscurity <q>and <q>ignorance.</q> </q> </q> </q> </q> </q> </body> <script>// screen / pointer mini library // last modified: 01 August 2015 by ge1doot var ge1doot = ge1doot || {}; ge1doot.screen = function(id) { "use strict"; var screen = { width:0, height:0, left:0, top: 0, elem: document.getElementById(id) }; screen.elem.onselectstart = function() { return false; } screen.elem.ondragstart = function() { return false; } screen.setSize = function() { var o = this.elem; var w = this.elem.offsetWidth * 1; var h = this.elem.offsetHeight * 1; if (w != this.width || h != this.height) { for (this.left = 0, this.top = 0; o != null; o = o.offsetParent) { this.left += o.offsetLeft; this.top += o.offsetTop; } this.width = this.elem.width = w; this.height = this.elem.height = h; this.resize && this.resize(); } } window.addEventListener('resize', screen.setSize.bind(screen), false); screen.setSize(); screen.pointer = { x: 0, y: 0, dx: 0, dy: 0, startX: 0, startY: 0, screen: screen, touchMode: false, isDown: false, sweeping: false } var started = false, endX = 0, endY = 0; var addEvent = function (elem, e, fn) { for (var i = 0, events = e.split(','); i < events.length; i++) { elem.addEventListener(events[i], fn.bind(screen.pointer), false ); } } var distance = function (dx, dy) { return Math.sqrt(dx * dx + dy * dy); } addEvent(window, "mousemove,touchmove", function (e) { e.preventDefault(); this.touchMode = e.targetTouches; var pointer = this.touchMode ? this.touchMode[0] : e; this.x = pointer.clientX - this.screen.left; this.y = pointer.clientY - this.screen.top; if (started) { this.sweeping = true; this.dx = endX - (this.x - this.startX); this.dy = endY - (this.y - this.startY); } if (this.move) this.move(e); }); addEvent(screen.elem, "mousedown,touchstart", function (e) { e.preventDefault(); this.touchMode = e.targetTouches; var pointer = this.touchMode ? this.touchMode[0] : e; this.startX = this.x = pointer.clientX - this.screen.left; this.startY = this.y = pointer.clientY - this.screen.top; started = true; this.isDown = true; if (this.down) this.down(e); setTimeout(function () { if (!started && Math.abs(this.startX - this.x) < 11 && Math.abs(this.startY - this.y) < 11) { if (this.tap) this.tap(e); } }.bind(this), 200); }); addEvent(window, "mouseup,touchend,touchcancel", function (e) { e.preventDefault(); if (started) { endX = this.dx; endY = this.dy; started = false; this.isDown = false; if (this.up) this.up(e); this.sweeping = false; } }); return screen; }</script> <script>// mini SVG library // last modified: 1 August 2015 by ge1doot var ge1doot = ge1doot || {}; ge1doot.SVGLib = function(o, antialias) { "use strict"; var __SVG = false; var __svgNS = false; if (document.createElementNS) { var __svgNS = "http://www.w3.org/2000/svg"; var __svg = document.createElementNS(__svgNS, "svg"); var __SVG = (__svg.x != null); } else return false; this.screen = document.createElementNS(__svgNS, "svg"); this.screen.style.position = "absolute"; this.screen.style.width = this.screen.style.height = "100%"; o.appendChild(this.screen); this.createLine = function (w, options) { var o = document.createElementNS(__svgNS, "line"); o.setAttribute("shape-rendering", antialias?"auto":"optimizeSpeed"); o.setAttribute("stroke-width", Math.round(w)+"px"); if (options.color) o.setAttribute("stroke", options.color); if (options.linecap) o.setAttribute("stroke-linecap", options.linecap); if (options.dasharray) o.setAttribute("stroke-dasharray", options.dasharray); o.move = function (x1, y1, x2, y2) { this.setAttribute("x1", x1); this.setAttribute("y1", y1); this.setAttribute("x2", x2); this.setAttribute("y2", y2); } o.color = function (c) { this.setAttribute("stroke", c); } o.RGBcolor = function (R, G, B) { this.setAttribute("stroke", "rgb("+Math.round(R)+","+Math.round(G)+","+Math.round(B)+")"); } o.strokeWidth = function (s) { this.setAttribute("stroke-width", Math.round(s)+"px"); } this.screen.appendChild(o); return o; } this.createText = function (text, font, size, col) { var o = document.createElementNS(__svgNS, "text"); o.setAttribute("shape-rendering", antialias?"auto":"optimizeSpeed"); o.textContent = text; if (col) o.setAttribute("fill", col); if (size) o.setAttribute("font-size", size); if (font) o.setAttribute("font-family", font); o.move = function (x, y) { this.setAttribute("x", x); this.setAttribute("y", y); } o.fillColor = function (c) { this.setAttribute("fill", c); } o.fontSize = function (c) { this.setAttribute("font-size", c); } o.fontWeight = function (c) { this.setAttribute("font-weight", c); } this.screen.appendChild(o); return o; } this.createOval = function (diam, filled) { var o = document.createElementNS(__svgNS, "circle"); o.setAttribute("shape-rendering", antialias?"auto":"optimizeSpeed"); o.setAttribute("stroke-width", 0); o.setAttribute("r", Math.round(diam / 2)); o.style.cursor = "pointer"; o.move = function (x1, y1, radius) { this.setAttribute("cx", x1); this.setAttribute("cy", y1); this.setAttribute("r", radius); } o.strokeColor = function (col) { this.setAttribute("stroke", col); } o.fillColor = function (col) { this.setAttribute("fill", col); } o.strokeWidth = function (sw) { this.setAttribute("stroke-width", sw); } this.screen.appendChild(o); return o; } }</script> <script type="text/javascript"> ! function () { "use strict"; var setup = { id: "tree", friction: 2, length: 300, reduction: 1.33, dotSize: 20, rotationSpeed: 0.002, collapsedNodeColor: "#fff", defaultNodeColor: "#f00", expandedNodeColor: "#666", line: {color: "#aaa", dasharray: "1,1"}, defaultNodeStrokeColor: "#aaa", overNodeColor: "#f00", defaultTextColor: "#888", selectedTextColor: "#000", textFont: "Abel, tempus sans itc, cursive, Helvetica, sans-serif" } // ==== node constructor ==== var Node = function (parent, label, col, link) { this.link = link; this.col = col; this.pR = 0; this.len = 0; this.lex = 0; this.angle = 0; this.expanded = false; this.children = []; this.parent = parent; this.visible = false; this.x = 0; this.y = 0; this.ini = { len: 0, lex: 0, angle: 0, parent: parent } if (parent != "") { // push child parent.children.push(this); // calculate lengths & angles var a = (2 * Math.PI) / parent.children.length; var b = (parent != "") ? Math.random():0; for (var i in parent.children) { var c = parent.children[i]; c.angle = c.ini.angle = Math.PI / 2 + a * i + b; c.len = c.ini.len = c.parent.ini.len / setup.reduction; } } else { // root this.visible = true; this.ini.len = setup.length * setup.reduction; } // create line & text elements this.line = svg.createLine(1, setup.line); this.text = svg.createText(label, setup.textFont, false, setup.defaultTextColor); } // create plot (separately > z-index) Node.prototype.createPlot = function () { this.pR = Math.round(Math.max(5, setup.dotSize * this.ini.len / 200)); this.plot = svg.createOval(this.pR * 2, true); this.plot.strokeColor(setup.defaultNodeStrokeColor); this.plot.strokeWidth(1); this.plot.obj = this; // font size this.text.fontSize(8 + this.pR); } // main animation Node.prototype.run = function () { if (this.visible) { // parent coordinates var xp = this.parent ? this.parent.x : drag.x; var yp = this.parent ? this.parent.y : drag.y; // trigo var a = Math.atan2( (this.y + Math.sin(this.angle + rotation) * setup.friction) - yp, (this.x + Math.cos(this.angle + rotation) * setup.friction) - xp ); if (this.lex < this.len) this.lex += (this.len - this.lex) * .1; this.x = xp + Math.cos(a) * this.lex; this.y = yp + Math.sin(a) * this.lex; // screen limits if (this.x < this.pR) this.x = this.pR; else if (this.x > screen.width - this.pR) this.x = screen.width - this.pR; if (this.y < this.pR) this.y = this.pR; else if (this.y > screen.height - this.pR) this.y = screen.height - this.pR; // move elements this.line.move(this.x, this.y, xp, yp); this.plot.move(this.x, this.y, this.pR); this.text.move(this.x + this.pR + 5, this.y + this.pR * 0.25); } } // collapse node Node.prototype.collapse = function () { this.expanded = false; this.text.fillColor(this.parent && this.parent.children.length == 1 ? setup.selectedTextColor : setup.defaultTextColor); this.text.fontWeight(this.parent && this.parent.children.length == 1 ? "bold" : "normal"); this.plot.fillColor((this.children.length) ? setup.collapsedNodeColor : this.col); for (var i = 0; i < this.children.length; i++) { var c = this.children[i]; c.visible = false; c.lex = 0; c.line.move(-1, -1, -1, -2); c.plot.move(-1000, -1, 0); c.text.move(-1000,0); c.expanded = false; c.collapse(); } } // expand node Node.prototype.expand = function () { // close all other branches if (this.ini.parent != "") { for (var i = 0; i < this.ini.parent.children.length; i++) { this.ini.parent.children[i].collapse(); } } // expand this.expanded = true; this.text.fillColor(setup.selectedTextColor); this.text.fontWeight("bold"); this.plot.fillColor(setup.expandedNodeColor); for (var i = 0; i < this.children.length; i++) { this.children[i].visible = true; this.children[i].lex = 0; } } // down Node.prototype.down = function () { if (pointer.isDraging) return; // offset mouse drag.ox = pointer.x - this.x; drag.oy = pointer.y - this.y; // change root if (drag.node != this) { // expanded color this.text.fillColor(setup.selectedTextColor); this.plot.fillColor(setup.expandedNodeColor); // reset var i = 0, node; while ( node = nodes[i++]) { node.parent = node.ini.parent; node.len = node.ini.len; node.lex = node.ini.len; node.angle = node.ini.angle; } // search root path var oc = []; var ow = this; oc.push(ow); while(ow.parent != "") { ow = ow.parent; oc.push(ow); } // inverse vectors for (var i = 1; i < oc.length; i++) { oc[i].lex = oc[i-1].ini.len; oc[i].len = oc[i-1].ini.len; oc[i].angle = oc[i-1].ini.angle - Math.PI; oc[i].parent = oc[i-1]; } // switch root this.parent = ""; this.len = 0; this.lex = 0; this.angle = 0; drag.node.plot.strokeColor(setup.defaultNodeStrokeColor); drag.node.plot.strokeWidth(1); drag.node = this; } } // main loop var run = function () { requestAnimationFrame(run); if (drag.ing) { drag.x = pointer.x - drag.ox, drag.y = pointer.y - drag.oy; } rotation += setup.rotationSpeed; var i = 0, node; while ( node = nodes[i++]) { node.run(); } } // parse menu var setMenuTree = function (theNode, parent) { if (theNode.tagName == "Q" || theNode.tagName == "A") { // Node Label var s = theNode.innerHTML; var d = s.toUpperCase().indexOf("<Q"); if (d > 0) s = s.substring(0, d); d = s.toUpperCase().indexOf("<A"); if (d > 0) s = s.substring(0, d); // create Node if (theNode.style.color != "") setup.defaultNodeColor = theNode.style.color; parent = new Node(parent, s, setup.defaultNodeColor, theNode.href); // push Node nodes.push(parent); } // recursive call for (var i = 0; i < theNode.childNodes.length; i++) { setMenuTree(theNode.childNodes[i], parent); } } // initializations var screen = ge1doot.screen("screen"); var screen = ge1doot.screen("screen"); var pointer = screen.pointer; var nodes = [], drag = { x: 0, y: 0, ox: 0, oy: 0, node: false, ing: false }, rotation = 0, nodeOver, setup; pointer.down = function (e) { if (e && e.target && e.target.obj) { drag.ing = true; e.target.obj.down(); } } pointer.up = function (e) { drag.ing = false; } pointer.tap = function (e) { if (e && e.target && e.target.obj) { drag.ing = false; if (drag.node.link) { // open hyperlink window.open(drag.node.link, "_blank"); } else { // expand / collapse if (drag.node.expanded) drag.node.collapse(); else drag.node.expand(); } } } pointer.move = function (e) { if (e && e.target && e.target.obj) { var o = e.target.obj; if (nodeOver) { nodeOver.plot.strokeColor(setup.defaultNodeStrokeColor); nodeOver.plot.strokeWidth(1); } o.plot.strokeColor(setup.overNodeColor); o.plot.strokeWidth(Math.round(Math.max(2, o.pR / 3))); nodeOver = o; } } // create SVG container var svg = new ge1doot.SVGLib(screen.elem, true); if (svg) { // init menu setup.length = Math.max(150, screen.height / 4); drag.x = screen.width / 2; drag.y = screen.height / 2; setMenuTree(document.getElementById(setup.id), ""); // create plots var i = 0, node; while ( node = nodes[i++]) { node.createPlot(); } // expand 1st Node drag.node = nodes[0]; nodes[0].collapse(); nodes[0].expand(); // start engine run(); } }(); </script> </html> 提示:你可以先修改部分代码再运行。 转载请注明:有爱前端 » 思维导图 喜欢 (0)or分享 (0)