继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Flex

largeQ
关注TA
已关注
手记 984
粉丝 92
获赞 585

传统的网页布局基于盒装模型,使用display,position,float属性来达成各种布局。
对于一些特殊的布局使用这些来实现不是很方便,比如垂直居中。
Flex应运而生,它可以简便、完整、响应式地实现各种页面布局。
Chrome 21,FF22,IE 10,Safari 6.1及以上的浏览器都支持Flex。
Flex布局将成为未来布局的首选方案。

基本概念

任何一个容器都可以指定为Flex布局。
块级元素:

.box{  display: -webkit-flex; /* Safari chrome*/
  display: flex;
}

行内元素:

.box{  display: -webkit-inline-flex;  display: inline-flex;
}

设为Flex布局以后,子元素的float、clear和vertical-align属性将失效。
采用Flex布局的元素,称为Flex容器(flex container)。它的所有子元素自动成为容器成员,称为Flex项目(flex item)。
容器默认存在两根轴:水平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴的开始位置(与边框的交叉点)叫做main start,结束位置叫做main end;交叉轴的开始位置叫做cross start,结束位置叫做cross end。
项目默认沿主轴排列。单个项目占据的主轴空间叫做main size,占据的交叉轴空间叫做cross size。

容器的属性

以下6个属性设置在容器上,用来控制容器中项目的排列方式和位置:

  • flex-direction

  • flex-wrap

  • flex-flow

  • justify-content

  • align-items

  • align-content

flex-direction

flex-direction属性决定主轴的方向(即项目的排列方向)。

.box {  flex-direction: row | row-reverse | column | column-reverse;
}

它可能有4个值。

  • row(默认值):主轴为水平方向,起点在左端。

  • row-reverse:主轴为水平方向,起点在右端。

  • column:主轴为垂直方向,起点在上沿。

  • column-reverse:主轴为垂直方向,起点在下沿。

flex-wrap

默认情况下,项目都排在一条线(又称”轴线”)上。flex-wrap属性定义,如果一条轴线排不下,如何换行。

.box{  flex-wrap: nowrap | wrap | wrap-reverse;
}

它可能取三个值

  • nowrap(默认):不换行。

  • wrap:换行,第一行在上方,或者第一列在左

  • wrap-reverse:换行,第一行在下方,或者第一列在右

flex-flow

flex-flow属性是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。

justify-content

justify-content属性定义了项目在主轴上的对齐方式。注意,主轴可能是水平的也可能是垂直的。

.box {  justify-content: flex-start | flex-end | center | space-between | space-around;
}

它可能取5个值,具体对齐方式与轴的方向有关。下面假设主轴为从左到右,其他方向类比即可:

  • flex-start(默认值):左对齐

  • flex-end:右对齐

  • center: 居中

  • space-between:两端对齐,项目之间的间隔都相等。

  • space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。

align-items

align-items属性定义项目在交叉轴上如何对齐,交叉轴是和主轴垂直的轴

.box {  align-items: flex-start | flex-end | center | baseline | stretch;
}
  • flex-start:交叉轴的起点对齐。

  • flex-end:交叉轴的终点对齐。

  • center:交叉轴的中点对齐。

  • baseline: 项目的第一行文字的基线对齐。

  • stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。

align-content

align-content属性定义了多根轴线的对齐方式。这个和之前的align-items不太一样,后者是规定元素相对本轴线的行为,

.box {  align-content: flex-start | flex-end | center | space-between | space-around | stretch;
}

该属性可能取6个值:

  • flex-start:与交叉轴的起点对齐。

  • flex-end:与交叉轴的终点对齐。

  • center:与交叉轴的中点对齐。

  • space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。

  • space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴与边框的间隔大一倍。

  • stretch(默认值):轴线占满整个交叉轴。

项目的属性

以下6个属性设置在项目上,用来设置项目的大小,对齐,次序等:

  • order

  • flex-grow

  • flex-shrink

  • flex-basis

  • flex

  • align-self

order

order属性定义项目的排列顺序。数值越小,排列越靠前,当然,这个前是相对于主轴顺序来说的,默认值为0。

