CSS - 网格布局(grid)

目录

什么是网格布局

网格布局与弹性布局的比较

网格布局中的概念名词

网格容器 display:grid、display:inline-grid

网格轨道 grid-template-rows、grid-template-columns

min-content、max-content

minmax

repeat函数

auto-fill、auto-fit

网格单元

网格项目

网格线

网格线占位

网格区域占位

grid-template

显示网格、隐式网格

密集模式

网格间距

网格单元的对齐

网格项目的对齐


什么是网格布局

网格布局非常类似于表格布局,它们都是通过定义n行m列,来将容器划分为n*m个单元格。

下面是表格布局

表格布局,是通过tr标签来定义行,通过td标签来定义每行的单元格(相当于定义列)

下面是网格布局

 网格布局,是为容器元素添加display:grid样式,来把容器元素变为网格容器,通过网格容器的grid-template-rows样式来定义各行高度,grid-template-columns样式来定义各列宽度。而网格容器的直接子级元素默认依次放入每个单元格中。

对比来看,表格实现行列依赖于标签结构,网格实现行列依赖于样式。

从页面渲染性能角度来看,肯定是网格的性能更佳,因为同样渲染一个单元格,网格中单元格的元素的层级更浅。一个完整表格单元格层级应该是table>tbody>tr>td,而网格单元格的层级是.container > .gird。

另外网格容器可以更好地支持单元格的布局,比如单元格的跨行跨列,比如单元格在网格中的位置。

网格布局与弹性布局的比较

我们再用弹性布局来实现上面的网格效果

可以发现,弹性布局似乎更加简单地实现了网格效果 ,但是这种网格效果本质是一维的,而不是二维的。

CSS - 弹性布局_伏城之外的博客-CSDN博客

之前,我们学习弹性布局时,了解到弹性布局其实是一维布局,所谓一维布局,即弹性项目元素只会沿着弹性容器的主轴方向排列,虽然上面例子中,弹性项目看上去是二维的,但是实际上,第二行,第三行的弹性项目元素都是被挤下来的,如果我们不设置换行wrap,则这些弹性项目元素将全部在一行上排列。

所以,弹性布局只能让元素在一维轴线上实现很好的布局,而无法实现真正二维网格的效果,比如弹性布局中弹性项目就很难实现单元格跨行,如让下图中1,4,7单元格合并。

因为弹性布局本质是一维布局,即1,4,7本质是一行上的,它们之间隔着2,3和5,6,所以无法轻松地实现合并。 

而网格布局是二维布局,它的单元格是由多条行线和列线分隔出来的,是真正二维意义上的网格效果。

我们可以很容易地依赖于网格线来完成单元格跨行跨列,如网格布局实现1,4,7合并 

网格布局中的概念名词

  • 网格容器(gird container)
  • 网格轨道(grid track)
  • 网格单元(grid cell)
  • 网格项目(grid item)
  • 网格线(grid line)
  • 网格区域(grid area)
  • 网格间距(grid cell gap)
  • 显示网格(explicit grid)& 隐式网格(implicit grid)

网格容器 display:grid、display:inline-grid

当我们为一个元素添加如下样式之一

  • display:grid
  • display:inline-grid

该元素就变成了网格容器。

在标准流中:

  • display:grid 网格容器表现为块级元素特性;
  • display:inline-grid 网格容器表现为行内块元素特性;

整个网格系统都在网格容器的 content-box 中。 

网格轨道 grid-template-rows、grid-template-columns

所谓网格轨道,即网格行和网格列的统称。

我们可以通过

  • grid-template-rows     来定义网格行的数目和尺寸   
  • grid-template-columns 来定义网格列的数目和尺寸

上例中:

  • grid-template-rows: 100px 100px 100px;  // 表示设置了3行,每行尺寸(高度)为100px
  • grid-template-columns: 100px 100px 100px; // 表示设置了3列,每列尺寸(宽度)为100px

我们需要注意的是网格轨道并非真实的DOM元素,而是一种逻辑概念,所以它不会被浏览器渲染出来,上例中网格轨道的效果图是通过浏览器的grid布局检测模拟出来的。

