1

那些你不知道的 CSS 自定义形状网格布局

 1 year ago
source link: https://blog.51cto.com/react/5592457
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

本文翻译自  CSS Grid and Custom Shapes, Part 1,略有删改

在正常的开发中,我们会遇到很多元素块排列对齐的需求,如九宫格抽奖,多张图片上传后等分布局预览,微信朋友圈多张图片展示等。这都是正常的正方形很规整的布局。

如下所示,如果图像不是完全正方形,而是形状像六边形或菱形怎么办?我们怎么做呢。事实上,我们将结合我们已经研究过的 CSS 网格技术,并加入一些 CSS clip-pathmask魔法,为您可以想象的任何形状创建精美的图像网格!

那些你不知道的 CSS 自定义形状网格布局_clip-path

相同的HTML

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

这就是说,让我们从以下的HTML开始:

<div class="gallery">
  <img src="..." alt="...">
  <img src="..." alt="...">
  <img src="..." alt="...">
  <img src="..." alt="...">
  <!-- as many times as we want -->
</div>

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

六边形 CSS 网格

这个形状有时也称为蜂窝网格。

那些你不知道的 CSS 自定义形状网格布局_css_02

首先,我们使用clip-path在图像上使用来创建六边形形状,并将它们全部放在同一个网格区域中,以便它们重叠。

.gallery {
  --s: 150px; /* controls the size */
  display: grid;
}

.gallery > img {
  grid-area: 1/1;
  width: var(--s);
  aspect-ratio: 1.15;
  object-fit: cover;
  clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0 50%);
}
clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0 50%)

此时所有的图像都是六边形并且重叠在一起。所以看起来我们只有一个六边形的图像元素,但实际上有七个。下一步将把图像平移到它们正确放置的网格上。

那些你不知道的 CSS 自定义形状网格布局_clip-path_03

保留其中一张图像在中心位置。其余图像使用 CSS translate 平移在它周围。这是我为网格中的每个图像提出的模拟公式:

translate((height + gap)*sin(0deg), (height + gap)*cos(0))
translate((height + gap)*sin(60deg), (height + gap)*cos(60deg))
translate((height + gap)*sin(120deg), (height + gap)*cos(120deg))
translate((height + gap)*sin(180deg), (height + gap)*cos(180deg))
translate((height + gap)*sin(240deg), (height + gap)*cos(240deg))
translate((height + gap)*sin(300deg), (height + gap)*cos(300deg))

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

.gallery {
  --s: 150px; /* control the size */
  --g: 10px;  /* control the gap */
  display: grid;
}
.gallery > img {
  grid-area: 1/1;
  width: var(--s);
  aspect-ratio: 1.15;
  object-fit: cover;
  clip-path: polygon(25% 0%, 75% 0%, 100% 50% ,75% 100%, 25% 100%, 0 50%);
  transform: translate(var(--_x,0), var(--_y,0));
}
.gallery > img:nth-child(1) { --_y: calc(-100% - var(--g)); }
.gallery > img:nth-child(7) { --_y: calc( 100% + var(--g)); }
.gallery > img:nth-child(3),
.gallery > img:nth-child(5) { --_x: calc(-75% - .87*var(--g)); }
.gallery > img:nth-child(4),
.gallery > img:nth-child(6) { --_x: calc( 75% + .87*var(--g)); }
.gallery > img:nth-child(3),
.gallery > img:nth-child(4) { --_y: calc(-50% - .5*var(--g)); }
.gallery > img:nth-child(5), 
.gallery > img:nth-child(6) { --_y: calc( 50% + .5*var(--g)); }

每个图像都由基于这些公式的–_x和变量转换。–_y只有第二张图片 ( nth-child(2)) 在任何选择器中未定义,因为它位于中心。如果您决定使用不同的顺序,它可以是任何图像。这是我使用的顺序:

那些你不知道的 CSS 自定义形状网格布局_布局_04

只需几行代码,我们就得到了一个很酷的图像网格。为此,我在图像上添加了悬停的效果,让交互效果更上一层楼,代码在线预览如下:

 https://code.juejin.cn/pen/7132450107156332552

CSS 菱形网格

菱形是将一个正方形旋转45度。

那些你不知道的 CSS 自定义形状网格布局_clip-path_05

还是相同的 HTML,我们首先在 CSS 中定义一个 2×2 的图像网格:

.gallery {
  --s: 150px; /* controls the size */

  display: grid;
  gap: 10px;
  grid: auto-flow var(--s) / repeat(2, var(--s));
  place-items: center;
}
.gallery > img {
  width: 100%; 
  aspect-ratio: 1;
  object-fit: cover;
}

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

.gallery {
  transform: rotate(45deg);
}
.gallery > img {
  transform: rotate(-45deg);
}

