如何使d3方向力图在流星中反作用? [英] How to make d3 directional force diagram reactive in meteor?

查看:110
本文介绍了如何使d3方向力图在流星中反作用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

尽我所能尝试,当涉及到方向力图时,我似乎无法让流星和d3发挥出色.由于我是Meteor和d3的新手,所以我不确定我的失败在哪里.

Try as I might, I can't seem to get meteor and d3 to play nicely when it comes to a directional force diagram. Since I'm new to both Meteor and d3, I'm not sure where my failing is..

我想做的是仅获得(重新)创建以下示例图,但其中一个对mongo数据源具有反应性.任何帮助将不胜感激!

What I'm trying to do is to get (re)create just the following sample graph, but one which is reactive to the mongo data source. Any help at all would be most appreciated!

(点击此处观看实时演示)

// get the data
d3.csv("force.csv", function(error, links) {

var nodes = {};

// Compute the distinct nodes from the links.
links.forEach(function(link) {
    link.source = nodes[link.source] || 
        (nodes[link.source] = {name: link.source});
    link.target = nodes[link.target] || 
        (nodes[link.target] = {name: link.target});
    link.value = +link.value;
});

var width = 960,
    height = 500;

var force = d3.layout.force()
    .nodes(d3.values(nodes))
    .links(links)
    .size([width, height])
    .linkDistance(60)
    .charge(-300)
    .on("tick", tick)
    .start();

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

// build the arrow.
svg.append("svg:defs").selectAll("marker")
    .data(["end"])      // Different link/path types can be defined here
  .enter().append("svg:marker")    // This section adds in the arrows
    .attr("id", String)
    .attr("viewBox", "0 -5 10 10")
    .attr("refX", 15)
    .attr("refY", -1.5)
    .attr("markerWidth", 6)
    .attr("markerHeight", 6)
    .attr("orient", "auto")
  .append("svg:path")
    .attr("d", "M0,-5L10,0L0,5");

// add the links and the arrows
var path = svg.append("svg:g").selectAll("path")
    .data(force.links())
  .enter().append("svg:path")
//    .attr("class", function(d) { return "link " + d.type; })
    .attr("class", "link")
    .attr("marker-end", "url(#end)");

// define the nodes
var node = svg.selectAll(".node")
    .data(force.nodes())
  .enter().append("g")
    .attr("class", "node")
    .call(force.drag);

// add the nodes
node.append("circle")
    .attr("r", 5);

// add the text 
node.append("text")
    .attr("x", 12)
    .attr("dy", ".35em")
    .text(function(d) { return d.name; });

// add the curvy lines
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", function(d) { 
        return "translate(" + d.x + "," + d.y + ")"; });
}

});
</script>

force.csv:

force.csv:

source,target,value
Harry,Sally,1.2
Harry,Mario,1.3
Sarah,Alice,0.2
Eveie,Alice,0.5
Peter,Alice,1.6
Mario,Alice,0.4
James,Alice,0.6
Harry,Carol,0.7
Harry,Nicky,0.8
Bobby,Frank,0.8
Alice,Mario,0.7
Harry,Lynne,0.5
Sarah,James,1.9
Roger,James,1.1
Maddy,James,0.3
Sonny,Roger,0.5
James,Roger,1.5
Alice,Peter,1.1
Johan,Peter,1.6
Alice,Eveie,0.5
Harry,Eveie,0.1
Eveie,Harry,2.0
Henry,Mikey,0.4
Elric,Mikey,0.6
James,Sarah,1.5
Alice,Sarah,0.6
James,Maddy,0.5
Peter,Johan,0.7


以下是我以当事人示例为起点的尝试:


Below is my attempt using the parties example as a starting point:

parties.html:

parties.html:

<head>
  <title>All Tomorrow's Parties</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>

<body>
  {{> page}}
</body>

<template name="page">

          <div class="span6">
            {{> map}}           
          </div>

  {{>updateNetwork}}
</template>

<template name="map">

</template>

<template name="details">
  <div class="details">

  </div>
</template>



<template name="updateNetwork">
<div align="right">
  <br/>
  <input id="addNodeBtn" type="button" value = "Add Some Nodes">
</div>
<div id="svgDiv">
</div>
</template>

client.js:

client.js:

