SVG - 对转换后的元素使用 checkIntersection()
SVG - Using checkIntersection() with transformed elements
TLDR;
我想使用 svg.checkIntersection()
,但很难 getting/calculating 函数所需的正确边界框。在初始坐标中获取 transformed SVG 元素 的边界框的最干净方法是什么SVG?
我正在尝试检查代码生成的 SVG 中是否有重叠的 text
元素。 text
元素位于 (0, 0)
并使用 transform
属性移动到正确的位置,如下所示:
<svg xmlns="http://www.w3.org/2000/svg" id="svg0" viewBox="-100 -100 200 200">
<text id="t0" transform="rotate(-30) translate(15 -5)">Text 0</text>
<text id="t1" transform="rotate(-10) translate(15 -10)">Text 1</text>
<!-- do they overlap? -->
</svg>
请注意,所使用的具体变换是不可预先确定的。
所以自然有人会尝试这个:
const mySvg = document.querySelector("#svg0");
const text0 = mySvg.querySelector("#t0");
const text1 = mySvg.querySelector("#t1");
const isOverlap = mySvg.checkIntersection(text0, /* bounding box of text1 */);
问题是没有简单的方法来获得这个边界框。 W3 definition 状态:
The values are in the initial coordinate system for the current ‘svg’ element.
因此:
getBoundingClientRect()
是不合适的,因为此 SVG 旨在成为 HTML DOM
中的一个组件,而 getBoundingClientRect()
会 return 坐标中的 HTML 值系统.
getBBox()
没有用,因为它 return 元素局部坐标系中的值,由于应用了 transform
,它与 SVG 的初始坐标不同。
getCTM()
似乎很有用,但是手动应用矩阵来获取边界框看起来像是很多手写数学。如果可能的话,我想避免这种情况。
所以最后...
鉴于没有直接的内置方法,什么是最干净and/or最简单的方法来获取用于 svg.checkIntersection()
的元素边界框?
我最终使用了 OSUblake here, combined with a simple rectangle intersection algorithm here 提供的解决方案。
基本上,首先计算从对象坐标space到SVG坐标space的变换矩阵:
const matrix = svg.getScreenCTM().inverse().multiply(element.getScreenCTM());
然后将矩阵应用于元素边界框的所有四个点,并根据最小值和最大值计算 x
、y
、width
和 height
。
const p = svg.createSVGPoint();
p.x = r.x;
p.y = r.y;
const pA = p.matrixTransform(matrix);
p.x = r.x + r.width;
p.y = r.y;
const pB = p.matrixTransform(matrix);
...
完整实施请参阅原文 post。
这最终确实成为了一个很大的数学函数,这是我真正想要避免的。但最终它无需 DOM 更改即可完成工作。结合简单的交集函数,它确实允许我也放弃 svg.checkIntersection()
,这首先给出了奇怪的结果(在 Chrome 83 上测试)。
TLDR;
我想使用 svg.checkIntersection()
,但很难 getting/calculating 函数所需的正确边界框。在初始坐标中获取 transformed SVG 元素 的边界框的最干净方法是什么SVG?
我正在尝试检查代码生成的 SVG 中是否有重叠的 text
元素。 text
元素位于 (0, 0)
并使用 transform
属性移动到正确的位置,如下所示:
<svg xmlns="http://www.w3.org/2000/svg" id="svg0" viewBox="-100 -100 200 200">
<text id="t0" transform="rotate(-30) translate(15 -5)">Text 0</text>
<text id="t1" transform="rotate(-10) translate(15 -10)">Text 1</text>
<!-- do they overlap? -->
</svg>
请注意,所使用的具体变换是不可预先确定的。
所以自然有人会尝试这个:
const mySvg = document.querySelector("#svg0");
const text0 = mySvg.querySelector("#t0");
const text1 = mySvg.querySelector("#t1");
const isOverlap = mySvg.checkIntersection(text0, /* bounding box of text1 */);
问题是没有简单的方法来获得这个边界框。 W3 definition 状态:
The values are in the initial coordinate system for the current ‘svg’ element.
因此:
getBoundingClientRect()
是不合适的,因为此 SVG 旨在成为HTML DOM
中的一个组件,而getBoundingClientRect()
会 return 坐标中的 HTML 值系统.getBBox()
没有用,因为它 return 元素局部坐标系中的值,由于应用了transform
,它与 SVG 的初始坐标不同。getCTM()
似乎很有用,但是手动应用矩阵来获取边界框看起来像是很多手写数学。如果可能的话,我想避免这种情况。
所以最后...
鉴于没有直接的内置方法,什么是最干净and/or最简单的方法来获取用于 svg.checkIntersection()
的元素边界框?
我最终使用了 OSUblake here, combined with a simple rectangle intersection algorithm here 提供的解决方案。
基本上,首先计算从对象坐标space到SVG坐标space的变换矩阵:
const matrix = svg.getScreenCTM().inverse().multiply(element.getScreenCTM());
然后将矩阵应用于元素边界框的所有四个点,并根据最小值和最大值计算 x
、y
、width
和 height
。
const p = svg.createSVGPoint();
p.x = r.x;
p.y = r.y;
const pA = p.matrixTransform(matrix);
p.x = r.x + r.width;
p.y = r.y;
const pB = p.matrixTransform(matrix);
...
完整实施请参阅原文 post。
这最终确实成为了一个很大的数学函数,这是我真正想要避免的。但最终它无需 DOM 更改即可完成工作。结合简单的交集函数,它确实允许我也放弃 svg.checkIntersection()
,这首先给出了奇怪的结果(在 Chrome 83 上测试)。