【问题标题】:Flexbox resizingFlexbox 调整大小
【发布时间】:2015-04-30 07:00:35
【问题描述】:

我正在尝试想出一种有效的方法来定义嵌套的弹性盒并允许调整它的大小。我认为它几乎就在那里:

http://jsfiddle.net/6j10L3x2/1/

我使用三个自定义元素纯粹是为了使标记更具声明性:

flex, flex-item, flex-resizer

一个 flex 代表容器。 flex-item 表示容器内的一个元素,flex-resizer 表示一个调整大小的小部件,可以放置在两个 flex-item 之间以在它们之间添加调整大小的功能。

这一切似乎都运作良好。但是,它只处理使用 flex-grow 调整大小的项目。如果定义了 flex-shrink 或 flex-basis,则计算根本不起作用。

谁能建议一种方法来修改它以使其适用于所有情况?我意识到在如何在具有各种 flex 配置的项目之间共享空间方面存在一些歧义,但欢迎提出任何意见。

也欢迎任何替代方法。谢谢。

【问题讨论】:

  • 在内容溢出的弹性项目中将溢出更改为自动之前效果很好;我认为你应该将 'scrollHeight' 和 'scrollWidth' 分别更改为 'offsetHeight' 和 'offsetWidth' 以便在弹性项目中有内容时 manageResize() 按预期工作。

标签: html flexbox


【解决方案1】:

哇。我印象深刻的是你如何使用'flexGrow'、优秀的想法和代码使用香草 javascript 调整 flexbox 元素的大小。

我已经从几个方面改进了您的代码,并且运行良好。

我做了什么?

1.- 我简化了 HTML:

  • 不要在 flex-item 中使用 flex 元素。

  • 总是使用 flexflex-item 元素! flex 元素。

2.- 解决了! 当可见的弹性项目大小小于其内容大小时,拆分器的跳转。

3.- 我添加了不同的光标来表示状态的变化(setupResizerEvents、onMouseUp)以提高可用性。

4.- 我添加了代码来防止光标在拖动时闪烁。

5.- 在 manageResize() 与 scrollWidth 和 scrollHeight 中使用 offsetWidth 和 offsetHeight 来避免当 flex-item 内容溢出(溢出:自动)时拆分器在调整大小时跳转。

代码如下:

function manageResize(md, sizeProp, posProp) {
    var r = md.target;

    var prev = r.previousElementSibling;
    var next = r.nextElementSibling;
    if (!prev || !next) {
        return;
    }

    md.preventDefault();

    var prevSize = prev[sizeProp];
    var nextSize = next[sizeProp];
    var sumSize = prevSize + nextSize;
    var prevGrow = Number(prev.style.flexGrow);
    var nextGrow = Number(next.style.flexGrow);
    var sumGrow = prevGrow + nextGrow;
    var lastPos = md[posProp];

    function onMouseMove(mm) {
        var pos = mm[posProp];
        var d = pos - lastPos;
        prevSize += d;
        nextSize -= d;
        if (prevSize < 0) {
            nextSize += prevSize;
            pos -= prevSize;
            prevSize = 0;
        }
        if (nextSize < 0) {
            prevSize += nextSize;
            pos += nextSize;
            nextSize = 0;
        }

        var prevGrowNew = sumGrow * (prevSize / sumSize);
        var nextGrowNew = sumGrow * (nextSize / sumSize);

        prev.style.flexGrow = prevGrowNew;
        next.style.flexGrow = nextGrowNew;

        lastPos = pos;
    }

    function onMouseUp(mu) {
        // Change cursor to signal a state's change: stop resizing.
        const html = document.querySelector('html');
        html.style.cursor = 'default';

        if (posProp === 'pageX') {
            r.style.cursor = 'ew-resize'; 
        } else {
            r.style.cursor = 'ns-resize';
        }
        
        window.removeEventListener("mousemove", onMouseMove);
        window.removeEventListener("mouseup", onMouseUp);
    }

    window.addEventListener("mousemove", onMouseMove);
    window.addEventListener("mouseup", onMouseUp);
}

function setupResizerEvents() {
    document.body.addEventListener("mousedown", function (md) {

        // Used to avoid cursor's flickering
        const html = document.querySelector('html');
        
        var target = md.target;
        if (target.nodeType !== 1 || target.tagName !== "FLEX-RESIZER") {
            return;
        }
        var parent = target.parentNode;
        var h = parent.classList.contains("h");
        var v = parent.classList.contains("v");
        if (h && v) {
            return;
        } else if (h) {
            // Change cursor to signal a state's change: begin resizing on H.
            target.style.cursor = 'col-resize';
            html.style.cursor = 'col-resize'; // avoid cursor's flickering

            // use offsetWidth versus scrollWidth (and clientWidth) to avoid splitter's jump on resize when a flex-item content overflow (overflow: auto).
            manageResize(md, "offsetWidth", "pageX");
            
        } else if (v) {
            // Change cursor to signal a state's change: begin resizing on V.
            target.style.cursor = 'row-resize';
            html.style.cursor = 'row-resize'; // avoid cursor's flickering

            manageResize(md, "offsetHeight", "pageY");
        }
    });
}

