Проблема заключается в том, как определить, находится ли точка (x,y)
внутри общего графического элемента SVG (например, rect
, ellipse
, path
) до ее отображения на экране. Это означает, что любые методы получения позиции в экранных координатах недоступны - могут использоваться только данные, указанные в файле SVG.
Исходным фоном является то, что моей компании нужна система для маркировки SVG-элементов из Adobe Illustrator, и, поскольку AI не допускает значительных модификаций вывода SVG, мы решили основать это на создании отдельного слоя tags
с текстовыми объектами. содержащий текст тега. Затем на стороне клиента файлы анализируются и теги добавляются в соответствующие элементы SVG, а затем удаляется группа tags
.
Получить основные координаты тегов легко (с помощью ES5 - извините). Для данной svg
строки:
var parser = new DOMParser();
var svgDom = parser.parseFromString(svg, "image/svg+xml");
var tagsElement = svgDom.getElementById("tags");
if (tagsElement) {
var textElementHtmlCollection = tagsElement.getElementsByTagName("text");
var textElementArray = Array.prototype.slice.call(textElementHtmlCollection);
var tagDataArray = textElementArray.map(function (el) {
return {
text: el.textContent,
pos: {
x: el.transform.baseVal[0].matrix.e,
y: el.transform.baseVal[0].matrix.f
}
};
});
Прямо сейчас работает ОЧЕНЬ базовая версия системы тегов, но каждый тип узла должен обрабатываться отдельно, и я дошел до реализации модифицированного алгоритма числового числа обмоток (https://en.wikipedia.org/wiki/Winding_number) для path
узлов (проблемы, возникающие в случае «близкого вызова», не учитываются для варианта использования):
function windingNumber (element,x,y) {
var isInside, p1 = undefined, p2 = undefined, px, py, R, wn, sum=0;
var totalLength = element.getTotalLength();
var resolution = 5;
var threshold = 0.1;
for (var len = 0; len <= totalLength; len += resolution) {
p1 = p2;
p2 = element.getPointAtLength(len);
if (p1) {
px = p1.x - x;
py = p1.y - y;
R = px*px + py*py;
sum += (px*(p2.y-p1.y)/R - py*(p2.x-p1.x)/R);
}
wn = sum/(2*Math.PI);
if (Math.abs(wn) > 1-threshold) {
isInside = true;
break;
}
}
return isInside;
}
Теперь я решил, что должен быть другой путь, так как система становится чрезвычайно сложной, принимая во внимание, что к элементам могут применяться все виды transform
.
Есть ли более простой способ сделать это? Идеи для разных подходов также приветствуются.
ОБНОВЛЕНИЕ Мой ответ ужасен , но это тот, который я в конечном итоге использовал. Инфраструктура, окружающая приложение, и проблема усложняли работу «правильного» решения (отображение SVG скрыто, используйте document.elementsFromPoint()
).
Тем не менее, чем больше математики в коде, тем веселее.