Template.map.rendered = function () {
  var self = this;
  self.node = self.find("svg");

  if (! self.handle) {
    self.handle = Deps.autorun(function () {


if (!Session.equals("alreadyRun", true))
{


_links = Links.find({}).fetch();


// The nodes array just contains name information. 
// Sample values:
// nodes["Jack"] = {name: "Jack"}
// nodes["Jill"]  = {name: "Jill"}

var nodes = {};

// Compute the distinct nodes from the links.
// Go through all links, and update the total weight of the edge as well for each link,
// within the links object.
//
_links.forEach(function(link) {
    link.source = nodes[link.source] || 
        (nodes[link.source] = {name: link.source});
    link.target = nodes[link.target] || 
        (nodes[link.target] = {name: link.target});
    link.value = +link.value;
});

  console.log("links: " + JSON.stringify(_links));

  var links = _links;

// At this point, the "links" object cotains info on the entire network, and
// is prepared to be rendered

var width = 300,
    height = 200;

 force = d3.layout.force();

force
    .nodes(d3.values(nodes))
    .links(links)
    .size([width, height])
    .linkDistance(60)
    .charge(-300)
    .on("tick", tick)
    .start();

Session.set("forceVariable", force);

Session.set("nodeArray", nodes);
//Session.set("linkArray", d3.layout.force().links());
Session.set("linkArray", links);

// Set the range
var  v = d3.scale.linear().range([0, 100]);

// Scale the range of the data
v.domain([0, d3.max(links, function(d) { return d.value; })]);

// asign a type per value to encode opacity
links.forEach(function(link) {
  if (v(link.value) <= 25) {
    link.type = "twofive";
  } else if (v(link.value) <= 50 && v(link.value) > 25) {
    link.type = "fivezero";
  } else if (v(link.value) <= 75 && v(link.value) > 50) {
    link.type = "sevenfive";
  } else if (v(link.value) <= 100 && v(link.value) > 75) {
    link.type = "onezerozero";
  }
});

var svg = d3.select("body").select("#svgDiv").append("svg")
    .attr("width", width)
    .attr("height", height);

//Session.set("currentSVG", svg);

// build the arrow.
svg.append("svg:defs").selectAll("marker")
    .data(["end"])      // Different link/path types can be defined here
  .enter().append("svg:marker")    // This section adds in the arrows
    .attr("id", String)
    .attr("viewBox", "0 -5 10 10")
    .attr("refX", 15)
    .attr("refY", -1.5)
    .attr("markerWidth", 6)
    .attr("markerHeight", 6)
    .attr("orient", "auto")
  .append("svg:path")
    .attr("d", "M0,-5L10,0L0,5");

// add the links and the arrows
var path = svg.append("svg:g").selectAll("path")
    .data(force.links())
  .enter().append("svg:path")
    .attr("class", function(d) { return "link " + d.type; })
    .attr("marker-end", "url(#end)");


console.log("At this point, force.nodes() is..." + JSON.stringify(force.nodes()));

// define the nodes
var node = svg.selectAll(".node")
    .data(force.nodes())  
  .enter().append("g")
    .attr("class", "node")
    .on("click", click)
    .on("dblclick", dblclick)
    .call(force.drag);


console.log("And now, at this point, force.nodes() is..." + JSON.stringify(force.nodes()));

// add the nodes
node.append("circle")
    .attr("r", 5);

// add the text 
node.append("text")
    .attr("x", 12)
    .attr("dy", ".35em")
    .text(function(d) { return d.name; });


  } // end if (Session.equals("alreadyRun", true))
else
{


console.log("nodeArrray: " + JSON.stringify(Session.get("nodeArray")));
  console.log("linkArray:" + Session.get("linkArray"));
  //console.log("currentSVG: " + JSON.stringify(Session.get("currentSVG")));
  //console.log("currentNodes: " + JSON.stringify(Session.get("currentSVG").nodes()));



// Session.set("nodeArray", nodes);
// Session.set("linkArray", d3.layout.force().links());



var svg = d3.select("body").select("#svgDiv").select("svg");
//var svg = Session.get("currentSVG");


// Try to access the force.nodes() object
//
//console.log("force.nodes: "+ JSON.stringify(svg.selectAll(".node").data(Session.get();)));




// build the arrow.
svg.append("svg:defs").selectAll("marker")
    .data(["end"])      // Different link/path types can be defined here
  .enter().append("svg:marker")    // This section adds in the arrows
    .attr("id", String)
    .attr("viewBox", "0 -5 10 10")
    .attr("refX", 15)
    .attr("refY", -1.5)
    .attr("markerWidth", 6)
    .attr("markerHeight", 6)
    .attr("orient", "auto")
  .append("svg:path")
    .attr("d", "M0,-5L10,0L0,5");

newNodes = Session.get("nodeArray");
newLinks = Session.get("linkArray");

newLinks.push({source:"Kobeeley" , target:"Cluff", value:0.8});

newLinks.forEach(function(link) {
    link.source = newNodes[link.source] || 
        (newNodes[link.source] = {name: link.source});
    link.target = newNodes[link.target] || 
        (newNodes[link.target] = {name: link.target});
    link.value = +link.value;
});

console.log("newNodes is now...." + JSON.stringify(newNodes));

//var force = d3.layout.force();

// add the links and the arrows
var path = svg.append("svg:g").selectAll("path")
    .data(newLinks)
  .enter().append("svg:path")
    .attr("class", function(d) { return "link " + d.type; })
    .attr("marker-end", "url(#end)");

// define the nodes
var node = svg.selectAll(".node")
    .data(d3.values(newNodes))
  .enter().append("g")
    .attr("class", "node")
    .call(force.drag);

 //   .on("click", click)
 //   .on("dblclick", dblclick)
 //   .call(force.drag);

// add the nodes
node.append("circle")
    .attr("r", 5);

// add the text 
node.append("text")
    .attr("x", 12)
    .attr("dy", ".35em")
    .text(function(d) { return d.name; });

    var width = 960,
    height = 500;


force.start();

/*
force
    .nodes(d3.values(newNodes))
    .links(newLinks)
    .size([width, height])
    .linkDistance(60)
    .charge(-300)
    .on("tick", tick)

   .start();
*/
/*
var force = d3.layout.force()
    .nodes(d3.values(newNodes))
    .links(newLinks)
    .size([width, height])
    .linkDistance(60)
    .charge(-300)
    .on("tick", tick)
    .start();
*/

} // end of if..else firstrun

// add the curvy lines
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", function(d) { 
        return "translate(" + d.x + "," + d.y + ")"; });
}

// action to take on mouse click
function click() {
    d3.select(this).select("text").transition()
        .duration(750)
        .attr("x", 22)
        .style("fill", "steelblue")
        .style("stroke", "lightsteelblue")
        .style("stroke-width", ".5px")
        .style("font", "20px sans-serif");
    d3.select(this).select("circle").transition()
        .duration(750)
        .attr("r", 16)
        .style("fill", "lightsteelblue");

}

// action to take on mouse double click
function dblclick() {
    d3.select(this).select("circle").transition()
        .duration(750)
        .attr("r", 6)
        .style("fill", "#ccc");
    d3.select(this).select("text").transition()
        .duration(750)
        .attr("x", 12)
        .style("stroke", "none")
        .style("fill", "black")
        .style("stroke", "none")
        .style("font", "10px sans-serif");
    }




});  // End of deps.autorun function


}// end of (! self.handle)

};// end of Template.map.rendered



