Skip to content Skip to sidebar Skip to footer

How Do I Get An Internal Svg Element's Position Relative To The Viewport Of An Outer Svg Element?

Say I have an SVG element containing some stuff:
viewBox="0 0 300 150". You can remove it.

Also I've added a rectangle to be able to see the position and the size of the <g> element. You can remove it as well.

How I would center the <g> element: Since the <g> element is transformed the easiest way to get it's size and position would be wrapping the <g> element in another one, in this case <g id="wrap"> Next I can get the bounding box of the wrap: wrap.getBBox()

In order to center the wrap I need to know the center of the main svg canvas: x = 300/2; y=150/2. Now I can translate the wrap into the center

let c = {x:150,y:75}//the center of the main svg elementlet bb = wrap.getBBox()//the bounding box of the wraplet 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}
<divstyle="margin-left:50px; width: 100%; min-height: 400px;"><svgid="main"viewBox="0 0 300 150" ><gid="wrap"><rectx="29.165"y="47.5"width="45.03"height="29.325"fill="gold"fill-opacity=".5" /><gtransform="translate(34.34,47.5) scale(0.345)"height="100%"width="100%"><svgx="20"y ="50"style="overflow: visible"><circlecx="0"cy="0"r="35"stroke="red"fill="blue"/><text>a bunch of text</text></svg><line /></g></g><pathd="M0,0L300,150M0,150L300,0" /><svg><div>

I hope this is what you were asking.

Solution 2:

I managed to figure out a solution using one of the d3.zoom transform methods (we're using d3.zoom to manage the translate/scale transform), and SVGElement.getBBox(). I originally was using this method but had messed up the calculation; this way it works though.

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

constscaleAndTransformTo = () => {
  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 positionconst 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;
}
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script><divid="page"><div><buttonid="run">Run</button><buttonid="reset">Reset</button></div><svgid="container"><gx="0"y="0"id="group"width="100%"height="100%"><linex1="20"y1="50"x2="150"y2="150"stroke="brown" /><svgx="20"y ="50"><circlecx="0"cy="0"r="35"stroke="red"fill="blue"><textx="35"y="0"height="100%"width="100%">a bunch of text</text></svg><linex1="100"y1="350"x2="160"y2="340"stroke="brown" /><svgx="100"y ="350"><circlecx="0"cy="0"r="35"stroke="red"fill="blue"><textx="35"y="0"height="100%"width="100%">a bunch of text 3</text></svg></g><svg><div>

Post a Comment for "How Do I Get An Internal Svg Element's Position Relative To The Viewport Of An Outer Svg Element?"