有向网络中的衰落网络连接 - d3js

时间:2016-02-26 15:36:34

标签: javascript d3.js

我有一个使用d3.layout.force的定向网络。调整this answer,如果连接(连接方向并不重要),我已设法获取节点和链接淡出。

我遇到的问题是,当标记所在的路径的不透明度因mouseover事件而改变时,能够更改标记的不透明度。

这是包含isConnected函数的脚本,用于确定连接的节点:

A live example is here.

<script>


  function bar() {
  console.log("click");
  force.stop();
  force.start();
}



  var links = [
  {source: "A", target: "D", type: "high"},
  {source: "A", target: "K", type: "high"},
  {source: "B", target: "G", type: "high"},
  {source: "C", target: "A", type: "low"},
  {source: "D", target: "K", type: "low"},
  {source: "E", target: "A", type: "low"},
  {source: "F", target: "B", type: "low"},
  {source: "K", target: "J", type: "low"},
  {source: "F", target: "A", type: "low"},
  {source: "F", target: "I", type: "low"},
  {source: "G", target: "H", type: "low"},
  {source: "E", target: "K", type: "high"},
  {source: "E", target: "G", type: "low"},
  {source: "E", target: "F", type: "high"},
  {source: "D", target: "E", type: "high"}  
];

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

var width = 960,
    height = 700;

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



force.on("start", function () {
    console.log("start");
});
force.on("end", function () {
    console.log("end");
});

R=18



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

// add defs-marker
svg.append('svg:defs')
    .append('svg:marker')
    .attr('id', 'end-arrow')
    .attr('viewBox', '0 0 10 10')
    .attr('refX', 2+R)
    .attr('refY', 5)
     .attr('markerWidth', 4)
     .attr('markerHeight', 4)
    .attr('orient', 'auto')
    .append('svg:path')
    .attr('d', 'M0,0 L0,10 L10,5 z');


var link = svg.selectAll(".link")
    .data(force.links())
    .enter()
    .append("line")
    .attr("class", "link")
    .attr('marker-end', 'url(#end-arrow)')
;  

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

node.append("circle")
    .attr("r", R)
    .on("mouseover", fade(.1))
    .on("mouseout", fade(1))
;

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




function tick() {
  link
      .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; })      
    ;

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



   var linkedByIndex = {};
    links.forEach(function(d) {
        linkedByIndex[d.source.index + "," + d.target.index] = 1;
    });

    function isConnected(a, b) {
        return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index] || a.index == b.index;
    }

 function fade(opacity) {
        return function(d) {
            node.style("stroke-opacity", function(o) {
                thisOpacity = isConnected(d, o) ? 1 : opacity;
                this.setAttribute('fill-opacity', thisOpacity);
                return thisOpacity;
            });

            link.style("stroke-opacity", function(o) {
                return o.source === d || o.target === d ? 1 : opacity;
            });

           marker.style("opacity", function(o) {
                return o.source === d || o.target === d ? 1 : opacity;
            });
        };
 }


</script>

一个与切线相关的问题是如何缩短路径,以便当节点和链路的不透明度消失时,到达每个节点中间的线路不明显。

1 个答案:

答案 0 :(得分:1)

由于渲染标记实例的方式,您的方法不可行。将我自己的answers中的一个变为Cannibalizing并引用SVG规范:

  

11.6.4 Details on how markers are rendered

     

[...]

     

标记的渲染效果就好像所引用的“标记”元素的内容被深深地克隆到标记的每个实例的单独的非暴露DOM树中。由于克隆的DOM树是非暴露的,因此SVG DOM不会显示标记的克隆实例。

只有原始标记元素(即声明的<marker>元素)可以使用CSS进行样式化,而通过属性marker-startmarker-midmarker-end引用的克隆实例是不可访问,因此不能单独设置样式。

  

CSS2选择器可以应用于原始(即引用)元素,因为它们是正式文档结构的一部分。 CSS2选择器不能应用于(概念上)克隆的DOM树,因为它的内容不是正式文档结构的一部分。

为了规避这些约束,你可以使用两个定义的marker元素,第二个是第一个克隆版本,不透明度降低。

