将SVG路径定位到rect元素的中间

时间:2017-08-24 09:27:28

标签: javascript d3.js svg rect

我正在使用D3库来绘制元素的互连图。我的节点circlesrects由定向行path连接。

我的问题是指向rects元素的线条有一个丑陋的可视化,因为线条在矩形的左上角而不是它的中心结束(就像圆圈一样)。

如何将路径线定位到circles元素和rect元素的中心?

<小时/> defs箭头的定义代码:

svg.append('defs')
  .append('marker')
  .attr('id', 'arrow')
  .attr('viewBox', '0 -5 10 10')
  .attr('refX', 17) // As far as I understood this provides the distance from the end of the path line.
  .attr('refY', -0.1)
  .attr('markerWidth', 6)
  .attr('markerHeight', 6)
  .attr('orient', 'auto')
  .attr('fill', function() {
    return 'red';
  })
  .append('path')
  .attr('d', 'M0,-5L10,0L0,5');

定向链接的定义:

let links = svg.selectAll('.link')
  .data(data.links)
  .enter()
  .append('path')
  .attr('id', function (d) {
    return d.id;
  })
  .attr('class', 'link')
  .attr('fill', 'none')
  .attr('stroke-width', 1.2)
  .attr('marker-end', 'url(#arrow)')
  .attr('stroke', function() {
    return 'blue';
  })
  .style('cursor', 'pointer');

正方形的定义

let squares = svg.selectAll('.square')
  .data(data.squares, function(d) {
    return d.id;
  })
  .enter().append('g')
  .call(dragger)
  .attr('class', 'square')
  .style('cursor', 'pointer');

squares.append('rect')
  .attr('width', 10)
  .attr('height', 10)
  .attr('fill', function (d) {
    return '#fff';
  })
  .style('opacity', 0.1)
  .style('stroke', function() {
    return '#555';
  })
  .style('stroke-width', '2');

在以下屏幕截图中,您可以看到它的行为方式。圆圈和圆圈的不透明度很低,无法显示路径目标的问题。

enter image description here

更新

添加了tick函数定义和用法。

simulation
  .nodes(data.nodes)
  .on('tick', _tick);

simulation
  .force('link')
  .distance(80)
  .links(data.links);

simulation.alpha(1).restart();

function _tick() {
  links.attr('d', function(d) {
    let dx = d.target.x - d.source.x;
    let dy = d.target.y - d.source.y;
    let 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);
  });
  circles.attr('transform', function (d) {
    return 'translate(' + d.x + ',' + d.y + ')';
  });
  squares.attr('transform', function (d) {
    return 'translate(' + d.x + ',' + d.y + ')';
  });
}

2 个答案:

答案 0 :(得分:2)

您现在拥有的是预期的行为。在力模拟中(我假设您正在运行力模拟),tick函数会更改基准对象的xy属性,您可以使用它们你想要的方式。

由于您没有共享tick功能,我认为您正在更新矩形&#39; xy这样的定位:

squares.attr("x", function(d) {
    return d.x
}).attr("y", function(d) {
    return d.y
});

如果事实上这是正确的,矩形的左上角对应d.xd.y坐标。而且,由于您使用相同的属性绘制路径,因此路径将从一个左上角转到另一个。

这很容易展示,请看一下这个演示:

&#13;
&#13;
var width = 200;
var height = 200;

var rectSide = 40;

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

var nodes = [{
  name: "foo",
  color: "blue"
}, {
  name: "bar",
  color: "green"
}, {
  name: "baz",
  color: "red"
}];

var links = [{
  "source": 0,
  "target": 1
}, {
  "source": 0,
  "target": 2
}];

var simulation = d3.forceSimulation()
  .force("link", d3.forceLink().distance(100))
  .force("charge", d3.forceManyBody().strength(-50))
  .force("center", d3.forceCenter(width / 2, height / 2));

var node = svg.selectAll(null)
  .data(nodes)
  .enter()
  .append("rect")
  .attr("width", rectSide)
  .attr("height", rectSide)
  .attr("fill", function(d) {
    return d.color
  });

var link = svg.selectAll(null)
  .data(links)
  .enter()
  .append("line")
  .style("stroke", "#222")
  .style("stroke-width", 2);

simulation.nodes(nodes);
simulation.force("link")
  .links(links);

simulation.on("tick", function() {

  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("x", function(d) {
    return d.x
  }).attr("y", function(d) {
    return d.y
  });

});
&#13;
<script src="https://d3js.org/d3.v4.js"></script>
&#13;
&#13;
&#13;

解决方案:您可以移动矩形或路径。

由于您的问题专门询问路径,解决方案很简单:向目标和源坐标添加半宽和半高:

link.attr("x1", function(d) {
        return d.source.x + rectangleWidth / 2;
    })
    .attr("y1", function(d) {
        return d.source.y + rectangleHeight / 2;
    })
    .attr("x2", function(d) {
        return d.target.x + rectangleWidth / 2;
    })
    .attr("y2", function(d) {
        return d.target.y + rectangleHeight / 2;
    })

这是一个演示:

&#13;
&#13;
var width = 200;
var height = 200;

var rectSide = 40;

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

var nodes = [{
  name: "foo",
  color: "blue"
}, {
  name: "bar",
  color: "green"
}, {
  name: "baz",
  color: "red"
}];

var links = [{
  "source": 0,
  "target": 1
}, {
  "source": 0,
  "target": 2
}];

var simulation = d3.forceSimulation()
  .force("link", d3.forceLink().distance(100))
  .force("charge", d3.forceManyBody().strength(-50))
  .force("center", d3.forceCenter(width / 2, height / 2));

var node = svg.selectAll(null)
  .data(nodes)
  .enter()
  .append("rect")
  .attr("width", rectSide)
  .attr("height", rectSide)
  .attr("fill", function(d) {
    return d.color
  });

var link = svg.selectAll(null)
  .data(links)
  .enter()
  .append("line")
  .style("stroke", "#222")
  .style("stroke-width", 2);

simulation.nodes(nodes);
simulation.force("link")
  .links(links);

simulation.on("tick", function() {

  link.attr("x1", function(d) {
      return d.source.x + rectSide / 2;
    })
    .attr("y1", function(d) {
      return d.source.y + rectSide / 2;
    })
    .attr("x2", function(d) {
      return d.target.x + rectSide / 2;
    })
    .attr("y2", function(d) {
      return d.target.y + rectSide / 2;
    })

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

});
&#13;
<script src="https://d3js.org/d3.v4.js"></script>
&#13;
&#13;
&#13;

答案 1 :(得分:-1)

  1. 你能翻译广场吗?如果你可以翻译它的一半 宽度。
  2. 你能找到像x2,y2那样在广场上的那条路径的终点吗?再加上方形宽度的一半,。
  3. “希望这能激发你的灵感”

    squares.append('rect')
      .attr('width', 10)
      .attr('height', 10)
      .attr("transform", "translate(-5,0)")
      .attr('fill', function (d) {
        return '#fff';
      })