Нажмите на ограничивающую рамку пути на внешне загруженном svg, используя D3 - PullRequest
3 голосов
/ 24 февраля 2020

Я использую D3 для загрузки внешней карты SVG, которая не использует topo json (так как карта была создана вручную и не является традиционной картой). Я пытаюсь настроить таргетинг на элементы #lines path, чтобы при щелчке каждый путь увеличивался и заполнял ограничивающий прямоугольник.

Я пытаюсь использовать этот пример от Майка Бостока, но не могу понять, как скопировать его с данными, которые не используют topo json. Смотрите эту строку:

.data(topojson.feature(us, us.objects.states).features)

Это вообще возможно?

Вот код, который я использую для загрузки SVG.

var mapContainer = $('.map');

d3.xml("../assets/subwaymap.svg", function(error, subwayMap) {
  if (error) throw error;
  $('.map').append(subwayMap.documentElement)

Я пытался получить ограничивающую рамку, используя .getBBOX, но не понимаю, как она подключается. Кажется, что все примеры, которые я видел, используют d3.create("svg"), а затем используют все функциональные возможности, но так как мои данные уже добавлены в DOM, будет ли это необходимо? Довольно новый для D3. Спасибо!

1 Ответ

2 голосов
/ 24 февраля 2020

Два начальных соображения: d3.create("svg") редко используется в реальных кодах D3. Кроме того, у вас нет данных, добавленных в DOM, только загруженные вами элементы SVG (если только вы не называете это «data» ).

Если вернуться к вашему вопросу, вы не Для работы кода не требуется path.bounds, на самом деле вам даже не нужно d3.zoom. Все, что вам нужно, это получить коробку элемента (с getBBox) и выполнить соответствующее преобразование.

Однако реальная проблема заключается в том, что вам нужно обернуть все элементы в <g>, потому что вы не можете примените преобразование к root SVG в SVG 1.1 (очевидно, это возможно в SVG 2).

Вот базовая демонстрация c. В этой демонстрации я использую внешний SVG, созданный с различными элементами (круг, прямоугольник, текст ...), который представляет SVG, который вы добавляете. Вы получаете этот SVG с:

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

Затем, учитывая, что вам каким-то образом удалось решить упомянутую проблему <g>, вы получаете эту группу ...

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

... и вы выбираете элементы, которые хотите увеличить (здесь все), привязывая слушателя событий:

const elements = g.selectAll("*")
    .on("click", clicked);

В этой демонстрации я использую математику Бостока, чтобы сэкономить (мое) время, но вы можете Измени это. Нажмите на элемент, чтобы увеличить его, нажмите еще раз, чтобы уменьшить.

const width = 500,
  height = 400;
const svg = d3.select("svg");
const g = svg.select("g");
const elements = g.selectAll("*")
  .each(function() {
    d3.select(this).datum({})
  })
  .on("click", clicked);

function clicked(d) {
  d.clicked = !d.clicked;
  const bounds = this.getBBox();
  const x0 = bounds.x;
  const x1 = bounds.x + bounds.width;
  const y0 = bounds.y;
  const y1 = bounds.y + bounds.height;
  g.transition().duration(1000).attr("transform", d.clicked ? "translate(" + (width / 2) + "," + (height / 2) + ") scale(" + (1 / Math.max((x1 - x0) / width, (y1 - y0) / height)) + ") translate(" + (-(x0 + x1) / 2) + "," + (-(y0 + y1) / 2) + ")" : "transform(0,0) scale(1)");
}
<svg width="500" height="400">
<g>
<circle cx="50" cy="50" r="30" stroke="black" stroke-width="3" fill="teal"></circle>
<rect x="300" y="20" rx="20" ry="20" width="150" height="150" style="fill:tomato;stroke:black;stroke-width:3"/>
<polygon points="200,100 250,190 160,210" style="fill:lavender;stroke:purple;stroke-width:3" />
<path d="M 140 350 q 150 -200 350 0" stroke="blue" stroke-width="5" fill="none" />
<text x="30" y="300" transform="rotate(-30, 30, 300)">Foo Bar Baz</text>
</g>
</svg>
<script src="https://d3js.org/d3.v5.min.js"></script>
...