// add defs-markers
svg.append('svg:defs').selectAll("marker")
    .data([{id:"end-arrow", opacity:1}, {id:"end-arrow-fade", opacity:0.1}])
  .enter().append('marker')
    .attr('id', function(d) { return d.id; })
    .attr('viewBox', '0 0 10 10')
    .attr('refX', 2+R)
    .attr('refY', 5)
    .attr('markerWidth', 4)
    .attr('markerHeight', 4)
    .attr('orient', 'auto')
  .append('svg:path')
    .attr('d', 'M0,0 L0,10 L10,5 z')
    .style("opacity", function(d) { return d.opacity; });

fade()功能中,您可以切换行marker-end属性以引用相应标记的ID:

link.attr("marker-end", function(o) {
  return opacity === 1 || o.source === d || o.target === d
    ? 'url(#end-arrow)' : 'url(#end-arrow-fade)';
});          

请查看以下代码片段以了解正常工作:

function bar() {
  console.log("click");
  force.stop();
  force.start();
}
  
var links = [
  {source: "A", target: "D", type: "high"},
  {source: "A", target: "K", type: "high"},
  {source: "B", target: "G", type: "high"},
  {source: "C", target: "A", type: "low"},
  {source: "D", target: "K", type: "low"},
  {source: "E", target: "A", type: "low"},
  {source: "F", target: "B", type: "low"},
  {source: "K", target: "J", type: "low"},
  {source: "F", target: "A", type: "low"},
  {source: "F", target: "I", type: "low"},
  {source: "G", target: "H", type: "low"},
  {source: "E", target: "K", type: "high"},
  {source: "E", target: "G", type: "low"},
  {source: "E", target: "F", type: "high"},
  {source: "D", target: "E", type: "high"}  
];

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

var width = 600,
    height = 600;

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

force.on("start", function () {
    console.log("start");
});
force.on("end", function () {
    console.log("end");
});

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

// add defs-markers
svg.append('svg:defs').selectAll("marker")
    .data([{id:"end-arrow", opacity:1}, {id:"end-arrow-fade", opacity:0.1}])
  .enter().append('marker')
    .attr('id', function(d) { return d.id; })
    .attr('viewBox', '0 0 10 10')
    .attr('refX', 2+R)
    .attr('refY', 5)
    .attr('markerWidth', 4)
    .attr('markerHeight', 4)
    .attr('orient', 'auto')
  .append('svg:path')
    .attr('d', 'M0,0 L0,10 L10,5 z')
    .style("opacity", function(d) { return d.opacity; });

var link = svg.selectAll(".link")
    .data(force.links())
    .enter()
    .append("line")
    .attr("class", "link")
    .attr('marker-end', 'url(#end-arrow)');  
 
var node = svg.selectAll(".node")
    .data(force.nodes())
    .enter().append("g")
    .attr("class", "node")
    .call(force.drag);

node.append("circle")
    .attr("r", R)
    .on("mouseover", fade(.1))
    .on("mouseout", fade(1))
;

node.append("text")
    .attr("x", 0)
    .attr("dy", ".35em")
    .text(function(d) { return d.name; });
  
function tick() {
  link.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; });

  node.attr("transform", function(d) {
    return "translate(" + d.x + "," + d.y + ")";
  });
}
  
var linkedByIndex = {};
links.forEach(function(d) {
  linkedByIndex[d.source.index + "," + d.target.index] = 1;
});

function isConnected(a, b) {
  return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index +   "," + a.index] || a.index == b.index;
}
  
function fade(opacity) {
  return function(d) {
    node.style("stroke-opacity", function(o) {
      thisOpacity = isConnected(d, o) ? 1 : opacity;
      this.setAttribute('fill-opacity', thisOpacity);
      return thisOpacity;
    });

    link.style("stroke-opacity", function(o) {
      return o.source === d || o.target === d ? 1 : opacity;
    });

    link.attr("marker-end", function(o) {
      return opacity === 1 || o.source === d || o.target === d
        ? 'url(#end-arrow)' : 'url(#end-arrow-fade)';
    });          
  };
 }
.node circle {
  fill: #DDD;
  stroke: #777;
  stroke-width: 2px;
}
.node text {
  font-family: sans-serif;
  text-anchor: middle;
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
}
.link {
  stroke: #88A;
  stroke-width: 4px;
}
text {
  font: 18px sans-serif;
  pointer-events: none;
}
#end-arrow {
  fill: #88A;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>