Template.updateNetwork.events({
  'click #addNodeBtn': function (event, template) {
    //if (! Meteor.userId()) // must be logged in to create events
    //  return;
    //var coords = coordsRelativeToElement(event.currentTarget, event);
    //openCreateDialog(coords.x / 500, coords.y / 500);



  var _linksToAdd = [
    {source: "Sully", target: "Roy", value: 0.2 },
    {source: "Roy", target: "Jack", value: 0.8},
    {source:"Juhuff", target: "Cluff", value: 0.9}
    ];

    Session.set("alreadyRun", true);

  // Update the collection
  //
  _linksToAdd.forEach(function (link) {
    Links.insert(link);
  })



  }
});

model.js:

Links = new Meteor.Collection("links");

Links.allow({
  insert: function (userId, doc) {
    return true; // no cowboy inserts -- use createParty method
  },
  update: function (userId, doc, fields, modifier) {
  //  if (userId !== party.owner)
  //    return true; // not the owner

  //  var allowed = ["title", "description", "x", "y"];
  //  if (_.difference(fields, allowed).length)
  //    return true; // tried to write to forbidden field

    // A good improvement would be to validate the type of the new
    // value of the field (and if a string, the length.) In the
    // future Meteor will have a schema system to makes that easier.
    return true;
  },
  remove: function (userId, doc) {
    // You can only remove parties that you created and nobody is going to.
  //  return party.owner === userId && attending(party) === 0;
  return true;
  }

});

推荐答案

以下是使用流星并结合

