前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >【React】【CSS】【案例】:Flex 弹性盒模型

【React】【CSS】【案例】:Flex 弹性盒模型

作者头像
WEBJ2EE
发布2020-04-24 15:16:54
发布2020-04-24 15:16:54
2.8K00
代码可运行
举报
文章被收录于专栏:WebJ2EEWebJ2EE
运行总次数:0
代码可运行

1. Flex 弹性盒模型

Flexible Box 模型,通常被称为 flexbox,是一种一维的布局模型。它给 flexbox 的子元素之间提供了强大的空间分布和对齐能力。

1.1. 知识体系总图

1.2. 浏览器兼容性

IE 是兼容性最棒的浏览器,没有之一 !

1.3. 主轴、垂轴、换行

当使用 flex 布局时,首先想到的是两根轴线 — 主轴和交叉轴。

  • 主轴由 flex-direction 定义,另一根轴垂直于它。
  • flexbox 的特性是沿着主轴或者交叉轴对齐之中的元素。
  • flexbox 不会对文档的书写模式提供假设。

1.3.1. 主轴方向控制:flex-direction

代码语言:javascript
代码运行次数:0
复制
flex-direction: 
   row           (主轴:左->右;垂轴:上->下)
   row-reverse   (主轴:右->左;垂轴:上->下) 
   column        (主轴:上->下;垂轴:左->右)
   column-reverse(主轴:下->上;垂轴:左->右)
默认值:row;

示例:flex-direction 动画演示

1.3.2. 换行控制:flex-wrap

代码语言:javascript
代码运行次数:0
复制
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;

测试代码:

代码语言:javascript
代码运行次数:0
复制
<!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 属性定义了如何分配顺着弹性容器主轴的元素之间及其周围的空间。

  • flex-start:从行首起始位置开始排列(默认值)
  • flex-end:从行尾位置开始排列
  • center:居中排列
  • space-between:均匀排列每个元素首个元素放置于起点,末尾元素放置于终点
  • space-around:均匀排列每个元素每个元素周围分配相同的空间

justify-content 主轴弹性元素对齐控制

1.5. 垂轴方向元素对齐

align-items 属性可以使元素在交叉轴方向对齐。

  • flex-start:元素向侧轴起点对齐。
  • flex-end:元素向侧轴终点对齐
  • center:元素在侧轴居中。
  • baseline:所有元素向基线对齐。侧轴起点到元素基线距离最大的元素将会于侧轴起点对齐以确定基线。
  • stretch:弹性元素被在侧轴方向被拉伸到与容器相同的高度或宽度。(默认值)

align-items 垂轴弹性元素对齐控制

1.6. 多条主轴的对齐

align-content 属性控制多条主轴在内容项之间和周围分配空间,该属性对单行弹性盒子模型无效

  • flex-start:所有行从垂直轴起点开始填充。第一行的垂直轴起点边和容器的垂直轴起点边对齐。接下来的每一行紧跟前一行。
  • flex-end:所有行从垂直轴末尾开始填充。最后一行的垂直轴终点和容器的垂直轴终点对齐。同时所有后续行与前一个对齐。
  • center:所有行朝向容器的中心填充。每行互相紧挨,相对于容器居中对齐。容器的垂直轴起点边和第一行的距离相等于容器的垂直轴终点边和最后一行的距离。
  • space-between:所有行在容器中平均分布。相邻两行间距相等。容器的垂直轴起点边和终点边分别与第一行和最后一行的边对齐。
  • space-around:所有行在容器中平均分布,相邻两行间距相等。容器的垂直轴起点边和终点边分别与第一行和最后一行的距离是相邻两行间距的一半。
  • stretch:拉伸所有行来填满剩余空间。剩余空间平均地分配给每一行。(默认值)

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 元素在主轴方向上的初始大小。

  • 当一个元素同时被设置了 flex-basis (除值为 auto 外) 和 width (或者在 flex-direction: column 情况下设置了height) , flex-basis 具有更高的优先级.
  • width 值可以是 <length>; 该值也可以是一个相对于其父弹性盒容器主轴尺寸的百分数 。负值是不被允许的。默认为 auto
  • 不要单独使用 flex-basis 属性,应该统一使用属性 flex 控制。
代码语言:javascript
代码运行次数:0
复制
/* 指定<'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)的收缩因子。

  • 负值无效

拉伸、收缩关键算法:

拉伸示例:

代码语言:javascript
代码运行次数:0
复制
<!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>
代码语言:javascript
代码运行次数:0
复制
计算过程:
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 的比例分配剩余空间

收缩示例:

代码语言:javascript
代码运行次数:0
复制
<!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>

代码语言:javascript
代码运行次数:0
复制
计算过程:
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。

代码语言:javascript
代码运行次数:0
复制
flex: <'flex-grow'> <'flex-shrink'>? || <'flex-basis'>

当使用一个或两个无单位数时, flex-basis会从auto变为0.

代码语言:javascript
代码运行次数: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. 水平、垂直居中

代码语言:javascript
代码运行次数:0
复制
<!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. 一侧固定、一侧自适应

代码语言:javascript
代码运行次数:0
复制
<!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. 圣杯布局

代码语言:javascript
代码运行次数:0
复制
<!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:

代码语言:javascript
代码运行次数:0
复制
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:

代码语言:javascript
代码运行次数:0
复制
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:

代码语言:javascript
代码运行次数:0
复制
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:

代码语言:javascript
代码运行次数:0
复制
@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:

代码语言:javascript
代码运行次数:0
复制
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:

代码语言:javascript
代码运行次数:0
复制
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/

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 WebJ2EE 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档