【学习CSS4】详解Flexbox

曾经,我天真地认为,浏览过即等于掌握;然而,职场多年的洗礼后,我才深刻领悟到这一认知的浅薄。那些一闪而过的知识碎片,如细沙般从指缝滑落,待到真正需要之时,却寻觅无踪,宛如海市蜃楼,令人怅然若失。

形成了时刻笔记的习性,就像拥有了一个忠实的旅伴,在漫漫人生路上,它记录下每一次心跳,每一缕微笑,不让任何美好稍纵即逝。这是一种对生命的热爱,对细节的关注,让平淡生活因记录而精彩,因回忆而丰满。

废话结束,干货来了!

Flexbox 是一种非常强大的布局模式。当我们真正了解它是如何工作的时,我们可以构建自动响应的动态布局,并根据需要重新排列。

一、先看要实现的效果

 不废话,直接上干货

下面是相关的css

form {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: 16px;
}
.name {
  flex-grow: 1;
  flex-basis: 160px;
}
.email {
  flex-grow: 3;
  flex-basis: 200px;
}
button {
  flex-grow: 1;
  flex-basis: 80px;
}

完整的例子

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="renderer" content="webkit" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
    />
    <title>详解Flexbox布局</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      .flex1 {
        padding: 10px;
        background: #1a1b1b;
      }
      form {
        display: flex;
        align-items: flex-end;
        flex-wrap: wrap;
        gap: 16px;
        padding: 8px;
        border: 1px solid #ccc;
        border-radius: 5px;
        color: #fff;
        width: 600px;
        margin: 0 auto;
      }
      .name {
        flex-grow: 1;
        flex-basis: 160px;
      }
      .email {
        flex-grow: 3;
        flex-basis: 200px;
      }
      button {
        flex-grow: 1;
        flex-basis: 80px;
      }
      input {
        width: 100%;
      }
    </style>
  </head>
  <body>
    <div class="flex1">
      <div style="color: #fff;width: 600px;margin: 0 auto;">
        <div>
          当前form的宽度:<span id="rangeWidth">600</span>
        </div>
        <input type="range" min="200" max="600" value="600" class="slider" id="myRange">
      </div>
      <form id="demo">
        <label class="name" for="name">
          姓名:<input type="text" id="name" />
        </label>
        <label class="email" for="email">
          邮箱:<input type="text" id="email" />
        </label>
        <button>提交</button>
      </form>
    </div>

    <script>
      window.onload = function () {
        const slider = document.getElementById('myRange');
        const output = document.getElementById('demo');
        const rangeWidth = document.getElementById('rangeWidth');

        slider.oninput = function() {
          output.style.width = this.value + 'px';
          rangeWidth.innerHTML = this.value;
        }
      }
    </script>
  </body>
</html>

我记得以前遇到这样的需求时,我完全感到困惑。当我学习了Flexbox 的基础知识后,这绝对是魔法 ,太神奇了!

在接下来的文中,我将继续分享详细的 Flexbox 知识。我们会通过每一个属性来学习 Flexbox 是如何工作的。无论你是CSS的初学者,还是已经使用Flexbox多年的人,我敢打赌你会学到很多东西!

让我们开始吧!

二、Flexbox 简介

CSS由许多不同的布局算法组成,官方称为“布局模式”。每种布局模式都是CSS中自己的子语言。默认的布局模式是 Flow 布局,但我们可以通过更改父容器上的display为 Flexbox。

当我们将 display 设置为 flex 时,我们创建了一个“flex formatting context”。这意味着,默认情况下,所有子项都将根据 Flexbox 布局算法进行定位。

每种布局算法都旨在解决特定问题。默认的“流”布局旨在创建流文档;它本质上是Microsoft Word布局算法。标题和段落作为块垂直堆叠,而文本、链接和图像等内容则不显眼地位于这些块中。

那么,Flexbox解决了什么问题呢?Flexbox 就是将一组项目排列成一行或一列,并让我们对这些项目的分布和对齐进行精准的控制。顾名思义,Flexbox 是关于灵活性的。我们可以控制内容是增长还是缩小,额外空间的分配方式等等。

你可能想知道:既然 CSS Grid 在现代浏览器中得到了很好的支持,那么 Flexbox 不是已经过时了吗?
CSS Grid 是一种很棒的布局模式,但它解决的问题与 Flexbox 不同。我们应该学习这两种布局模式,并使用正确的工具来完成这项工作。
Flexbox 在动态、流畅的 UI 中以垂直或水平列表排列项目时仍然占据主导地位。我们将在本文中看到一个示例,这是使用 CSS Grid 无法轻松完成的。

作为一个对 CSS Grid 和 Flexbox 都感到满意的人,我仍然发现自己经常接触 Flexbox!

三、Flex direction 布局方向

如上所述,Flexbox 就是控制一行或一列中元素的分布。默认情况下,项将并排堆叠成一行,但我们可以控制是行还是列显示。

flex-direction: row ,主轴从左到右水平延伸,块内的子项从左到右排列。

flex-direction: column ,主轴从上到下垂直运行,块内的子项从上到下排列。

先看效果:

在 Flexbox 中,一切都基于主轴。该算法不关心垂直/水平,甚至不关心行/列。所有规则都是围绕这个主轴和垂直运行的十字轴构建的。

当我们学习了 Flexbox 的规则时,我们可以从水平布局无缝切换到垂直布局。所有规则都会自动适应。此功能是 Flexbox 布局模式所独有的。

下图是这些规则的快速可视化效果:

在 Flexbox 中,我们决定主轴是水平运行还是垂直运行。这是所有 Flexbox 计算都与此挂钩的根。

四、Flex direction 对齐方式

我们可以使用属性 justify-content 更改子项沿主轴的分布布局方式。

当涉及到主轴时,我们通常不会从对齐单个子项的角度来考虑。相反,这完全与组的分布有关。

我们可以将所有项目集中在特定位置(有 flex-start 、 center 和 flex-end ),或者我们可以将它们分开(带有 space-between 、 space-around 和 space-evenly )。

先看下图(gif录制软件略有瑕疵):

对于十字轴,垂直方向,情况略有不同。我们使用该属性align-items

如图:

是不是挺有趣的,在align-items 中,我们有一些与 justify-content 相同的选项,但没有完全重叠,如下图。 

为什么他们不共享相同的选项?我们很快就会解开这个谜团,首先,我先分享另一个对齐属性: align-self 。

与 align-items 和justify-content不同 , align-self ,它应用于子元素,而不是容器。它允许我们更改特定子项沿十字轴的对齐方式,请看下图:

align-self 具有与 align-items 相同的所有值。事实上,它们改变了完全相同的事情。 align-items 是句法糖,一种方便的速记,可以自动一次在所有孩子上设置对齐方式。

没有 justify-self 。要理解为什么不,我们需要更深入地研究 Flexbox 算法。

五、Content vs items

根据我目前所了解的,Flexbox可能看起来相当随意。为什么是justify-contentalign-items,而不是justify-items,或者align-content?

就此而言,为什么有一个align-self,但没有justify-self??

这些问题触及了Flexbox最重要也是最容易被误解的地方。为了更好的理解,我用一个比喻。

在Flexbox中,项目沿主轴分布。默认情况下,它们很好地并排排列在一起。我们可以画一条水平直线穿过全部的孩子,这像不像一个烤肉串?如下图

然而横轴是不同的。一条直的垂直线只会和一个孩子相交。

这不太像烤肉串,而更像一群混合的香肠?看图:

这里有一个显著的不同。有了混合的香肠,每个项目都可以沿着它的签子移动而不干涉其他项目 

相比之下,我们沿着主轴移动每个兄弟姐妹,每个都不可以沿着它的签子移动,否则会撞到它的兄弟姐妹! 

这就是主轴/横轴之间的根本区别。当我们通过轴对齐时,每一项都可以为所欲为。在这里我们只能考虑如何分配每一项。

这就是为什么没有 justify-self.这对于中间的部分来说意味着什么justify-self: flex-start?那里已经有另一块了!

考虑到所有这些情况,让我们给我们一直在谈论的4个术语下一个正确的定义:

  • justify—沿主坐标轴放置某物.

  • align—沿横轴放置某物.

  • content—一组可以分发的“东西”。

  • items—可以单独定位的单个项目。

所以:我们有justify-content来控制群体在主轴上的分布,我们有align-items沿横轴单独定位每个项目。这是我们用来管理Flexbox布局的两个主要属性。

没有justify-items因为同样的原因justify-self;当谈到主轴时,我们必须把这些物品看作一个整体,作为可以分发的内容。

