Добавьте смайлик в кружок SVG - PullRequest
0 голосов
/ 29 августа 2018

Я хочу использовать 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 смайлик будет расположен неправильно.

Не могли бы вы дать мне несколько указателей, чтобы «привязать» смайлик к элементу круга?

Edit:

Пример 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 голосов
/ 29 августа 2018

Я считаю, что хорошим решением здесь было бы создание функции, в которую можно передавать атрибуты кругов (cx, cy и r), которая создала бы смайлики, основываясь только на этих значениях.

Создание кругов самостоятельно

Так, например, предположим, что данные нашего круга имеют x, y и r в качестве этих атрибутов. Мы можем создать функцию с именем 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>
0 голосов
/ 29 августа 2018

Вместо использования уникального идентификатора в ваших SVG-элементах, используйте вместо этого класс, подобный этому:

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

Тогда в вашем D3 вы можете ссылаться на все экземпляры этого класса следующим образом:

let svg = d3.selectAll(".face");
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...