grid-template-rows、grid-template-columns不仅支持固定尺寸,还支持百分比尺寸、fr尺寸、auto尺寸、min-content、max-content尺寸、minmax()尺寸

这里百分比尺寸的基数是网格容器的尺寸。如网格的百分比尺寸的基数,就是网格容器的content-box的height,网格的百分比尺寸的基数,就是网格容器的content-box的width

fr是一种单位,每个定义了fr尺寸的网格轨道会按fr比例分配网格容器的剩余可用空间。

比如上例中,将三个网格列尺寸分别定义为:1fr 2fr 1fr

即表示,将网格容器的剩余可用空间分为四等分,第一列、第三列占1分,第二列占2分。

咋一看,似乎fr尺寸和百分比尺寸没有区别,其实二者还是存在区别的。

百分比的基数是网格容器尺寸,fr是网格容器剩余空间的占比,所以二者基数不同。

我们通过下面例子来理解百分比尺寸和fr尺寸的区别:

上例中,为网格行之间、网格列之间添加都添加了10px的网格间距,通过结果可以发现:

  • 使用百分比尺寸的网格行的尺寸并没有因为网格间距的加入而改变,所以,网格行尺寸 + 网格间距,超出了网格容器范围;
  • 而网格列使用的是fr尺寸,它的基数是网格容器剩余空间,这里的剩余空间 = 网格容器尺寸 - 网格间距,这保证了网格列不会超出网格容器范围。

网格轨道尺寸设为auto,意思是占据网格容器剩余全部空间。

  • 百分比尺寸结合auto尺寸可以保证网格轨道不会超出网格容器范围,如上例中网格尺寸设置。 
  • 而fr尺寸也是基于网格容器剩余空间计算的,所以必然和auto尺寸存在冲突,而通过上例网格可以发现,fr尺寸直接分配完了剩余空间,导致auto分配时,就没有剩余空间了。

需要注意的是虽然auto分得尺寸为0,但是也会生成网格轨道。如上例中,第二列左侧也有gap,而gap只存在于网格轨道之间。

min-content、max-content

Intrinsic Sizing In CSS - Ahmad Shadeed (ishadeed.com)

一个元素的width、height设置可以分为两种情况:

  • 外部尺寸(extrinsic)
  • 内部尺寸(intrinsic)

我们设置元素的width、height为固定尺寸,如100px,或者设置为百分比尺寸,如50%,都算是外部尺寸。

元素的外部尺寸的不会受自身内容的影响。(行内元素由于不可以设置width,所以不在此列。)

而有时候,我们期望元素的width、height可以适应其内容,CSS为此设计了三种特殊内部尺寸值:

  • min-content
  • max-content
  • fit-contnt

 当一个元素的width为min-content时:

  • 如果内容为全英文,则width:min-content表示将元素宽度设为最长英文单词的长度
  • 如果内容为全中文,则width:min-content表示将元素宽度设为单个中文字符的长度
  • 如果内容为标签元素,则width:min-content表示将元素宽度设为最大宽度子元素的宽度

当一个元素的width:max-content时,则表示将该元素的宽度设置为内容的宽度

当我们设置元素width:fit-content时,注意观察上例中盒子模型width的变化,发现:

元素可用空间,一般指的是元素所在父盒子内容区的宽高,例子中,元素可用空间即container容器的内容区宽度。

  • 当元素可用空间 availble >= 元素 max-content 时,则元素宽度为其max-content
  • 当元素可用空间 availble在 元素(mincontent,max-content) 之间  时,则元素宽度为avaible
  • 当元素可用空间 availble < 元素 min-content时,则元素宽度为其min-content

所以网格布局中,grid-template-rows或grid-template-columns轨道尺寸设置为min-content或max-content时,会根据轨道中网格项目内容来决定轨道实际尺寸。

上图网格容器中创建了一行一列,且网格行尺寸为max-content,即为网格项目内容实际高度,网格列尺寸为min-content,即为网格项目内容中最长的英文单词的长度。 

minmax

minmax是css的函数,用于定义网格轨道的尺寸范围(闭区间);

