CSS
在正常的开发中,会遇到很多元素块排列对齐的需求,如九宫格抽奖,多张图片上传后等分布局预览,微信朋友圈多张图片展示等。这都是正常的正方形很规整的布局。
如下所示,如果图像不是完全正方形,而是形状像六边形或菱形怎么办?怎么做呢。事实上,结合已经研究过的 CSS 网格技术,并加入一些 CSS clip-pathmask魔法,为您可以想象的任何形状创建精美的图像网格!
CSS 自定义形状网格布局 - 图1

相同的HTML

将要研究的大多数布局乍一看似乎很容易实现,但具有挑战性的部分是使用相同的 HTML 标记来实现它们。可以使用很多包装器、divs 等等,但这篇文章的目标是使用相同且最少的 HTML 代码,并且仍然可以实现想要的所有不同风格的网格。
这就是说,从以下的HTML开始:

  1. <div class="gallery">
  2. <img src="..." alt="...">
  3. <img src="..." alt="...">
  4. <img src="..." alt="...">
  5. <img src="..." alt="...">
  6. <!-- as many times as we want -->
  7. </div>

一个带有图像的容器就是在这里所需要的。足已!

六边形 CSS 网格

这个形状有时也称为蜂窝网格。CSS 自定义形状网格布局 - 图2
首先,使用clip-path在图像上使用来创建六边形形状,并将它们全部放在同一个网格区域中,以便它们重叠。

  1. .gallery {
  2. --s: 150px; /* controls the size */
  3. display: grid;
  4. }
  5. .gallery > img {
  6. grid-area: 1/1;
  7. width: var(--s);
  8. aspect-ratio: 1.15;
  9. object-fit: cover;
  10. clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0 50%);
  11. }

clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0 50%)
此时所有的图像都是六边形并且重叠在一起。所以看起来只有一个六边形的图像元素,但实际上有七个。下一步将把图像平移到它们正确放置的网格上。
CSS 自定义形状网格布局 - 图4
保留其中一张图像在中心位置。其余图像使用 CSS translate 平移在它周围。这是为网格中的每个图像提出的模拟公式:

  1. translate((height + gap)*sin(0deg), (height + gap)*cos(0))
  2. translate((height + gap)*sin(60deg), (height + gap)*cos(60deg))
  3. translate((height + gap)*sin(120deg), (height + gap)*cos(120deg))
  4. translate((height + gap)*sin(180deg), (height + gap)*cos(180deg))
  5. translate((height + gap)*sin(240deg), (height + gap)*cos(240deg))
  6. translate((height + gap)*sin(300deg), (height + gap)*cos(300deg))

经过一些计算和优化后,得到以下最终 CSS

  1. .gallery {
  2. --s: 150px; /* control the size */
  3. --g: 10px; /* control the gap */
  4. display: grid;
  5. }
  6. .gallery > img {
  7. grid-area: 1/1;
  8. width: var(--s);
  9. aspect-ratio: 1.15;
  10. object-fit: cover;
  11. clip-path: polygon(25% 0%, 75% 0%, 100% 50% ,75% 100%, 25% 100%, 0 50%);
  12. transform: translate(var(--_x,0), var(--_y,0));
  13. }
  14. .gallery > img:nth-child(1) { --_y: calc(-100% - var(--g)); }
  15. .gallery > img:nth-child(7) { --_y: calc( 100% + var(--g)); }
  16. .gallery > img:nth-child(3),
  17. .gallery > img:nth-child(5) { --_x: calc(-75% - .87*var(--g)); }
  18. .gallery > img:nth-child(4),
  19. .gallery > img:nth-child(6) { --_x: calc( 75% + .87*var(--g)); }
  20. .gallery > img:nth-child(3),
  21. .gallery > img:nth-child(4) { --_y: calc(-50% - .5*var(--g)); }
  22. .gallery > img:nth-child(5),
  23. .gallery > img:nth-child(6) { --_y: calc( 50% + .5*var(--g)); }

每个图像都由基于这些公式的--_x和变量转换。--_y只有第二张图片 ( nth-child(2)) 在任何选择器中未定义,因为它位于中心。如果决定使用不同的顺序,它可以是任何图像。这是使用的顺序:
CSS 自定义形状网格布局 - 图5
只需几行代码,就得到了一个很酷的图像网格。为此,在图像上添加了悬停的效果,让交互效果更上一层楼,代码在线预览如下:
点击查看【codepen】

CSS 菱形网格

菱形是将一个正方形旋转45度。CSS 自定义形状网格布局 - 图6
还是相同的 HTML,首先在 CSS 中定义一个 2×2 的图像网格:

  1. .gallery {
  2. --s: 150px; /* controls the size */
  3. display: grid;
  4. gap: 10px;
  5. grid: auto-flow var(--s) / repeat(2, var(--s));
  6. place-items: center;
  7. }
  8. .gallery > img {
  9. width: 100%;
  10. aspect-ratio: 1;
  11. object-fit: cover;
  12. }

然后设置旋转,请注意如何将它们都旋转45deg,但方向相反。

  1. .gallery {
  2. transform: rotate(45deg);
  3. }
  4. .gallery > img {
  5. transform: rotate(-45deg);
  6. }

向负方向旋转图像可防止它们与网格一起旋转,因此它们保持笔直。现在应用 clip-path 从它们中剪出菱形。
clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%)
此时的图像并没有按预期的间距排列,需要纠正图像的大小以使它们适合在一起。否则,它们的间距会很远,以至于看起来不像图像网格。
CSS 自定义形状网格布局 - 图8
图像在绿色圆圈的边界内,即放置图像的网格区域的内切圆。想要的是将图像放大以适合红色圆圈,即网格区域的外接圆。

  1. .gallery > img {
  2. width: 141%; /* 100%*sqrt(2) = 141% */
  3. aspect-ratio: 1;
  4. object-fit: cover;
  5. transform: rotate(-45deg);
  6. clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
  7. }

