简体   繁体   中英

Control arrowheads dimension on a force-directed layout in d3.js

i've done a graph and now i want to add directed links.

I've a problem because i want the function that when i move the mouse over the link it gets bigger and when i click on the node it gets bigger and this two functions make a mess with my arrow on the links.

I've tryied many solutions found here on stackoverflow but nothing worked for me...Can somebody help me? Thanks

here my code:

<!DOCTYPE html>
      <meta charset="utf-8">
      <title>Modifying a force layout v4</title>

      <style>

        .link {
          stroke: #3B3B3B;
          /*stroke-width: 1px;*/
        }

        .node {
          stroke: #000;
          stroke-width: 1.5px;
        }

        .svg {
          border:3px solid black;
          border-radius:12px; 
          margin:auto; 
        }
        #arrow {
          fill:green;
        }

      </style>
      <body>
      Node:
      <div id="log"></div>
      Link:
      <div id="log2"></div>
        <script src="//d3js.org/d3.v4.js"></script>
        <script>



          var width = 960,
          height = 500;
          radius = 17;

          var expnsed = false;

          var color = d3.scaleOrdinal(d3.schemeCategory20);

          var nodes = [],
          links = [];


          var charge =  d3.forceManyBody().strength(-150);
          var simulation = d3.forceSimulation()
            .force("link", d3.forceLink().distance(130).strength(.7))
            .force("charge", charge)
            // use forceX and forceY instead to change the relative positioning
            // .force("centering", d3.forceCenter(width/2, height/2))
            .force("x", d3.forceX(width/2))
            .force("y", d3.forceY(height/2))
            .on("tick", tick);


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

          svg.append("defs").append("marker")
              .attr("id", "arrow")
              .attr("viewBox", "0 -5 10 10")
              .attr("refX", 15)
              .attr("refY", 0)
              .attr("markerWidth", 6)
              .attr("markerHeight", 6)
              .attr("orient", "auto")
              //.attr("fill","red") 
            .append("svg:path")
              .attr("d", "M0,-5L10,0L0,5");
          var url = "https://api.myjson.com/bins/lj6ob";
          d3.json(url, function(error, graph) {
            if (error) throw error;
            nodes = graph.nodes;
            links=graph.links;
            console.log("graph.links.length: "+links.length)

            for (var i = 0; i < links.length; i++){
              links[i].source = find(links[i].source);
              links[i].target = find(links[i].target);
            }

            console.log("Link source: " + links[0].target)


            start();
          })

        function find(name){
          for(var i = 0; i < nodes.length; i++){
            if (name == nodes[i].id){
              console.log("name: " + name)
              console.log("id: " + nodes[i].id)
              return i;
            }
          }
        }


        function start() {
          var nodeElements = svg.selectAll(".node").data(nodes, function(d){return d.id});
          var linkElements = svg.selectAll(".line").data(links).attr("class","links");
          //console.log(nodes)
          nodeElements.enter().append("circle").attr("class", function(d) {return "node " + d.index; }).attr("r", 17).attr("fill", function(d) { return color(d.group); });
          linkElements.enter().insert("line", ".node").attr("class", "link").attr("stroke-width", function(d) { return Math.sqrt(d.value)*2;});

          d3.selectAll("line").attr("marker-end", "url(#arrow)");

          nodeElements.exit().remove();
          linkElements.exit().remove();

          simulation.nodes(nodes)
          simulation.force("link").links(links)  
    // NOTE: Very important to call both alphaTarget AND restart in conjunction
        // Restart by itself will reset alpha (cooling of simulation)
        // but won't reset the velocities of the nodes (inertia)

        //remove alpha for slow incoming!!
        //.alpha(1)
          simulation.alpha(1).restart();
        }

        function tick() {
          var nodeElements = svg.selectAll(".node");
          var linkElements = svg.selectAll(".link");

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

          linkElements.on("mouseover", function(d) {
                  var g = d3.select(this); // The node
                  document.getElementById('log2').innerHTML = '<br> '+d.value;
                  g.attr("stroke-width", function(d) { return Math.sqrt(d.value)*4; })
                });

          linkElements.on("mouseout", function(d) {
            var g = d3.select(this); // The node
            g.attr("stroke-width", function(d) { return Math.sqrt(d.value)*2; })
          });


          nodeElements.append("title")
            .text(function(d) {
              return "node name: "+d.id + ", node group: "+d.group; 
            });

          nodeElements.on("mouseout", function(d) {
                  var g = d3.select(this); // The node
                  g.attr("fill", function(d) { return color(d.group); })
                })
                .on("mouseover", function(d) {
                  document.getElementById('log').innerHTML = '<br> '+d.id;
                  var g = d3.select(this); // The node
                  g.attr("fill", "red")
                });
          nodeElements.on("click",click);

          nodeElements.call(d3.drag()
              .on("start", dragstarted)
              .on("drag", dragged)
              .on("end", dragended));

          /*nodeElements.attr("cx", function(d,i) {return d.x; })
          .attr("cy", function(d) { return d.y; })*/
          nodeElements.attr("cx", function(d) { return d.x = Math.max(radius, Math.min(width - radius, d.x)); })
            .attr("cy", function(d) { return d.y = Math.max(radius, Math.min(height - radius, d.y)); });

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

        function click(d) {
            d3.select(this).attr("r", 
              function(d){if (d3.select(this).attr("r")==17) {radius = 23;return "23"}else{radius = 17; return "17"}}
            );
          //expand();
          var E = "E";
          if(d.id == E && expand){
            expand=false;
            //expand_2();
          }
        };

        function expand(){
          console.log("expand")
          b = createNode("A")
          nodes.push(b);
          start();
        }

        function expand_2(){
          d3.json("nodes_2.json", function(error, graph) {
            for(var i = 0; i < graph.nodes.length; i++){
              var n = graph.nodes[i];
              nodes.push(n);
            }
            for(var i = 0; i < graph.links.length; i++){
              graph.links[i].source = find(graph.links[i].source)
              graph.links[i].target = find(graph.links[i].target)
              var l = graph.links[i];
              links.push(l);
            }
            start();
          })
        }

        function zoomed() {
            g.attr("transform", d3.event.transform);
        }

        function dragstarted(d) {
          if (!d3.event.active) simulation.alphaTarget(0.3).restart();
          d.fx = d.x;
          d.fy = d.y;
        }

        function dragged(d) {
          d.fx = d3.event.x;
          d.fy = d3.event.y;
        }

        function dragended(d) {
          if (!d3.event.active) simulation.alphaTarget(0);
          d.fx = null;
          d.fy = null;
        }
        function createNode(id) {
          return {id: id, x: width/2, y:height/2}
        }

      </script>

You can give the arrow a fixed size if you define markerUnits="userSpaceOnUse" . To recreate your current sizes:

      svg.append("defs").append("marker")
          .attr("id", "arrow")
          .attr("viewBox", "0 -5 10 10")
          .attr("refX", 15)
          .attr("refY", 0)
          .attr("markerUnits", "userSpaceOnUse")
          .attr("markerWidth", 30)
          .attr("markerHeight", 30)
          .attr("orient", "auto")

If you want to resize the marker on hover, the best solution might be to have a second marker element arrow-hover that is bigger and that you temporarily exchange via CSS:

line {
    marker-end: url(#arrow);
}
line:hover {
    marker-end: url(#arrow-hover);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM