Flexbox布局的正确使用姿势

简介: Flexbox布局的正确使用姿势


在项目中,我们还会大量使用到flexbox的新旧属性,但大多数人一般只会写新属性,旧属性交由autoprefixer处理,但其实完成同样功能的新旧属性表现形式却不尽相同。还有部分人只使用“万能”的flex:number属性为伸缩项目分配空间,但有些特殊情景却无法满足,此文为此梳理了flexbox的新旧属性区别和分配空间的原理,为大家用flexbox布局的项目通通渠。


Flexbox兼容性

PC端的兼容性

移动端的兼容性

如上图,为了兼容IE10-11和Android4.3-,UC,我们仍需要使用Flexbox的旧属性。


Flexbox新旧属性

Flexbox的新属性提供了很多旧版本没有的功能,但是目前Android4.x和UC仍有一定市场占有率需要兼容,因此目前只使用新旧属性都有的功能。

能实现相同功能的Flexbox新旧属性如下表:

但想象是美好的,现实是残酷的,新旧属性里有那么几个顽固分子并不能乖乖的表现的一样,总有那么一点不同。下面我们来看看是哪些新旧属性有不同:

flex-direction:row-reverse vs box-orient:horizontal;box-direction:reverse

相同点:改变主轴方向和伸缩项目的排列顺序;在ltr下伸缩项目从右到左排列。不同点:

flex-direction:row-reverse:第一个伸缩项目向主轴起点对齐

box-orient:horizontal;box-direction:reverse:最后一个伸缩项目向主轴终点对齐

flex-direction:column-reverse vs box-orient:vertical;box-direction:reverse

相同点:改变主轴方向和伸缩项目的排列顺序;在ltr下伸缩项目从下到上排列。

不同点:

flex-direction:column-reverse:第一个伸缩项目向主轴起点对齐。

box-orient:vertical;box-direction:reverse:最后一个伸缩项目向主轴终点对齐。

oreder:integer vs box-ordinal-group:integer

相同点:定义伸缩项目显示顺序。

不同点:

oreder:integer:默认值为0;可以为负值。

box-ordinal-group:integer:默认值为1;取值大于1。

flex-grow:number vs box-flex:number

相同点:定义伸缩项目的扩展因素。

不同点:box-flex:number同时定义了伸缩项目的缩小因素。

flex-shrink:number vs box-flex:number

相同点:定义伸缩项目的缩小因素。

不同点:box-flex:number同时定义了伸缩项目的扩展因素。


Flexbox分配空间原理

影响Flexbox布局分配空间的属性有三个,分别是flex-growflex-shrinkflex-basis

  • flex-grow:当伸缩项目在主轴方向的总宽度 < 伸缩容器,伸缩项目根据扩展因素分配伸缩容器的剩余空间。
  • flex-shrink:当伸缩项目在主轴方向的总宽度 > 伸缩容器,伸缩项目根据缩小因素分配总宽度超出伸缩容器的空间。
  • flex-basis:伸缩基础,在进行计算剩余空间或超出空间前,给伸缩项目重新设置一个宽度,然后再计算。

我们先来看看如何计算计算拉伸后的伸缩项目宽度,先简单明了的给个公式,再通过栗子来验证。

伸缩项目扩展宽度 = (项目容器宽度 - 项目宽度或项目设置的flex-basis总和) * 对应的flex-grow比例

拉伸后伸缩项目宽度 = 原伸缩项目宽度 + 扩展宽度

 

.flexbox-wrap{
    width:550px;
    display: flex;
}
.flexbox-item{
    &:nth-child(1){
        width:60px;
    }
    &:nth-child(2){
        width:70px;
    }
    &:nth-child(3){
        flex-basis:80px;
    }
    &:nth-child(4){
        flex-basis:90px;
    }
    &:nth-child(5){
         flex-basis:100px;
    }
}
@for $i from 1 through 5 {
    .flexbox-item:nth-child(#{$i}){
        flex-grow: $i;
        background-color: rgba(35 * (6-$i), 20 * $i, 35 * $i,1);
    }
}