minmax函数包含两个参数,依次是网格轨道的最小尺寸min、最大尺寸max,表示:

  • 当轨道可用空间 availble >= max尺寸时,轨道尺寸为max尺寸
  • 当轨道可用空间 availble in (min, max) 尺寸时,轨道尺寸为avaible尺寸
  • 当轨道可用空间 avaible <= min尺寸时,轨道尺寸为min尺寸

minmax的作用其实非常类似于元素内部尺寸fit-content,只是minmax的可以自定义最小和最大尺寸。

如果我们设置轨道尺寸为 minmax(min-content, max-content),其实就是设置轨道尺寸为fit-content,但是轨道尺寸却不支持fit-content。

minmax函数参数可以是固定尺寸,也可以是百分比尺寸,fr尺寸,auto尺寸,需要注意的是:

  • 如果 max尺寸 < min尺寸,则max尺寸被忽略,并且minmax函数结果为min尺寸。
  • fr尺寸不能作为 minmax的最小尺寸参数值,只能作为最大尺寸参数值
  • auto尺寸作为最大尺寸参数值时,等于max-content(存疑;作为最小尺寸时,等于轨道中单元格最小长宽 (由min-width/min-height) 的最大值(注意不等价于min-content)。

如果minmax函数的最大尺寸参数值(如50px)小于最小尺寸参数值(如100px),则最大尺寸参数(如50px)作废,minmax函数结果固定是最小尺寸参数值(如100px)。 

可以发现,当将fr尺寸作为minmax函数最小尺寸参数时,则minmax无效。

当将fr尺寸作为minmax函数最大尺寸参数时,则等价于布局容器剩余空间。

可以发现,minmax的最大尺寸参数设为auto时,并不等价于max-content,而是占满容器剩余空间。

 

 可以发现,minmax的最小尺寸参数设为auto时,等价于轨道中项目元素的min-width/min-height。

css - What happens when minmax is: minmax(auto, auto) - Stack Overflow

如果轨道中项目元素没有in-width/min-height,则auto等价于轨道的min-content。 

repeat函数

如果我们的网格行都是等高的、网格列都是等宽,假设需要五行五列,则写法如下

grid-template-rows:1fr 1fr 1fr 1fr 1fr;

grid-template-columns:1fr 1fr 1fr 1fr 1fr;

如果需要十行十列,则

grid-template-rows:1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;

grid-template-columns:1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;

显然这种写法非常不友好,所以我们需要使用css的repeat函数。

css的repeat函数专门用于帮助grid-template-rows、grid-template-columns来生成重复尺寸。

repeat函数接收两个参数,第二个参数是要被重复的网格轨道尺寸,第一个参数是重复次数

需要注意的是,repeat函数的第二个参数既可以是一个尺寸,也可以多个尺寸

如上例中,grid-template-columns:repeat(2, 2fr 3fr) 相当于生成 grid-template-columns: 2fr 3fr 2fr 3fr 

还有需要注意的是,repeat函数的结果就是一串尺寸片段,我们可以继续在后面拼接新的尺寸

auto-fill、auto-fit

repeat函数的第一个参数还支持如下特殊值:

  • auto-fill
  • auto-fit

mdn上关于auto-fill、auto-fit的介绍非常晦涩,我们可以参考

[译] CSS Grid 之列宽自适应:`auto-fill` vs `auto-fit` - 掘金 (juejin.cn)

auto-fill、auto-fit的作用是啥?

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .grid {
        display: grid;
        grid-template-columns: repeat(12, 1fr);
      }

      .grid > div {
        height: 100px;
      }
    </style>
  </head>
  <body>
    <div class="grid">
      <div>1</div>
      <div>2</div>
      <div>3</div>
      <div>4</div>
      <div>5</div>
      <div>6</div>
      <div>7</div>
      <div>8</div>
      <div>9</div>
      <div>10</div>
      <div>11</div>
      <div>12</div>
    </div>
  </body>
</html>

上述代码,我们可以得到一个被分为12个等宽列轨道的网格容器。

并且网格容器的宽度是动态的,这意味着分给12个等宽列轨道的空间也是变化的。

此时为了避免列轨道的宽度太小,导致轨道项目元素的内容溢出,我们需要为列轨道设定一个最小宽度。

 grid-template-columns: repeat(12, minmax(100px, 1fr));

上面代码的意思是,将网格容器分为等宽的12列:

  • 如果(网格容器宽度 / 12)  > 100px,则每列列宽为(网格容器宽度 / 12)
  • 如果 (网格容器宽度 / 12)<= 100px,则每列列宽为100px

这虽然可以保轨道列中项目元素不会被挤压变形,但是可能会导致部分网格轨道超出网格容器范围。

那么有没有一种办法,既能保证网格轨道不会超出网格容器范围,也可以保证网格列的最小宽度呢?

为了保证所有网格轨道的完整显示,只能将超出网格容器的网格单元换行显示。

即,此时网格容器上的轨道列数目应该是动态的,而不是固定的。

此时,我们可以使用repeat函数的auto-fill和auto-fit来实现动态轨道列数目。

grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));

 上面代码的意思是,在网格容器的一行上尽可能多(即采用最小尺寸,如100px)地放入(不定数目auto-fill个)网格轨道,对于一行放不下的网格单元采取换行显示,保证其不会超出网格容器范围。

  grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));

