深入理解css 笔记(6)

网格布局:flexbox 彻底改变了网页布局方式,但这只是开始。它还有一个大哥:另一个称作网格布局模块的新规范。这两个规范提供了一种前所未有的全功能布局引擎。跟 flexbox 类似,网格布局也是作用于两级的 dom 结构。设置为 display:grid 的元素成为一个网格容器(grid container)。它的子元素则变成网格元素。

<!DOCTYPE html>
<html lang="cn" class="has-bottombar">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

    <meta
      content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
      name="viewport"
    />
    <style>
      .grid {
        display: grid;
        grid-template-columns: 1fr 1fr 1fr;
        grid-template-rows: 1fr 1fr;
        grid-gap: 0.5em;
      }

      .grid > * {
        background-color: darkgray;
        color: white;
        padding: 2em;
        border-radius: 0.5em;
      }
    </style>
  </head>

  <body>
    <div class="grid">
      <div class="a">a</div>
      <div class="b">b</div>
      <div class="c">c</div>
      <div class="d">d</div>
      <div class="e">e</div>
      <div class="f">f</div>
    </div>
  </body>
</html>

  在支持网格布局的浏览器中,这段代码会渲染三列,共六个大小相等的盒子。首先,使用 display:grid 定义一个网格容器。容器会表现得像一个块级元素。100%填充可用宽度。也可以使用 inline-grid,这样元素就会在行内流动,且宽度只能够包含子元素,不过 inline-grid 的使用频率不高。接下来是新属性:grid-template-columns 和 grid-template-rows。这两个属性定义了网格每行每列的大小。本例使用了一种新单位 fr,代表每一行或每一列的分数单位(fraction unit)。这个单位跟 flexbox 中的 flex-grow 因子的表现一样。grid-template-columns:1fr 1fr 1fr 表示三列等宽。不一定非得用分数单位,可以使用其他的单位,比如 px,em 或者百分数。也可以混搭这几种单位,例如 grid-template-columns:300px 1fr 定义了一个固定宽为 300px 的列,后面跟着一个会填满剩余可用空间的列。2fr 的列宽是 1fr 的两倍。最后,grid-gap 属性定义了每个网格单元之间的间距。也可以用两个值分别指定垂直和水平方向的间距。比如(grid-gap:0.5em 1em;)

  理解网格各个部分很重要。前面已经提及网格容器和网格元素,这些事网格布局的基本元素。grid line 网格线:网格线构成了网格的框架。一条网格线可以水平或垂直,也可以位于一行或一列的任意一侧。如果指定了 grid-gap 的话,它就位于网格线上。grid track 网格轨道:一个网络轨道上两条相邻网格线之间的空间。网格有水平轨道和垂直轨道。网格单元 grid cell:网格上的单个空间,水平和垂直的网格轨道交叉重叠的部分。grid area 网格区域:网格上的矩形区域,由一个到多个网格单元组成。该区域位于两条垂直网格线和两条水平网格线之间。

  构建网格布局时会涉及这些组成部分。比如声明 grid-template-columns:1fr 1fr 1fr 就会定义三个等宽且垂直的网格轨道,同时还定义了四条垂直的网格线:一条在网格最左边,两条在每个网格轨道之间,还有一条在最右边。讲 flexbox 的时候构建了一个网页,假如用网格来实现。有一点值得注意的事,使用网格并不会让 flexbox 失去用武之地。用网格实现同样的布局需要改一下 html 结构:将嵌套的 html 拉平。放在网格里的每个元素都必须是主要网格容器的子元素。