怎么样align-content?实际上,这个存在于Flexbox中!稍后,我们会谈到flex-wrap特性

六、设定尺寸

让我们来谈谈我对Flexbox最大的领悟。

假设我有下面的CSS:

.item {
  width: 2000px;
}

正常的话我们看到这个肯定会说:这简单,会得到一个2000像素宽的元素”。但这是真的吗?

让我们来测试一下,先看效果图

参考代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>验证设置width=2000px的效果</title>
  <style>
    .flex-wrapper {
      display: flex;
    }
    .item {
      width: 2000px;
      height: 200px;
      background: #ccc;
      border: 1px solid #000;
      margin-bottom: 20px;
    }
  </style>
</head>
<body>
  <div class="item">display: block</div>
  
  <div class="flex-wrapper">
    <div class="item">display: flex</div>
  </div>
</body>
</html>

这是不是很有趣?

这两个项目应用了完全相同的CSS。他们都有width: 2000px。然而,第一项比第二项宽得多!

不同之处在于布局模式。第一个项目是使用流布局呈现的,在流布局中,width是一个硬约束。当我们设置width: 2000px时,我们将得到一个2000像素宽的元素。

然而在Flexbox里,在width属性的实现方式是不同的。与其说是硬性约束,不如说是建议。

该规范为此命名为假设尺寸。这是一个元素的大小在一个完美的乌托邦世界里,没有任何阻碍。

事情貌似就是这么简单。在这种情况下,限制因素是父元素没有那么宽的空间能放下一个2000px宽的孩子。所以,孩子的尺寸被缩小了,这样才合适。

这是Flexbox理念的核心部分。事物是流动的,灵活的,可以适应世界的限制。

算法的输入

我们倾向于认为CSS语言是属性的集合,但是我认为这是错误的思维模式。正如我们已经看到的width属性的行为因所使用的布局模式而异!

相反,我喜欢把CSS看作是布局模式的集合。每种布局模式都是一种算法,可以实现或重新定义每个CSS属性。我们用CSS声明(键/值对)提供一个算法,算法决定如何使用它们。

换句话说,我们编写的CSS是这些算法的输入,就像传递给函数的参数。如果我们想真实地;诚挚地;准确地对CSS感到舒服,光学属性是不够的;我们必须了解算法是如何使用这些属性。

这是我这篇博客的中心思想,
面向JavaScript开发人员的CSS。我不会让你记住一堆难以理解的CSS片段,而是打开这门语言的思路,学习所有布局模式是如何工作的。

 七、伸缩属性

我们已经在上面的例子了解了,Flexbox算法具有一些内置的灵活性,包括设定尺寸宽度实际中为了要了解Flexbox的流动性,我们需要讨论3个属性:flex-growflex-shrink,以及flex-basis.

让我们来看看每个属性。

flex弹性基础:flex-basis

我承认:在过去的很长一段时间,我真的不明白flex-basis是干什么的。😅

简单来说:在柔性行中,flex-basiswidth做同样的事情。在柔性柱中,flex-basisheight做同样的事情.

正如我们所了解的,Flexbox中的一切都是盯住主轴/横轴。举个例子,justify-content将沿着主轴分布子对象,无论主轴是水平还是垂直,其工作方式都完全相同。

但是widthheight不会遵循这个规则!

width总是会影响水平尺寸。当我们翻转flex-directionrowcolumn时,它不会突然变成height。

因此,Flexbox的作者创建了一个通用“大小”的属性,称为flex-basis。这就像width或者height,但它盯住了主坐标轴。像其他属性一样。它允许我们设置假设尺寸无论是水平还是垂直方向。

在这里试一试。每个孩子都得到了flex-basis: 50px,但是当调整第一个子项时会发生什么?

先看动图效果:

就像我们看到的widthflex-basis与其说是硬约束,不如说是建议。在某一点上,没有足够的空间来容纳所有的元素,所以他们不得不妥协,以避免溢出。

一个不确定的事物

一般来说,我们可以使用widthflex-basis在弹性行中可以互换,但也有一些例外。

例如,在width属性对图像等被替换元素的影响不同于flex-basis

还有,width可以将项目缩小到其以下最低限度尺寸,而flex-basis不能。

这已经超出了这篇博文的范围,但是我想提一下,因为您可能偶尔会遇到这两种属性具有不同效果的边缘情况。

flex灵活增长:flex-grow