auto-fit同理。

auto-fill与auto-fit的区别在于:

网格容器的尺寸大于全部最小尺寸模式下的网格单元尺寸之和,比如网格容器1904px宽度,而12列等宽轨道最小尺寸之和为1200px,此时网格容器还剩余704px宽度空间:

  • 对于auto-fill来说,会再生成七列最小宽度(100px)轨道,然后将4px平分到19个轨道列宽度
  • 对于auto-fit来说,会直接将704px剩余空间平分给已有的12个轨道列

 也就是说,auto-fill和auto-fit在:

  • 网格容器尺寸(如行宽)不足(放不下所有网格单元)时,表现相同,都会将超出网格容器的网格单元换行显示,

  • 网格容器尺寸(如行宽)充足(可以放下所有网格单元)时,对于网格容器剩余空间的处理不同,auto-fill会继续创建轨道,直到剩余空间不足轨道尺寸,然后再将剩余空间按比例分配给各轨道;而auto-fit会直接将剩余空间按比分配给已有轨道。

网格单元

网格行与网格列相交便形成了网格单元,网格单元是grid布局的最小单元。网格单元和网格轨道一样也并非真实DOM元素,只是一种逻辑概念,所以它不会被浏览器渲染出来。

如上小节例子中,网格容器被分为3行3列,9个网格单元。

网格项目

网格容器元素的直接子元素就是网格容器的网格项目,类似于弹性容器与弹性项目的关系。

默认情况下,网格项目会按代码顺序依次放入网格容器的网格单元中。

网格线

网格轨道是由网格线组成的,每个网格轨道由两条网格线组成。网格线不是真实DOM,所以不会被浏览器渲染。

网格线默认通过数字索引(从左往右,从上往下,起始索引1)引用,也可以通过自定义名字来引用。

关于为网格线自定义名字:

grid-template-rows、grid-template-colums用于定义网格轨道,而每个网格轨道由两条网格线构成;

grid-template-rows、grid-template-colums 不仅支持网格轨道数目、尺寸,还支持定义组成网格轨道的网格线的名字,语法如下:

grid-template-rows:[lineName1] size1 [lineName2] size2 [lineName3] ... 

特点就是一根网格轨道夹在两根网格线之间。

对于使用repeat函数生成的网格轨道,我们可以

grid-template-rows:repeat(3, [r-start] size [r-end])

此时实际生成结果为

grid-template-rows:[r-start 1] size [r-end 1] [r-start 2] size [r-end 2] [r-start 3] size [r-end 3]

此时网格线 r-end 1和r-start 2其实同一条网格线。

网格线占位

之前我们说过网格单元是被网格容器中的网格轨道分隔出来的,而本质上来说,网格单元其实是被网格容器中的网格线分割出来的。

我们可以用四条网格线来描述网格容器中的任意一个网格单元或多个网格单元。网格线引用包括:

  • grid-row-start
  • grid-row-end
  • grid-column-start
  • grid-column-end

这四个样式都属于网格项目元素,而不是网格容器。

网格项目元素通过四条网格线,来设定自身占据那个网格单元。

如下图,通过默认的数字索引网格线,来定义网格项目位置。

 ​​​​​如下图通过自定义网格线名字来定位网格项目位置

