带有标签的有界力图d3.js

时间:2015-11-19 09:23:01

标签: javascript d3.js force-layout

我正在尝试使用标签创建有界力图。 This是没有标签的有界力图的官方示例。有一次,我添加了我需要使用不同滴答功能的标签。当我使用适当的滴答功能时,圆形节点确实保持在边界内,但是,如果拖动超出边界线则继续行,这与我链接的示例不同。这是我带有标签的有界图的jsfiddle,但是如果拖过边界线仍然会移动(除了节点之外你可以拖动整个图形)。下面是代码。如果你可以帮我弄清楚如何将线条保持在边界内并模仿我链接的示例的行为,这将是很好的。感谢。

JS

var width = 280
height = 370
radius = 6;

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

var link = svg.selectAll(".link")
    .data(force.links())
    .enter().append("line")
    .attr("class", "link");

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

node.append("circle")
    .attr("r", radius)
    .style("fill", "#C71585");

node.append("text")
    .attr("x", 14)
    .attr("dy", ".35em")
    .style("fill", "#333")
    .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;
    });

    var dx = function(d) {return Math.max(radius, Math.min(width - radius, d.x))}
    var dy = function(d) {return Math.max(radius, Math.min(width - radius, d.y))}

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

function mouseover() {
    d3.select(this).select("circle").transition()
        .duration(750)
        .attr("r", 16);
}

function mouseout() {
    d3.select(this).select("circle").transition()
        .duration(750)
        .attr("r", 12);
}
// action to take on mouse click
function click() {
    d3.select(this).select("text").transition()
        .duration(750)
        .attr("x", 22)
        .style("stroke-width", ".5px")
        .style("opacity", 1)
        .style("fill", "#E34A33")
        .style("font", "17.5px serif");
    d3.select(this).select("circle").transition()
        .duration(750)
        .style("fill", "#E34A33")
        .attr("r", 16)
}

// action to take on mouse double click
function dblclick() {
    d3.select(this).select("circle").transition()
        .duration(750)
        .attr("r", 12)
        .style("fill", "#E34A33");
    d3.select(this).select("text").transition()
        .duration(750)
        .attr("x", 14)
        .style("stroke", "none")
        .style("fill", "#E34A33")
        .style("stroke", "none")
        .style("opacity", 0.6)
        .style("font", "14px serif");
}



var links = [{
  "source": "Analytics",
  "target": "Science"
}, {
  "source": "Analytics",
  "target": "Software"
}, {
  "source": "Analytics",
  "target": "Story"
}, {
  "source": "Science",
  "target": "Math"
}, {
  "source": "Science",
  "target": "Statistics"
}, {
  "source": "Software",
  "target": "R"
}, {
  "source": "Software",
  "target": "SAS"
}, {
  "source": "Software",
  "target": "Other"
}, {
  "source": "Story",
  "target": "Business Communication"
}, {
  "source": "Story",
  "target": "Visualization"
}];
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 = 280
height = 370
radius = 6;

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

var link = svg.selectAll(".link")
  .data(force.links())
  .enter().append("line")
  .attr("class", "link");

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

node.append("circle")
  .attr("r", radius)
  .style("fill", "#C71585");

node.append("text")
  .attr("x", 14)
  .attr("dy", ".35em")
  .style("fill", "#333")
  .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;
    });

  var dx = function(d) {
    return Math.max(radius, Math.min(width - radius, d.x))
  }
  var dy = function(d) {
    return Math.max(radius, Math.min(width - radius, d.y))
  }

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

function mouseover() {
  d3.select(this).select("circle").transition()
    .duration(750)
    .attr("r", 16);
}

function mouseout() {
    d3.select(this).select("circle").transition()
      .duration(750)
      .attr("r", 12);
  }
  // action to take on mouse click

function click() {
  d3.select(this).select("text").transition()
    .duration(750)
    .attr("x", 22)
    .style("stroke-width", ".5px")
    .style("opacity", 1)
    .style("fill", "#E34A33")
    .style("font", "17.5px serif");
  d3.select(this).select("circle").transition()
    .duration(750)
    .style("fill", "#E34A33")
    .attr("r", 16)
}

