(参考代码片段(我正在尝试绘制一条路径,从异物#item-A
内部的一个元素#item-A > div
到另一个元素的顶部#item-B > div
异物#item-B
内部。div 高度是可变的。
对于不同的浏览器缩放级别,路径M ${item_A_div_Bottom - svgOffset.y}
值给出不同的结果。这会破坏布局。
我浏览了各种文档,试图了解这种行为的原因:
- https://www.w3.org/TR/SVG/coords.html
- https://www.w3.org/TR/css-transforms-1
- https://www.w3.org/TR/SVG2/embedded.html
window.onload = function() {
var svgEl = document.querySelector('svg'),
itemA = document.querySelector('#item-A'),
itemB = document.querySelector('#item-B'),
path = document.createElementNS("http://www.w3.org/2000/svg", 'path');
const svgOffset = svgEl.getBoundingClientRect();
const {
a
} = svgEl.getScreenCTM();
const {
x: item_A_BBoxX,
} = itemA.getBBox();
const {
bottom: item_A_div_Bottom
} = document.querySelector(`#item-A > div`).getBoundingClientRect();
const {
y: item_B_BBoxY,
} = document.querySelector(`#item-B`).getBBox();
const path_d = `
M ${item_A_BBoxX},${(item_A_div_Bottom - svgOffset.y) * (1 / a)}
V ${item_B_BBoxY}
`;
path.setAttribute('d', path_d);
path.setAttribute('stroke', 'red');
path.setAttribute('stroke-width', '2px');
svgEl.appendChild(path);
}
#header {
width: 100%;
height: 30px;
background-color: purple;
}
#svg-wrapper {
display: flex;
justify-content: center;
height: 600px;
width: 100%;
background-color: grey;
}
svg {
width: 80%;
height: 100%;
}
foreignObject {
background-color: black;
}
<div id="header">Header</div>
<div id="svg-wrapper">
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<style>
div {
color: white;
font: 18px serif;
height: 40px;
overflow: auto;
background-color: black;
}
</style>
<foreignObject id="item-A" x="20" y="20" width="60" height="30">
<div xmlns="http://www.w3.org/1999/xhtml">
Item-A
</div>
</foreignObject>
<foreignObject id="item-B" x="20" y="120" width="60" height="30">
<div xmlns="http://www.w3.org/1999/xhtml">
Item-B
</div>
</foreignObject>
</svg>
</div>
如果放大或缩小,然后重新运行代码段,则路径位置和长度会发生变化。
如何防止布局因不同的缩放级别而中断?
element.getBoundingClientRect()
随浏览器缩放而更改返回的属性值。如 MDN 网络文档中给出的
Element.getBoundingClientRect(( 方法返回元素的大小及其相对于视口的位置。
在计算边界矩形时,将考虑视口区域(或任何其他可滚动元素(的滚动量。这意味着每次滚动位置更改时,矩形的边界边缘(顶部、右侧、底部、左侧(都会更改其值(因为它们的值是相对于视口的,而不是绝对值(。
不使用element.getBoundingClientRect()
,而是使用具有object.getBBox()
属性的element.clientHeight
来计算所需的位置。
window.onload = function() {
var svgEl = document.querySelector('svg'),
itemA = document.querySelector('#item-A'),
itemB = document.querySelector('#item-B'),
path = document.createElementNS("http://www.w3.org/2000/svg", 'path');
const {
clientTop: item_A_ClientTop,
clientHeight: item_A_ClientHeight,
} = itemA;
const {
x: item_A_BBoxX,
y: item_A_BBoxY
} = itemA.getBBox();
const {
clientHeight: item_A_div_ClientHeight
} = document.querySelector(`#item-A > div`);
const {
y: item_B_BBoxY,
} = document.querySelector(`#item-B`).getBBox();
const path_d = `
M ${item_A_BBoxX},${item_A_BBoxY + item_A_div_ClientHeight}
V ${item_B_BBoxY}
`;
path.setAttribute('d', path_d);
path.setAttribute('stroke', 'red');
path.setAttribute('stroke-width', '2px');
svgEl.appendChild(path);
}
#header {
width: 100%;
height: 30px;
background-color: purple;
}
#svg-wrapper {
display: flex;
justify-content: center;
height: 600px;
width: 100%;
background-color: grey;
}
svg {
width: 80%;
height: 100%;
}
foreignObject {
background-color: black;
}
<div id="header">Header</div>
<div id="svg-wrapper">
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<style>
div {
color: white;
font: 18px serif;
height: 40px;
overflow: auto;
background-color: black;
}
</style>
<foreignObject id="item-A" x="20" y="20" width="60" height="30">
<div xmlns="http://www.w3.org/1999/xhtml">
Item-A
</div>
</foreignObject>
<foreignObject id="item-B" x="20" y="120" width="60" height="30">
<div xmlns="http://www.w3.org/1999/xhtml">
Item-B
</div>
</foreignObject>
</svg>
</div>