<!DOCTYPE html>
<html lang="cn" class="has-bottombar">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

    <meta
      content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
      name="viewport"
    />
    <style>
      :root {
        box-sizing: border-box;
      }

      *,
      :before,
      :after {
        box-sizing: inherit;
      }

      body {
        background-color: #709b90;
        font-family: Arial, Helvetica, sans-serif;
      }

      .container {
        display: grid;
        grid-template-columns: 2fr 1fr;
        grid-template-rows: repeat(4, auto);
        grid-gap: 1.5em;
        max-width: 1080px;
        margin: 0 auto;
      }

      header,
      nav {
        grid-column: 1/3;
        grid-row: span 1;
      }

      .main {
        grid-column: 1/2;
        grid-row: 3/5;
      }

      .sidebar-top {
        grid-column: 2/3;
        grid-row: 3/4;
      }

      .sidebar-bottom {
        grid-column: 2/3;
        grid-row: 4/5;
      }

      .tile {
        padding: 1.5em;
        background-color: #fff;
      }

      .tile > :first-child {
        margin-top: 0;
      }

      .tile * + * {
        margin-top: 1.5em;
      }
    </style>
  </head>

  <body>
    <div class="container">
      <header>
        <h1 class="page-heading">Ink</h1>
      </header>

      <nav>
        <ul class="site-nav">
          <li><a id="button1">Home</a></li>
          <li><a id="button2">Features</a></li>
          <li><a id="button3">Pricing</a></li>
          <li class="nav-right">
            <a href="/about">About</a>
          </li>
        </ul>
      </nav>

      <main class="main tile">
        <h1>Team collaboration done right</h1>
        <p>
          Thousands of teams from all over the world turn to <b>Ink</b> to
          communicate and get things done.
        </p>
        <h1>Team collaboration done right</h1>
        <p>
          Thousands of teams from all over the world turn to <b>Ink</b> to
          communicate and get things done.
        </p>
        <h1>Team collaboration done right</h1>
        <p>
          Thousands of teams from all over the world turn to <b>Ink</b> to
          communicate and get things done.
        </p>
        <h1>Team collaboration done right</h1>
        <p>
          Thousands of teams from all over the world turn to <b>Ink</b> to
          communicate and get things done.
        </p>
        <h1>Team collaboration done right</h1>
        <p>
          Thousands of teams from all over the world turn to <b>Ink</b> to
          communicate and get things done.
        </p>
        <h1>Team collaboration done right</h1>
        <p>
          Thousands of teams from all over the world turn to <b>Ink</b> to
          communicate and get things done.
        </p>
        <h1>Team collaboration done right</h1>
        <p>
          Thousands of teams from all over the world turn to <b>Ink</b> to
          communicate and get things done.
        </p>
        <h1>Team collaboration done right</h1>
        <p>
          Thousands of teams from all over the world turn to <b>Ink</b> to
          communicate and get things done.
        </p>
      </main>

      <div class="sidebar-top tile">
        <form class="login-form">
          <h3>Login</h3>
          <p>
            <label for="username">Username</label>
            <input id="username" type="text" name="username" />
          </p>
          <p>
            <label for="password">Password</label>
            <input id="password" type="password" name="password" />
          </p>
          <button>Login</button>
        </form>
      </div>

      <div class="sidebar-bottom tile centered">
        <small>Starting at</small>
        <div class="cost">
          <span class="cost-currency">$</span>
          <span class="cost-dollars">20</span>
          <span class="cost-cents">.00</span>
        </div>
        <a class="cta-button" href="/pricing"> Sign up </a>
      </div>
    </div>
  </body>
</html>

  这段代码引入了很多新概念。代码首先设置了网格容器,并用 grid-template-columns 和 grid-template-rows 定义了网格轨道。因为列的分数单位分别是 2fr 和 1fr,所以第一列的宽度是第二列的两倍。定义行的时候用到了一个 repeat()函数。它再说声明多个网格轨道的时候提供了简写方式。grid-template-rows:repeat(4,auto);定义了四个水平网格轨道,高度为 auto,这等价于 grid-template-rows:auto auto auto auto。轨道大小设置为 auto,轨道会根据自身内容扩展。用 repeat()符号还可以定义不同的重复模式,比如 repeat(3,2fr 1fr)会重复三遍这个模式,从而定义 6 个网格轨道,重复的结果是 2fr 1fr 2fr 1fr 2fr 1fr。grid-template-columns:1fr repeat(3,3fr) 1fr;还可以将 repeat()作为一个更长的模式的一部分。比如 grid-template-columns:1fr repeat(3,3fr) 1fr 定义了一个 1fr 的列,接着是三个 3fr 的列,最后还有一个 1fr 的列。可以看出来因为展开的写法无法一目了然,所以才产生了 repeat()这种简写方式。

  网格轨道定义好之后,要将每个网格元素放到特定的位置上。可以在 grid-column 和 grid-row 属性中用网格线的编号指定网格元素的位置。如果想要一个网格元素在垂直方向上跨越 1 号网格线到 3 号网格线,就需要给元素设置 grid-column:1/3。或者设置 grid-row:3/5 让让元素在水平方向上跨越 3 号网格线到 5 号网格线。这两个属性一起就能指定一个元素应该放置的网格区域。

