Flexbox resizing

后端 未结 2 711
旧巷少年郎
旧巷少年郎 2021-01-19 16:04

I\'m trying to come up with an effective way of defining a nested flexbox and allow it to be resized. I think it\'s almost there:

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

相关标签:
2条回答
  • 2021-01-19 16:46

    Note: There is also the new, basic resize CSS property, but it's only for bottom right corner dragging.


    I did some research on this, and the first 3 framework-free, fully baked results I came across were, in order of appearances (untested):

    1. https://daybrush.com/moveable

      • "Moveable is Draggable, Resizable, Scalable, Rotatable, Warpable, Pinchable, Groupable, Snappable"
      • I love the look of things here, both visually and code-wise! Seems highly functional and extremely flexible as well.
      • See also: https://github.com/daybrush/moveable https://daybrush.com/moveable/release/latest/doc
      • UPDATE: I tried this one out, I actually would not recommend it. It's very complicated to use, poorly documented, and I'd rather write my own JS instead.
    2. https://split.js.org

      • Looks nice, but it appears to only be for split panels specifically, no e.g. corner dragging. If that's all you want, this might be a good option.
      • See also: https://github.com/nathancahill/split/tree/master/packages/splitjs https://github.com/nathancahill/split/tree/master/packages/split-grid
    3. https://jspanel.de

      • Not as aesthetic as I would like, but seems like it has a good set of functionality and options.
      • See also: https://github.com/Flyer53/jsPanel4

    I also found this: http://w2ui.com/web/home https://github.com/vitmalina/w2ui

    0 讨论(0)
  • 2021-01-19 17:02

    Wow. I am impressed how you resize the flexbox elements with vanilla javascript using 'flexGrow', excelent idea and code.

    I have improve your code in a few ways and it is working very well.

    What I did?

    1.- I simplified the HTML:

    • Do not use a flex element inside a flex-item.

    • Use a flex or flex-item element, always!, inside another flex element.

    2.- Solved! Splitter's jump when the visible flex-item size is smaller that its content size.

    3.- I'd added different cursors to signal a state's change (setupResizerEvents, onMouseUp) to improve usability.

    4.- I've added code to prevent the cursor from flickering when dragging.

    5.- use of offsetWidth and offsetHeight in manageResize() versus scrollWidth and scrollHeight to avoid splitter's jump on resize when a flex-item content overflow (overflow: auto).

    Here is the code:

    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>

    Or see it on Codesandbox:

    I hope it helps!

    0 讨论(0)
提交回复
热议问题