1. Flex 弹性盒模型
Flexible Box 模型,通常被称为 flexbox,是一种一维的布局模型。它给 flexbox 的子元素之间提供了强大的空间分布和对齐能力。
1.1. 知识体系总图
1.2. 浏览器兼容性
IE 是兼容性最棒的浏览器,没有之一 !
1.3. 主轴、垂轴、换行
当使用 flex 布局时,首先想到的是两根轴线 — 主轴和交叉轴。
1.3.1. 主轴方向控制:flex-direction
flex-direction:
row (主轴:左->右;垂轴:上->下)
row-reverse (主轴:右->左;垂轴:上->下)
column (主轴:上->下;垂轴:左->右)
column-reverse(主轴:下->上;垂轴:左->右)
默认值:row;
示例:flex-direction 动画演示
1.3.2. 换行控制:flex-wrap
flex-wrap:
nowrap(不换行)
wrap(允许换行,新行沿垂轴堆叠)
wrap-reverse(允许换行,并翻转垂轴方向,新行沿垂轴堆叠)
默认值:nowrap
示例:flex-wrap 动画演示
1.3.3. 主轴、垂轴方向总结
flex-flow: row wrap;
flex-flow: row-reverse wrap;
flex-flow: column wrap;
flex-flow: column-reverse wrap;
flex-flow: row wrap-reverse;
测试代码:
<!DOCTYPE html>
<html>
<head>
<title></title>
<style type="text/css">
#flex-container{
color: #fff;
font-size: 40px;
text-align: center;
}
#flex-container{
display: flex;
}
</style>
</head>
<body>
<div id="flex-container" style="width: 800px; height: 600px; border:1px solid blue; float: left;">
<div style="width:50px; height: 50px; background-color: silver;">1</div>
<div style="width:100px; height: 100px; background-color: red;">2</div>
<div style="width:150px; height: 150px; background-color: green;">3</div>
<div style="width:200px; height: 200px; background-color: orange;">4</div>
<div style="width:250px; height: 250px; background-color: purple;">5</div>
<div style="width:200px; height: 200px; background-color: blue;">6</div>
<div style="width:150px; height: 150px; background-color: yellow;">7</div>
<div style="width:100px; height: 100px; background-color: grey;">8</div>
<div style="width:50px; height: 50px; background-color: black;">9</div>
</div>
flex-direction:
<select id="flex-direction-select" onchange="onFlexDirectionChange()">
<option value="row">row</option>
<option value="row-reverse">row-reverse</option>
<option value="column">column</option>
<option value="column-reverse">column-reverse</option>
</select>
flex-wrap:
<select id="flex-wrap-select" onchange="onFlexWrapChange()">
<option value="nowrap">nowrap</option>
<option value="wrap">wrap</option>
<option value="wrap-reverse">wrap-reverse</option>
</select>
<script type="text/javascript">
function onFlexDirectionChange(){
var flexContainer = document.getElementById("flex-container");
var flexDirectionSelect = document.getElementById("flex-direction-select");
flexContainer.style.flexDirection = flexDirectionSelect.value;
}
function onFlexWrapChange(){
var flexContainer = document.getElementById("flex-container");
var flexDirectionSelect = document.getElementById("flex-wrap-select");
flexContainer.style.flexWrap = flexDirectionSelect.value;
}
</script>
</body>
</html>
1.4. 主轴方向元素对齐
主轴方向是通过 flex-direction 设置的方向,justify-content 属性定义了如何分配顺着弹性容器主轴的元素之间及其周围的空间。
justify-content 主轴弹性元素对齐控制
1.5. 垂轴方向元素对齐
align-items 属性可以使元素在交叉轴方向对齐。
align-items 垂轴弹性元素对齐控制
1.6. 多条主轴的对齐
align-content 属性控制多条主轴在内容项之间和周围分配空间,该属性对单行弹性盒子模型无效。
align-content 多主轴对齐控制
1.7. 视觉顺序控制
CSS order 属性规定了弹性容器中的可伸缩项目在布局时的顺序。元素按照 order 属性的值的增序进行布局。拥有相同 order 属性值的元素按照它们在源代码中出现的顺序进行布局。
order 弹性元素视觉顺序控制
1.8. flex-basis、flex-grow、flex-shrink 与 flex
flex-basis、flex-grow、flex-shrink 决定了弹性元素在弹性容器中的尺寸。
1.8.1. flex-basis
CSS 属性 flex-basis 指定了 flex 元素在主轴方向上的初始大小。
/* 指定<'width'> */
flex-basis: 10em;
flex-basis: 3px;
flex-basis: auto;
// "flex-basis:auto" 的含义是 "参照我的width和height属性".
1.8.2. flex-grow、flex-shrink
CSS flex-grow 属性定义弹性盒子项(flex item)的拉伸因子。
CSS flex-shrink 属性定义弹性盒子项(flex item)的收缩因子。
拉伸、收缩关键算法:
拉伸示例:
<!DOCTYPE html>
<html>
<head>
<title></title>
<style type="text/css">
.container {
color: #fff;
text-align: center;
width: 900px;
height: 100px;
border:1px solid blue;
}
.container {
display: flex;
flex-direction: row;
}
</style>
</head>
<body>
<div class="container">
<div class="item1" style="flex:1 1 100px; background-color: blue;"></div>
<div class="item2" style="flex:2 1 100px; background-color: red;"></div>
<div class="item3" style="flex:3 1 100px; background-color: green;"></div>
</div>
</body>
</html>
计算过程:
1. 剩余空间: 900px - 3*100px = 600px;
2. item1: 100 + 600 * (1 / (1+2+3)) = 200px
3. item2: 100 + 600 * (2 / (1+2+3)) = 300px
4. item3: 100 + 600 * (3 / (1+2+3)) = 400px
总结:按 flex-grow 的比例分配剩余空间
收缩示例:
<!DOCTYPE html>
<html>
<head>
<title></title>
<style type="text/css">
.container {
color: #fff;
text-align: center;
width: 900px;
height: 100px;
border:1px solid blue;
}
.container {
display: flex;
flex-direction: row;
}
</style>
</head>
<body>
<div class="container">
<div class="item1" style="flex:1 1 400px; background-color: blue;"></div>
<div class="item2" style="flex:1 2 600px; background-color: red;"></div>
<div class="item3" style="flex:1 3 800px; background-color: green;"></div>
</div>
</body>
</html>
计算过程:
1. 过剩空间: (400px + 600px + 800px) - 900px = 900px;
2. item1: 400 - 900* (1*400 / (1*400 + 2*600 + 3*800)) = 310px
3. item2: 600 - 900 * (2*600 / (1*400 + 2*600 + 3*800)) = 330px
4. item3: 800 - 900 * (3*800 / (1*400 + 2*600 + 3*800)) = 260px
总结:按 (flex-shrink * flex-basis) 的比例分配剩余空间
1.8.3. flex
flex 是一个简写属性,用来设置 flex-grow, flex-shrink 与 flex-basis。
flex: <'flex-grow'> <'flex-shrink'>? || <'flex-basis'>
当使用一个或两个无单位数时, flex-basis会从auto变为0.
flex: auto; ==> flex: 1 1 auto;
flex: none; ==> flex: 0 0 auto;
flex: 1; ==> flex: 1 1 0;
flex: 1 1; ==> flex: 1 1 0;
2. Flex 应用案例
2.1. 水平、垂直居中
<!DOCTYPE html>
<html>
<head>
<title></title>
<style type="text/css">
.container {
color: #fff;
font-size: 40px;
text-align: center;
width: 600px;
height: 400px;
border:1px solid blue;
}
.container {
display: flex;
justify-content: center;
align-items: center;
}
.item {
width:200px; height: 200px;
background-color: orange;
}
</style>
</head>
<body>
<div class="container">
<div class="item">4</div>
</div>
</body>
</html>
2.2. 一侧固定、一侧自适应
<!DOCTYPE html>
<html>
<head>
<title></title>
<style type="text/css">
html,
body {
width: 100%;
height: 100%;
}
.container {
color: #fff;
text-align: center;
width: 600px;
height: 80%;
border:1px solid blue;
}
.container {
display: flex;
flex-direction: column;
}
.item.fixed {
flex: none;
background-color: orange;
}
.item.fill{
flex: 1;
overflow: auto;
background-color: green;
}
</style>
</head>
<body>
<div class="container">
<div class="item fixed">
<h1>这里是固定高度区域,例如Banner</h1>
</div>
<div class="item fill">
<div style="width:400px; height:500px; background-color: blue;">
这里是内容区域
</div>
</div>
</div>
</body>
</html>
2.3. 圣杯布局
<!DOCTYPE html>
<html>
<head>
<title></title>
<style type="text/css">
html,
body {
width: 100%;
height: 100%;
}
.layout {
color: #fff;
text-align: center;
width: 90%;
height: 90%;
border:1px solid blue;
}
.layout {
display: flex;
flex-direction: column;
}
.layout > .top,
.layout > .bottom {
background-color: blue;
flex: none;
}
.layout > .middle {
flex: 1;
}
.layout > .middle {
display: flex;
flex-direction: row;
}
.layout > .middle > .left,
.layout > .middle > .right {
background-color: green;
flex: none;
}
.layout > .middle > .center {
background-color: purple;
flex: 1;
}
</style>
</head>
<body>
<div class="layout">
<div class="top">
<h1>TOP</h1>
</div>
<div class="middle">
<div class="left">
<h1>LEFT</h1>
</div>
<div class="center">
<h1>CENTER</h1>
</div>
<div class="right">
<h1>RIGHT</h1>
</div>
</div>
<div class="bottom">
<h1>BOTTOM</h1>
</div>
</div>
</body>
</html>
3. React 小组件
对业务系统中常见的几种布局进行封装,提升编程效率,避免被 CSS 细节淹没。
3.1. 场景覆盖
场景1:
import React from "react"
import {RowFlex, FlexItem, ColumnFlex} from "../components/flex"
export default function Layout1(){
return (
<ColumnFlex>
<FlexItem flexBasic={"auto"}>
<div style={{height: 120, backgroundColor:"green"}}>
<h1>FlexItem -> height="auto" -> 高度由内容决定</h1>
<h2>例如:这里放置网站标题栏</h2>
</div>
</FlexItem>
<FlexItem>
<div style={{height: "100%", backgroundColor:"red"}}>
<h1>FlexItem -> 自适应区域 -> 自动填充剩余空间</h1>
<h2>FlexItem -> overflow:auto -> 当自适应区域内容要溢出自适应容器时,使用滚动条</h2>
<div style={{height: 600, backgroundColor: "grey"}}>
<h1>这是内容区域</h1>
</div>
</div>
</FlexItem>
</ColumnFlex>
);
}
场景2:
import React from "react"
import {RowFlex, FlexItem, ColumnFlex} from "../components/flex"
export default function Layout2(){
return (
<div>
<div style={{height: 300, backgroundColor:"grey"}}>
<h1>RowFlex</h1>
<h2>水平方向特性:在水平方向上,实现部分固定,部分自适应能力</h2>
<h3>水平方向支持百分比、支持固定宽度</h3>
<h2>竖直方向特性:高度由内容决定 -> 如果有滚动条 -> 出现在 RowFlex 的容器元素身上</h2>
<h3>基本上是,为使用布局,单独使用RowFlex,只是需要它的水平方向控制能力</h3>
</div>
<RowFlex>
<FlexItem flexBasic={"70%"}>
<div style={{height: 300, backgroundColor:"green"}}>
<h2>左侧300</h2>
</div>
</FlexItem>
<FlexItem>
<div style={{height: 900, backgroundColor:"red"}}>
<h2>右侧900</h2>
</div>
</FlexItem>
</RowFlex>
</div>
);
}
场景3:
import React from "react"
import {RowFlex, FlexItem, ColumnFlex} from "../components/flex"
export default function Layout3(){
return (
<ColumnFlex>
<FlexItem flexBasic={300} style={{backgroundColor:"grey"}}>
<h1>RowFlex</h1>
<h2>水平方向特性:在水平方向上,实现部分固定,部分自适应能力</h2>
<h3>水平方向支持百分比、支持固定宽度</h3>
<h2>竖直方向特性:高度充满容器 -> 如果有滚动条 -> 各分区出现滚动条</h2>
<h3>基本上是,RowFlex 与 ColumnFlex 混合使用,例如,系统首页中的 Tree 与 Leaf</h3>
</FlexItem>
<FlexItem>
<RowFlex height={"100%"}>
<FlexItem flexBasic={"70%"}>
<div style={{height: 300, backgroundColor:"green"}}>
<h2>左侧300,这个区域没有滚动条</h2>
</div>
</FlexItem>
<FlexItem>
<div style={{height: 900, backgroundColor:"red"}}>
<h2>右侧900,这个区域有滚动条</h2>
</div>
</FlexItem>
</RowFlex>
</FlexItem>
</ColumnFlex>
);
}
3.2. 关键代码
index.less:
@flexPrefixCls: mousex-flex;
/* flexbox */
.@{flexPrefixCls} {
display: flex;
&&-row {
flex-direction: row;
}
&&-column {
height: 100%;
flex-direction: column;
}
& &-item {
&-auto {
flex: none;
}
&-fixed {
flex: none;
}
&-fill {
flex: 1;
}
// 分区滚动条处理
&-overflow-auto{
overflow: auto
}
&-overflow-hidden{
overflow: hidden
}
}
}
RowFlex.less:
import React from 'react';
import classNames from 'classnames';
export interface RowFlexProps {
prefixCls?: string,
className?: string,
style?: React.CSSProperties,
height?: number | string,
}
export default class RowFlex extends React.Component<RowFlexProps, any> {
static defaultProps = {
prefixCls: 'mousex-flex',
};
render() {
const {
prefixCls,
className,
style,
height,
children,
} = this.props;
const wrapCls = classNames(prefixCls, className, {
[`${prefixCls}-row`]: true
});
let cssStyle = {};
if (height) {
cssStyle = {
...cssStyle,
height
}
}
if (style) {
cssStyle = {
...cssStyle,
...style
}
}
return (
<div className={wrapCls} style={cssStyle}>
{children}
</div>
);
}
}
FlexItem.tsx:
import classNames from 'classnames';
import * as React from 'react';
export interface FlexItemProps {
prefixCls?: string;
className?: string;
style?: React.CSSProperties;
flexBasic?: number | string | "auto" | "fill";
overflow?: "auto" | "hidden"
}
const FlexItem: React.FunctionComponent<FlexItemProps> = (props) => {
const {
children, className, prefixCls, style,
flexBasic, overflow, ...restProps
} = props;
let cssStyle = {
};
if (flexBasic !== "auto" && flexBasic !== "fill") {
cssStyle = {
...cssStyle,
flexBasis: flexBasic
};
}
if(style){
cssStyle = {
...cssStyle,
...style
};
}
const wrapCls = classNames(`${prefixCls}-item`, className, {
[`${prefixCls}-item-auto`]: flexBasic === "auto",
[`${prefixCls}-item-fill`]: flexBasic === "fill",
[`${prefixCls}-item-fixed`]: flexBasic !== "auto" && flexBasic !== "fill",
[`${prefixCls}-item-overflow-auto`]: overflow === "auto",
[`${prefixCls}-item-overflow-hidden`]: overflow === "hidden"
});
return (
<div className={wrapCls} style={cssStyle} {...restProps}>
{children}
</div>
);
};
FlexItem.defaultProps = {
prefixCls: 'mousex-flex',
flexBasic: "fill",
overflow: "auto",
};
export default FlexItem;
参考:
can i use: https://www.caniuse.com/#feat=flexbox MDN: https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox CSS Flexible Box Layout Module Level 1: https://www.w3.org/TR/css-flexbox-1/