<style>
  header,
  nav {
    grid-column: 1/3;
    grid-row: span 1;
  }
  .main {
    grid-column: 1/2;
    grid-row: 3/5;
  }

  .sidebar-top {
    grid-column: 2/3;
    grid-row: 3/4;
  }

  .sidebar-bottom {
    grid-column: 2/3;
    grid-row: 4/5;
  }
</style>

  这段代码将 main 元素放在第一列,(1 号到 2 号网格线之间)跨越第三行到第四行(3 号到 5 号网格线)的位置。侧边栏的两个板块放在右列(2 号到 3 号网格线之间),并且在第三行到第四行上下排列。这些属性实际上是简写属性:grid-column 是 grid-column-start 和 grid-column-end 的简写;grid-row 是 grid-row-start 和 grid-row-end 的简写。中间的斜线只在简写属性用于区分两个值,斜线前后的空格不作要求。定位 header 和 nav 的规则集稍有变化。代码里使用之前介绍的 grid-column 写法,让网格元素占满网格的宽度。其实还可以用一个特别的关键字 span 来指定 grid-row 和 grid-column 的值(这里用在了 grid-row 上)。这个关键字告诉浏览器元素需要占据一个网格轨道。因为这里没有指出具体是哪一行,所以会根据网格元素的布局算法自动将其放到合适的位置。布局算法会将元素放在网格上可以容纳该元素的第一处可用空间。

  与 flexbox,grid 几乎是一起开发出来的,虽然它们的功能有一些重叠的地方,但是它们各自擅长的场景不一样。在一个设计设计场景里,要根据特定的需求来做出选择。这两种布局方式有以下两个重要区别。flexbox 本质是一维的,而网格是二维的。flexbox 是以内容为切入点由内向外工作的,而网格是以布局为切入点从外向内工作的。因为 flexbox 是一维的,所以它很适合用在相似的元素组成的列(或行)上。它支持用 flex-wrap 换行,但是没法让上一行元素跟下一行元素对齐。相反,网格是二维的,指在解决一个轨道的元素跟另一个轨道的元素对齐的问题。

  用网格给网页的主区域定位是因为我们希望内容能限制在它所在的网格内,但是对于网页上的其他元素,比如导航菜单,则允许内容对布局有更大的影响。也就是说,文字多的元素可以宽一些,文字烧的元素则可以窄一些。同时这还是一个水平(一维)布局。因此,用 flexbox 来处理这些元素更合适。当设计要求元素在两个维度上都对齐时,使用网格。当只关心一维的元素排列时,使用 flexbox.在实践中,这通常意味着网格更适合用于整体的网页布局,而 flexbox 更适合对网格区域内的特定元素布局。继续用网格和 flexbox,你就会对不同情况下该用哪种布局方式得心应手。

  布局网格元素还有另外两个替代语法:命名的网格线和命名的网格区域。至于选择哪个纯属个人偏好。有时候记录所有网格线的编号实在太麻烦了,尤其是在处理很多网格轨道时。为了能简单点,可以给网格线命名,并在布局时使用网格线的名称而不是编号。声明网格轨道时,可以在中括号内写上网格线的名称:grid-template-columns:[start] 2fr [center] 1fr [end];这条声明定义了两列的网格,三条垂直的网格线分别叫做 start,center,end.之后定义网格元素在网格中的位置时,可以不用编号而是这些名称来声明,grid-column:start/center;接下来还有命名网格区域。不过暂时感觉不会用。

  在某些场景下,可能不清楚该把元素放在网格的哪个位置上。当处理大量的网格元素时,挨个指定元素的位置未免太不方便。当元素是从数据库获取时,元素的个数可能是未知的。在这些情况下,以一种宽松的方式定义网格可能更合理,剩下的交给布局算法来放置网格元素。这时需要用到隐式网格。使用 grid-template-*属性定义网格轨道时,创建的是显式网格,但是有些网格元素仍然可以放在显式轨道外面,此时会自动创建隐式轨道以扩展网格,从而包含这些元素。

  隐式网格轨道默认大小为 auto,也就是它们会扩展到能容纳网格元素的内容。可以给网格容器设置 grid-auto-columns 和 grid-auto-rows,为隐式网格轨道指定一个大小。在指定网格线的时候,隐式网格轨道不会改变负数的含义。接下来实现一个照片墙。这个布局很有意思,因为它用 flexbox 或者浮动很难实现。