.item {  order: <integer>;
}

flex-grow

flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。
如果所有项目的flex-grow属性都为x,则它们将等分剩余空间(如果有的话,值为0的元素不参加等分)。如果一个项目的flex-grow属性为2x,其他项目都为x,则前者占据的剩余空间将比其他项多一倍。

flex-shrink

flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。
如果所有项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小。如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小。

flex-basis

flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。
它可以设为跟width(主轴横向)或height属性一样的值,则项目将占据固定空间。

flex

flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。
该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。
建议优先使用这个属性,而不是单独写三个分离的属性,因为浏览器会推算相关值。这点一定要注意,你设置flex-grow:1和flex:1效果不一样的。

align-self

align-self属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch。

.item {  align-self: auto | flex-start | flex-end | center | baseline | stretch;
}

该属性可能取6个值,除了auto,其他都与align-items属性完全一致。

实例

骰子布局

我们的骰子和骰子上的点的结构是这样的:

<div class="one-container">
    <div class="point"></div>
    <div class="point"></div>
    <!--...--></div>

骰子的公共样式:

div{
  display: flex;
  width:104px;
  height:104px;
  margin:16px;
  padding:4px;
  background-color: #e7e7e7;
  box-shadow:
          inset 0 5px white,
          inset 0 -5px #bbb,
          inset 5px 0 #d7d7d7,
          inset -5px 0 #d7d7d7;
  border-radius: 10%;
  .point{
    width: 24px;
    height: 24px;
    border-radius: 50%;
    margin: 4px;
    background-color: #333;
    box-shadow: inset 0 3px #111, inset 0 -3px #555;
  }
}

左上一个点

Flex布局默认就是首行左对齐,所以啥都不用写,里面放一个point就好。

中上一个点

设置项目在主轴上的对齐方式可以调整点的水平位置:

.one-container{  //top center
  justify-content:center;
}

右上一个点

同理

.one-container{  //top right
  justify-content:flex-end;
}

中间行,下行一个点

设置项目在交叉轴上的对齐方式,可以将点移到中间行和最下行,再配合刚才的justify-content,9个位置的单独点就都可以达成了。

.one-container{  //top left

  //top center
  justify-content:center;  //top right
  justify-content:flex-end;  //center left
  align-items:center;  //center center
  align-items:center;
  justify-content:center;  //center right
  align-items:center;
  justify-content:flex-end;  //bottom left
  align-items:flex-end;  //bottom center
  align-items:flex-end;
  justify-content:center;  //bottom right
  align-items:flex-end;
  justify-content:flex-end;
}

两个点

当有两个项目的时候,我们先来看看一行上放两个点,两个点排在一行的两端。
使用justify-content,你有两个选择,space-around和space-between。
一个两边不留空等分间距,一个两边留空

.two-container{ justify-content:space-between;
}

竖着排列:

.two-container{  //row two sperad
  justify-content:space-between;  //column
  flex-direction: column;
}

我们知道骰子的两个点是斜着排列的,那怎么办呢,这时align-self就起作用了:

.two-container{  //row two sperad
  justify-content:space-between;  //column
  flex-direction: column;
  .point:nth-child(2) {
    align-self: flex-end;
  }
}

三个点

这里主轴水平,第2个和第3个点使用align-self放到中间行和最下行,这里注意调整一下项目在主轴上的分布,这样3个点就会在水平的中央不偏了。

.third-container{  justify-content:space-between;  .point:nth-child(2) {
    align-self: center;
  }  .point:nth-child(3) {    align-self: flex-end;
  }
}

四个点

骰子的4个点是分布在四个角上的这个有两种实现方法
如果你的骰子是固定大小的,给第一第三个点增加一个右外边距使得一行只能容下两个点:

.four-container{  justify-content:space-between;  align-content: space-between;  flex-wrap:wrap;  .point:nth-child(2n+1) {
    margin-right:40px;
  }
}

或者你使用两个flex布局嵌套:

<div class="four-container-other">
    <div class="row">
        <div class="point"></div>
        <div class="point"></div>
    </div>
    <div class="row">
        <div class="point"></div>
        <div class="point"></div>
    </div></div>
