【翻】如何使用CSS Grid 网格实现杂志布局

原文地址:www.smashingmagazine.com/2023/02/bui…

简要

在web开发中,通过CSS实现的内容,变得越来越复杂。借助于CSS Grid的新增功能,可以实现看起来像是手工布局的布局效果。让我们通过解决一个实际的例子,看看如何实现这样的效果。

在这篇文章中,我想跟大家分享下CSS grid的的神奇之处,以及它是如何实现更接近印刷设计的复杂布局效果的。这里我将以工作时为客户完成一个项目(稍作改动)以用作演示,展示CSS Grid的两个重要用途:

  1. 使用静态网格,可以为每个待布局元素指定起始点和结束点;
  2. 使用CSS网格模板区域(CSS grid template areas),可以轻松地重新排序简单的HTML布局,且无需更新HTML代码。

此外,我们还将涉及object-fitaspect-ratio的使用,它们作为额外的加分项,同样的非常实用。

magzine.png

如上图所示,我们将要实现的设计效果:左边是桌面版本,右边是移动端的裁剪版本(仅展示了1和2的部分,3和4部分类似)。可以看到页面上有很多的内容,且都是杂乱无章的行和列,整张图片以不均匀的网格布局排列,有些地方还有重叠,同时还有一部分涉及到内嵌的文本和一个作为设计元素编号元素。

手工图像布局

首先,让我们来观察下每幅彩色页面内部的图像网格元素布局。虽然有四幅彩色页面,但只有两个重复的布局。为了更容易比较,我把桌面版分成了两半,并将这两半放在一起,这样更容易比较。正如你所看到的,第一和第三个是相同的,第二和第四个也是相同的。如果我们仅比较第一和第二个,它们是不同的,但基本构建块非常相似(都是一个全尺寸的背景色,一个大的图像块,一个带有编号的列和一些文本)。因此,我们可以将其视为同一个组件,只是有两个不同的选择。

m2.png

在过去,我们可能需要在Photoshop中创建图像网格,然后将其作为一张图片添加到页面中。显然,我们仍然可以这样做,但这种解决方案对于响应式网站来说从来都不是特别好的选择,虽然使用<picture>元素可能会起作用,但如果我们想要更改一张图片,就需要在Photoshop中重新做多个布局,并重新进行所有操作。每次添加该元素时,我们都需要进行这些操作,同时使用不同的图片。

如果没有替代方案,我们在本文中就不会谈及这一部分!但现在已经可以放心地使用CSS Grid,它能够以只有几行CSS代码的方式非常简洁地处理这种布局。

CSS Grid允许我们在父元素上定义一个形式化的网格结构,包括列和行,并且可以指定子元素在网格中的位置。我们还可以使用与flex相似的对齐和排列功能。这消除了包裹多层div的需求,也使CSS代码更简洁。

需要注意的一点是,由于这种方式,你的图形布局可能与HTML中的文档结构不同。

但屏幕阅读器仍然依赖HTML结构,所以请将最重要的信息放在前面,并尽量保持一切的合理顺序。

现在让我们来看看我们的图像。至少需要什么?一个图像容器和图像本身。而你知道吗?使用CSS Grid,实际上这就足够了—————和包裹五层的div说再见吧!

<div class="grid image-grid-3-m4">
   <img class="image-0 " src="" /> 
   <img class="image-1 " src="" />
   <img class="image-2 " src="" />
   <img class="image-3 " src="" />
</div>


关于标记语言的一点说明:为了使样式更容易设置,我为每个图像添加了一个索引(因为我们想要重复使用它们,所以必须使用类而不是ID),并在周围的div上添加了两个类,用于定义基本样式:一个用于网格布局的实用类,另一个用于标识变体。对于在桌面视图中左侧有三个图像,右侧有第四个图像的变体,我花了一些时间思考如何解决第四个图像的问题:我们是否将其添加到与其他图像相同的容器中并尝试将其移到另一侧,还是在移动设备上移动它,等等。最后,我决定将第四个图像添加到图像容器中,但在桌面上通过CSS将其隐藏,并在另一个位置使用相同的图像在桌面版本中显示。使用display: none隐藏该版本对屏幕阅读器不可见。

现在我们已经有了基本的HTML和图像位置,是时候专注于CSS了。如果你对CSS Grid完全不熟悉,这篇有用的文章将介绍完整的语法并详细解释。遗憾的是,我无法在本文中描述完整的语法。

首先,我们需要定义我们的网格。由于我有一个设计稿可以参考,我使用了一种工具,可以在图像上放置线条,将一条线条放在图像的每个边缘,并查看其之间的像素尺寸。如果设计师已经使用了一个正式的网格并告诉我,那非常好,但不幸的是,并非如此,因此我使用了比例尺寸作为网格中应该使用的近似值。基本上,我问自己每个元素的最小公约数是多少(留有一些余地),然后使用它。我的目标是在保持列数或行数的灵活性的同时,使所有网格列和行的大小相同。

m3.png