如下通过repeat下的网格线自定义名字来定位网格项目位置。

上面设定网格线的方式比较繁琐,我们可以进行简化,即使用复合语法:

  • grid-row:grid-row-start grid-row-end
  • grid-column: grid-column-satrt grid-column-end

其实,我们还可以继续简化网格线引用,即使用进一步的复合语法:

grid-area:grid-row-start / grid-column-start / grid-row-end / grid-column-end

我们需要注意的是,网格线不仅可以描述一个网格单元,还可以描述多个网格单元,即让一个网格项目占据多个网格单元

另外,我们目前都是基于四条网格线来完成的网格单元描述,这种描述方式其实不太好在脑海中想象,我们可以只定义两条起始网格线,另外两条结束网格线通过偏移来描述。

grid-row-end:span 2; // 含义是相对于grid-row-start网格线向下偏移两个网格单元 

grid-column-end:span 2; // 含义是相对于grid-column-start网格线向右偏移两个网格单元

支持简写形式

网格区域占位

通过网格线描述网格单元其实使用起来语义化非常差

grid-area: 1 / 2 / span 2 / span 2

比如第一眼看到上面样式,我们很难直观感受到该项目所处网格单元的位置。

所以,网格容器提供了 grid-template-areas 样式直接描述网格单元,网格项目通过grid-area直接引用网格区域即可。

 如果网格区域对应多个网格单元,则直接将处于同一个网格区域内的网格单元区域名称改为一致即可。

对于用不到区域命名的网格单元,我们可以用.来作为其区域名

grid-template

grid-template是grid-template-rows、grid-template-columns、grid-template-areas的复合写法。

下面以三行三列的网格容器定义为例:

grid-template复合写法一:网格区域 + 网格轨道

grid-template:"grid-template-areas#1" grid-template-rows#1

                         "grid-template-areas#2" grid-template-rows#2

                         "grid-template-areas#3" grid-template-rows#3 / grid-template-columns;

grid-template复合写法二:网格线 + 网格轨道

grid-template: grid-template-rows / grid-template-columns

grid-template复合写法三:网格线 + 网格区域 + 网格轨道

grid-template:[line1] "grid-template-areas#1" grid-template-rows#1 [line2]

                        [line3]  "grid-template-areas#2" grid-template-rows#2 [line4]

                        [line5]  "grid-template-areas#3" grid-template-rows#3 [line6] / grid-template-columns;

grid-template:none

表示网格行列隐式生成,使用grid-auto-rows、grid-auto-columns来定义网格行列尺寸。

显示网格、隐式网格

我们通过 grid-template-columns 和 grid-template-rows 创建出来的轨道,称为显示网格(轨道);但是这些轨道不一定能容纳所有的网格项目, 浏览器根据网格项目的数量计算出来需要更多的轨道, 就会自动生成新的轨道来容纳多出来的网格项目, 而这些自动生成的轨道被称为隐式网格(轨道)。

另外,如果我们未给网格容器定义显示网格轨道,则浏览器会根据网格项目自动创建隐式网格轨道。

隐式网格轨道的尺寸通过如下属性控制:

  • grid-auto-rows:用于指定隐式创建的行轨道尺寸,默认值auto
  • grid-auto-columns:用于指定隐式创建的列轨道尺寸,默认值auto

我们可以将这两个属性理解为网格轨道的默认尺寸设置,即:

  • 如果未定义网格容器的grid-template-rows和grid-template-columns,则网格轨道尺寸采用grid-auto-rows和grid-auto-columns
  • 如果已定义网格容器的grid-template-rows和grid-template-columns,但是显示网格轨道不足以放下所有网格项目元素,则自动创建的隐式轨道的尺寸会采用grid-auto-rows或grid-auto-columns

需要注意的是,隐式创建的轨道只能是行轨道或列轨道之一,不可能同时创建两个方向的隐式轨道,而具体创建哪个方向的隐式轨道,由属性 grid-auto-flow 决定,该属性有如下值:

  • row(默认值)
  • column

当grid-auto-flow:row时,只能创建隐式行轨道。

当grid-auto-flow:column时,只能创建隐式列轨道。

