function foreLayoutChart(){ // defalut properties var width = 500, height = 400, linkDistance = 100, charge = -800, // circle,cross,diamond,square,triangle-down,triangle-up symboleTyeps = 'cross,diamond,square,triangle-down,triangle-up', symboleSize = 750, symbolColor = 'steelblue'; // define a closure function function chart(selection){ selection.each(function(data){ // pan and zoom function onZoom(){ svg.attr('transform', 'translate(' + d3.event.translate + ')' + 'scale(' + d3.event.scale + ')'); }; var zoomBehavior = d3.behavior.zoom() .scaleExtent([0.1, 10]) .on('zoom', onZoom); // construct a svg canvas with the current selection var svg = d3.select(this) .append('svg') .attr({width: width, height: height}) .call(zoomBehavior).append('g'); // data transformation data.edges = data.edges.map(function(d,i){ // {source: x1, target: x2}: meet d3's requirsite return {source: d[0], target: d[1]} }); console.log(data.edges); // force layout data processing var force = d3.layout.force() .nodes(data.nodes) .links(data.edges) .size([width, height]) .linkDistance(linkDistance) .charge(charge) .on('tick', tickRun) .start(); var edges = svg.selectAll('line') .data(data.edges) .enter() .append('line') .style('stroke', '#ccc') .style('stroke-width', 1); var nodes = svg.selectAll('g') .data(data.nodes) .enter() .append('g') .call(force.drag() .on('dragstart', function(D){ d3.event.sourceEvent.stopPropagation(); D.fixed = true; d3.select(this) .select('path') .attr('stroke-width', 5); d3.select(this.parentNode) .selectAll('line') .each(function(d){ // select the line and mark it // if it points from source to target. if(d.source.index == D.index){ d3.select(this) .attr('stroke-dasharray', '5,5') .style('stroke', 'red') } }) })) .on('dblclick', function(D){ D.fixed = false; d3.select(this).select('path').attr('stroke-width', 0); d3.select(this.parentNode).selectAll('line') .each(function(d){ if(d.source.index == D.index){ d3.select(this) .attr('stroke-dasharray', '0,0') .style('stroke', '#ccc') }; }); }); // define symbole // get all types in data // such as ['Gen1', 'Gen2', 'Gen3'] var dataGenre = d3.set(data.nodes .map(function(d){ return d.type; }) ) .values() .sort(); symboleTyeps = symboleTyeps.split(','); var symbol = d3.svg.symbol() .size(symboleSize); // nodes groups now can add symbols and text nodes.append('path') .attr('d', function(d){ var shape = symboleTyeps[dataGenre.indexOf(d.type)]; return symbol.type(shape)(); }) .attr('stroke', 'red') .attr('stroke-width', 0) .attr('fill', 'steelblue'); nodes.append('text') .text(function(d){ return d.name; }) .attr('fill', 'black') .style('text-anchor', 'middle'); // tick function definition function tickRun(){ // tick for lines edges.attr({ x1: function(d){ return d.source.x; }, y1: function(d){ return d.source.y; }, x2: function(d){ return d.target.x; }, y2: function(d){ return d.target.y; }, }); // tick for groups nodes.attr('transform', function(d){ return 'translate(' + [d.x, d.y] + ')'; }); }; }) } // getter-setter functions // symbolColor chart.width = function(_){ if(!arguments.length) return width; width = _; return chart; }; chart.height = function(_){ if(!arguments.length) return height; height = _; return chart; }; chart.linkDistance = function(_){ if(!arguments.length) return linkDistance; linkDistance = _; return chart; }; chart.charge = function(_){ if(!arguments.length) return charge; charge = _; return chart; }; chart.symboleTyeps = function(_){ if(!arguments.length) return symboleTyeps; symboleTyeps = _; return chart; }; chart.symboleSize = function(_){ if(!arguments.length) return symboleSize; symboleSize = _; return chart; }; chart.symbolColor = function(_){ if(!arguments.length) return symbolColor; symbolColor = _; return chart; }; // return chart return chart; }