使用D3在外部加载的SVG上缩放以适应路径边界框

3
我正在使用D3加载一个外部SVG地图,该地图未使用topojson(因为该地图是手动创建的而非传统地图)。我尝试针对元素#lines path,使得当被点击时,每个路径都会缩放并填充其边界框。
我尝试使用Mike Bostock的这个示例,但无法弄清如何使用不使用topojson的数据进行复制。请看这行代码: .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相当新。谢谢!


请尝试添加足够的代码(例如示例SVG)以供我们复制。https://stackoverflow.com/help/minimal-reproducible-example - Alex L
你能不能只是选择你附加的svg,然后按照你提到的例子来操作呢? - Alex L
1个回答

2
两个初始考虑因素:实际上很少在真正的D3代码中使用d3.create("svg")。此外,您没有将数据附加到DOM中,只有您加载的SVG元素(除非您正在调用"data")。
回到您的问题,您不需要使用path.bounds使您的代码工作,实际上您甚至不需要使用d3.zoom。您所需要的就是获取元素的框(使用getBBox)并进行适当的转换。
然而,真正的问题是您需要将所有元素包装在一个<g>中,因为您无法将变换应用于SVG 1.1中的根SVG(显然这在SVG 2中是可能的)。
这里是一个基本演示。在此演示中,我使用了由不同元素(圆形、矩形、文本等)制作的外部SVG,该SVG表示您正在附加的SVG。您可以使用以下方式获取此SVG:
const svg = d3.select("svg");

然后,假设您成功解决了我提到的 <g> 问题,您将获得该组...

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

然后,您可以选择要缩放的元素(这里是全部元素),并绑定一个事件监听器:

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

在这个演示中,我使用了Bostock的数学库,以节省时间,但你可以更改它。点击元素进行放大,再次点击进行缩小。

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>


非常感谢!所有这些都非常有帮助,我从查找您建议的内容中学到了很多。 - lexiexiexi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接