grid-auto-flow 还决定了 网格容器中 网格项目元素的排列方向

  • row:该关键字指定自动布局算法按照通过逐行(从左到右)来排列元素,在必要时增加新行(隐式网格)
  • column:该关键字指定自动布局算法通过逐列(从上到下)来排列元素,在必要时增加新列(隐式网格)

上例中,由于网格容器没有定义显示网格轨道,所以默认采用隐式网格轨道尺寸,然而这里也没有自定义隐式网格轨道,因此采用默认的隐式网格轨道尺寸auto。auto的含义是占据剩余空间。

这里因为grid-auto-flow是row方向,所以网格项目逐行填充,必要时新增行,列数目不会变化,因此grid-auto-columns:auto会产生占据网格容器全部宽度的唯一列,而grid-auto-rows:auto会产生平分网格容器高度的多个行。

grid-auto-flow:column同理 

上例中,网格容器只有2行2列,理论只能放4个项目元素,但是实际上有9个项目元素,所以必然有5个项目元素放不下,此时只能生成隐式网格轨道来放。

需要注意的是,此时grid-auto-flow:row决定了只会创建隐式网格行轨道,不会创建隐式网格列轨道。

我们可以这样理解:

grid-auto-flow: row; // 只会新增行,不会新增列

grid-template-rows: repeat(2, 100px); // 显示行使用显示网格尺寸

grid-auto-row: auto; // 隐式行使用隐式网格轨道尺寸

grid-template-columns: repeat(2, 100px);

grid-auto-columns: auto; // 没有新增列,所以不需要使用隐式网格轨道尺寸

密集模式

grid-auto-flow不仅可以控制网格项目的排列方向及隐式网格创建,还可以控制网格项目的包装模式。

grid-auto-flow实际上有四种值:

  • row
  • row dense
  • column
  • column dense

其中row、column是稀疏包装模式,row dense、column dense是密集包装模式。

我们通过几个例子来理解稀疏包装模式和密集包装模式:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .grid {
        display: grid;
        grid-template-rows: repeat(4, 100px);
        grid-template-columns: repeat(4, 100px);
      }

      .red {
        grid-area: 1 / 2 / 2 / 3;
        background-color: red;
      }

      .blue {
        grid-area: 2 / 1 / 3 / 3;
        background-color: blue;
      }

      .green {
        grid-row: 1 / 3;
        background-color: green;
      }

      .yellow {
        grid-row: 1 / 2;
        background-color: yellow;
      }
    </style>
  </head>
  <body>
    <div class="grid">
      <div class="red">1</div>
      <div class="blue">2</div>
      <div class="green">3</div>
      <div class="yellow">4</div>
    </div>
  </body>
</html>

上面例子中,网格容器就是稀疏包装模式,因为默认的grid-auto-flow是row,此时我们发现yellow项目只需要grid-row: 1 / 2,即在第一行就行,而第一行刚好还剩两个空白网格单元,但是yellow项目却没有被放入第一个空白网格单元中。

这种情况就是稀疏包装模式导致的。

如果我们将grid-auto-flow改为row dense,则可以让yellow项目放入第一个空白网格单元中。

还有一个例子

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .grid {
        display: grid;
        grid-template-rows: repeat(4, 100px);
        grid-template-columns: repeat(4, 100px);
      }

      .red {
        grid-area: 1 / 2 / 2 / 3;
        background-color: red;
      }

      .blue {
        grid-column: 2 / 3;
        background-color: blue;
      }

      .green {
        grid-column: 1 / 2;
        background-color: green;
      }
    </style>
  </head>
  <body>
    <div class="grid">
      <div class="red">1</div>
      <div class="blue">2</div>
      <div class="green">3</div>
    </div>
  </body>
</html>

 green项目只需要占据grid-column: 1 / 2,即第一列中某个网格单元,但是green却没有占据第一行第一列网格单元,这也是稀疏包装模式导致的。

我们只需要让grid-auto-flow变为row dense即可

至于稀疏包装模式为什么会导致上述现象,这涉及到网格项目自动放置算法,可以借鉴如下资料

CSS 网格布局中的自动定位 - CSS(层叠样式表) | MDN (mozilla.org)

