Преобразование абсолютных значений в относительные CSS с помощью Javascript

мне было интересно, возможно ли и возможно преобразовать абсолютные значения CSS в относительные сохраняя пропорции использование Javascript. Решение должно работать как с HTML, так и с разметкой SVG.

скажите, что вы получили это:

<div style="width:100%;height:800px">
  <svg width="1000" height="500">
    <rect width="30" height="50" x="34" y="24"/>
  </svg>
</div>

как бы вы этого добиться?

<div style="width:100%;height:100%">
  <svg width="83%" height="62.5%">
    <rect width="3%" height="10%" x="3.4% y="4.8%"/>
  </svg>
</div>

Я

  1. начинается с корневого элемента (div)
  2. get .width() и .height() of this и родителем элемент
  3. рассчитать долю (this_height/parent_height*100)
  4. задать ширину и высоту соответственно
  5. повторите над детьми, установите child как root, начните с 1.

каковы общие подводные камни, которые могут произойти? Вероятно, есть теги, у которых нет набора ширины или высоты, например svg:g. То есть нестандартные определения размера как в svg:circle или атрибуты, о которых вы не думаете сначала, например svg:texts dy. Есть ли способ взять образованная догадка о производительности хита из-за массивных отражений? Что еще?

большое спасибо!

1 ответов


ОК. Итак, вот что я понимаю, что вы хотите сделать моими словами.

петля через все элементы DOM, преобразующие абсолютные размеры (т. е. высота / ширина в пикселях) в относительные размеры (т. е. высота/ширина в процентах). Это должно быть сделано с чувствительность к тому, задано ли измерение с помощью CSS или HTML атрибуты, которые можно использовать для элементов SVG.

это, как говорится, есть одно серьезное ограничение: если вы хотите преобразовать абсолютные размеры в относительные, используя <x> в качестве корневого элемента, затем <x> должны иметь абсолютные размеры. В противном случае относительные размеры внутри контейнера изменят размер в зависимости от их содержимого вместо общего размера родительского <x>.

С этим условием то, что вы просите, довольно легко. Это алгоритм, который необходимо использовать.

  1. Пуск на <body>
  2. дать <body> фиксированный размер
  3. прогулка DOM-дерево для <body> выполнение следующих действий для каждого элемента
    1. получить размеры элемента
    2. получить размеры родителя
    3. преобразовать в относительные размеры (ширина / родитель).
    4. применить относительные размеры

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

<div style="width:100%;height:800px">
  <svg width="150" height="200" style="background: red;">
    <rect width="30" height="50" x="34" y="24"/>
  </svg>

  <div style="background: green; width: 50px; height: 50px;">
      <p style="background: teal; width: 100px; height: 20px;">Content</p>
  </div>
</div>

это JavaScript. Он довольно хорошо комментируется и обеспечивает достойный консольный выход. Для производственного кода я бы рекомендовал удалить журнал консоли.

пожалуйста, имейте в виду, что код выполняется не сразу, а с задержкой в 2 секунды. Таким образом, вы можете увидеть, как выглядит DOM, прежде чем он будет изменен.

для тех, кто не знает, следующий код вернет a если он присутствует, а не false и b иначе.

foo = a || b;

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

foo = a ? a : b;

if(a)
    foo = a
else
    foo = b

здесь jsFiddle.

и код!

/** convertToRelative
 * Convert absolute width/height to relative. Should work for
 * Both HTML and SVG objects. Tries to use CSS attributes, falls
 * back on HTML attributes.
 */
function convertToRelative(element) {
    // Get Element and Parent
    element = $(element);
    parent  = element.parent();

    // If there is no valid with, we need to use attributes
    var useAttr   = !element.width();

    // Get dimensions from css or attributes
    var width     = element.width()  || element.attr('width');
    var height    = element.height() || element.attr('height');
    var pWidth    = parent.width()   || parent.attr('width');
    var pHeight   = parent.height()  || parent.attr('height');

    // Find relative dimensions
    var relWidth  = (width  / pWidth)  * 100.0;
    var relHeight = (height / pHeight) * 100.0;

    // Log info
    console.log(element);
    console.log("1: "+ parent.height() +", 2: "+ parent.attr('height'));
    console.log("Width: "+ width +", Height: "+ height);
    console.log("PWidth: "+ pWidth +", pHeight: "+ pHeight);
    console.log("relWidth: "+ relWidth +", relHeight: "+ relHeight);

    // Clear current stylings
    element.removeAttr('width');
    element.removeAttr('height');

    // Apply relative dimensions
    if (useAttr) {
        element.attr('width', relWidth +'%');
        element.attr('height', relHeight +'%');
    } else {
        element.width(relWidth +"%");
        element.height(relHeight +"%");
    }
}

/** walk
 * Walk through a DOM element and all its sub elements running
 * the convertToRelative function to convert absolute positions
 * to relative positions.
 * DOES NOT CONVERT THE FIRST ELEMENT, ONLY CHILDREN.
 */
function walk(element) {
    $(element).children().each(function() {
        convertToRelative(this);
        walk(this);
    });
}

/** fixBody
 * In order to play with relative dimensions for children of the
 * body, we need to fix the dimensions of the body. Otherwise it
 * will resize as we begin to use relative dimensions.
 */
function fixBody() {
    $('body').css('width', $('body').width() +"px");
    $('body').css('height', $('body').height() +"px");
}

// Walk the body and all children on a 2 second delay.
setTimeout(function() {
    console.log('Running Conversion');
    fixBody()
    walk('body');
    console.log('Conversion Finished');
}, 2000);

Дайте мне знать если это не то, что вы хотели, или вы столкнулись с проблемой!

для вашего вопросы

каковы общие подводные камни, которые могут произойти?

наиболее распространенной ловушкой будет неправильное преобразование. Что-то пойдет не так в преобразовании, и полученный DOM не будет выглядеть как вход.

вероятно, есть теги, у которых нет набора ширины или высоты, например svg:g. Тогда есть нестандартные определения размера, как в svg: круг или атрибуты, которые вы не думаете о сначала, как svg: тексты dy.

есть теги, которые не имеют width/height элемент. Однако использование jQuery должно дать нам немного больше надежности в этом отделе. Я не много работал с jQuery и SVG, но есть плагин для поддержки, если у нас возникнут проблемы с более неясными элементами.

есть ли способ сделать обоснованное предположение о производительности хита из-за массивных отражений?

я считаю, что этот код будет работать в O(m * n) здесь m = delay for a single reflow и n = number of nodes. Отражения должны иметь довольно последовательное изменение, потому что мы преобразуем самые большие элементы перед самыми маленькими. но я могу оказаться неправ в этом последнем утверждении.

что еще?

Success!