可以在D3中使用SVG标记吗?

时间:2018-01-10 06:27:35

标签: d3.js svg

我遇到了一些问题,让我的SVG标记能够在我触发'tick'回调时特别适用于不同的力量。我正在尝试在模拟运行时更新元素的cxcy属性,但它们本身并不具有我可以更新的属性。例如:

var shape = d3.selectAll('circle')
.on('mouseenter', handleCircleMouseEnter)
.on('mouseout', handleCircleMouseOut)
.on('mousemove', handleMouseMove)
.attr('cursor', 'pointer')
.attr('class', 'shapes')
.attr('cx', function(d) {return d.x;})
.attr('cy', function(d) {return d.y;})

var shapes = d3.selectAll('.shapes')

var simulation = d3.forceSimulation(shapes)

simulation
    .force('charge_force', d3.forceManyBody())
    .force('charge', d3.forceCollide().radius(5))
    .force('center_force', d3.forceCenter(width / 2, height / 2))
    .on('tick', ticked)

function ticked() {
    // always returns with 'd' as undefined
    shapes.attr('cx', function(d) {return d.x;})
    shapes.attr('cy', function(d) {return d.y;})
}

奇怪的是,如果我在'tick'属性中没有返回任何内容,我会用力量移动,但d永远不会被传入。

我正在做的一件奇怪的事情是我从来没有用D3构建这个SVG,我在标记中有一个SVG元素,我用力修改。不确定这是可能的,但似乎它几乎正常工作。不知所措......

非常感谢任何帮助!

1 个答案:

答案 0 :(得分:4)

如果我正确地理解了你的问题(我很想听到你这么做的话),你想对SVG中已存在的元素进行力模拟。如果是这种情况,答案是。但有一个问题:

力模拟使用对象数组来设置相关属性。根据{{​​3}}:

  

simulation.nodes([nodes])<>

     

如果指定了节点,则将模拟的节点设置为指定的对象数组,必要时初始化它们的位置和速度,然后重新初始化任何绑定力。 (强调我的)

该对象数组只是绑定到它们的数据。但是,当您获取SVG中已存在的元素时,没有数据绑定(除非我们手动绑定它们,继续阅读......),这解释了为什么d返回undefined。< / p>

因此,我们必须将数据绑定到元素。让我们看看如何做到这一点。假设这个SVG已经存在3个圆圈:

<svg>
  <circle cx="20" cy="75" r="20" fill="green"></circle>
  <circle cx="150" cy="75" r="20" fill="red"></circle>
  <circle cx="280" cy="75" r="20" fill="blue"></circle>
</svg>

要使用力模拟,我们首先设置每个圆的基准:

var circles = svg.selectAll("circle");
circles.each(function() {
  d3.select(this).datum({
    x: +d3.select(this).attr("cx"),
    y: +d3.select(this).attr("cy")
  })
});

它可以是任何对象,即使这样也可以:

var circles = svg.selectAll("circle");
circles.each(function() {
  d3.select(this).datum({foo: "bar"})
});

然后,我们将该对象数组传递给模拟:

var data = circles.data()
simulation.nodes(data)
  .on("tick", ticked);

这是一个演示:

var svg = d3.select("svg");
var simulation = d3.forceSimulation()
  .force("collide", d3.forceCollide(20))
  .force("charge", d3.forceManyBody().strength(+100))
var circles = svg.selectAll("circle");
circles.each(function() {
  d3.select(this).datum({
    x: +d3.select(this).attr("cx"),
    y: +d3.select(this).attr("cy")
  })
});
var data = circles.data()
simulation.nodes(data)
  .on("tick", ticked);

function ticked() {
  circles.attr("cx", function(d) {
      return d.x;
    })
    .attr("cy", function(d) {
      return d.y;
    });
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg>
  <circle cx="20" cy="75" r="20" fill="green"></circle>
  <circle cx="150" cy="75" r="20" fill="red"></circle>
  <circle cx="280" cy="75" r="20" fill="blue"></circle>
</svg>