css网格_CSS网格中自动放置算法的循序渐进指南 - 灰信网(软件开发博客聚合) (freesion.com)

我这边做了一个半成品总结:

通过前面两个例子,大家可以发现,空白网格单元的出现,必然伴随着 定行不定列 或者 定列不定行 的网格项目,所谓

定行不定列:即只定义了grid-row,未定义grid-column

定列不定行:即只定义了grid-column,未定义grid-row

无论是稀疏模式,还是密集模式,定行不定列项目、定列不定行项目都不会占用已被占用的网格单元,即它们都不会和其他项目共享网格单元,如果它们首选的网格单元已被占用,则会换行或换列找下一个符合要求的网格单元。

最关键的是,稀疏模式下定行不定列项目之间、定列不定行项目之间会产生约束,在DOM中,

  • 后渲染的定行不定列项目 一定排在 先渲染的定行不定列项目 之后
  • 后渲染的定列不定行项目 一定排在 先渲染的定列不定行项目 之后

这是产生空白网格的关键原因。

而密集模式下,上面两条约束就失效了。

网格间距

网格间距指的是网格轨道之间的间距,而不是网格项目之间的间距。

网格间距可以通过网格容器上的样式属性定义:

  • row-gap:行轨道之间的间距
  • column-gap:列轨道之间的间距

我们需要注意的是,如果网格容器具有固定width、height的话,则网格间距的加入可能会导致网格轨道溢出

如上例中,网格容器内容区是300*300尺寸,而网格容器的行轨道尺寸之和是300px,列轨道尺寸之和也是300px,刚好填满网格容器内容区。但是此时,我们又给行轨道之间加入10px间距,列轨道之间加入10px间距,这就导致网格系统多出了水平、垂直方向各多出了20px,导致了网格轨道溢出网格容器。

所以我们在使用网格间距时,一定要注意设置网格轨道的尺寸要兼容网格间距,避免溢出。

我们可以在计算网格轨道尺寸时,手动去除掉网格间距尺寸,但是这种计算太麻烦

因此,我们可以借助fr尺寸,auto尺寸来实现网格轨道尺寸自动减去网格间距

最后,我们的row-gap、column-gap还可以复合写为gap:row-gap column-gap

网格单元的对齐

如果网格容器划分完轨道后,依旧还能剩余空闲空间,如上例中,网格容器300px*300px,列轨道尺寸总计200px,行轨道尺寸总计100px,则在垂直方向上还剩200px空闲空间,在水平空间上还剩100px空闲空间。

此时我们就可以基于网格容器,对网格单元进行水平方向、垂直方向的对齐操作。

  • justify-content:start | center | end | space-around | space-between | space-evenly
  • align-content:center | space-around | space-between | space-evenly | stretch

具体效果看上面动图。

另外,justify-content、align-content可以复合写为

place-content:align-content  justify-content

网格项目的对齐

网格项目的对齐,即网格项目在网格单元中的对齐。它分为两类:

  • 所有网格项目设置统一对齐方式
  • 某个网格项目设置单独对齐方式

统一对齐,需要在网格容器上设置如下属性:

  • justify-items:stretch | start | center | end
  • align-items:stretch | start | center | end | baseline

justify-items、align-items默认值为stretch,即当网格项目未定义width、height时,默认被拉伸填充网格单元。

统一对齐对网格容器中所有网格项目有效。

另外,justify-items、align-items可以复合写为

place-items:align-items  justify-items

单独对齐,需要在网格项目上设置如下属性:

  • justify-self:stretch | start | center | end
  • align-self:stretch | start | center | end | baseline

justify-self、align-self的功能和justify-items、align-items基本一致,只是justify-self、align-self只能作用于所在的网格项目。

另外, justify-self、align-self可以复合写为