.four-container-other{
  align-content:space-between;
  flex-wrap: wrap;
  .row{
    width:100%;
    display: flex;
    justify-content:space-between;
  }
}

五个点

由四个点递推,第一个方法不行了,只能使用第二个办法。

<div class="five-container">
    <div class="row">
        <div class="point"></div>
        <div class="point"></div>
    </div>
    <div class="row">
        <div class="point"></div>
    </div>
    <div class="row">
        <div class="point"></div>
        <div class="point"></div>
    </div></div>
.five-container{
  align-content:space-between;
  flex-wrap: wrap;
  .row{
    width:100%;
    display: flex;
    justify-content:space-between;
  }
  .row:nth-child(2) {
    justify-content: center;
  }
}

六个点

这个反倒简单了,因为3个点自己就会排满一行:

<div class="six-container">
    <div class="point"></div>
    <div class="point"></div>
    <div class="point"></div>
    <div class="point"></div>
    <div class="point"></div>
    <div class="point"></div></div>
.six-container{  flex-wrap: wrap;  align-content:space-between;  justify-content:space-between;
}

网格布局

基本网格布局

最简单的网格布局,就是平均分布。在容器里面平均分配空间。只要给项目设置了相同的flex-basis(不能是auto),flex-grow和flex-shrink,不管是多少个项目都会均分空间。或者你干脆设置一个flex:1,这样浏览器会自动推算出:

flex-basis: 0px;
flex-grow: 1;
flex-shrink: 1;

如果你只设置了flex-grow是不行的,虽然flex-shrink默认就是1可以不管,但是由于flex-basis默认为auto,所以内容比较多的元素会占比较大的主轴空间。

<div class="row even">
    <div class="cell">
        <div class="cell">1/2</div>
    </div>
    <div class="cell">
        <div class="cell">1/2</div>
    </div></div><div class="row even">
    <div class="cell">
        <div class="cell">1/3</div>
    </div>
    <div class="cell">
        <div class="cell"></div>
    </div>
    <div class="cell">
        <div class="cell">1/3</div>
    </div></div>
.row{  display: flex;
  flex-wrap: nowrap;  margin: -1em 0 1em -1em;
  .cell{    padding: 1em 0 0 1em;
    text-align: center;    flex: 1;    //flex-basis: 0px;    //flex-grow: 1;    //flex-shrink: 1;    color:white;    display: flex;
    .item{
      background-color: hsla(31,15%,50%,.2);
      border-radius: 4px;      padding: .8em 1em 0;      width:100%;
      &:after {        content: '\00a0';        display: block;
        margin-top: 1em;        height: 0;        visibility: hidden;
      }
    }
  }
}

这里设置了flex=1。
你会注意到,这里的cell只是一个容器,项目的样子是由cell里的item设置的,cell之所以设置了display:flex是为了使cell里的item的高度利用align-items的默认值stretch填充满cell,即使没有内容或内容不够。
还有这里的row的margin、cell的padding、item的伪元素是有讲究的,是为了在flex布局嵌套时互相抵消用的,这个会在下面解释

百分比布局

某个网格的宽度为固定的百分比,其余网格平均分配剩余的空间。
平均分配剩余空间的项目其实还是靠的flex都为相同的值,对于那个要占固定位置的,通过给flex-basis一个百分比来设置它占主轴多宽,flex-shrink和flex-grow都设为0来保证其大小不会自适应的调整。如果你的项目是有margin的,那么这个百分比是不准的,和width一样,所以项目之间的间距最好使用padding来达成。

<div class="row fix-precentage">
    <div class="cell c-100">
        <div class="item">full</div>
    </div>
    <div class="cell">
        <div class="item">nowhere to go</div>
    </div></div><div class="row fix-precentage">
    <div class="cell c-50">
        <div class="item">full</div>
    </div>
    <div class="cell">
        <div class="item">even rest</div>
    </div>
    <div class="cell">
        <div class="item">even rest</div>
    </div>
    <div class="cell">
        <div class="item">even rest</div>
    </div></div>