我们来计算一下上面栗子中第一个伸缩项目拉伸后的宽度。对应着公式一步步计算:

 

// 项目容器宽度
container = 550
// 项目宽度或项目设置的flex-basis总和
itemSum = 60 + 70 + 80 + 90 + 100 = 400
// 第一个伸缩项目对应的flex-grow比例
flexRatio = 1 / ( 1 + 2 + 3 + 4 + 5 ) = 1/15
// 第一个伸缩项目扩展宽度
extendWidth = ( 550 - 400 ) * 1/15 = 10
// 第一个伸缩项目拉伸后的宽度
itemWidth = 60 + 10 = 70

计算后得到第一个伸缩项目拉伸后的宽度是70px,我们通过chrome上的盒子模型来看看是否正确

chrome计算的结果和我们计算的结果是一致的。

根据拉伸的计算公式是不是很容易就能推演出压缩的计算公式呢?

伸缩项目缩小宽度 = (项目宽度或项目设置的flex-basis总和 - 项目容器宽度) * 对应的flex-shrink比例

压缩后伸缩项目宽度 = 原伸缩项目宽度 - 缩小宽度

继续用个栗子来验证公式是否正确

 

.flexbox-wrap{
    width:250px;
    display: flex;
}
.flexbox-item{
    &:nth-child(1){
        width:60px;
    }
    &:nth-child(2){
        width:70px;
    }
    &:nth-child(3){
        flex-basis:80px;
    }
    &:nth-child(4){
        flex-basis:90px;
    }
    &:nth-child(5){
         flex-basis:100px;
    }
}
@for $i from 1 through 5 {
    .flexbox-item:nth-child(#{$i}){
        flex-shrink: $i;
        background-color: rgba(35 * (6-$i), 20 * $i, 35 * $i,1);
    }
}

我们来计算一下上面栗子中第一个伸缩项目压缩后的宽度。对应着公式一步步计算:

 

// 项目容器宽度
container = 250
// 项目宽度或项目设置的flex-basis总和
itemSum = 60 + 70 + 80 + 90 + 100 = 400
// 第一个伸缩项目对应的flex-shrink比例
flexRatio = 1 / ( 1 + 2 + 3 + 4 + 5 ) = 1/15
// 第一个伸缩项目缩小宽度
extendWidth = ( 400 - 250 ) * 1/15 = 10
// 第一个伸缩项目压缩后的宽度
itemWidth = 60 - 10 = 50

计算后得到第一个伸缩项目压缩后的宽度是50px,我们通过chrome上的盒子模型来看看是否正确

chrome计算的结果和我们计算的结果不一样。

伸缩项目压缩的计算方式和拉伸的不一样,是因为压缩会有极端情况,我们把第一个伸缩项目的flex-shrink修改为10,此时缩小宽度为( 400 - 250 ) * ( 10 / 24) = 62.5,缩小的宽度比原宽度要大,计算的压缩后的宽度变成了负数。

为了避免这种极端情况,计算缩小比例是要考虑伸缩项目的原宽度。

正确的公式是这样的

伸缩项目缩小宽度 = (项目宽度或项目设置的flex-basis总和 - 项目容器宽度) (对应的flex-shrink 项目宽度或项目设置的flex-basis比例)

压缩后伸缩项目宽度 = 原伸缩项目宽度 - 缩小宽度

对应着公式一步步计算:

 

// 项目容器宽度
container = 250
// 项目宽度或项目设置的flex-basis总和
itemSum = 60 + 70 + 80 + 90 + 100 = 400
// 第一个伸缩项目对应的flex-shrink比例
flexRatio = (1*60) / (1*60+2*70+3*80+4*90+5*100) = 6/130
// 第一个伸缩项目缩小宽度
extendWidth = ( 400 - 250 ) * 6/130 ≈ 6.922
// 第一个伸缩项目压缩后的宽度
itemWidth = 60 - 6.922 = 53.078

计算后得到第一个伸缩项目压缩后的宽度是53.078px,和chrome上的盒子模型是一样的。


Flexbox属性缩写陷阱

上面介绍的flex-growflex-shrinkflex-basis有一个缩写的写法flex

flex: flex-grow [flex-shrink] [flex-basis]

flex各种缩写的值

  • flex: initial == flex: 0 1 auto
  • flex: none == flex: 0 0 auto
  • flex: auto == flex: 1 1 auto
  • flex: number == flex: number 1 0%

在实际项目中,会直接写使用缩写的flex来给伸缩项目分配空间,但是使用缩写属性会留下一些陷阱,导致表现的结果不尽如人意。

分别使用flexflex-grow来把伸缩项目拉伸填满容器,看看表现的差异。

首先看看使用flex-grow拉伸伸缩项目的效果

 

.flexbox-wrap{
    width:550px;
    display: flex;
}
.flexbox-item{
    flex-grow:1;
    &:nth-child(1){
        width:60px;
    }
    &:nth-child(2){
        width:70px;
    }
    &:nth-child(3){
        width:80px;
    }
    &:nth-child(4){
        width:90px;
    }
    &:nth-child(5){
         width:100px;
    }
}
@for $i from 1 through 5 {
    .flexbox-item:nth-child(#{$i}){
        background-color: rgba(35 * (6-$i), 20 * $i, 35 * $i,1);
    }
}

每个伸缩项目在原宽度上拉伸相同的宽度

通过上面的计算拉伸后的伸缩项目宽度,可以计算第一个伸缩项目拉伸后的宽度

 

// 项目容器宽度
container = 550
// 项目宽度或项目设置的flex-basis总和
itemSum = 60 + 70 + 80 + 90 + 100 = 400
// 第一个伸缩项目对应的flex-grow比例
flexRatio = 1 / ( 1 + 1 + 1 + 1 + 1 ) = 1/5
// 第一个伸缩项目扩展宽度
extendWidth = ( 550 - 400 ) * 1/5 = 30
// 第一个伸缩项目拉伸后的宽度
itemWidth = 60 + 30 = 90

然后我们把flex-grow:1替换成flex:1,下面是表现的效果,伸缩项目拉伸后的宽度变成一样了。

从chrome的盒子模型可看到伸缩项目拉伸后宽度变成了110px,伸缩容器等分了容器的宽度。

flex:1展开后是flex:1 1 0%flex-grow:1相当于flex:1 1 auto,两者的区别在于flex-basis的值不同。flex:1为项目宽度重新设置了宽度为0,所以可分配空间为整个容器,从公式计算上可以更直观理解:

 

// 项目容器宽度
container = 550
// 项目宽度或项目设置的flex-basis总和
itemSum = 0 + 0 + 0 + 0 + 0 = 0
// 第一个伸缩项目对应的flex-grow比例
flexRatio = 1 / ( 1 + 1 + 1 + 1 + 1 ) = 1/5
// 第一个伸缩项目扩展宽度
extendWidth = ( 550 - 0 ) * 1/5 = 110
// 第一个伸缩项目拉伸后的宽度
itemWidth = 0 + 110 = 110


需要注意的Flexbox特性

无效属性

  • column-*在伸缩容器无效
  • float和clear在伸缩项目无效
  • vertical-align在伸缩项目无效
  • ::first-line and ::first-letter在伸缩容器无效

伸缩容器中的非空字符文本节点也是伸缩项目

 

<div class="flexbox-wrap">
    <span class="flexbox-item">1</span>
    <span class="flexbox-item">2</span>
    我是个假文本
    <span class="flexbox-item">3</span>
    <span class="flexbox-item">4</span>
    <span class="flexbox-item">5</span>
</div>

margin折叠

  • 伸缩容器和伸缩项目的margin不会折叠
  • 伸缩项目间的margin不会折叠


旧版Flexbox的BUG

伸缩项目为行内元素要加display:block;或display:flex

欢迎关注:Leechikit

原文链接:segmentfault.com

到此本文结束,欢迎提问和指正。写原创文章不易,若本文对你有帮助,请点赞、推荐和关注作者支持。