<!DOCTYPE html>
<html lang="cn" class="has-bottombar">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

    <meta
      content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
      name="viewport"
    />
    <style>
      :root {
        box-sizing: border-box;
      }

      *,
      ::before,
      ::after {
        box-sizing: inherit;
      }

      body {
        background-color: #709b90;
        font-family: Helvetica, Arial, sans-serif;
      }

      .portfolio {
        display: grid;
        grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
        grid-auto-rows: 1fr;
        grid-gap: 1em;
      }

      .portfolio > figure {
        margin: 0;
      }

      .portfolio img {
        max-width: 100%;
      }

      .portfolio figcaption {
        padding: 0.3em 0.8em;
        background-color: rgba(0, 0, 0, 0.5);
        color: #fff;
        text-align: right;
      }
    </style>
  </head>

  <body>
    <div class="portfolio">
      <figure class="featured">
        <img src="images/monkey.jpg" />
        <figcaption>Monkey</figcaption>
      </figure>
      <figure>
        <img src="images/eagle.jpg" />
        <figcaption>Eagle</figcaption>
      </figure>
      <figure class="featured">
        <img src="images/bird.jpg" />
        <figcaption>Bird</figcaption>
      </figure>
      <figure>
        <img src="images/bear.jpg" />
        <figcaption>Bear</figcaption>
      </figure>

      <figure class="featured">
        <img src="images/swan.jpg" />
        <figcaption>Swan</figcaption>
      </figure>
      <figure>
        <img src="images/elephants.jpg" />
        <figcaption>Elephants</figcaption>
      </figure>
      <figure>
        <img src="images/owl.jpg" />
        <figcaption>Owl</figcaption>
      </figure>
    </div>
  </body>
</html>

  有时候我们不想给一个网格轨道设置固定尺寸,但是又希望限制它的最小值和最大值。这时候需要用到 minmax()函数。它指定两个值:最小尺寸和最大尺寸。浏览器会确保网格轨道的大小介于这两者之间。(如果最大尺寸小于最小尺寸,最大尺寸就会被忽略。)通过指定 minmax(200px,1fr),浏览器确保了所有的轨道至少宽 200px。repeat()函数里的 auto-fill 关键字是一个特殊值。auto-fill 和 minmax(200px,1fr)加在一起,就会让网格在可用的空间内尽可能多地产生网格列,并且每个列的宽度不会小于 200px。

  接下来,我们让特定图片变大,增加一些视觉上的趣味性。现在每个网格元素都占据了 1x1 的区域,将特定图片的尺寸增加到 2x2 的网格区域。由于元素按顺序排列,增加某些网格元素的大小会导致网格中出现空白区域。网格布局模块规范提供了另一个属性 grid-auto-flow,它可以控制布局算法的行为。它的初始值是 row,上一段描述的就是这个值的行为。如果值为 column,它就会将元素优先放在网格列中,只有当一列填满了,才会移动到下一行。还可以额外加一个关键字 dense(grid-auto-flow:column dense)。它让算法紧凑的填满网格里的空白,尽管这会改变某些网格元素的顺序。加上这个关键字,小元素就会回填大元素造成的空白区域。

<style>
  :root {
    box-sizing: border-box;
  }

  *,
  ::before,
  ::after {
    box-sizing: inherit;
  }

  body {
    background-color: #709b90;
    font-family: Helvetica, Arial, sans-serif;
  }

  .portfolio {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    grid-auto-flow: dense;
    grid-auto-rows: 1fr;
    grid-gap: 1em;
    width: 1080px;
    margin: 0 auto;
  }

  .portfolio > figure {
    display: flex;
    flex-direction: column;
    margin: 0;
  }

  .portfolio img {
    flex: 1;
    object-fit: cover;
    max-width: 100%;
  }

  .portfolio .featured {
    grid-row: span 2;
    grid-column: span 2;
  }

  .portfolio figcaption {
    padding: 0.3em 0.8em;
    background-color: rgba(0, 0, 0, 0.5);
    color: #fff;
    text-align: right;
  }
</style>
posted @ 2022-10-21 10:56  艾路  阅读(31)  评论(0编辑  收藏  举报