Подтвердить что ты не робот

Как рассчитывается метод getBBox() SVGRect?

У меня есть элемент g, содержащий один или несколько элементов path. Как я уже упоминал в еще одном вопросе, я масштабирую и переводит элемент g, вычисляя атрибут transform так, чтобы он соответствовал сетке в другой части холста.

Расчет выполняется с использованием разницы между двумя прямоугольниками, getBBox() от элемента g и прямоугольника вокруг сетки.

Вот вопрос - после выполнения преобразования я обновляю содержимое элемента g и вызываю getBBox() снова, не удаляя transform. Полученный прямоугольник, по-видимому, вычисляется без учета transform. Я бы ожидал, что это отразит изменения. Согласуется ли это поведение с спецификацией SVG? Как получить ограничивающий прямоугольник преобразованного прямоугольника?

Это, BTW, находится в документе HTML 5, работающем в Firefox 4, если это имеет значение.

Обновление: похоже, это поведение кажется довольно явно нарушением спецификации. Из текста здесь, в w3c:

SVGRect getBBox()

Возвращает ограниченный ограничивающий прямоугольник в текущем пользовательском пространстве (т.е. после применения атрибута "transform", если он есть) в геометрии всех содержащихся графических элементов, исключая эффекты поглаживания, обрезки, маскировки и фильтрации). Обратите внимание, что getBBox должен возвращать фактический ограничивающий прямоугольник во время вызова метода даже в том случае, если элемент еще не был отображен.

Я читаю это правильно? Если это так кажется ошибкой в ​​реализации SVG, Firefox использует; У меня не было возможности попробовать что-либо другое. Я бы опубликовал отчет об ошибке, если кто-то мог указать мне куда.

4b9b3361

Ответ 1

Поведение, которое вы видите, является правильным и соответствует спецификации. Преобразование применяется, тогда bbox вычисляется в "текущих пользовательских единицах", то есть в текущем пользовательском пространстве. Поэтому, если вы хотите увидеть результат преобразования в элементе, вам нужно будет посмотреть на bbox родительского node или аналогичного. Это немного запутывает, но объясняет намного лучше в спецификации SVG Tiny 1.2 для SVGLocatable Это содержит ряд примеров, которые разъясняют, что он должен делать.

Ответ 2

Люди часто путаются поведенческой разницей getBBox и getBoundingClientRect.

getBBox - это собственный метод SVG Element, эквивалентный поиску смещения/клиентской переменной элемента HTML DOM. Ширина и высота никогда не изменятся, даже когда элемент вращается. Он не может использоваться для элементов HTML DOM.

getBoundingClientRect является общим как для HTML, так и для элементов SVG. Ограниченный прямоугольник ширина и высота будет меняться при повороте элемента или при группировке большего количества элементов.

Ответ 3

есть как минимум 2 простых, но несколько хакерских способа сделать то, что вы просите... если есть более приятные (менее хаки) пути, я еще не нашел их

ЛЕГКО ХАКА № 1:
a) настроить прямоугольник, который соответствует "нетрансформированному" bbox, который возвращает group.getBBox()
б) применить группу "непримененное преобразование" к этому rect
c) rect.getBBox() должен теперь вернуть нужный вам bbox

EASY HACKY # 2: (проверено только в хромированном состоянии)
a) используйте element.getBoundingClientRect(), который возвращает достаточно информации для вас, чтобы построить bbox, который вы ищете

Ответ 4

По-видимому, getBBox() не принимает во внимание преобразования.

Я могу указать вас сюда, к сожалению, я не смог заставить его работать: http://tech.groups.yahoo.com/group/svg-developers/message/22891

Ответ 5

У SVG групп есть неприятная практика - не накапливать все сделанные преобразования. У меня есть свой способ справиться с этой проблемой. Я использую свои собственные атрибуты для хранения текущих данных преобразования, которые я включаю в любое дальнейшее преобразование. Используйте XML-совместимые атрибуты, такие как alttext, value, name.... или просто x и y для хранения накопленного значения в качестве атрибута.

Пример:

