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

Получить ограничительную рамку элемента, учитывающего его преобразование

Учитывая этот SVG:

<svg xmlns="http://www.w3.org/2000/svg">
  <rect width="50" height="50"
        transform="translate(75,75) rotate(45) translate(-25,-25)" />
  <script>
    var bb = document.querySelector('rect').getBBox();
    console.log([bb.x,bb,y,bb.width,bb.height]);
  </script>
</svg>​

Результирующий результат [0, 0, 50, 50].
Желаемый результат: [39.645,39.645,70.711,70.711].

Визуальная версия: http://jsfiddle.net/2wpju/7/

Какой самый простой и эффективный способ вычисления ограничивающей рамки элемента по отношению к его родительскому элементу?


Ниже приведен лучший ответ, который я придумал до сих пор, но есть две проблемы:

  • Кажется, что много работы за то, что можно обработать лучше, и
  • Как показано в этой демонстрации, это не обязательно минимальный ограничивающий прямоугольник (обратите внимание на верхний круг), так как выровненная по оси ограничительная рамка ограничиваемой по оси оси рамки всегда увеличивает размер.

 

// Calculate the bounding box of an element with respect to its parent element
function transformedBoundingBox(el){
  var bb  = el.getBBox(),
      svg = el.ownerSVGElement,
      m   = el.getTransformToElement(el.parentNode);

  // Create an array of all four points for the original bounding box
  var pts = [
    svg.createSVGPoint(), svg.createSVGPoint(),
    svg.createSVGPoint(), svg.createSVGPoint()
  ];
  pts[0].x=bb.x;          pts[0].y=bb.y;
  pts[1].x=bb.x+bb.width; pts[1].y=bb.y;
  pts[2].x=bb.x+bb.width; pts[2].y=bb.y+bb.height;
  pts[3].x=bb.x;          pts[3].y=bb.y+bb.height;

  // Transform each into the space of the parent,
  // and calculate the min/max points from that.    
  var xMin=Infinity,xMax=-Infinity,yMin=Infinity,yMax=-Infinity;
  pts.forEach(function(pt){
    pt = pt.matrixTransform(m);
    xMin = Math.min(xMin,pt.x);
    xMax = Math.max(xMax,pt.x);
    yMin = Math.min(yMin,pt.y);
    yMax = Math.max(yMax,pt.y);
  });

  // Update the bounding box with the new values
  bb.x = xMin; bb.width  = xMax-xMin;
  bb.y = yMin; bb.height = yMax-yMin;
  return bb;
}
4b9b3361

Ответ 1

Все, что вам нужно использовать, это простая функция Javascript:

object.getBoundingClientRect();

Поддерживается в:

Chrome  Firefox (Gecko)   Internet Explorer Opera   Safari
1.0           3.0 (1.9)   4.0               (Yes)   4.0

Посмотрите здесь, чтобы узнать больше. Ссылка

Ответ 2

Один из способов - сделать набор raphael и получить bb его. Или заверните все элементы внутри g и получите bb из g, я предполагаю, что g должен быть самым быстрым.

Ответ 3

Это, по-видимому, текущий метод, если/до тех пор, пока getBBox() не будет рассматривать преобразование объекта как прямое определение ограничивающей рамки, даже если SVG 1.1 SE определяет:

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

Я предполагаю, что все это о том, как вы определяете "текущее пространство пользователя"

Ответ 4

Это ответ, который учитывает вращающиеся эллипсы. Метод преобразования работает нормально для прямоугольников, но на эллипсах возвращает ограничивающий прямоугольник, значительно больший, чем объект, если он повернут.

Более простой ответ на использование встроенной функции JavaScript мне не очень полезен, потому что его все равно придется перевести обратно в пространство рисования.

Этот код не обрабатывает перекос или масштаб для эллипсов (но делает для любого другого объекта), но в моей программе на http://nwlink.com/~geoffrey/svgBuild Я намерен обрабатывать перекос и масштаб, рисуя совершенно новый эллипс с применением искаженного или масштабированного.

Формула для эллипсов исходила отсюда: Как вы вычисляете рамку с выравниванием по оси эллипса? большое спасибо за помощь там.

el - элемент d3.js, а не node.

function toNumberIfNumeric(x) {
    var result = parseFloat(x);
    return isNaN(result) ? x : result;
}

function getAttrs(el, attributes) {
    var result = {};
    attributes.forEach(function (attr) {
        result[attr] = toNumberIfNumeric(el.attr(attr));
    });
    return result;
}    

function getTransformedBbox(el, referenceNode) {

    var node = el.node();

    switch (node.nodeName) {
        case "ellipse":

            var rotate = getTransform(el, "rotate");
            if (rotate.angle === 0) {
                return node.getBBox();
            }
            var attr = getAttrs(el, ["cx", "cy", "rx", "ry"]);
            var radians = rotate.angle / 180 * Math.PI;
            var radians90 = radians + Math.PI / 2;

            var ux = attr.rx * Math.cos(radians);
            var uy = attr.rx * Math.sin(radians);
            var vx = attr.ry * Math.cos(radians90);
            var vy = attr.ry * Math.sin(radians90);

            var halfWidth = Math.sqrt(ux * ux + vx * vx);
            var halfHeight = Math.sqrt(uy * uy + vy * vy);

            return {
                x: attr.cx - halfWidth,
                y: attr.cy - halfHeight,
                width: 2 * halfWidth,
                height: 2 * halfHeight
            };

        default:
            var bb = node.getBBox(),
                svg = node.ownerSVGElement,
                m = node.getTransformToElement(referenceNode);

            // Create an array of all four points for the original bounding box
            var pts = [
                svg.createSVGPoint(), svg.createSVGPoint(),
                svg.createSVGPoint(), svg.createSVGPoint()
            ];
            pts[0].x = bb.x;
            pts[0].y = bb.y;
            pts[1].x = bb.x + bb.width;
            pts[1].y = bb.y;
            pts[2].x = bb.x + bb.width;
            pts[2].y = bb.y + bb.height;
            pts[3].x = bb.x;
            pts[3].y = bb.y + bb.height;

            // Transform each into the space of the parent,
            // and calculate the min/max points from that.    
            var xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity;
            pts.forEach(function (pt) {
                pt = pt.matrixTransform(m);
                xMin = Math.min(xMin, pt.x);
                xMax = Math.max(xMax, pt.x);
                yMin = Math.min(yMin, pt.y);
                yMax = Math.max(yMax, pt.y);
            });

            // Update the bounding box with the new values
            bb.x = xMin;
            bb.width = xMax - xMin;
            bb.y = yMin;
            bb.height = yMax - yMin;
            return bb;
    }
}