使用Javascript将绝对值转换为相对CSS值

pra*_*yer 7 html javascript css svg

我想知道在使用Javascript 保持比例同时将绝对CSS值转换为相对值是否可行和可行.该解决方案必须与HTML和SVG标记一起使用.

说你有这个:

<div style="width:100%;height:800px">
  <svg width="1000" height="500">
    <rect width="30" height="50" x="34" y="24"/>
  </svg>
</div>
Run Code Online (Sandbox Code Playgroud)

你会如何实现这一目标?

<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>
Run Code Online (Sandbox Code Playgroud)

ID

  1. 从根元素开始(div)
  2. get .width().height()of this和父元素
  3. 计算比例(this_height/parent_height*100)
  4. 相应地设置宽度和高度
  5. 迭代孩子,将孩子设置为root,从1开始.

可能发生的常见陷阱是什么?可能有标签没有宽度或高度设置,如svg:g.然后是非标准的大小定义,比如in svg:circle或者你最初没有想到的属性,比如svg:texts dy.有没有办法对由于大规模回流造成的性能损失进行有根据的猜测?还有什么?

非常感谢你!

Dan*_*ahn 2

好的。这就是我理解的你想要用我自己的话来说的事情。

循环遍历所有 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>
Run Code Online (Sandbox Code Playgroud)

这就是 JavaScript。它得到了很好的评论,并提供了不错的控制台输出。对于生产代码,我建议删除控制台日志记录。

请记住,代码不会立即执行,而是延迟 2 秒执行。这样您就可以看到 DOM 被修改之前的样子。

对于那些不知道以下代码的人,a如果存在且不为 false,则返回,b否则返回。

foo = a || b;
Run Code Online (Sandbox Code Playgroud)

我们可以像下面两个之一一样重写,但这更简洁。

foo = a ? a : b;

if(a)
    foo = a
else
    foo = b
Run Code Online (Sandbox Code Playgroud)

这是一个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);
Run Code Online (Sandbox Code Playgroud)

如果这不是您想要的,或者您遇到了问题,请告诉我!

对于您的问题

可能发生哪些常见陷阱?

最常见的陷阱是不正确的转换。转换过程中可能会出现问题,生成的 DOM 看起来与输入完全不同。

可能有一些标签没有设置宽度或高度,例如 svg:g。然后还有非标准尺寸定义,例如 svg:circle 或您一开始没有考虑到的属性,例如 svg:texts dy。

有些标签没有width/height元素。然而,使用 jQuery 应该会让我们在这方面更加稳健。我没有太多使用 jQuery 和 SVG,但是如果我们在使用更晦涩的元素时遇到任何问题,可以使用一个插件来提供支持。

有没有办法对大规模回流造成的性能影响进行有根据的猜测?

我相信这段代码将在O(m * n)Wherem = delay for a single reflow和中运行n = number of nodes。回流应该有相当一致的变化,因为我们先转换最大的元素,然后再转换最小的元素。我的最后一句话可能被证明是错误的。

还有什么?

成功!