Qiitaソーシャルグラフの視覚化。
……失敗かな。
各ユーザーのフォローデータはAPIから取得しました。
関係性(フォローの状態)を Force Layoutにしてみましたが、ぶっちゃけあまり見やすくありません。
分かり易く表示するなら、もっと別の表現を使用した方がよさそうですね。
サンプル
pathの先頭に矢印を付けるのと、アイコン画像を丸く切り取るのに、svgのdef要素とclipPath要素を使用しています。
1 2 3 4 5 6 7 8 9 10 |
<svg> <clipPath id="cut-off-circle"> <circle cx=0 cy=0 r=15 /> </clipPath> <defs> <marker id="licensing" viewBox="0 -5 10 10" refX="15" refY="-1.5" markerWidth="6" markerHeight="6" orient="auto"> <path d="M0,-5L10,0L0,5"></path> </marker> </defs> </svg> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
d3.json('links.json', function(d){ var links = d; var nodes = {}; links.forEach(function(link) { link.source = nodes[link.node1] || (nodes[link.node1] = {name: link.node1}); link.target = nodes[link.node2] || (nodes[link.node2] = {name: link.node2, pref: link.node2_prf}); }); nodes['_shimizu']['pref'] = {url_name: "_shimizu", profile_image_url:"https://si0.twimg.com/profile_images/604833171/twitter_bigger_normal.jpg"}; var graph = {nodes:nodes, links:links}; d3draw(graph); }); function d3draw(graph) { var w = 1200; var h = 1200; var force = d3.layout.force() .nodes(d3.values(graph.nodes)) .links(graph.links) .size([w, h]) .gravity(0) .linkDistance(300) .linkStrength(1) .charge(-50) .on("tick", tick) .start(); var svg = d3.select("svg") .attr({ "width": w, "height": h }); //フォロー状態を示すpathを追加 var path = svg.append("g").selectAll("path") .data(force.links()) .enter() .append("path") .attr({ "class": "link licensing", "marker-end": "url(#licensing)" }); //ユーザーイメージ追加 var userImg = svg.append("g").selectAll("a") .data(force.nodes()) .enter() .append("a") .attr({ "xlink:href": function(d){ return "http://qiita.com/" + d.pref.url_name }, "target":"_blank" }) .append("image") .attr({ "class": "userImg", "xlink:href": function(d){ return d.pref.profile_image_url }, //ノード用画像の設定 "x": "-15px", "y": "-15px", "width": "30px", "height": "30px", "clip-path": "url(#cut-off-circle)" }) .call(force.drag); //ユーザー名ラベル追加 var label = svg.append("g").selectAll("g") .data(force.nodes()) .enter() .append("g"); label.append("text") //ラベル縁取り .attr({ "x": 8, "y": ".51em", "class": "shadow" }) .text(function(d) { return d.name; }); label.append("text") //ラベル本文 .attr({ "x": 8, "y": ".51em" }) .text(function(d) { return d.name; }); //アニメーション処理 function tick() { path.attr("d", function(d) { var dx = d.target.x - d.source.x; var dy = d.target.y - d.source.y; var dr = Math.sqrt(dy* dy+ dx*dx); return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y; }); userImg.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); label.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); } } |