.c-100{  flex: 0 0 100%;
}.c-50{  flex: 0 0 50%;
}

响应式布局

利用媒体查询加flex就可以做到响应式的布局咯:

.row.responsive{
  flex-wrap: wrap;  @media all and (max-width: 768px)  {
    .cell {
      flex:0 0 100%;
    }
  }
}

网格布局嵌套

在一个flex项目div中再嵌套一个row的时候,row的左上负margin会和div的padding抵消。
row下面的正margin-bottom会和div的伪元素的margin-top重合。
这样一个row就正常的放在了div里,没有任何多于的边距。

<div class="row nest">
    <div class="cell">
        <div class="item">
            <div class="row">
                <div class="cell">
                    <div class="item">1/2</div>
                </div>
                <div class="cell">
                    <div class="item">
                        <div class="row">
                            <div class="cell">
                                <div class="item">1/2</div>
                            </div>
                            <div class="cell">
                                <div class="item">1/2</div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="cell">
        <div class="item">responsive</div>
    </div>
    <div class="cell">
        <div class="item">responsive</div>
    </div></div>

圣杯布局

圣杯布局指的是一种最常见的网站布局。页面从上到下,分成三个部分:头部(header),躯干(body),尾部(footer)。其中躯干又水平分成三栏,从左到右为:导航、主栏、副栏。
我们使用一个总的垂直的flex布局,放上header,body,footer。
其中header和footer固定高度,flex:none
body自动拉伸,在内容不够时配合整个容器的最小高度100vh使footer处于页面最底;在内容多时拉伸配合内容,flex:flex: 1 0 auto;

.HolyGrail{
  display: flex;
  flex-direction: column;
  min-height: 100vh;  //height:100%;
  header,
  footer {
    flex: none;
  }
  .HolyGrail-body {
    display: flex;
    flex: 1 0 auto; /* 2 */
  }
}

在body中,flex水平布局,左右边栏固定宽度,中间内容随屏幕铺开:

.HolyGrail-content {  flex: 1;
}.HolyGrail-nav, .HolyGrail-ads {  /* two aside fix to 12em */
  flex: 0 0 12em;
}

nav放在最左侧:

.HolyGrail-nav {  /* nav left */
  order: -1;
}

在屏幕宽度太小时,body中内容竖直排列比较合适

@media all and (max-width: 768px) {  .HolyGrail-body {    flex-direction: column;    flex: 1;
  }  .HolyGrail-nav,  .HolyGrail-ads,  .HolyGrail-content {    flex: auto;
  }
}

输入框布局

我们常常需要在输入框的前方添加提示,后方添加按钮。
我们想让这个提示或按钮是固定宽度的,然后input填满剩下的宽度,但是在以前这并不容易,现在有了 flex一切就变得简单了

<!-- appending --><div class="InputAddOn">
    <input class="InputAddOn-field">
    <button class="InputAddOn-item">Submit</button></div><!-- prepending --><div class="InputAddOn">
    <span class="InputAddOn-item">Hint:</span>
    <input class="InputAddOn-field"></div><!-- both --><div class="InputAddOn">
    <span class="InputAddOn-item">Hint:</span>
    <input class="InputAddOn-field">
    <button class="InputAddOn-item">Submit</button></div>

核心就两个

.InputAddOn {  display: flex;
}.InputAddOn-field {  flex: 1;
}

不过当然要美化一下啦~

.InputAddOn {  margin:1em;  display: flex;
}.InputAddOn-field:first-child, .InputAddOn-item:first-child {  border-radius: 2px 0 0 2px;
}.InputAddOn-field:not(:last-child), .InputAddOn-item:not(:last-child) {  border-right: 0;
}.InputAddOn-field:last-child, .InputAddOn-item:last-child {  border-radius: 0 2px 2px 0;
}.InputAddOn-field, .InputAddOn-item {  border: 1px solid hsla(31,15%,50%,.25);  padding: .5em .75em;
}.InputAddOn-field {  flex: 1;
}.InputAddOn-item {  background-color: hsla(31,15%,50%,.1);  color: #666;  font: inherit;  font-weight: 400;
}

