向SVG圈子添加笑脸

时间:2018-08-29 13:45:15

标签: javascript d3.js svg

我想使用d3向包含许多circle元素的现有SVG中添加笑脸(或皱眉)。

到目前为止,通过将元素直接附加到SVG根目录,我已经能够实现这一目标。可行,但这只是因为它们的坐标恰好以正确的方式设置。

我想扩展它,以便能够在任意数量的圆圈中添加笑脸,无论它们位于何处。

我尝试过选择圈子并将其添加到圈子,但这是行不通的。

这是我到目前为止所取得的成就:

let svg = d3.select("#mySvg");

let appendedTo = svg;
//let appendedTo = svg.select(".mainCircle");

appendedTo.append("circle")
.attr("cx",13)
.attr("cy",15)
.attr("r",5);

appendedTo.append("circle")
.attr("cx",37)
.attr("cy",15)
.attr("r",5);


var arc = d3.svg.arc()
    .innerRadius(10)
    .outerRadius(11)
    .startAngle(3*(Math.PI/2)) //converting from degs to radians
    .endAngle(5 * (Math.PI/2)) //just radians
    
appendedTo.append("path")
    .attr("d", arc)
    .attr("transform", "translate(25,40)");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg width = 50 height = 50 id="mySvg">
  <circle class="mainCircle" cx=25 cy=25 r=25 fill="red"></circle>
</svg>

问题在于,圆圈在HTML页面上的位置发生了变化,笑脸的定位不正确。

您能给我一些指导,将笑脸“锚定”到圆形元素上吗?

编辑:

SVG示例:

<svg width = 500 height = 500 id="mySvg">
  <circle class="mainCircle" cx=25 cy=25 r=25 fill="red"></circle>
  <circle class="mainCircle" cx=125 cy=65 r=50 fill="red"></circle>
  <circle class="mainCircle" cx=200 cy=12 r=10 fill="red"></circle>
  <circle class="mainCircle" cx=210 cy=300 r=90 fill="red"></circle>
  <circle class="mainCircle" cx=320 cy=25 r=5 fill="red"></circle>
  <circle class="mainCircle" cx=400 cy=120 r=50 fill="red"></circle>
  <circle class="mainCircle" cx=410 cy=230 r=25 fill="red"></circle>        
</svg>

2 个答案:

答案 0 :(得分:2)

我认为这里的一个好的解决方案是创建一个函数,您可以将圆的属性(cxcyr)传递给该函数,从而创建基于表情的仅在这些值上。

自己创建圈子

例如,假设我们圈子的数据具有xyr作为这些属性。我们可以创建一个名为makeSmileys的函数,该函数绘制容器组中的圆圈和路径:

function makeSmileys(group, xPos, yPos, radius) {

  //left eye
  group.append("circle")
    .attr("cx", xPos - radius / 3)
    .attr("cy", yPos - radius / 3)
    .attr("r", radius / 8)
    .style("fill", "black");

  //right eye
  group.append("circle")
    .attr("cx", xPos + radius / 3)
    .attr("cy", yPos - radius / 3)
    .attr("r", radius / 8)
    .style("fill", "black");

  arc.innerRadius(radius / 2)
    .outerRadius(radius / 2.2);

  //mouth
  group.append("path")
    .attr("d", arc)
    .attr("transform", "translate(" + xPos + "," + yPos + ")");
}

如您所见,两只眼睛(圆圈)和嘴巴(路径)的位置仅基于参数。您可以按照自己的方式调整这些位置。

为使此功能正常工作,我们必须创建容器组,然后在相应的选择中调用它:

circlesGroup.each(function(d) {
  d3.select(this).call(makeSmileys, d.x, d.y, d.r)
})

因为我使用的是selection.call,所以第一个参数(即group)就是选择本身。或者,如果您不想使用selection.call,只需将函数作为普通的JavaScript函数调用,并将容器传递给它即可。

这是一个演示,带有10个随机生成的圆圈:

const svg = d3.select("svg");

