Полигон d3 и значок корзины на краю

Я хочу визуализировать многоугольники в d3.js, но на краю каждого многоугольника добавить значок корзины, чтобы можно было удалить сам многоугольник.

Затем следите за оставшимися полигонами.

  1. как вы добавляете значки кругов / корзины к краю границ многоугольника — в верхней части фигуры?
  2. как вы делаете/рендерите значки корзины — изображение png или другую форму svg, которая прикрепляется

http://jsfiddle.net/o9t6ardh/

<script src = "https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script src = "http://d3js.org/d3.v3.min.js" charset = "utf-8"></script>

<script>
  $(document).ready(function() {

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

    var scaleX = d3.scale.linear()
      .domain([-30, 30])
      .range([0, 600]);

    var scaleY = d3.scale.linear()
      .domain([0, 50])
      .range([500, 0]);

    var data = [{
        "code": "BR23",
        "points": "405,100.5625,331,108.5625,300,120.5625,290,141.5625,283,158.5625,278,171.5625,289,209.5625,304,231.5625,332,255.5625,351,264.5625,383,268.5625,400,266.5625,440,266.5625,462,266.5625,510,257.5625,522,230.5625,530,210.5625,534,183.5625,524,163.5625,512,148.5625,490,134.5625,462,124.5625"
      },
      {
        "code": "FR23",
        "points": "702,164.5625,594,250.5625,689,324.5625,749,261.5625,770,206.5625"
      }
    ];

    vis.selectAll("polygon")
      .data(data)
      .enter().append("polygon")
      .attr("points", function(d, i) {
        return d.points
      })
      .attr("fill", "transparent")
      .attr("stroke", "red")
      .attr("stroke-width", 2)
      .style("stroke-dasharray", ("15, 15")) // <== This line here!!
      .on("click", function(d, i) {
        console.info("d", d);
        d3.select(this).style("fill", "blue");
      });


    vis.append('circle')
      .attr('cx', 110)
      .attr('cy', 110)
      .attr('r', 20)
      .attr('stroke', 'black')
      .attr('fill', '#69a3b2')
      .on("click", function(d, i) {
        console.info("d", d);
      });


  });
</script>

Вам нужно разработать границы фигур?

Как выровнять точки на внешних краях многоугольника для более толстой границы в svg?

последние jsfiddles

http://jsfiddle.net/n6bdavxk/

http://jsfiddle.net/5kqLftzy/1/

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
0
59
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Удалите первые два скрипта и используйте document.querySelectorAll, тогда все заработает.

это не объясняет, как установить другой объект svg или изображения на границу многоугольника вверху

The Old County 27.05.2023 10:16

d3.js и jquery требуются для запуска сценария приложения. Удаление этих ресурсов сценария нарушит работу сценария. Кроме того, нет никаких проблем, связанных с выбором. Пожалуйста, исправьте свой ответ.

herrstrietzel 28.05.2023 21:24

Нет, вам не нужно использовать jquery, тогда он работает. Я проверил ваш код.

Johanna Platter 29.05.2023 08:39

Справедливо, но как насчет D3? И где бы вы использовали querySelectorAll()? Как это решает проблему OP с расположением значков? Пожалуйста, предоставьте работающий пример.

herrstrietzel 30.05.2023 10:39
Ответ принят как подходящий

Подход 1: получить ограничивающую рамку многоугольника

Как уже упоминалось, вам необходимо рассчитать координаты ограничивающей рамки для каждого полигона в вашем наборе данных.

Вероятно, будет удобнее добавить эти координаты непосредственно в массив данных, чтобы вы могли использовать эти данные в цепочке функций D3 для позиционирования.

Вот простая вспомогательная функция:

function getDataBounds(data) {
    data.forEach((item, i) => {
      let coords = item.points.split(",").map((val) => {
        return +val;
      });
      let xAll = coords.filter((coord, i) => i % 2 === 0);
      let yAll = coords.filter((coord, i) => i % 2 !== 0);
      let xMin = Math.min(...xAll);
      let xMax = Math.max(...xAll);
      let yMin = Math.min(...yAll);
      let yMax = Math.max(...yAll);
      let bb = {
        x: xMin,
        y: yMin,
        cx: xMin + (xMax - xMin) / 2,
        cy: yMin + (yMax - yMin) / 2,
        top: yMin,
        bottom: yMax,
        right: xMax,
        left: xMin,
        width: xMax - xMin,
        height: yMax - yMin
      };
      data[i].bb = bb;
    });
    return data;
  }

Он перебирает все точки многоугольника и вычисляет ограничивающую рамку в соответствии с экстремумами x и y.

