在做可视化的很多时候,我们需要在主图的一角设置一个缩略图来掌握全局情况。本次将使用力导向图作为例子,完成缩略图的实现。
绘制的原理就是依靠主图的数据再画一个图出来,无需再次计算,只改变图形形态。
最终效果
主图节点拖动,缩略图跟着变化。
代码解析
数据
依然使用之前例子的力导向图绘制数据及方法
var nodes = [ {value:"66666666",type:"home",index:"0"}, {value:"11111111111",type:"phone",index:"1"}, {value:"22222222222",type:"phone",index:"2"}, {value:"33333333333",type:"phone",index:"3"}, {value:"44444444444",type:"phone",index:"4"}, {value:"55555555555",type:"phone",index:"5"}, {value:"aaa",type:"weixin",index:"6"}, {value:"bbb",type:"weixin",index:"7"}, {value:"ccc",type:"weixin",index:"8"}, {value:"ddd",type:"weixin",index:"9"}, {value:"eee",type:"weixin",index:"10"}, {value:"fff",type:"weixin",index:"11"},];var links = [ {source:0,target:1}, {source:0,target:2}, {source:0,target:3}, {source:0,target:4}, {source:0,target:5}, {source:2,target:6}, {source:2,target:7}, {source:2,target:8}, {source:3,target:9}, {source:3,target:10}, {source:3,target:11},]
力导向图绘制
var height = 500;var width = 500;var svg = d3.select("#forceMap").append("svg") .attr("width",width) .attr("height",height) .attr("id","forceSvg");var mapG = svg.append("g").attr("id","forceGroup");var force = d3.layout.force() .nodes(nodes) .links(links) .size([width,height]) .linkDistance(100) .charge([-1250]) .gravity(0.5) .friction(0.5);force.start();var linkG = mapG.selectAll(".link").data(links).enter().append("line").attr("class","link").attr("stroke","#ccc");var nodeG = mapG.selectAll(".node").data(nodes).enter().append("circle").attr("class","node").attr("r",8).attr("fill",function(d){ switch(d.type){ case "home": return "red";break; case"phone": return "blue";break; case "weixin": return "green";break; }}) .call(force.drag); //这个例子为节点添加了可拖动的功能force.on("tick", function () { linkG.attr("x1", function (d) { return d.source.x; }) .attr("y1", function (d) { return d.source.y; }) .attr("x2", function (d) { return d.target.x; }) .attr("y2", function (d) { return d.target.y; }); nodeG.attr("cx", function (d) { return d.x }) .attr("cy", function(d){ return d.y }); drawThumb(); //在tick最后,执行绘制缩略图});
这次代码中添加了drag方法,为了使节点可拖动。
绘制缩略图
function drawThumb(){//每次绘制前删除之前的图形(这是一种简单有效的动画理论,但是比较消耗资源,之后会介绍如何节省资源完成需求) d3.select("#thumbSvg").remove(); var thumbSvg = d3.select("#thumbMap").append("svg") .attr("width",100) .attr("height",100) .attr("id","thumbSvg"); var thumbG = thumbSvg.append("g") .attr("id","thumbGroup"); var thumbLink = thumbG.selectAll(".tlink") .data(links) .enter() .append("line") .attr("class","tlink") .attr("stroke","#ccc") //缩略图绘制和主图的差异在这,不需要tick,只需要把节点的坐标直接赋予即可 .attr("x1", function (d) { return d.source.x/5;//这里的除5是缩略图和主图的比例,thumbWidth/forceWidth }) .attr("y1", function (d) { return d.source.y/5; }) .attr("x2", function (d) { return d.target.x/5; }) .attr("y2", function (d) { return d.target.y/5; });var thumbNode = thumbG.selectAll(".tnode").data(nodes).enter().append("circle").attr("class","tnode").attr("r",1.2)//图形尺寸都要缩小.attr("fill",function(d){ switch(d.type){ case "home": return "red";break; case"phone": return "blue";break; case "weixin": return "green";break; }}).attr("cx", function (d) { return d.x/5 }) .attr("cy", function(d){ return d.y/5 });}
在绘制缩略图时,我在最前面有一个remove方法,就是用新的图形代替旧的图形,完成缩略图的动画效果,模拟了tick。
但在实际使用中,数据量很大时,如此删了画画了删,非常耗资源。所以更好的方法是判断是否有thumbSvg,有的话就只改变node和link的x,y,没有的话就按上面代码一样绘制。ok,缩略图的绘制完成,很简单的例子,按照这个思路可以完成大部分可视化的缩略图绘制。除了拖动外,还可以添加缩放和平移功能,但注意缩放时所有尺寸的比例都会跟着变化,会复杂很多。