默认情况下,Flex上下文中的元素将沿着主轴收缩到它们最小的舒适尺寸。这通常会产生额外的空间。

我们可以指定如何使用该空间flex-grow属性:

先看动图效果:

flex-grow的默认值为0,这意味着增长是选择加入的。如果我们想让孩子吃掉容器中任何多余的空间,我们需要明确地告诉他。

如果有多个孩子设置了flex-grow在这种情况下,额外的空间会根据孩子的flex-grow价值进行计算。

用文字不太好描述,我觉得还是用实例直观地解释会更容易。我们看下图中尝试增加/减少每个孩子的glex-grow值。

 

flex收缩:flex-shrink 

在我们到目前为止看到的大多数例子中,我们都有额外的工作空间。但是如果我们的孩子长的太壮了,超出了当前空间?

我们来测试一下,当尝试缩小父容器的空间,看看会发生什么?如下动图:

很有趣吧?两个盒子都在变化,但是它们按比例缩小。第一个孩子的宽度总是第二个孩子的两倍。

友情提示:flex-basiswidth的目的相同。我们将使用flex-basis因为这是惯例,但是我们会得到完全相同的结果如果我们使用width!

flex-basiswidth“设置元素”假设尺寸。Flexbox算法可能会将元素缩小到期望的大小以下,但默认情况下,它们将总是一起缩放,保持两个元素之间的比例。

现在,如果我们希望我们的元素按比例缩小?那就要用到flex-shrinkl

花几分钟时间看看这个演示。看看你能不能搞清楚这是怎么回事。我们将在下面探讨。

如上我们有两个孩子,每个孩子的假设尺寸都是250像素。容器至少要有500像素宽,才能容纳假设大小的这些孩子。

假设我们把容器缩小到400像素。我们不能把500像素的内容塞进一个400像素的袋子里!我们有100像素的超出容器。我们的元素总共需要放弃100像素,才能适配。

flex-shrink会让我们决定如何支配差额。

默认情况下,两个孩子都有flex-shrink: 1,所以每个孩子都会减去50像素。他们每人损失50像素,实际大小从250像素缩小到200像素。

现在,让我们假设我们把第一个孩子摇到flex-shrink: 3:

我们的总超额为100像素。通常,每个孩子都会减去宽度,但是因为我们设置了flex-shrink,第一个元素结束支付(78px),第二个元素支付(26px)。

注意,绝对值并不重要,都是比例的问题。如果两个孩子都有flex-shrink: 1,每个孩子将减去总差额的。如果两个孩子都被拐到flex-shrink: 1000,每个孩子将减去1000/2000。不管怎样,结果都是一样的。

收缩和比例

在我们看到的例子中,两个Flex子元素都有相同的假设大小(250像素)。当计算出如何缩小它们时,我们可以只使用flex-shrink.

正如我们前面看到的,收缩算法也会尝试保持兄弟姐妹之间的比例。如果第一个孩子的大小是第二个孩子的两倍,那么它会收缩得更厉害。

所以,这个全部计算包括查看每个孩子的flex-shrink 它的相对大小。

不久前我有了一个顿悟flex-shrink:我们可以把它看作是的“逆”flex-grow。它们是同一枚硬币的两面:

  • flex-grow控制如果有额外的空间被分配时,当设置了flex-grow的盒子空间小于剩余空间时会把额外的空间分配到这个盒子里,充满剩余空间。

  • flex-shrink控制如果空间不够时,当容器里的物品(盒子)大于最外层的容器空间时,各个孩子会自动按照比例分配减少超出的空间

这意味着这些属性可以同时激活。如果有多余的空间,flex-shrink没有影响,因为项目不需要收缩。如果孩子们对于他们的容器来说太大了,flex-grow没有影响,因为没有多余的空间来分配。

我喜欢把它想象成两个独立的领域。你要么在地球上,要么在地球外。每个世界都有自己的规则。

防止收缩

有时候,我们不会想要我们的一些孩子变小了。

对于SVG图标和一些特定的形状,我总是注意到这一点。让我们看一个简化的例子,注意两边的圆形。

当容器变窄时,我们的两个圆被挤压成一个椭圆形。如果我们想让它们保持圆形呢?

我们可以通过设置flex-shrink: 0:

当我们设置了flex-shrink为0的时候,这两圆选择退出了收缩过程。Flexbox算法将处理flex-basis(或者width)作为硬性的最低限制。

更简单的方法?