最后,还是给图像增加悬停的效果,在线代码如下:
点击查看【codepen】

三角形的 CSS 网格

CSS 自定义形状网格布局 - 图9
现在可能知道,最大的窍门是找出clip-path想要的形状。对于这个网格,每个元素都有自己的clip-path值,而最后两个网格使用一致的形状。所以,这一次,就像正在处理几个不同的三角形形状,它们组合在一起形成一个矩形的图像网格。
CSS 自定义形状网格布局 - 图10
底部的三张图片
使用以下 CSS 将它们放置在 3×2 网格中:

  1. .gallery {
  2. display: grid;
  3. gap: 10px;
  4. grid-template-columns: auto auto auto; /* 3 columns */
  5. place-items: center;
  6. }
  7. .gallery > img {
  8. width: 200px; /* controls the size */
  9. aspect-ratio: 1;
  10. object-fit: cover;
  11. }
  12. /* the clip-path values */
  13. .gallery > img:nth-child(1) { clip-path: polygon(0 0, 50% 0, 100% 100% ,0 100%); }
  14. .gallery > img:nth-child(2) { clip-path: polygon(0 0, 100% 0, 50% 100%); }
  15. .gallery > img:nth-child(3) { clip-path: polygon(50% 0, 100% 0, 100% 100%, 0 100%); }
  16. .gallery > img:nth-child(4) { clip-path: polygon(0 0, 100% 0, 50% 100%, 0 100%); }
  17. .gallery > img:nth-child(5) { clip-path: polygon(50% 0, 100% 100%, 0% 100%); }
  18. .gallery > img:nth-child(6) { clip-path: polygon(0 0, 100% 0 ,100% 100%, 50% 100%); }
  19. }

最终得到的效果如下图所示:CSS 自定义形状网格布局 - 图12
最后一点是使中间列的宽度等于0消除图像之间的空间。在菱形网格中遇到了同样的间距问题,但对使用的形状采用了不同的方法:

  1. grid-template-columns: auto 0 auto;

最终的在线代码如下:
点击查看【codepen】

比萨形状的 CSS 网格

基于上面的三角形网格通过添加简单的border-radius和overflow就可以实现另一个很酷的网格,比萨形状的 CSS 网格。
CSS 自定义形状网格布局 - 图13

拼图风格的 CSS 网格

这次将使用 CSS mask 属性来使图像看起来像拼图。
CSS 自定义形状网格布局 - 图14
现在设置网格应该是小菜一碟,所以把注意力集中在mask上。需要两个渐变来创建最终的拼图形状。一个渐变创建一个圆形(绿色部分),另一个渐变创建红色区域并填充半圆白色区域。CSS 自定义形状网格布局 - 图15

  1. --g: 6px; /* controls the gap */
  2. --r: 42px; /* control the circular shapes */
  3. background:
  4. radial-gradient(var(--r) at left 50% bottom var(--r), green 95%, #0000),
  5. radial-gradient(calc(var(--r) + var(--g)) at calc(100% + var(--g)) 50%, #0000 95%, red)
  6. top/100% calc(100% - var(--r)) no-repeat;

两个变量控制形状。--g变量控制网格间隙,相对不是最重要的。重要的是考虑间隙之间如何正确放置圆圈,以便在组装整个拼图时它们完美重叠。该--r变量则控制拼图形状的圆形部分的大小。CSS 自定义形状网格布局 - 图16
然后使用相同的 CSS 值并针对不同的位置稍加调整来创建其他三个形状:CSS 自定义形状网格布局 - 图17
此时整体拼图形状好了,但没有按预期重叠在一起。因为每个图像都被限制在它所在的网格单元中,所以现在形状有点混乱是对的:
CSS 自定义形状网格布局 - 图18
需要通过增加图像的高度/宽度来创建溢出。从上图中,必须增加第一个和第四个图像的高度,同时增加第二个和第三个图像的宽度。可能已经猜到需要使用--r变量来增加它们。

  1. .gallery > img:is(:nth-child(1),:nth-child(4)) {
  2. width: 100%;
  3. height: calc(100% + var(--r));
  4. }
  5. .gallery > img:is(:nth-child(2),:nth-child(3)) {
  6. height: 100%;
  7. width: calc(100% + var(--r));
  8. }

此时左边两张图片按预期展示了,但默认情况下,图像要么在右侧(如果增加宽度)重叠,要么在底部(如果增加高度)重叠。但这不是想要的第二张和第四张图片。解决方法是在这两个图像上使用place-self: end,最后完整代码如下:
点击查看【codepen】
最后来一个不一样的,为保障gif图加载速度,将图片替换成纯色图像,这次使用clip-path,因为它是可以动画的属性,只需更新控制形状的自定义属性即可获得很酷的悬停效果。设置一个自定义变量控制默认的三角形的角度,在鼠标悬停时设置该变量为0则回到正常的正方形,效果图如下:
CSS 自定义形状网格布局 - 图19
在线代码如下:
点击查看【codepen】

最后

本文通过将已经了解的有关 CSS Grid 的知识与一些附加 clip-path 的mask 魔法相结合,能够制作不同形状的网格布局。而且每次都使用相同的 HTML 代码!代码本身只不过是一个包含少量图像元素的容器!看完是不是觉得很简单很神奇呢,有兴趣的同学可以自己试试看,兴许能创造出更多有趣的网格图形。