向负方向旋转图像可防止它们与网格一起旋转,因此它们保持笔直。现在,我们应用 clip-path 从它们中剪出菱形。

clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%)

此时的图像并没有按我们的预期的间距排列,我们需要纠正图像的大小以使它们适合在一起。否则,它们的间距会很远,以至于看起来不像图像网格。

那些你不知道的 CSS 自定义形状网格布局_布局_06

图像在绿色圆圈的边界内,即放置图像的网格区域的内切圆。我们想要的是将图像放大以适合红色圆圈,即网格区域的外接圆。

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

最后,还是给图像增加悬停的效果,在线代码如下:
 https://code.juejin.cn/pen/7132464081167974434

三角形的 CSS 网格

那些你不知道的 CSS 自定义形状网格布局_布局_07

你现在可能知道,最大的窍门是找出clip-path我们想要的形状。对于这个网格,每个元素都有自己的clip-path值,而最后两个网格使用一致的形状。所以,这一次,就像我们正在处理几个不同的三角形形状,它们组合在一起形成一个矩形的图像网格。

那些你不知道的 CSS 自定义形状网格布局_css_08
那些你不知道的 CSS 自定义形状网格布局_布局_09

我们使用以下 CSS 将它们放置在 3×2 网格中:

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

最终得到的效果如下图所示:

那些你不知道的 CSS 自定义形状网格布局_css_10

最后一点是使中间列的宽度等于0消除图像之间的空间。我们在菱形网格中遇到了同样的间距问题,但对我们使用的形状采用了不同的方法:

grid-template-columns: auto 0 auto;

最终的在线代码如下:

 https://code.juejin.cn/pen/7132468930584510502

比萨形状的 CSS 网格

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

那些你不知道的 CSS 自定义形状网格布局_css_11

拼图风格的 CSS 网格

这次我们将使用 CSS mask 属性来使图像看起来像拼图。

那些你不知道的 CSS 自定义形状网格布局_css_12

现在设置网格应该是小菜一碟,所以让我们把注意力集中在mask上。我们需要两个渐变来创建最终的拼图形状。一个渐变创建一个圆形(绿色部分),另一个渐变创建红色区域并填充半圆白色区域。

那些你不知道的 CSS 自定义形状网格布局_clip-path_13
--g: 6px; /* controls the gap */
--r: 42px;  /* control the circular shapes */

background: 
  radial-gradient(var(--r) at left 50% bottom var(--r), green 95%, #0000),
  radial-gradient(calc(var(--r) + var(--g)) at calc(100% + var(--g)) 50%, #0000 95%, red)
  top/100% calc(100% - var(--r)) no-repeat;

两个变量控制形状。–g变量控制网格间隙,相对不是最重要的。重要的是考虑间隙之间如何正确放置我们的圆圈,以便在组装整个拼图时它们完美重叠。该–r变量则控制拼图形状的圆形部分的大小。

那些你不知道的 CSS 自定义形状网格布局_clip-path_14

然后我们使用相同的 CSS 值并针对不同的位置稍加调整来创建其他三个形状:

那些你不知道的 CSS 自定义形状网格布局_布局_15

此时整体拼图形状好了,但没有按我们的预期重叠在一起。因为每个图像都被限制在它所在的网格单元中,所以现在形状有点混乱是对的:

那些你不知道的 CSS 自定义形状网格布局_css_16

我们需要通过增加图像的高度/宽度来创建溢出。从上图中,我们必须增加第一个和第四个图像的高度,同时增加第二个和第三个图像的宽度。您可能已经猜到我们需要使用–r变量来增加它们。

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

此时左边两张图片按预期展示了,但默认情况下,我们的图像要么在右侧(如果我们增加宽度)重叠,要么在底部(如果我们增加高度)重叠。但这不是我们想要的第二张和第四张图片。解决方法是在这两个图像上使用place-self: end,最后我们的完整代码如下:

 https://code.juejin.cn/pen/7132472038211452942

最后来一个不一样的,为保障gif图加载速度,我将图片替换成纯色图像,这次我们使用clip-path,因为它是我们可以动画的属性,我们只需更新控制形状的自定义属性即可获得很酷的悬停效果。我们设置一个自定义变量控制默认的三角形的角度,在鼠标悬停时设置该变量为0则回到正常的正方形,效果图如下:

那些你不知道的 CSS 自定义形状网格布局_布局_17

在线代码如下:
 https://code.juejin.cn/pen/7133195476844675103

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

看到最后如果觉得有用,记得点个赞收藏起来吧,说不定哪天就用上啦。

专注前端开发,分享前端相关技术干货,公众号:南城大前端(ID: nanchengfe)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK