弹性盒模型Flex指南

弹性盒模型Flex指南

Web layout 是Web UI中的基础架构, 重要性不言而喻. 传统的盒模型, 借助display, position, float 属性应对普通布局游刃有余, 但针对复杂的或自适应布局, 常常捉襟见肘. 比如垂直居中, 就是一个老大难的问题, 借助flex弹性盒模型, 两行代码就可以优雅的实现之. (该方法曾在 16种方法实现水平居中垂直居中 一文中提到). 当然, 本次我们不会只讨论垂直居中的问题, 我将努力尽可能的还原flex的应用场景. 本文后面还将讲解flex子项目压缩比计算, 多层flex嵌套的常见问题.

Flex

Flex即弹性盒模型, 该布局方案由W3C于2009年提出. 此后, Flex方案便历经v2009, v2011, v2012, v2014, v2015, v2016等版本, 最近方案是2016年5月26日起草的 CSS Flexible Box Layout Module Level 1.

兼容性

首先, 我们来回顾下如今PC端的兼容性(以下为完全兼容版本).

IEEdgeFirefoxChromeSafariOpera

-12+28+21+6.1+12.1+

以上, IE10+仅支持2012版W3C的flex语法, 且存在较多已知的bug, 此时使用flex布局需谨慎.

Chrome浏览器v21~v28版本需要添加 "-webkit-" 前缀.

Safari浏览器v6.1~v8版本需要添加 "-webkit-" 前缀.

Opera浏览器v15~v16版本需要添加 "-webkit-" 前缀.

因此, 看到一些sass编译后的css文件中带有 "-webkit-" 前缀无需惊慌.

平时开发时最为担心的便是移动端兼容性, 请看:

IOS SafariOpera miniAndroidAndroid ChromeUC微信

7.1+√4.4+55-当前支持

微信当前版本已支持flex.

UC不对外提供webview内核, 除去一些H5app的应用, 各种分享页基本(常在微信下打开)基本不需要担心对其兼容性, 实在需要实现, UC还是支持老版本的弹性盒子的, 可以优雅降级. 可见, Android4.4以上基本可以安心使用flex.

Autoprefixer

强记各种浏览器的前缀是没有必要的, 因为autoprefixer该做的, 都帮我们做了. 因此建议尝试下以下三个插件之一.

grunt-autoprefixer

gulp-autoprefixer

PostCSS-autoprefixer

优势

Flex布局使得子项目能够"弹性"的改变其高宽, 自由填充容器剩余空间, 以适应容器变大, 或者压缩子项目自身, 以适应容器变小; 同时还可以方便的调节子项目方向和顺序. flex常用于高宽需要自适应, 或子项目大小成比例, 或水平垂直对齐等场景.

概念铺垫

Flex弹性盒模型里, 有容器和项目之分. 设置display:flex的为容器, 容器内的元素称作它的子项目, 容器有容器的一套属性, 子项目有子项目的另一套属性. (可以这么理解: father作为弹性盒子, 制定行为规范, son享受盒子的便利, 按照规范划分各自的"辖区").

以下图片摘自大漠的一个完整的Flexbox指南文中.

father制定的规范, 基于两个方向 — 水平和垂直.

水平方向的称之为主轴(main axis), 垂直方向的称之为交叉轴(cross axis).

主轴起始位置, 叫做main start, 末尾位置叫做main end;

交叉轴起始位置, 叫做cross start, 末尾位置叫做cross end.

子项目在主轴上所占的宽(高)度, 叫做main size, 在交叉轴上所占的高(宽)度, 叫做cross size.

属性

display: flex | inline-flex;(元素将升级为弹性盒子). 前者容器升级为块级盒子, 后者容器将升级为行内盒子. 元素采用flex布局以后, 子元素的float, clear, vertical-align属性都将失效.

容器属性

容器具有以下6个属性.

flex-direction 指定主轴的方向.

flex-direction的值描述

row(默认)指定主轴水平, 子项目从左至右排列➜

row-reverse指定主轴水平, 子项目从右至左排列⬅︎

column指定主轴垂直, 子项目从上至下排列⬇︎

column-reverse指定主轴垂直, 子项目从下至上排列⬆︎

flex-wrap 指定如何换行.

flex-wrap的值描述

nowrap(默认)默认不换行

wrap正常换行

wrap-reverse换行, 且前面的行在底部

flex-flow 它是flex-direction 和 flex-wrap的简写形式, 默认值为row nowrap.

justify-content 指定主轴上子项目的对齐方式.(通常为水平方向对齐方式)

justify-content的值描述(子项目--主轴方向)

flex-start(默认)子项目起始位置与main start位置对齐

flex-end子项目末尾位置与main end位置对齐

center在主轴方向居中于容器

space-between与交叉轴两端对齐, 子项目之间的间隔全部相等

space-around子项目两侧的距离相等, 它们之间的距离两倍于它们与主轴起始或末尾位置的距离.

align-items 指定交叉轴上子项目的对齐方式.(通常为垂直方向对齐方式)

align-items的值描述(子项目—交叉轴方向)

flex-start子项目起始位置与cross start位置对齐

flex-end子项目末尾位置与cross end位置对齐

center在交叉轴方向居中于容器

baseline第一行文字的基线对齐

stretch(默认)高度未定(或auto)时, 将占满容器的高度

align-content 指定多根主轴的对齐方式. 若只有一根主轴, 则无效.

align-content的值描述(子项目)

flex-start顶部与cross start位置对齐

flex-end底部与cross end位置对齐

center在交叉轴方向居中于容器

space-between与交叉轴两端对齐, 间隔全部相等

space-around子项目两侧的距离相等, 它们之间的距离两倍于它们与主轴起始或末尾位置的距离.

stretch(默认)多根主轴上的子项目充满交叉轴

子项目属性

子项目具有以下6个属性.

flex-grow 指定子项目的放大比例, 默认为0(即不放大). 该属性可取值为任何正整数. 假设各个子项目的放大比例之和为n, 那么容器内剩余的空间将分配n份, 每个子项目各自分到x/n份. (x为该子项目的放大比例)

flex-shrink 指定子项目的缩小比例, 默认为1. 设置为0时, 空间不足该子项目将不缩小. 我们知道, 容器的缩小总宽度=子项目所需要的总宽度-容器实际宽度, 假设容器需要缩小的宽度为W, 某子项目的默认宽度为L, 其缩小比例为p, 那么该子项目实际的宽度为L-p*W.

上面轻描淡写的给出了子项目的缩小比例, 可能会给你一种错觉— "缩小比例很容易计算", 实际上, 我们在计算元素需要缩小比例时, 总是要考虑到元素自身默认的大小.

假设上述子项目其flex-shrink值为x1, 另一个子项目的默认宽度为R, flex-shrink值为x2, 考虑到元素自身大小. 最终第一个子项目的缩小比例是加权了自身默认大小后的结果, 即rate = L*x1/(L*x1 + R*x2).

为什么计算会如此复杂, 如此不直观??? 这是因为, 子项目的大小各不一致, 假如一个子项目是另一个子项目主轴宽度的9倍, 前者的flex-shrink值为1, 后者为9, 而容器实际上只有他们默认总宽度的一半. 这意味着, 这两个子项目共计要压缩为默认的一半. 如果仅仅按照flex-shrink值来决定比例, 那么第二个子项目需要压缩其默认的9/10, 而我们知道, 它默认是如此的小, 即使全部压缩了, 也无济于事; 而第一个元素仅需要压缩其默认的1/10, 简直就是九牛一毛, 根本达不到默认总宽度压缩一半的效果. 很明显, 这种压缩比例的分配方式是不合理的. 因此最终的压缩比例加入了默认宽度值(即flex-basis值), 表达式的分子为 flex-shrink * flex-basis, 分母为各子项目 flex-shrink * flex-basis 之和.

flex-basis 指定子项目分配的默认空间, 默认为auto. 即该子项目的原本大小.

flex 是 flex-grow, flex-shrink, flex-basis 3个属性的缩写. 默认为0 1 auto. 该属性取值为auto时等同于设置为1 1 auto, 取值为none时等同于设置为0 0 auto.

align-self 指定单个子项目独立的对齐方式. 默认为auto, 表示继承父元素的align-items属性, 如无父元素, 则等同于stretch. 该属性共有6种值, 其他值与上述align-items属性保持一致.

order 指定子项目的顺序, 数值越小, 顺序越靠前, 默认为0.

flex属性的优先级

我们可以给input设置flex:1, 使其充满一行, 并且随着父元素大小变化而变化. 也可以给div设置flex:1使其充满剩余高度.

使用flex布局这些都不是难事, 需要注意的是, 这其中有坑. 为了避免踩坑, 我们先来看下flex属性的优先级:

width|height > 自适应文本内容的宽度或高度 > flex:数值

这意味着, 首先是元素宽高的值优先, 其次是内容的宽高, 再次是flex数值. 现在我们来看看坑是什么.

给input元素设置flex:1时需要注意, 通常input拥有一个默认宽度(用于展示默认数量的字符), 在chrome v55下, 这个宽度默认为126px(同时还包含2px的border). 因此想要实现input宽度自适应, 可以设置其width为0.

给div元素设置flex:1时, 因div的高度会受子级元素影响, 为了使得该div占满其父元素剩余的高度, 且不超出, 建议将该div的height属性设置为0.

场景回顾

想要实现垂直居中的效果, 只需要设置父元素为display:flex;justify-content:center 即可. (当然, 父元素样式采用:display:table;, 子元素样式采用:display:table-cell;vertical-align:middle 也是可以实现的), 如下图.

想要实现左右两个元素等高(父元素高度由子元素撑开), 并且各占一半的宽度. 如上图.

早期的实现方案, 需要借助负margin. 父元素样式设置为overflow:hidden, 子元素样式设置为margin-bottom:-10000px;padding-bottom:10000px;, 这样, 每个子元素便能借助padding撑开, 同时, 借助负margin和overflow合理裁剪.

第二种方案就是借助IE8都支持的display:table属性, 父元素样式设置为display:table , 子元素设置为display:table-cell. 利用表格的行高一致性, 轻松实现行高一致.

最终, 我们发现, 还是flex弹性盒模型来得方便快捷, 它只需要父级元素样式设置为display:flex.

有关flex的旧语法, 请戳这篇回顾 Flex布局新旧混合写法详解(兼容微信) .

有关移动端的最佳实践, 请戳这篇围观 移动端全兼容的flexbox速成班 .

当然, 这里还有一个 Flexbugs 列表, github上已有近6k的star, 感兴趣可以前去看看.

相关链接:

https://www.cnblogs.com/xiaohuochai/p/5323146.html

https://www.jianshu.com/p/53c44e02427c

https://segmentfault.com/a/1190000009061028推荐

http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html?utm_source=tuicool

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容