通过这种方法,我确定在移动设备上希望有14列和7行,并加入一些统一的间隔。这样可以近似地分布在布局中,同时保持了接近他们设想的纵横比。根据这个,我们得到以下的CSS代码:

.grid {

      display: grid;

}



.image-grid-3-m4 {

      grid-template-rows: repeat(7, 1fr);

      grid-template-columns: repeat(14, 1fr);

      gap: 0.5rem;

}


有了这四行CSS代码,我们就有了一个可以填充的网格。如果按照步骤操作,你会注意到图像现在填充了第一行中的每个网格单元。这是浏览器使用的自动布局机制,根据你想要实现的效果,它可以在几秒钟内定义一个均匀布局的理想选择。

    <div class="grid image-grid-3-m4">
       <img class="image-0 " src="" /> 
       <img class="image-1 " src="" />
       <img class="image-2 " src="" />
       <img class="image-3 " src="" />
    </div>
.grid {

      display: grid;

}



.image-grid-3-m4 {

      grid-template-rows: repeat(7, 1fr);

      grid-template-columns: repeat(14, 1fr);

      gap: 0.5rem;

}




html, body , .grid {
  height: 100%;
  margin: 0;
}



/* For presentation only, no need to copy the code below */

.grid * {
  border: 1px solid red;
  position: relative;
}

.container *:after {
  content:attr(class);
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: grid;
  align-items: center;
  justify-content: center;
}

但显然,我们想要做得更有创意,不要均匀的布局。为了实现这一点,我们需要一些额外的CSS规则:具体来说,就是grid-columngrid-row。它们允许我们指定元素在网格中的位置。结合我的准线和曲线数学,我可以将每个图像放置在正确的位置。例如,第一张图像从左上角开始,因此我们从网格的列线1和行线1开始,它将跨越14列中的6列和4行。除了告诉元素要跨越多少个单元格之外,你还可以指定结束值。个人而言,我更喜欢使用span,因为它更容易跟踪和阅读。

.image-0 {
  grid-column: 1 / span 6;
  grid-row: 1 / span 4;
}

除了确定图像放置位置的信息外,还有三条规则在这里发挥了重要作用:

img {
  object-fit:cover;
  height: 100%;
  width: 100%;
}

通过这些规则,我们告诉浏览器图像始终跨越其完整容器的大小,而容器的大小由网格确定。然后我们使用object-fit属性来确保图像占据整个空间,并隐藏超出部分(如果你想了解更多关于object-fit的信息,这里有一个很好的入门指南)。显然,这意味着图像的部分区域将不会显示出来 —— 在选择要用于此布局的图像时,尤其要注意这一点 —— 特别是如果它们的纵横比不完全匹配。

说到纵横比,右上角的图像看起来确实是方形的。如果我们能告诉它保持方形,而不管图像本身的尺寸如何,那岂不是很好?现在我们确实可以做到。有一个名为aspect-ratio的CSS属性可以实现这一点,在这种情况下,它允许我们告诉图像它应该是方形的。但需要注意的是,对于向下兼容性的问题,特别是在iOS上。因此,在某些情况下,您可能需要使用polyfill来实现这一功能。但aspect-ratio属性在未来将非常有用,您可以在Stephanie Eckles的这篇文章中详细了解它。

现在,我们可以首次看到四张图像的完整效果了:

对于桌面版本,我们只需要移动图像的起始和结束位置。为此,我们需要一个媒体查询,在其中重新定义网格和位置。我再次计算了所需的行和列,以及每个元素的位置,并将这些信息添加到了我的CSS中。

现在,我们拥有一个根据屏幕大小变化布局的图像网格,一旦屏幕大小发生变化,图像将相应地更新其显示大小。

最困难的部分在于理解语法并掌握显式定位元素的方法。但是一旦掌握了这些,就像打开了一个全新的可能性大门。虽然在具有可重用组件的情况下,并非总是可以这样做,但对于特定的使用情况,它可以添加特殊效果或解决复杂的问题。

移动组件中的块元素

现在,我们已经完成了图片部分中最困难的部分,是时候关注下内容主体结构了。再次强调,我们将继续使用CSS Grid,并且这一次我们将使用grid-template-areas属性。个人而言,我非常喜欢这个功能,因为它对主体结构的布局,在桌面和移动设备之间提供了很大的灵活性,无需添加大量的包裹div或重复的信息。这个模板是一个很好的展示,让我们开始吧。

我们再次观察设计稿,那里只有少数几个元素————有几张图片,在较大的视口中可能分为两个部分:一个白色框中的编号和所有文本元素。
m4.png

那么,最少需要的HTML是什么呢?同样,我们不需要太多额外的东西;我们只需要一个包围的div和每个部分一个div:

<div class="container">
  <div class="images"></div>
  <div class="numbering"></div>
  <div class="text"></div>
  <div class="single-image"></div>
</div>

