Flexbox resizing

我的梦境 提交于 2020-01-04 09:26:30

问题


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/

I'm using three custom elements purely to make the mark-up more declarative:

flex, flex-item, flex-resizer

A flex represents the container. A flex-item presents an element within the container, and flex-resizer represents a resizer widget which can be placed between two flex-items to add resizing functionality between them.

This all appears to work really well. However, it only handles items sized with flex-grow. If flex-shrink or flex-basis is defined, then the calculations simply don't work.

Can anyone suggest a way to amend this to allow it work for all cases? I realise that there is some ambiguity in regards to how the space should be shared between items with various flex configurations, but any input would be welcome.

Any alternative approaches would be welcome also. Thanks.


回答1:


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.

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 clientWidth versus scrollWidth to avoid splitter's jump on resize.
			manageResize(md, "clientWidth", "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, "clientHeight", "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;
}

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;">Flex 2</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!



来源:https://stackoverflow.com/questions/28767221/flexbox-resizing

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!