Here's the answer that worked for me using meteor and incorporating some awesome code from another D3 question:

client.js:

client.js:

Things = new Meteor.Collection("things");
Links = new Meteor.Collection("links");

if (Meteor.isClient) {
  Template.diagram.rendered = function () {
    var graph;
    graph = new myGraph("#svgdiv");
    Things.find().observe({
      added: function (doc) {
        graph.addNode(doc._id);
      },
      removed: function (doc) {
        graph.removeNode(doc._id);
      }
    });

    Links.find().observe({
      added: function (doc) {
        graph.addLink(doc._id, doc.source, doc.target, doc.value);
      },
      removed: function (doc) {
        graph.removeLink(doc._id);
      }
    });
  };
}

function myGraph(el) {

  // Add and remove elements on the graph object
  this.addNode = function (id) {
    nodes.push({"id":id});
    update();
  };

  this.removeNode = function (id) {
    var i = 0;
    var n = findNode(id);
    while (i < links.length) {
      if ((links[i]['source'] == n)||(links[i]['target'] == n))
        {
          links.splice(i,1);
        }
        else i++;
    }
    nodes.splice(findNodeIndex(id),1);
    update();
  };

  this.removeLink = function (id){
    for(var i=0;i<links.length;i++)
    {
      if(links[i].id === id)
        {
          links.splice(i,1);
          break;
        }
    }
    update();
  };

  this.removeallLinks = function(){
    links.splice(0,links.length);
    update();
  };

  this.removeAllNodes = function(){
    nodes.splice(0,links.length);
    update();
  };

  this.addLink = function (id, source, target, value) {
    links.push({id: id, "source":findNode(source),"target":findNode(target),"value":value});
    update();
  };

  var findNode = function(id) {
    for (var i in nodes) {
      if (nodes[i]["id"] === id) return nodes[i];};
  };

  var findNodeIndex = function(id) {
    for (var i=0;i<nodes.length;i++) {
      if (nodes[i].id==id){
        return i;
      }
    };
  };

  // set up the D3 visualisation in the specified element
  var w = 500,
  h = 500;
  var svg = d3.select(el)
    .append("svg:svg")
    .attr("width", w)
    .attr("height", h)
    .attr("id","svg")
    .attr("pointer-events", "all")
    .attr("viewBox","0 0 "+w+" "+h)
    .attr("perserveAspectRatio","xMinYMid");
  var vis = svg.append('svg:g');

  var force = d3.layout.force();

  var nodes = force.nodes(),
  links = force.links();

  // build the arrow.
  svg.append("svg:defs").selectAll("marker")
      .data(["end"])      // Different link/path types can be defined here
    .enter().append("svg:marker")    // This section adds in the arrows
      .attr("id", String)
      .attr("viewBox", "0 -5 10 10")
      .attr("refX", 15)
      .attr("refY", -1.5)
      .attr("markerWidth", 6)
      .attr("markerHeight", 6)
      .attr("orient", "auto")
    .append("svg:path")
      .attr("d", "M0,-5L10,0L0,5");

  var update = function () {
    var link = vis.selectAll("path")
      .data(links, function(d) {
        return d.id;
      });

    link.enter().append("svg:path")
      .attr("id",function(d){return d.id;})
      .attr("class","link")
      .attr("marker-end", "url(#end)");

    link.append("title")
      .text(function(d){
        return d.value;
      });

    link.exit().remove();

    var node = vis.selectAll("g.node")
    .data(nodes, function(d) { return d.id;});

    var nodeEnter = node.enter().append("g")
      .attr("class", "node")
      .call(force.drag);

    nodeEnter.append("svg:circle")
      .attr("r", 16)
      .attr("id",function(d) { return "Node;"+d.id;})
      .attr("class","nodeStrokeClass");

    nodeEnter.append("svg:text")
      .attr("class","textClass")
      .text( function(d){return d.id;}) ;

    node.exit().remove();

    force.on("tick", function() {
      node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y         + ")"; });

      link.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;
      });

    });

      // Restart the force layout.
      force
        .gravity(.05)
        .distance(50)
        .linkDistance( 50 )
        .size([w, h])
        .start();
  };


  // Make it all go
  update();
}

main.html:

main.html:

<body>
  {{> diagram}}
</body>

<template name="diagram">
  {{#constant}}
    <div id="svgdiv"></div>
  {{/constant}}
</template>

这篇关于如何使d3方向力图在流星中反作用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