在示例中,每个区域都设置一个类名,用于标识它们将来要容纳的内容。然而,如果我们看一下移动布局,数字是在图像的顶部!CSS网格的一个很棒的特点是可以分层叠放元素。我们在上面已经使用了这个特性来处理图像。正如你所看到的,其中两个图像重叠在一起,我们也可以对整个区域进行重叠。分层叠放将由z-index控制。规则与往常一样:z-index越高的元素将覆盖在前面。

基于这一点,我们可以创建两个区域:顶部一个区域,用图像填充该区域,并且编号作为顶层显示,大部分透明以显示图像;第二个区域位于下方,用于放置文本。我们可以使用之前使用过的网格列和行的语法,但在这种情况下,我们可以使用grid-template-areas更加轻松地完成。通过这种方法,你可以用grid-area为网格的部分添加名称,然后为每个元素决定它应该出现在哪个网格区域。特别是对于页面或组件框架,这是一种更加简单和快速的工作方式,而且在以后阅读代码时比使用无意义的数字更方便。

我想通过一个例子来更容易理解这一点。

.container {  
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: repeat(2, 1fr);
  gap: 1.2rem;
  grid-auto-flow: row;
  grid-template-areas:
    "numerology"
    "text";
}

.images { grid-area: numerology; }

.numbering { grid-area: numerology; }



.text { grid-area: text; }
.single-image {display:none}


我们再次将容器定义为一个网格,为其添加了两行,并使用grid-template-areas为这些行命名。这个语法对于CSS来说非常不寻常,但它可以让你在布局中看到一个迷你视图:第一行的单元格命名为Numerolog,第二行命名为Tex

有了这些命名的行,我们可以轻松地定位我们的元素。图像和编号的容器都放在第一行,文本放在第二行。所以在上面的例子中,我们为应用到HTML中的div的类添加了grid-area的CSS属性。通过这几行代码,我们定义了布局。

为了实现编号的叠加效果,白色框将位于其容器中,并具有固定的宽度和高度。然后我们可以使用flex将其居中放置在容器中。

但是你可能会问,我们如何从这个布局过渡到桌面版本呢?实际上非常简单!对于整个网站的整体设计,在桌面上我们已经使用了一个具有14列的网格来布局所有元素。如果我们在设计中叠加一些网格标记,我们就可以看到每个元素应该占据的大致宽度。

m5.png

显然,我们在移动视图中定义的命名区域在桌面版本中并没有什么用处,但我们可以在媒体查询中简单地更新它们,并定义不同的区域名称:

.container {  display: grid;
  grid-template-columns: repeat(14, minmax(0, 1fr));
  grid-template-rows: repeat(2, 1fr);
  gap: 1.2rem;
  grid-auto-flow: row;
  grid-template-areas:
    "images images images images images . numbering numbering numbering single-image single-image single-image single-image single-image"
    "images images images images images . text text text single-image single-image single-image single-image single-image";
}




.images { grid-area: images; }

.numbering { grid-area: numbering; }

.text { grid-area: text; }

.single-image { grid-area: single-image; }


让我先说一句,是的,这确实不是一种很优雅的定义方式,但是,template-areas语法不包括像列定义中的repeat关键字。

但是请花点时间仔细观察。你可以看到,我们将前五列定义为两行中的图像区域,然后是一个点号,表示此处没有内容,接着是第一行的三列编号区域和第二行的三列文本区域,最后是五列单图像区域。个人而言,我喜欢使用一个在线生成器来直观地定义这些区域并复制所需的CSS。

现在,仅用不到20行代码,我们完全改变了布局,而且完全没有改动非常简单的HTML结构!那么对于第2个和第4个的替代版本呢?它们只是使用了稍微不同的布局,为什么不在容器上添加一些.version-a.version-b的类,并在桌面上通过这些类来定义grid-template-areas呢?就是这么简单。看一下下面的代码:

  .version-1 {
      grid-template-areas:
    "images images images images images . numbering numbering numbering single-image single-image single-image single-image single-image"
    "images images images images images . text text text single-image single-image single-image single-image single-image";
  }
.version-1 .single-image {
  grid-area: single-image;
  display:block;
 }  
  .version-2 {
        grid-template-areas:
    ". numbering numbering numbering . . images images images images images images images images"
    ". text text text . . images images images images images images images images";
  }



说实话,对我来说,在很长一段时间里,这样的布局将是完全无法实现的,或者会非常复杂,而且在版本1和版本2之间共享HTML几乎是不可能的,至少对于完整的HTML而言。现在我们只需要挥一挥魔杖,就可以更新元素的显示位置真是令人兴奋的事情。

这种方法还在另一个实际的例子中帮助了我很多,那就是在电子商务网站的产品详情页中定义区域。能够根据不同的上下文将元素移动到合适的位置是令人惊奇的,但这也意味着你需要对HTML和CSS之间的连接方式进行一些调整。而且这只是个开始。随着容器查询和层的出现,未来将有许多新的可能性在前方等待着我们,我对此非常期待!

最后,这里是将所有内容整合在一起的设计的完整版本:

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYx1RBP4' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片