如何获得内部SVG元素相对于外部SVG元素的视口的位置?

时间:2019-05-24 16:40:52

标签: javascript html firefox svg

说我有一个包含某些内容的SVG元素:

<div style="margin-left:50px; width: 100%; min-height: 400px;">
  <svg>
    <g transform="translate(34.34,47.5) scale(0.345)" height="100%" width="100%">
      <svg x="20" y ="50" style="overflow: visible">
        <circle cx="0" cy="0" r="35" stroke="red" fill="blue">
        <text>a bunch of text</text>
      </svg>
      <line />
    </g>
  <svg>
<div>

我试图找到<g>相对于外部<svg>元素的视口的中心位置,以便我可以将<g>转换为在外部外部居中的位置<svg>,然后将其缩放到适合的大小。

我能够使用getBoundingClientRect()并调整转换比例使其工作,但是这在Firefox中不起作用,因为<svg>容器内的<g>元素不受限制内容显示部分的边界框(它的大小与外部<svg>相同,但有一定比例)。

可能有一种使用createSVGPoint()getScreenCTM()getCTM()的解决方案,但坦率地说,我不确定应该使用什么。

2 个答案:

答案 0 :(得分:0)

没有viewBox属性的SVG的宽度为300px,高度为150px。我添加了viewBox="0 0 300 150"。您可以删除它。

我还添加了一个矩形,以查看<g>元素的位置和大小。您也可以将其删除。

如何将<g>元素居中:由于<g>元素已经转换,因此获取其大小和位置的最简单方法就是包装<g>元素中的另一个元素,在这种情况下为<g id="wrap">,接下来我可以得到包装的边框:wrap.getBBox()

为了使包装居中,我需要知道主svg画布的中心:x = 300/2; y = 150/2。现在,我可以将套环平移到中心

let c = {x:150,y:75}//the center of the main svg element
let bb = wrap.getBBox()//the bounding box of the wrap

let transformation = `translate(${c.x - bb.x - bb.width/2},
                                ${c.y - bb.y - bb.height/2})`

wrap.setAttributeNS(null,"transform",transformation)
svg{border:1px solid;width:100vh;}
text{fill:black;}
path{fill:none;stroke:black}
<div style="margin-left:50px; width: 100%; min-height: 400px;">
  <svg id="main" viewBox="0 0 300 150" >
    <g id="wrap">
      <rect x="29.165" y="47.5" width="45.03" height="29.325" fill="gold" fill-opacity=".5" />
      <g transform="translate(34.34,47.5) scale(0.345)" height="100%" width="100%">
      <svg x="20" y ="50" style="overflow: visible">
        <circle cx="0" cy="0" r="35" stroke="red" fill="blue"/>
        <text>a bunch of text</text>
      </svg>
      <line />
    </g>
    </g>
    
    <path d="M0,0L300,150M0,150L300,0" />
  <svg>
<div>

我希望这就是你的要求。

答案 1 :(得分:0)

我设法使用... # variables have been defined at this point $variablesToPass = @{} 'BeQuiet,URL,LotsaMore' -split ',' | ForEach-Object { $variablesToPass[$_] = Get-Variable $_ -ValueOnly } Import-Module TheModule -ArgumentList $variablesToPass 变换方法(我们使用d3.zoom来管理平移/缩放变换)和d3.zoom中的一种来找到解决方案。我最初使用的是这种方法,但是却弄乱了计算结果。这样就可以了。

SVGElement.getBBox()
const selection = d3.select(group);
const zoomBehavior = d3.zoom().on('zoom', () => {
  selectionTransform = d3.event.transform;
});
selection.call(zoomBehavior);

const scaleAndTransformTo = () => {
  selection.call(zoomBehavior.translateBy, Math.random() * 100, Math.random() * 150);

  group.setAttribute("transform", selectionTransform.toString());
}
scaleAndTransformTo();

reset.addEventListener('click', scaleAndTransformTo);
run.addEventListener('click', () => {
  const { width: containerWidth, height: containerHeight } = container.getBoundingClientRect();
  const containerCenter = [containerWidth / 2, containerHeight / 2];

  const { height, width, x, y } = group.getBBox();
  const nodeBBoxCenter = [x + (width / 2), y + (height / 2)];

  // Apply the current interpolated translate/scale to the BBox center to get the actual position
  const groupCenterCoords = selectionTransform.apply(nodeBBoxCenter);

  const translationOffset = [
    (containerCenter[0] - groupCenterCoords[0]) / selectionTransform.k,
    (containerCenter[1] - groupCenterCoords[1]) / selectionTransform.k,
  ];

  selection.call(zoomBehavior.translateBy, ...translationOffset);

  group.setAttribute("transform", selectionTransform.toString());
});
#page {
  display: flex;
  flex-direction: column;
  position: relative;
  align-items: stretch;
  margin-left: 100px;
}

#container {
  background-color: grey;
  flex-grow: 1;
  flex-shrink: 0;
  min-height: 500px;
  border: 1px solid red;
}

#group > svg {
  overflow: visible;
}

#group > svg > circle {
  overflow: visible;
}

text {
  fill: black;
}