说我有一个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中不起作用,因为<g>
容器内的<svg>
元素不受限制地限制到其内容的显示部分的边界框(而不是它的大小与外部<svg>
相同,带有一些缩放(。
可能使用createSVGPoint()
和getScreenCTM()
或getCTM()
有一个解决方案,但坦率地说,我不确定我应该使用什么。
没有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>
我希望这就是您要问的。
我设法使用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;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="page">
<div>
<button id="run">Run</button>
<button id="reset">Reset</button>
</div>
<svg id="container">
<g x="0" y="0" id="group" width="100%" height="100%">
<line x1="20" y1="50" x2="150" y2="150" stroke="brown" />
<svg x="20" y ="50">
<circle cx="0" cy="0" r="35" stroke="red" fill="blue">
<text x="35" y="0" height="100%" width="100%">a bunch of text</text>
</svg>
<line x1="100" y1="350" x2="160" y2="340" stroke="brown" />
<svg x="100" y ="350">
<circle cx="0" cy="0" r="35" stroke="red" fill="blue">
<text x="35" y="0" height="100%" width="100%">a bunch of text 3</text>
</svg>
</g>
<svg>
<div>