Force layout test/experiment with images
Sources:
Force layout test/experiment with images
Sources:
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>Marvel Characters | Force layout with images</title> | |
| <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> | |
| <style> | |
| @import url(http://fonts.googleapis.com/css?family=Source+Code+Pro:400,600); | |
| body {font-family: "Source Code Pro", Consolas, monaco, monospace; line-height: 160%; font-size: 16px; margin: 0; } | |
| path.link { | |
| fill: none; | |
| stroke-width: 2px; | |
| } | |
| .node:not(:hover) .nodetext { | |
| display: none; | |
| } | |
| h1 { font-size: 36px; margin: 10px 0; text-transform: uppercase; font-weight: normal;} | |
| h2, h3 { font-size: 18px; margin: 5px 0 ; font-weight: normal;} | |
| header {padding: 20px; position: absolute; top: 0; left: 0;} | |
| a:link { color: #EE3124; text-decoration: none;} | |
| a:visited { color: #EE3124; } | |
| a:hover { color: #A4CD39; text-decoration: underline;} | |
| a:active { color: #EE3124; } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <h1>Marvel Characters</h1> | |
| <h2>Click to view their identity</h2> | |
| <h3>And link to their web page!</h3> | |
| </header> | |
| <!-- container for force layout visualisation --> | |
| <section id="vis"></section> | |
| <script> | |
| // some colour variables | |
| var tcBlack = "#130C0E"; | |
| // rest of vars | |
| var w = 960, | |
| h = 800, | |
| maxNodeSize = 50, | |
| x_browser = 20, | |
| y_browser = 25, | |
| root; | |
| var vis; | |
| var force = d3.layout.force(); | |
| vis = d3.select("#vis").append("svg").attr("width", w).attr("height", h); | |
| d3.json("marvel.json", function(json) { | |
| root = json; | |
| root.fixed = true; | |
| root.x = w / 2; | |
| root.y = h / 4; | |
| // Build the path | |
| var defs = vis.insert("svg:defs") | |
| .data(["end"]); | |
| defs.enter().append("svg:path") | |
| .attr("d", "M0,-5L10,0L0,5"); | |
| update(); | |
| }); | |
| /** | |
| * | |
| */ | |
| function update() { | |
| var nodes = flatten(root), | |
| links = d3.layout.tree().links(nodes); | |
| // Restart the force layout. | |
| force.nodes(nodes) | |
| .links(links) | |
| .gravity(0.05) | |
| .charge(-1500) | |
| .linkDistance(100) | |
| .friction(0.5) | |
| .linkStrength(function(l, i) {return 1; }) | |
| .size([w, h]) | |
| .on("tick", tick) | |
| .start(); | |
| var path = vis.selectAll("path.link") | |
| .data(links, function(d) { return d.target.id; }); | |
| path.enter().insert("svg:path") | |
| .attr("class", "link") | |
| // .attr("marker-end", "url(#end)") | |
| .style("stroke", "#eee"); | |
| // Exit any old paths. | |
| path.exit().remove(); | |
| // Update the nodes… | |
| var node = vis.selectAll("g.node") | |
| .data(nodes, function(d) { return d.id; }); | |
| // Enter any new nodes. | |
| var nodeEnter = node.enter().append("svg:g") | |
| .attr("class", "node") | |
| .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }) | |
| .on("click", click) | |
| .call(force.drag); | |
| // Append a circle | |
| nodeEnter.append("svg:circle") | |
| .attr("r", function(d) { return Math.sqrt(d.size) / 10 || 4.5; }) | |
| .style("fill", "#eee"); | |
| // Append images | |
| var images = nodeEnter.append("svg:image") | |
| .attr("xlink:href", function(d) { return d.img;}) | |
| .attr("x", function(d) { return -25;}) | |
| .attr("y", function(d) { return -25;}) | |
| .attr("height", 50) | |
| .attr("width", 50); | |
| // make the image grow a little on mouse over and add the text details on click | |
| var setEvents = images | |
| // Append hero text | |
| .on( 'click', function (d) { | |
| d3.select("h1").html(d.hero); | |
| d3.select("h2").html(d.name); | |
| d3.select("h3").html ("Take me to " + "<a href='" + d.link + "' >" + d.hero + " web page ⇢"+ "</a>" ); | |
| }) | |
| .on( 'mouseenter', function() { | |
| // select element in current context | |
| d3.select( this ) | |
| .transition() | |
| .attr("x", function(d) { return -60;}) | |
| .attr("y", function(d) { return -60;}) | |
| .attr("height", 100) | |
| .attr("width", 100); | |
| }) | |
| // set back | |
| .on( 'mouseleave', function() { | |
| d3.select( this ) | |
| .transition() | |
| .attr("x", function(d) { return -25;}) | |
| .attr("y", function(d) { return -25;}) | |
| .attr("height", 50) | |
| .attr("width", 50); | |
| }); | |
| // Append hero name on roll over next to the node as well | |
| nodeEnter.append("text") | |
| .attr("class", "nodetext") | |
| .attr("x", x_browser) | |
| .attr("y", y_browser +15) | |
| .attr("fill", tcBlack) | |
| .text(function(d) { return d.hero; }); | |
| // Exit any old nodes. | |
| node.exit().remove(); | |
| // Re-select for update. | |
| path = vis.selectAll("path.link"); | |
| node = vis.selectAll("g.node"); | |
| function tick() { | |
| path.attr("d", function(d) { | |
| var dx = d.target.x - d.source.x, | |
| dy = d.target.y - d.source.y, | |
| dr = Math.sqrt(dx * dx + dy * dy); | |
| return "M" + d.source.x + "," | |
| + d.source.y | |
| + "A" + dr + "," | |
| + dr + " 0 0,1 " | |
| + d.target.x + "," | |
| + d.target.y; | |
| }); | |
| node.attr("transform", nodeTransform); | |
| } | |
| } | |
| /** | |
| * Gives the coordinates of the border for keeping the nodes inside a frame | |
| * http://bl.ocks.org/mbostock/1129492 | |
| */ | |
| function nodeTransform(d) { | |
| d.x = Math.max(maxNodeSize, Math.min(w - (d.imgwidth/2 || 16), d.x)); | |
| d.y = Math.max(maxNodeSize, Math.min(h - (d.imgheight/2 || 16), d.y)); | |
| return "translate(" + d.x + "," + d.y + ")"; | |
| } | |
| /** | |
| * Toggle children on click. | |
| */ | |
| function click(d) { | |
| if (d.children) { | |
| d._children = d.children; | |
| d.children = null; | |
| } else { | |
| d.children = d._children; | |
| d._children = null; | |
| } | |
| update(); | |
| } | |
| /** | |
| * Returns a list of all nodes under the root. | |
| */ | |
| function flatten(root) { | |
| var nodes = []; | |
| var i = 0; | |
| function recurse(node) { | |
| if (node.children) | |
| node.children.forEach(recurse); | |
| if (!node.id) | |
| node.id = ++i; | |
| nodes.push(node); | |
| } | |
| recurse(root); | |
| return nodes; | |
| } | |
| </script> | |
| </body> | |
| </html> |
| { | |
| "name": "marvel", | |
| "img": "http://marvel-force-chart.surge.sh/marvel_force_chart_img/marvel.png", | |
| "children": [ | |
| { | |
| "name": "Heroes", | |
| "children": [ | |
| { | |
| "hero": "Spider-Man", | |
| "name": "Peter Benjamin Parker", | |
| "link": "http://marvel.com/characters/54/spider-man", | |
| "img": "http://marvel-force-chart.surge.sh/marvel_force_chart_img/top_spiderman.png", | |
| "size": 40000 | |
| }, | |
| { | |
| "hero": "CAPTAIN MARVEL", | |
| "name": "Carol Danvers", | |
| "link": "http://marvel.com/characters/9/captain_marvel", | |
| "img": "http://marvel-force-chart.surge.sh/marvel_force_chart_img/top_captainmarvel.png", | |
| "size": 40000 | |
| }, | |
| { | |
| "hero": "HULK", | |
| "name": "Robert Bruce Banner", | |
| "link": "http://marvel.com/characters/25/hulk", | |
| "img": "http://marvel-force-chart.surge.sh/marvel_force_chart_img/top_hulk.png", | |
| "size": 40000 | |
| }, | |
| { | |
| "hero": "Black Widow", | |
| "name": "Natalia 'Natasha' Alianovna Romanova", | |
| "link": "http://marvel.com/characters/6/black_widow", | |
| "img": "http://marvel-force-chart.surge.sh/marvel_force_chart_img/top_blackwidow.png", | |
| "size": 40000 | |
| }, | |
| { | |
| "hero": "Daredevil", | |
| "name": "Matthew Michael Murdock", | |
| "link": "http://marvel.com/characters/11/daredevil", | |
| "img": "http://marvel-force-chart.surge.sh/marvel_force_chart_img/top_daredevil.png", | |
| "size": 40000 | |
| }, | |
| { | |
| "hero": "Wolverine", | |
| "name": "James Howlett", | |
| "link": "http://marvel.com/characters/66/wolverine", | |
| "img": "http://marvel-force-chart.surge.sh/marvel_force_chart_img/top_wolverine.png", | |
| "size": 40000 | |
| }, | |
| { | |
| "hero": "Captain America", | |
| "name": "Steven Rogers", | |
| "link": "http://marvel.com/characters/8/captain_america", | |
| "img": "http://marvel-force-chart.surge.sh/marvel_force_chart_img/top_captainamerica.png", | |
| "size": 40000 | |
| }, | |
| { | |
| "hero": "Iron Man", | |
| "name": "Anthony Edward 'Tony' Stark", | |
| "link": "http://marvel.com/characters/29/iron_man", | |
| "img": "http://marvel-force-chart.surge.sh/marvel_force_chart_img/top_ironman.png", | |
| "size": 40000 | |
| }, | |
| { | |
| "hero": "THOR", | |
| "name": "Thor Odinson", | |
| "link": "http://marvel.com/characters/60/thor", | |
| "img": "http://marvel-force-chart.surge.sh/marvel_force_chart_img/top_thor.png", | |
| "size": 40000 | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "Villains", | |
| "children": [ | |
| { | |
| "hero": "Dr. Doom", | |
| "name": "Victor von Doom", | |
| "link": "http://marvel.com/characters/13/dr_doom", | |
| "img": "http://marvel-force-chart.surge.sh/marvel_force_chart_img/drdoom.png", | |
| "size": 40000 | |
| }, | |
| { | |
| "hero": "Mystique", | |
| "name": "Unrevealed", | |
| "link": "http://marvel.com/characters/1552/mystique", | |
| "img": "http://marvel-force-chart.surge.sh/marvel_force_chart_img/mystique.png", | |
| "size": 40000 | |
| }, | |
| { | |
| "hero": "Red Skull", | |
| "name": "Johann Shmidt", | |
| "link": "http://marvel.com/characters/1901/red_skull", | |
| "img": "http://marvel-force-chart.surge.sh/marvel_force_chart_img/redskull.png", | |
| "size": 40000 | |
| }, | |
| { | |
| "hero": "Ronan", | |
| "name": "Ronan", | |
| "link": "http://marvel.com/characters/49/ronan", | |
| "img": "http://marvel-force-chart.surge.sh/marvel_force_chart_img/ronan.png", | |
| "size": 40000 | |
| }, | |
| { | |
| "hero": "Magneto", | |
| "name": "Max Eisenhardt", | |
| "link": "http://marvel.com/characters/35/magneto", | |
| "img": "http://marvel-force-chart.surge.sh/marvel_force_chart_img/magneto.png", | |
| "size": 40000 | |
| }, | |
| { | |
| "hero": "Thanos", | |
| "name": "Thanos", | |
| "link": "http://marvel.com/characters/58/thanos", | |
| "img": "http://marvel-force-chart.surge.sh/marvel_force_chart_img/thanos.png", | |
| "size": 40000 | |
| }, | |
| { | |
| "hero": "Black Cat", | |
| "name": "Felicia Hardy", | |
| "link": "http://marvel.com/characters/271/black_cat", | |
| "img": "http://marvel-force-chart.surge.sh/marvel_force_chart_img/blackcat.png", | |
| "size": 40000 | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "Teams", | |
| "children": [ | |
| { | |
| "hero": "Avengers", | |
| "name": "", | |
| "link": "http://marvel.com/characters/68/avengers", | |
| "img": "http://marvel-force-chart.surge.sh/marvel_force_chart_img/avengers.png", | |
| "size": 40000 | |
| }, | |
| { | |
| "hero": "Guardians of the Galaxy", | |
| "name": "", | |
| "link": "http://marvel.com/characters/70/guardians_of_the_galaxy", | |
| "img": "http://marvel-force-chart.surge.sh/marvel_force_chart_img/gofgalaxy.png", | |
| "size": 40000 | |
| }, | |
| { | |
| "hero": "Defenders", | |
| "name": "", | |
| "link": "http://marvel.com/characters/534/defenders", | |
| "img": "http://marvel-force-chart.surge.sh/marvel_force_chart_img/defenders.png", | |
| "size": 40000 | |
| }, | |
| { | |
| "hero": "X-Men", | |
| "name": "", | |
| "link": "http://marvel.com/characters/71/x-men", | |
| "img": "http://marvel-force-chart.surge.sh/marvel_force_chart_img/xmen.png", | |
| "size": 40000 | |
| }, | |
| { | |
| "hero": "Fantastic Four", | |
| "name": "", | |
| "link": "http://marvel.com/characters/69/fantastic_four", | |
| "img": "http://marvel-force-chart.surge.sh/marvel_force_chart_img/fantasticfour.png", | |
| "size": 40000 | |
| }, | |
| { | |
| "hero": "Inhumans", | |
| "name": "", | |
| "link": "http://marvel.com/characters/1040/inhumans", | |
| "img": "http://marvel-force-chart.surge.sh/marvel_force_chart_img/inhumans.png", | |
| "size": 40000 | |
| } | |
| ] | |
| } | |
| ] | |
| } |