Flexbox resizing

后端 未结 2 741
旧巷少年郎
旧巷少年郎 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 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,");
    }
    
    flex.v > flex-resizer {
        cursor: ns-resize;
        background-image: url("data:image/svg+xml;utf8,");
    }
    
    
    
        
        
        
        flex-splitter
        
        
    
    
    
        
            Flex 1
            
            
                
          
          
            

    Flex 3 Flex 4 Flex 5 Flex 6

    Or see it on Codesandbox:

    I hope it helps!

提交回复
热议问题