setupResizerEvents();
body {
    /* margin:0; */
    border: 10px solid #aaa;
}

flex {
    display: flex;
    overflow: hidden;
}

/* flex-item > flex {
    position: absolute;
    width: 100%;
    height: 100%;
} */

flex.h {
    flex-direction: row;
}

flex.v {
    flex-direction: column;
}

flex-item {
    /* display: flex; */
    /* position: relative; */
    /* overflow: hidden; */
    overflow: auto;
}

flex > flex-resizer {
    flex: 0 0 10px;
    /* background: white; */
    background-color: #aaa;
    background-repeat: no-repeat;
    background-position: center;
}

flex.h > flex-resizer {
    cursor: ew-resize;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='30'><path d='M2 0 v30 M5 0 v30 M8 0 v30' fill='none' stroke='black'/></svg>");
}

flex.v > flex-resizer {
    cursor: ns-resize;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='30' height='10'><path d='M0 2 h30 M0 5 h30 M0 8 h30' fill='none' stroke='black'/></svg>");
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>flex-splitter</title>
    <link rel="stylesheet" href="./src/styles.css">
    <script src="./src/index.js" defer></script>
</head>

<body>
    <flex class="v" style="flex: 1; height: 500px;">
        <flex-item style="flex: 1;">Flex 1</flex-item>
        <flex-resizer></flex-resizer>
        <flex class="h" style="flex: 1;">
            <flex-item style="flex: 1; background-color: aqua;">
      
      <!-- 
        The next section is an example to test the splitter when there is content inside a flex-item
      -->
        <section>
                    <div>
                        <label for="CursorCoor" style="display: block;">showCursorCoor: </label>
                        <textarea id="CursorCoor" rows="6" cols="50" wrap="soft" readonly></textarea>
                    </div>
                
                    <br />
                
                    <div>
                        <label for="boxInfo" style="display: block;">showBoxInfo: </label>
                        <textarea id="boxInfo" rows="6" cols="50" wrap="soft" readonly></textarea>
                    </div>
                </section>
        
      </flex-item>
            <flex-resizer></flex-resizer>
            <flex class="v" style="flex: 2; ">
                <flex-item style="flex: 1; background: pink;">Flex 3</flex-item>
                <flex-resizer></flex-resizer>
                <flex class="h" style="flex: 1">
                    <flex-item style="flex: 1; background: green;">Flex 4</flex-item>
                    <flex-resizer></flex-resizer>
                    <flex-item style="flex: 2;">Flex 5</flex-item>
                    <!-- <flex-resizer></flex-resizer> -->
                    <flex-item style="flex: 3; background: darkorange;">Flex 6</flex-item>
                </flex>
            </flex>
        </flex>
    </flex>
    
</body>
</html>

或在 Codesandbox 上查看:

希望对你有帮助!

【讨论】:

  • 你为什么使用 flex html 标签 - 它们是有效的 html 吗?
  • @joedotnot 我可以使用一个简单的
    元素,但我只是按照主要问题中的原始代码进行操作。无论如何,在这种情况下 标签是一个独立的自治自定义元素(请参阅developer.mozilla.org/en-US/docs/Web/Web_Components/…
【解决方案2】:

注意:还有新的基本resize CSS 属性,但它仅用于右下角拖动。


我对此进行了一些研究,我遇到的前 3 个无框架、完全烘焙的结果是,按出现顺序(未经测试):

  1. https://daybrush.com/moveable

    • “Moveable 是可拖动的、可调整大小的、可缩放的、可旋转的、可弯曲的、可捏合的、可分组的、可对齐的”
    • 我喜欢这里的外观,无论是视觉还是代码方面!看起来功能强大且非常灵活。
    • 另见:https://github.com/daybrush/moveablehttps://daybrush.com/moveable/release/latest/doc
    • 更新:我试过这个,实际上我不推荐它。使用起来很复杂,文档也很差,我宁愿自己写 JS。
  2. https://split.js.org

  3. https://jspanel.de

我还发现了这个:http://w2ui.com/web/homehttps://github.com/vitmalina/w2ui

【讨论】:

    猜你喜欢
    • 2017-03-01
    • 2016-05-28
    • 1970-01-01
    • 2021-10-02
    • 2018-04-30
    • 2016-03-17
    • 1970-01-01
    • 2015-11-15
    • 1970-01-01
    相关资源
    最近更新 更多