目录
相关文章
|
调度 Apache
airflow scheduler -D 是什么作用
【6月更文挑战第30天】airflow scheduler -D 是什么作用
280 1
|
计算机视觉 C++
基于Qt的简易图片浏览器设计与实现
基于Qt的简易图片浏览器设计与实现
537 1
|
11月前
|
C# 开发者
C# 一分钟浅谈:Socket 编程基础
【10月更文挑战第7天】本文介绍了Socket编程的基础知识、基本操作及常见问题,通过C#代码示例详细展示了服务器端和客户端的Socket通信过程,包括创建、绑定、监听、连接、数据收发及关闭等步骤,帮助开发者掌握Socket编程的核心技术和注意事项。
278 3
C# 一分钟浅谈:Socket 编程基础
|
6月前
|
人工智能 算法 搜索推荐
人工智能技术对未来就业的影响
人工智能大模型技术正在重塑全球就业市场,但其核心是"增强"而非"取代"人类工作。虽然AI在数据处理、模式识别等标准化任务上表现出色,但在创造力、情感交互和复杂决策等人类专属领域仍存在明显局限。各行业呈现差异化转型:IT领域人机协同编程成为常态,金融业基础分析岗位减少但复合型人才需求激增,医疗行业AI辅助诊断普及但治疗决策仍依赖医生,制造业工人转向技术管理,创意产业中人类聚焦高端设计。未来就业市场将形成人机协作新生态,要求个人培养创造力、情商等AI难以替代的核心能力,企业重构工作流程。AI时代将推动人类向更高价值的认知活动跃升,实现人机优势互补的协同发展。
776 2
|
存储 关系型数据库 MySQL
MySQL系列: undo和redo工作原理
MySQL系列: undo和redo工作原理
381 1
|
11月前
|
安全 大数据 网络安全
网络安全与信息安全:守护数字世界的坚盾在数字化浪潮席卷全球的今天,网络安全已成为维系社会稳定、促进经济发展的重要基石。本文旨在深入探讨网络安全漏洞、加密技术及安全意识等核心议题,通过分享前沿知识与实用策略,助力构建更加安全可靠的网络环境。
【10月更文挑战第8天】 本文聚焦网络安全领域的关键要素,包括安全漏洞的识别与防御、加密技术的演进与应用,以及安全意识的培养与提升。通过对最新研究成果和实际案例的分析,文章揭示了网络安全威胁的多样性和复杂性,强调了综合防护策略的重要性。同时,倡导社会各界共同参与,形成全民网络安全意识,共筑数字空间的安全防线。
281 0
|
设计模式 存储 安全
18 Java反射reflect(类加载+获取类对象+通用操作+设计模式+枚举+注解)
18 Java反射reflect(类加载+获取类对象+通用操作+设计模式+枚举+注解)
287 0
|
小程序
基于微信小程序的音乐播放器设计与实现(源码+lw+部署文档+讲解等)
基于微信小程序的音乐播放器设计与实现(源码+lw+部署文档+讲解等)
474 0
基于微信小程序的音乐播放器设计与实现(源码+lw+部署文档+讲解等)
|
运维 测试技术 持续交付
微服务项目部署实践:使用Gitlab Runner实现微服务项目的持续集成,持续交付和持续部署
本文通过详细的步骤一步一步说明在微服务架构的项目中如何进行项目部署的操作实践,通过Gitlab实现项目的持续集成,持续部署和持续交付。详解介绍的Gitlab中实现项目持续部署的工具GitLab Runner的具体使用步骤。通过这篇文章,可以熟悉微服务项目持续集成,持续交付和持续部署,学会使用GitLab Runner的具体使用方式,极大简化微服务项目的部署。
1063 0
微服务项目部署实践:使用Gitlab Runner实现微服务项目的持续集成,持续交付和持续部署
|
机器学习/深度学习 存储 自然语言处理
机器学习面试笔试知识点-贝叶斯网络(Bayesian Network) 、马尔科夫(Markov) 和主题模型(T M)1
机器学习面试笔试知识点-贝叶斯网络(Bayesian Network) 、马尔科夫(Markov) 和主题模型(T M)
377 0
机器学习面试笔试知识点-贝叶斯网络(Bayesian Network) 、马尔科夫(Markov) 和主题模型(T M)1