place-self:align-self  justify-self

  • 18
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 在Android中实现注册样式的步骤如下: 1. 创建一个布局文件,用于显示注册页面的各个控件,例如输入框、按钮等。 2. 在Java代码中编写逻辑,实现注册功能。具体实现方式可以使用数据库存储用户信息,或者调用第三方API实现注册。 3. 在AndroidManifest.xml文件中添加注册页面的Activity信息,以便应用程序可以启动该页面。 以下是一个简单的注册页面布局文件示例: ``` <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="16dp"> <EditText android:id="@+id/et_username" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="用户名" /> <EditText android:id="@+id/et_password" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="密码" android:inputType="textPassword" /> <Button android:id="@+id/btn_register" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="注册" /> </LinearLayout> ``` 在Java代码中,可以使用以下代码实现注册逻辑: ``` public class RegisterActivity extends AppCompatActivity { private EditText etUsername; private EditText etPassword; private Button btnRegister; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_register); etUsername = findViewById(R.id.et_username); etPassword = findViewById(R.id.et_password); btnRegister = findViewById(R.id.btn_register); btnRegister.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 获取用户名和密码 String username = etUsername.getText().toString(); String password = etPassword.getText().toString(); // TODO: 实现注册逻辑 // 注册成功后跳转到登录页面 Intent intent = new Intent(RegisterActivity.this, LoginActivity.class); startActivity(intent); finish(); } }); } } ``` 最后,在AndroidManifest.xml文件中添加Activity信息: ``` <activity android:name=".RegisterActivity" /> ``` ### 回答2: 要实现一个注册样式的Android应用,可以采用以下步骤: 1. 创建一个新的Android项目并设置项目名称。 2. 在项目的布局文件中,设计注册页面的UI界面。可以使用TextView、EditText和Button等元素来设计输入框和按钮。 3. 在Java类中,编写逻辑代码来实现注册功能。可以使用OnClickListener接口来监听按钮的点击事件,获取用户输入的注册信息。 4. 将用户输入的信息保存到数据库或云端服务器中。可以使用SQLite数据库或者后端开发工具,如Firebase等。 5. 在代码中进行验证用户输入的合法性。可以使用正则表达式来验证邮箱、手机号等格式,或者自定义规则来判断用户名和密码的规范性。 6. 如果注册成功,跳转到登录页面。可以使用Intent来实现页面之间的跳转。 7. 如果注册失败,根据错误类型,可以给出相应的提示信息,如用户名已存在、密码过于简单等。 8. 添加必要的错误处理机制,如网络连接错误、服务器故障等情况下的处理方式。 通过以上步骤,可以实现一个基本的注册样式的Android应用。当用户打开应用时,显示注册页面,用户输入完整的注册信息后,点击注册按钮进行注册。系统会对用户输入进行验证,并根据验证结果给出相应的提示。注册成功后,跳转到登录页面,用户可以使用注册的账号进行登录。 ### 回答3: 要在Android上实现注册样式,可以按照以下步骤进行: 1. 创建一个注册页面的布局文件,可以使用XML语言来定义布局元素。可以包括输入框、按钮、文本框等。可以使用LinearLayout、RelativeLayout等布局容器来组织界面元素的位置和大小。 2. 在注册页面对应的Activity类中,使用setContentView()方法将布局文件与该Activity关联起来。 3. 在Activity类中,使用findViewById()方法来获取布局文件中的各个界面元素的引用。可以使用id来定位元素,并将其赋值给相应的变量。 4. 可以对界面元素进行一些样式的设置,例如设置字体颜色、大小、背景图片等。可以使用setTextColor()、setTextSize()等方法进行设置。 5. 为注册按钮设置一个事件监听器,监听按钮点击事件。可以使用setOnClickListener()方法为按钮设置监听器,当按钮被点击时,执行相应的逻辑操作。 6. 在事件监听器中,可以编写注册逻辑的代码。例如,可以获取输入框中的用户名和密码,然后将其保存到数据库中或进行其他处理。 7. 可以在验证用户输入时进行一些逻辑判断,例如检查用户名和密码是否符合要求、检查用户名是否已存在等。可以使用if语句等进行判断,并根据判断结果显示相应的提示信息。 8. 可以根据注册结果,使用Toast等方式向用户展示相应的提示信息,例如注册成功或失败的提示。 9. 可以添加一些其他的界面元素或逻辑,以增强注册页面的功能和用户体验。例如,可以添加验证码功能、密码强度检查功能等。 以上是基本的实现注册样式的步骤,根据具体需求和设计,可以进行相应的调整和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

伏城之外

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

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

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

打赏作者

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

抵扣说明:

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

余额充值