Координаты центра тяжести будут:

    cx: xMin + (xMax - xMin) / 2,
    cy: yMin + (yMax - yMin) / 2,  

Если ваши полигоны уже добавлены в DOM, вы также можете использовать getBBox().

Создать значок кнопки корзины

Я бы рекомендовал создать значок в редакторе, таком как inkscape, включая фоновый круг. Таким образом, вам не нужно добавлять несколько элементов (имхо, выбор родительских узлов в d3 может быть немного «неинтуитивным» ...).

body{
  background:#ccc;
}

svg{
  color:red
}
<h3>Icon Design</h3>
<svg  viewBox = "0 0 640 720" width = "40" height = "40">
  <circle cx = "50%" cy = "50%" r = "50%" fill = "white"/>
  <path d = "M152.6 576v-378h-24.6v-36h112.8v-18h158.4v18h112.8v36h-24.6v378h-334.8zm36-36h262.8v-342h-262.8v342zm63.6-51.6h36v-239.4h-36v239.4zm99.6 0h36v-239.4h-36v239.4zm-163.2-290.4v342v-342z" fill = "currentColor"/>
</svg>


<h3>Reusable Icon asset</h3>
<svg width = "40" height = "40">><use href = "#icnTrash"/></svg>
<svg width = "40" height = "40">><use href = "#icnTrash" style = "color:green"/></svg>

<!-- hidden icon asset -->
<svg style = "width:0; height:0">
  <symbol id = "icnTrash" viewBox = "0 0 640 720">
    <circle cx = "50%" cy = "50%" r = "50%" fill = "white" />
    <path d = "M152.6 576v-378h-24.6v-36h112.8v-18h158.4v18h112.8v36h-24.6v378h-334.8zm36-36h262.8v-342h-262.8v342zm63.6-51.6h36v-239.4h-36v239.4zm99.6 0h36v-239.4h-36v239.4zm-163.2-290.4v342v-342z" fill = "currentColor" />
  </symbol>
</svg>

Пример

$(document).ready(function () {
  var vis = d3
    .select("body")
    .append("svg")
    .attr("width", 1000)
    .attr("height", 667);

  var scaleX = d3.scale.linear().domain([-30, 30]).range([0, 600]);
  var scaleY = d3.scale.linear().domain([0, 50]).range([500, 0]);

  var data = [
    {
      code: "BR23",
      points:
        "405,100.5625,331,108.5625,300,120.5625,290,141.5625,283,158.5625,278,171.5625,289,209.5625,304,231.5625,332,255.5625,351,264.5625,383,268.5625,400,266.5625,440,266.5625,462,266.5625,510,257.5625,522,230.5625,530,210.5625,534,183.5625,524,163.5625,512,148.5625,490,134.5625,462,124.5625"
    },
    {
      code: "FR23",
      points: "702,164.5625,594,250.5625,689,324.5625,749,261.5625,770,206.5625"
    }
  ];

  /**
   * calculate bounds for polygons
   *
   */
  function getDataBounds(data) {
    data.forEach((item, i) => {
      let coords = item.points.split(",").map((val) => {
        return +val;
      });
      let xAll = coords.filter((coord, i) => i % 2 === 0);
      let yAll = coords.filter((coord, i) => i % 2 !== 0);
      let xMin = Math.min(...xAll);
      let xMax = Math.max(...xAll);
      let yMin = Math.min(...yAll);
      let yMax = Math.max(...yAll);
      let bb = {
        x: xMin,
        y: yMin,
        cx: xMin + (xMax - xMin) / 2,
        cy: yMin + (yMax - yMin) / 2,
        top: yMin,
        bottom: yMax,
        right: xMax,
        left: xMin,
        width: xMax - xMin,
        height: yMax - yMin
      };
      data[i].bb = bb;
    });
    return data;
  }

  // add boundaries to data array
  data = getDataBounds(data);
  let iconWidth = 40;
  let iconHeight = 40;

  vis
    .selectAll("polygon")
    .data(data)
    .enter()
    .append("polygon")
    .attr("id", (d, i) => {
      return "polygon" + d.code;
    })
    .attr("points", function (d, i) {
      return d.points;
    })
    .attr("fill", "transparent")
    .attr("stroke", "red")
    .attr("stroke-width", 2)
    .style("stroke-dasharray", "15, 15") // <== This line here!!
    .on("click", function (d, i) {
      d3.select(this).node().classList.toggle("selected");
      //let btn = document.getElementById("btnPoly-" + d.code);
      //btn.classList.toggle("hidden");
    });

  // add circles
  vis
    .selectAll("use")
    .data(data)
    .enter()
    .append("use")
    .attr("class", "btnTrash")
    .attr("id", function (d, i) {
      return "btnPoly-" + d.code;
    })
    .attr("width", iconWidth)
    .attr("height", iconHeight)
    .attr("href", "#icnTrash")
    .attr("x", function (d, i) {
      return d.bb.cx - iconWidth / 2;
    })
    .attr("y", function (d, i) {
      return d.bb.top - iconHeight / 2 - 20;
    })
    .on("click", (d, i) => {
      let poly = document.getElementById("polygon" + d.code);
      let btnTrash = document.getElementById("btnPoly-" + d.code);
      btnTrash.remove();
      poly.remove();
    });
});
body{
  background:#ccc;
}

