向节点添加上下文菜单

时间:2018-07-22 11:10:43

标签: javascript d3.js

我正在尝试向this jsfiddle中给定的jakosz's example中的静态节点添加上下文菜单。

右键单击节点时,它确实显示菜单,但是在屏幕的左上方,我无法弄清楚如何传递节点坐标,因此上下文菜单出现在节点周围而不是某处其他。

在帖子中复制相同的jsfiddle。

package.in
<?php
function base64url_encode($data) {
  return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}

function base64url_decode($data) {
  return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
}
?>

1 个答案:

答案 0 :(得分:2)

您要获取父组的位置,而不是圆的位置:

menu(d3.mouse(this.parentNode)[0], d3.mouse(this.parentNode)[1]);

或者,按照最惯用的方式,只需将SVG用作容器:

menu(d3.mouse(svg.node())[0], d3.mouse(svg.node())[1]);

这是您的代码,只有该更改:

.node {
  fill: #000;
}

.cursor {
  fill: green;
  stroke: brown;
  pointer-events: none;
}


.node text {
  pointer-events: none;
  font: 10px sans-serif;
}

path.link {
  fill: none;
  stroke: #666;
  stroke-width: 1.5px;
}



.link {
  fill: none;
  stroke: #666;
  stroke-width: 1.5px;
}

circle {
  fill: green;
  stroke: red;
  stroke-width: 1.5px;
}

text {
  font: 10px sans-serif;
  pointer-events: none;
  text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
    <script>
    var width = 960,    height = 500;
    var links = [{source:"simulator",target:"monitor" ,type:"resolved"} , {source:"web",target:"monitor" ,type:"resolved"} ];

 
 
    var nodes = [ {"id":"monitor", "grp":"system"}, {"id":"simulator", "grp":"system"}, {id:"web", grp:"client"}];


      function reset() {
        }

 
function contextMenu() {
    var height,
        width, 
        margin = 0.1, // fraction of width
        items = [], 
        rescale = false, 
        style = {
            'rect': {
                'mouseout': {
                    'fill': 'rgb(244,244,244)', 
                    'stroke': 'white', 
                    'stroke-width': '1px'
                }, 
                'mouseover': {
                    'fill': 'rgb(200,200,200)'
                }
            }, 
            'text': {
                'fill': 'steelblue', 
                'font-size': '13'
            }
        }; 
    
    function menu(x, y) {
        d3.select('.context-menu').remove();
        scaleItems();

        // Draw the menu
        d3.select('svg')
            .append('g').attr('class', 'context-menu')
            .selectAll('tmp')
            .data(items).enter()
            .append('g').attr('class', 'menu-entry')
            .style({'cursor': 'pointer'})
            .on('mouseover', function(){ 
                d3.select(this).select('rect').style(style.rect.mouseover) })
            .on('mouseout', function(){ 
                d3.select(this).select('rect').style(style.rect.mouseout) });
        
        d3.selectAll('.menu-entry')
            .append('rect')
            .attr('x', x)
            .attr('y', function(d, i){ return y + (i * height); })
            .attr('width', width)
            .attr('height', height)
            .style(style.rect.mouseout);
        
        d3.selectAll('.menu-entry')
            .append('text')
            .text(function(d){ return d; })
            .attr('x', x)
            .attr('y', function(d, i){ return y + (i * height); })
            .attr('dy', height - margin / 2)
            .attr('dx', margin)
            .style(style.text);

        // Other interactions
        d3.select('body')
            .on('click', function() {
                d3.select('.context-menu').remove();
            });

    }
    
    menu.items = function(e) {
        if (!arguments.length) return items;
        for (i in arguments) items.push(arguments[i]);
        rescale = true;
        return menu;
    }

    // Automatically set width, height, and margin;
    function scaleItems() {
        if (rescale) {
            d3.select('svg').selectAll('tmp')
                .data(items).enter()
                .append('text')
                .text(function(d){ return d; })
                .style(style.text)
                .attr('x', -1000)
                .attr('y', -1000)
                .attr('class', 'tmp');
            var z = d3.selectAll('.tmp')[0]
                      .map(function(x){ return x.getBBox(); });
            width = d3.max(z.map(function(x){ return x.width; }));
            margin = margin * width;
            width =  width + 2 * margin;
            height = d3.max(z.map(function(x){ return x.height + margin / 2; }));
            
            // cleanup
            d3.selectAll('.tmp').remove();
            rescale = false;
        }
    }

    return menu;
}


var width = 900,
  height = 600,
    radius = 8;


var map = {}
nodes.forEach(function(d,i){
  map[d.id] = i;
})

links.forEach(function(d) {
  d.source = map[d.source];
  d.target = map[d.target];
})

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

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


// Per-type markers, as they don't inherit styles.
svg.append("defs").selectAll("marker")
    .data(["suit", "licensing", "resolved"])
  .enter().append("marker")
    .attr("id", function(d) { return d; })
    .attr("viewBox", "0 -5 10 10")
    .attr("refX", 15)
    .attr("refY", -1.5)
    .attr("markerWidth", 6)
    .attr("markerHeight", 6)
    .attr("orient", "auto")
  .append("path")
    .attr("d", "M0,-5L10,0L0,5");

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

var menu = contextMenu().items('first item', 'second option', 'whatever, man');

var circle = svg.append("g").selectAll("circle")
    .data(force.nodes())
  .enter().append("circle")
    .attr("r", 6)
    .call(force.drag)
    .on('contextmenu', function(){ 
        d3.event.preventDefault();
        menu(d3.mouse(svg.node())[0], d3.mouse(svg.node())[1]);
    });

var text = svg.append("g").selectAll("text")
    .data(force.nodes())
  .enter().append("text")
    .attr("x", 8)
    .attr("y", ".31em")
    .text(function(d) { return d.id; });

var node = svg.selectAll(".node"),
  link = svg.selectAll(".link");

  
function mousedownNode(d, i) {
  nodes.splice(i, 1);
  links = links.filter(function(l) {
    return l.source !== d && l.target !== d;
  });
  d3.event.stopPropagation();

  refresh();
}
  
// Use elliptical arc path segments to doubly-encode directionality.
function tick() {
  path.attr("d", linkArc);
  circle.attr("transform", transform);
  text.attr("transform", transform);
}

function linkArc(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;
}

function transform(d) {
  return "translate(" + d.x + "," + d.y + ")";
}



    </script>