首页 > 解决方案 > 为 SVG 圆圈添加笑脸

问题描述

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

到目前为止,我已经能够通过将元素直接附加到 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>

标签: javascriptd3.jssvg

解决方案


我认为这里的一个好的解决方案是创建一个函数,您可以将圆圈的属性(cxcy)传递给该函数,该函数r将仅基于这些值创建笑脸。

自己创建圈子

因此,例如,假设我们圈子的数据具有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)
});

请注意这里的一元加号,因为 getter 返回这些属性的字符串。

这是演示:

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>


推荐阅读