.btnTrash{
  color:red
}

.hidden{
  opacity:0
}

.selected{
  stroke: green
}
<script src = "https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script src = "http://d3js.org/d3.v3.min.js" charset = "utf-8"></script>

<!-- hidden icon asset -->
<svg style = "width:0; height:0">
  <symbol id = "icnTrash" viewBox = "0 0 640 720">
    <circle cx = "50%" cy = "50%" r = "50%" fill = "white" />
    <path d = "M152.6 576v-378h-24.6v-36h112.8v-18h158.4v18h112.8v36h-24.6v378h-334.8zm36-36h262.8v-342h-262.8v342zm63.6-51.6h36v-239.4h-36v239.4zm99.6 0h36v-239.4h-36v239.4zm-163.2-290.4v342v-342z" fill = "currentColor" />
  </symbol>
</svg>

Подход 2: получить самую верхнюю точку контура многоугольника

Как и в первом подходе, мы обновляем исходный объект данных. На этот раз мы ищем самую верхнюю точку контура многоугольника.

Мы получаем эту точку, сравнивая значения y каждой вершины многоугольника.

  function getTopMostPoint(data) {
    data.forEach((item, i) => {
      let points = item.points.split(',');
      let topY = 999999;
      let pTop = {}
      for(let p=0; p<points.length; p+=2){
        let pt = {x:+points[p], y:+points[p+1]}
        if (pt.y<topY){
          pTop = pt;
          topY = pt.y;
        }
      }
      data[i].anchor = {
        x: pTop.x,
        y: pTop.y
      };
     
    });
    return data;
  }

  
  var data = [
    {
      code: "BR23",
      points:
        "405,100.5625,331,108.5625,300,120.5625,290,141.5625,283,158.5625,278,171.5625,289,209.5625,304,231.5625,332,255.5625,351,264.5625,383,268.5625,400,266.5625,440,266.5625,462,266.5625,510,257.5625,522,230.5625,530,210.5625,534,183.5625,524,163.5625,512,148.5625,490,134.5625,462,124.5625"
    },
    {
      code: "FR23",
      points: "702,164.5625,594,250.5625,689,324.5625,749,261.5625,770,206.5625"
    }
  ];

/**
   * calculate anchor for polygons
   * find topmost point on polygon outline
   *
   */
  function getTopMostPoint(data) {
    data.forEach((item, i) => {
      let points = item.points.split(',');
      let topY = 999999;
      let pTop = {}
      for(let p=0; p<points.length; p+=2){
        let pt = {x:+points[p], y:+points[p+1]}
        if (pt.y<topY){
          pTop = pt;
          topY = pt.y;
        }
      }
      data[i].anchor = {
        x: pTop.x,
        y: pTop.y
      };
     
    });
    return data;
  }



$(document).ready(function () {
  var vis = d3
    .select("body")
    .append("svg")
    .attr("width", 1000)
    .attr("height", 667);

  var scaleX = d3.scale.linear().domain([-30, 30]).range([0, 600]);
  var scaleY = d3.scale.linear().domain([0, 50]).range([500, 0]);

  // get topmost point
  data = getTopMostPoint(data);

  let iconWidth = 40;
  let iconHeight = 40;

  vis
    .selectAll("polygon")
    .data(data)
    .enter()
    .append("polygon")
    .attr("id", (d, i) => {
      return "polygon" + d.code;
    })
    .attr("points", function (d, i) {
      return d.points;
    })
    .attr("fill", "transparent")
    .attr("stroke", "red")
    .attr("stroke-width", 2)
    .style("stroke-dasharray", "15, 15") // <== This line here!!
    .on("click", function (d, i) {
      d3.select(this).node().classList.toggle("selected");
      //let btn = document.getElementById("btnPoly-" + d.code);
      //btn.classList.toggle("hidden");
    });

  // add circles
  vis
    .selectAll("use")
    .data(data)
    .enter()
    .append("use")
    .attr("class", "btnTrash")
    .attr("id", function (d, i) {
      return "btnPoly-" + d.code;
    })
    .attr("width", iconWidth)
    .attr("height", iconHeight)
    .attr("href", "#icnTrash")
    .attr("x", function (d, i) {
      return d.anchor.x - iconWidth / 2;
    })
    .attr("y", function (d, i) {
      return d.anchor.y - iconHeight / 2 - 20;
    })
    .on("click", (d, i) => {
      let poly = document.getElementById("polygon" + d.code);
      let btnTrash = document.getElementById("btnPoly-" + d.code);
      btnTrash.remove();
      poly.remove();
    });
});
body{
  background:#ccc;
}