const data = d3.range(10).map(function(d) {
  return {
    x: 50 + Math.random() * 500,
    y: 50 + Math.random() * 300,
    r: Math.random() * 50
  }
});

const arc = d3.arc()
  .startAngle(1 * (Math.PI / 2))
  .endAngle(3 * (Math.PI / 2));

const circlesGroup = svg.selectAll(null)
  .data(data)
  .enter()
  .append("g");

circlesGroup.each(function(d) {
  d3.select(this).append("circle")
    .attr("cx", d => d.x)
    .attr("cy", d => d.y)
    .attr("r", d => d.r)
    .style("fill", "yellow")
    .style("stroke", "black")
})

circlesGroup.each(function(d) {
  d3.select(this).call(makeSmileys, d.x, d.y, d.r)
})

function makeSmileys(group, xPos, yPos, radius) {
  group.append("circle")
    .attr("cx", xPos - radius / 3)
    .attr("cy", yPos - radius / 3)
    .attr("r", radius / 8)
    .style("fill", "black");

  group.append("circle")
    .attr("cx", xPos + radius / 3)
    .attr("cy", yPos - radius / 3)
    .attr("r", radius / 8)
    .style("fill", "black");

  arc.innerRadius(radius / 2)
    .outerRadius(radius / 2.2);

  group.append("path")
    .attr("d", arc)
    .attr("transform", "translate(" + xPos + "," + yPos + ")");
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg width="600" height="400"></svg>

使用先前存在的圈子

如果您已有SVG(如您在已编辑的问题中明确指出的那样),则可以使用选择器选择所有圈子...

const circles = svg.selectAll("circle");

...然后获取它们的属性,最后调用该函数:

circles.each(function() {
  const x = +d3.select(this).attr("cx");
  const y = +d3.select(this).attr("cy");
  const r = +d3.select(this).attr("r");
  makeSmileys(x, y, r)
});

在这里注意一元加号,因为获取器返回这些属性的字符串。

这是演示:

const svg = d3.select("svg");

const arc = d3.arc()
  .startAngle(1 * (Math.PI / 2))
  .endAngle(3 * (Math.PI / 2));

const circles = svg.selectAll("circle");

circles.each(function() {
  const x = +d3.select(this).attr("cx");
  const y = +d3.select(this).attr("cy");
  const r = +d3.select(this).attr("r");
  makeSmileys(x, y, r)
})

function makeSmileys(xPos, yPos, radius) {
  svg.append("circle")
    .attr("cx", xPos - radius / 3)
    .attr("cy", yPos - radius / 3)
    .attr("r", radius / 8)
    .style("fill", "black");

  svg.append("circle")
    .attr("cx", xPos + radius / 3)
    .attr("cy", yPos - radius / 3)
    .attr("r", radius / 8)
    .style("fill", "black");

  arc.innerRadius(radius / 2)
    .outerRadius(radius / 2.2);

  svg.append("path")
    .attr("d", arc)
    .attr("transform", "translate(" + xPos + "," + yPos + ")");
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg width="500" height="500" id="mySvg">
  <circle class="mainCircle" cx=25 cy=25 r=25 fill="yellow"></circle>
  <circle class="mainCircle" cx=125 cy=65 r=50 fill="yellow"></circle>
  <circle class="mainCircle" cx=200 cy=12 r=10 fill="yellow"></circle>
  <circle class="mainCircle" cx=210 cy=300 r=90 fill="yellow"></circle>
  <circle class="mainCircle" cx=320 cy=25 r=5 fill="yellow"></circle>
  <circle class="mainCircle" cx=400 cy=120 r=50 fill="yellow"></circle>
  <circle class="mainCircle" cx=410 cy=230 r=25 fill="yellow"></circle>   
</svg>

答案 1 :(得分:-2)

不要在SVG元素上使用唯一标识符,而应使用类似这样的类:

<svg width="50" height="50" class="face">

然后在D3中,您可以像这样引用此类的所有实例:

let svg = d3.selectAll(".face");