所以,我在整理这个概念时,时不时地在考虑,有人肯定会想为什么我们要这么麻烦的使用flex-shrink会不会有更简单的方法可用,比如设置

.item-round {  min-width: 32px;}

在几年前的话,我会同意的。如果我们设置一个最小宽度,这个项目不就可以实现了吗!

我认为这是很容易混淆“熟悉的”和“简单的”的情况之一。你可能会觉得min-widthflex-shrink更好用,但这并不意味着flex-shrink就更复杂了!

经过几年的练习,我真的觉得flex-shrink: 0是解决这个特殊问题的更直接的方法。不过,min-width在Flexbox算法中仍然扮演着重要的角色!我们接下来会谈到这一点。

最小尺寸问题

这里还有一件事需要说一下,超级重要!这可能是整篇文章中唯一最有帮助的东西! 假设我们正在为一个电子商务商店构建一个流动的搜索表单。

当容器收缩到某一宽度以下时,内容就会溢出! 但是为什么呢?flex-shrink的默认值是1,我们还没有删除它,所以搜索输入应该能够根据需要缩小!为什么它拒绝收缩?

事情是这样的:除了假设的大小,Flexbox算法还关心另一个重要的大小:最小大小。

Flexbox算法拒绝将子项缩小到最小尺寸以下。内容将溢出而不是进一步收缩,无论我们将伸缩曲柄设置得多高! 文本输入的默认最小尺寸为170px-200px(因浏览器而异)。

这就是我们在上面遇到的限制。 在其他情况下,限制因素可能是元素的内容。例如,尝试调整此容器的大小。

 对于包含文本的元素,最小宽度是最长的单词的长度。 好消息是:我们可以用min-width属性重新定义最小尺寸。

我们给input设置min-width:0

通过直接在Flex子对象上input设置min-width: 0px,我们告诉Flexbox算法覆盖“内置”最小宽度。因为我们已经将它设置为0px,所以元素可以根据需要收缩。 同样的技巧也适用于具有min-height属性的Flex列(尽管这个问题似乎不经常出现)。

警告:小心行事!!!

值得注意的是,内置的最小尺寸确实有其用途。这意味着作为一个保护,以防止更糟糕的事情发生。

当我们将min-width: 0px应用于包含文本的Flex子对象时,事情会变得更糟!如下图:

强大的能力意味着巨大的责任,对于Flexbox来说,最小宽度是一个特别强大的特性。它不止一次让我摆脱困境,但我总是小心翼翼地确保我没有让事情变得更糟!

八、gaps 间距

近年来 Flexbox 生活质量最大的改进之一是 gap 属性,曾经为了实现间距还是傻傻的用margin。

gap 允许我们在每个 Flex 子项之间创建空间。这对于导航标头等内容非常有用。

gap 是 Flexbox 语言的一个相对较新的新增功能,自 2021 年初以来,它已在所有现代浏览器中实现。

在flex中使用自动边距margin auto

在分享一个与间距相关的技巧。它从 Flexbox 的早期就已经存在了,但它相对晦涩难懂,当我第一次发现它时,它让我大吃一惊

margin 属性用于在特定元素周围添加空间。在某些布局模式中,例如 Flow 和 Positioned,它甚至可以用于将元素居中,并使用 margin: auto

如果把自动边距放在 Flexbox 中会发生什么呢?我们先看图:

 之前,我们看到了 flex-grow 属性如何吞噬任何额外的空间,并将其应用于子元素。

上图我们看到了Auto margins 将吞噬多余的空间,并将其应用于元素的 margin。它使我们能够精确控制在何处分配额外的空间。

常见的标题布局的一侧是 logo,另一侧是一些导航链接。以下是我们如何使用 auto margins 构建此布局:

 logo图标是列表中的第一个列表项。通过给它 margin-right: auto,我们收集了所有额外的空间,并强制它位于第 1 项和第 2 项之间。

我们可以使用浏览器控制台查看这里发生了什么:

还有很多其他方法可以解决这个问题:我们可以将导航链接分组到它们自己的 Flex 容器中,或者我们可以使用 flex-grow 来增加第一个列表项。自从知道了这个方法后,我更喜欢用自动边距解决方案。我们将额外的空间视为一种资源,并决定它应该去哪里。

源码

详解flex相关动图的js源码(gitee)地址

未完待续.......

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天才和人才就差了二

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值