.btnTrash{
  color:red
}

.selected{
  stroke: green
}
<script src = "https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script src = "http://d3js.org/d3.v3.min.js" charset = "utf-8"></script>

<!-- hidden icon asset -->
<svg style = "width:0; height:0">
  <symbol id = "icnTrash" viewBox = "0 0 640 720">
    <circle cx = "50%" cy = "50%" r = "50%" fill = "white" />
    <path d = "M152.6 576v-378h-24.6v-36h112.8v-18h158.4v18h112.8v36h-24.6v378h-334.8zm36-36h262.8v-342h-262.8v342zm63.6-51.6h36v-239.4h-36v239.4zm99.6 0h36v-239.4h-36v239.4zm-163.2-290.4v342v-342z" fill = "currentColor" />
  </symbol>
</svg>

ваш код не отображает мусорный ящик на границе полигонов?

The Old County 28.05.2023 00:48

Не уверен ... они будут отображаться только при нажатии на полигон. Вы также можете отобразить их при первоначальном рендеринге. Просто удалите правило непрозрачности css.

herrstrietzel 28.05.2023 00:58

да - они должны быть при первоначальном рендеринге - я думаю - а затем, если пользователь щелкнет корзину, он удалит многоугольник - я не думаю, что есть изменение css, чтобы заполнить синим цветом сам многоугольник - поскольку там будет изображение под d3.js

The Old County 28.05.2023 03:54

Взгляните на второй пример, основанный на getPointAtLength. В этом примере будут найдены привязки, которые всегда находятся на контуре полигонов. Кнопки всегда видны. Я добавил образец выбранного класса, который переключается при щелчке по многоугольнику.

herrstrietzel 28.05.2023 20:39

@The Old County: глупый я! Нам не нужна функция getPointAtLength(), но мы можем использовать уже известные вершины многоугольника, что намного быстрее! Проверьте обновленный пример. Если вам нужен (неуклюжий) предыдущий пример, см. этот codepen

herrstrietzel 28.05.2023 21:20

Возможность создать еще один вариант, в котором белый фон мусорной корзины больше похож на «значок местоположения», например, в форме капли слезы, в соответствии с исходным эталоном / дизайном.

The Old County 30.05.2023 06:23

@The Old County: зачем мне добавлять ссылку jsfiddle, если есть запущенные фрагменты? (всегда предпочтительнее).

herrstrietzel 30.05.2023 10:49

Вот снова ссылки jsfiddle -- jsfiddle.net/n6bdavxk -- jsfiddle.net/5kqLftzy/1

The Old County 31.05.2023 00:58

Итак, из двух подходов - какой вы считаете лучшим и почему - я не был уверен, в чем разница - один использует расчет границы?

The Old County 31.05.2023 00:59

@The Old County: с точки зрения дизайна я бы предпочел второй подход, поскольку значок всегда будет «касаться» контура многоугольника. Первый подход может быть удобен, если вам нужно расположить кнопку удаления по центру. Я прошу прощения за то, что был немного резок в отношении jsfiddle. Просто лучше включить примеры во фрагменты SO, так как вы можете удалить внешние скрипки/или кодовые ручки и т. д. Это особенно верно, если отвечающий пользователь не создал эту скрипку (кроме того, я думаю, что jsfiddle должен пересмотреть свой пользовательский интерфейс - отзывчивый вид вроде s * кс =)

herrstrietzel 31.05.2023 01:12

Все в порядке — я знаю, что это отстой — фрагменты в порядке — но мне нравится делать jsfiddle и пытаться сохранить бесплатную онлайн-ссылку — а также играть локально. Я просто не хотел ограничиваться определенной строкой как единственным способом увидеть это в сообщении в блоге. У тебя бесценные знания, лол.

The Old County 31.05.2023 17:40

Другие вопросы по теме