// action to take on mouse double click
function dblclick() {
  d3.select(this).select("circle").transition()
    .duration(750)
    .attr("r", 12)
    .style("fill", "#E34A33");
  d3.select(this).select("text").transition()
    .duration(750)
    .attr("x", 14)
    .style("stroke", "none")
    .style("fill", "#E34A33")
    .style("stroke", "none")
    .style("opacity", 0.6)
    .style("font", "14px serif");
}

.link {
  stroke: #666;
  opacity: 0.6;
  stroke-width: 1.5px;
}
.node circle {
  stroke: #fff;
  opacity: 0.6;
  stroke-width: 1.5px;
}
text {
  font: 15px serif;
  opacity: 0.6;
  pointer-events: none;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
&#13;
&#13;
&#13;

2 个答案:

答案 0 :(得分:1)

如果要创建拖动边界,则可以重新定义拖动方法而不是刻度线。

创建一个新的拖动:

var drag = force.drag()
  .on("drag", dragmove);

在节点上使用此拖动。

在新的dragmove方法中对节点的x,y坐标进行约束:

function dragmove(d) {
    var dx = function(d) {return Math.max(radius, Math.min(width - radius, d.x))}
    var dy = function(d) {return Math.max(radius, Math.min(width - radius, d.y))}
    d.px = Math.min(d3.event.x,200);
    d.py = Math.min(d3.event.y, 200);
    d.x = Math.min(d3.event.x, 200);
    d.y = Math.min(d3.event.y, 200);
}

你可以在这里查看: https://jsfiddle.net/n58to0tn/10/

答案 1 :(得分:1)

刻度函数需要稍微更改。使用节点转换函数内的计算有界值更新d.x和d.y位置,然后更新链接位置属性。

我已经改变了svg的背景颜色以清楚地看到边界效果。

var links = [{
  "source": "Analytics",
  "target": "Science"
}, {
  "source": "Analytics",
  "target": "Software"
}, {
  "source": "Analytics",
  "target": "Story"
}, {
  "source": "Science",
  "target": "Math"
}, {
  "source": "Science",
  "target": "Statistics"
}, {
  "source": "Software",
  "target": "R"
}, {
  "source": "Software",
  "target": "SAS"
}, {
  "source": "Software",
  "target": "Other"
}, {
  "source": "Story",
  "target": "Business Communication"
}, {
  "source": "Story",
  "target": "Visualization"
}];
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 = 280
height = 370
radius = 6;

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

var link = svg.selectAll(".link")
  .data(force.links())
  .enter().append("line")
  .attr("class", "link");

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

node.append("circle")
  .attr("r", radius)
  .style("fill", "#C71585");

node.append("text")
  .attr("x", 14)
  .attr("dy", ".35em")
  .style("fill", "#333")
  .text(function(d) {
    return d.name;
  });


function tick() {
  node.attr("transform", function(d) {
    var r = 16;
    d.x = Math.max(r, Math.min(width - r, d.x));
    d.y = Math.max(r, Math.min(height - r, d.y));
    return "translate(" + d.x + "," + d.y + ")"
  });

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

function mouseover() {
  d3.select(this).select("circle").transition()
    .duration(750)
    .attr("r", 16);
}

function mouseout() {
    d3.select(this).select("circle").transition()
      .duration(750)
      .attr("r", 12);
  }
  // action to take on mouse click

function click() {
  d3.select(this).select("text").transition()
    .duration(750)
    .attr("x", 22)
    .style("stroke-width", ".5px")
    .style("opacity", 1)
    .style("fill", "#E34A33")
    .style("font", "17.5px serif");
  d3.select(this).select("circle").transition()
    .duration(750)
    .style("fill", "#E34A33")
    .attr("r", 16)
}

// action to take on mouse double click
function dblclick() {
  d3.select(this).select("circle").transition()
    .duration(750)
    .attr("r", 12)
    .style("fill", "#E34A33");
  d3.select(this).select("text").transition()
    .duration(750)
    .attr("x", 14)
    .style("stroke", "none")
    .style("fill", "#E34A33")
    .style("stroke", "none")
    .style("opacity", 0.6)
    .style("font", "14px serif");
}
.link {
  stroke: #666;
  opacity: 0.6;
  stroke-width: 1.5px;
}
.node circle {
  stroke: #fff;
  opacity: 0.6;
  stroke-width: 1.5px;
}
text {
  font: 15px serif;
  opacity: 0.6;
  stroke: green;
  pointer-events: none;
}
svg{
  background-color: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>