悬挂式布局

左边是头像,右边是文章的布局用flex实现也很合适:

<div class="media">    <img class="media-figure" src="kitten.jpg" />
    <div class="media-body">
        <h3>Media Object Title</h3>
        <p>Phasellus vel felis purus. Aliquam consequat pellentesque dui, non mollis erat dictum sit amet. Curabitur non quam dictum, consectetur arcu in, vehicula justo.</p>
    </div></div>
.media {  display: flex;  align-items: flex-start;
}.media-figure {  margin-right: 1em;
}.media-body {  flex: 1;
}

可以套用刚才的grid系统实现多列和嵌套:

<div class="hang-layout grid">
    <div class="row nest">
        <div class="cell c-50">
            <div class="item">
                <div class="media">
                    <img class="media-figure" src="kitten.jpg" />
                    <div class="media-body">
                        <h3>Media Object Title</h3>
                        <p>Phasellus vel felis purus. Aliquam consequat pellentesque dui, non mollis erat dictum sit amet. Curabitur non quam dictum, consectetur arcu in, vehicula justo.</p>
                    </div>
                </div>
            </div>
        </div>
        <div class="cell">
            <div class="item">
                <div class="media">
                    <img class="media-figure" src="kitten.jpg" />
                    <div class="media-body">
                        <h3>Media Object Title</h3>
                        <p>Phasellus vel felis purus. Aliquam consequat pellentesque dui, non mollis erat dictum sit amet. Curabitur non quam dictum, consectetur arcu in, vehicula justo.</p>
                        <div class="row nest">
                            <div class="cell">
                                <div class="item">
                                    <div class="media">
                                        <img class="media-figure" src="kitten.jpg" />
                                        <div class="media-body">
                                            <h3>Media Object Title</h3>
                                            <p>Phasellus vel felis purus. Aliquam consequat pellentesque dui, non mollis erat dictum sit amet. Curabitur non quam dictum, consectetur arcu in, vehicula justo.</p>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div></div>
.hang-layout{
  display: flex;
  flex-wrap: wrap;
  background-color: white;
  .media{
    display: flex;
    flex-basis: 50%;
    align-items: flex-start;
    padding:10px;
    text-align: left;
    color: #2b2d2f;
    .media-figure {
      margin-right: 1em;
      width:50px;
    }
    .media-body {
      flex: 1;
    }
  }
}

流式布局

即每行的项目数固定,自动分行,以前是用float实现的。
主要就是设置项目的flex-grow,flex-shrink,flex-basis。

<div class="flowLayout">
    <div class="flowItem"></div>
    <div class="flowItem"></div>
    <div class="flowItem"></div>
    <div class="flowItem"></div>
    <div class="flowItem"></div></div>
.flowLayout{
  display: flex;
  flex-wrap: wrap;
  .flowItem{
    border: 2px solid red;
    padding:2em;
    flex: 0 0 25%;
  }
}

垂直居中

这个一直是网页布局的噩梦,如果你想垂直居中一个大小不定的元素,还有一些hack的办法,如果你想垂直一组大小不定,数目不定的元素。。。。good luck
而使用flex,这实在太轻松了,使用align-items,align-self,justify-content你可以居中任何东西!

<div class="vertical">
    <div class="item">
        <h3>We are Centered Anyway</h3>
        <p contenteditable="true">click me and try</p>
    </div>
    <div class="item">
        <h3>We are Centered Anyway</h3>
        <p contenteditable="true">click me and try</p>
    </div>
    <div class="item">
        <h3>We are Centered Anyway</h3>
        <p contenteditable="true">click me and try</p>
    </div></div>
.vertical{
  background-color: hsla(31,15%,50%,.1);  /*only these importent*/
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;  /*only these importent*/
  padding:1em;
  min-height: 900px;
  .item{
    background-color: hsla(31,15%,50%,.1);
    max-width:50%;
    padding:1em;
    margin: 1em;
    border-radius: 4px;
  }
}



作者:exialym
链接:https://www.jianshu.com/p/7c4eca814055


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP

热门评论

只有代码没有预览图,看起来不是很直观

查看全部评论