<g id="group" x="20" y="100" transform="translate(20, 100)">
<g id="subgroup" alttext="45" transform="rotate(45)">
<line...etc...

Поэтому, когда я делаю преобразования, я беру эти значения атрибутов ручной работы, а когда записываю их обратно, я записываю как преобразование, так и одно и то же значение с атрибутами, которые я создал для сохранения всех накопленных значений.

Пример для вращения:

function symbRot(evt) {

  evt.target.ondblclick = function () {

    stopBlur();
    var ptx=symbG.parentNode.lastChild.getAttribute("cx");
    var pty=symbG.parentNode.lastChild.getAttribute("cy");
    var currRot=symbG.getAttributeNS(null, "alttext");

    var rotAng;
    if (currRot == 0) {
      rotAng = 90
    } else if (currRot == 90) {
      rotAng = 180
    } else if (currRot == 180) {
      rotAng = 270
    } else if (currRot == 270) {
      rotAng = 0
    };
    symbG.setAttributeNS(null, "transform", "rotate(" + rotAng + "," + ptx + ", " + pty + ")");
    symbG.setAttributeNS(null, "alttext", rotAng );
  };
}

Ответ 6

Я сделал вспомогательную функцию, которая возвращает различные метрики элемента svg (также bbox преобразованного элемента).

Код здесь:

SVGElement.prototype.getTransformToElement = 
SVGElement.prototype.getTransformToElement || function(elem) { 
  return elem.getScreenCTM().inverse().multiply(this.getScreenCTM()); 
};

function get_metrics(el) {
    function pointToLineDist(A, B, P) {
        var nL = Math.sqrt((B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y));
        return Math.abs((P.x - A.x) * (B.y - A.y) - (P.y - A.y) * (B.x - A.x)) / nL;
    }

    function dist(point1, point2) {
        var xs = 0,
            ys = 0;
        xs = point2.x - point1.x;
        xs = xs * xs;
        ys = point2.y - point1.y;
        ys = ys * ys;
        return Math.sqrt(xs + ys);
    }

    var b = el.getBBox(),
        objDOM = el,
        svgDOM = objDOM.ownerSVGElement;
    // Get the local to global matrix
    var matrix = svgDOM.getTransformToElement(objDOM).inverse(),
        oldp = [[b.x, b.y], [b.x + b.width, b.y], [b.x + b.width, b.y + b.height], [b.x, b.y + b.height]],
        pt, newp = [],
        obj = {},
        i, pos = Number.POSITIVE_INFINITY,
        neg = Number.NEGATIVE_INFINITY,
        minX = pos,
        minY = pos,
        maxX = neg,
        maxY = neg;

    for (i = 0; i < 4; i++) {
        pt = svgDOM.createSVGPoint();
        pt.x = oldp[i][0];
        pt.y = oldp[i][1];
        newp[i] = pt.matrixTransform(matrix);
        if (newp[i].x < minX) minX = newp[i].x;
        if (newp[i].y < minY) minY = newp[i].y;
        if (newp[i].x > maxX) maxX = newp[i].x;
        if (newp[i].y > maxY) maxY = newp[i].y;
    }

    // The next refers to the transformed object itself, not bbox
    // newp[0] - newp[3] are the transformed object corner
    // points in clockwise order starting from top left corner
    obj.newp = newp; // array of corner points
    obj.width = pointToLineDist(newp[1], newp[2], newp[0]) || 0;
    obj.height = pointToLineDist(newp[2], newp[3], newp[0]) || 0;
    obj.toplen = dist(newp[0], newp[1]);
    obj.rightlen = dist(newp[1], newp[2]);
    obj.bottomlen = dist(newp[2], newp[3]);
    obj.leftlen = dist(newp[3], newp[0]);
    // The next refers to the transformed object bounding box
    obj.BBx = minX;
    obj.BBy = minY;
    obj.BBx2 = maxX;
    obj.BBy2 = maxY;
    obj.BBwidth = maxX - minX;
    obj.BBheight = maxY - minY;
    return obj;
}

и полный функциональный пример здесь:http://jsbin.com/acowaq/1