本文翻译自 CSS Grid and Custom Shapes, Part 3,略有删改
原作者:Temani Afif
基于CSS自定义网格已发布第一部分和第二部分,本文将开始第三部分探讨更多的奇特自定义形状,与之前的文章一样,核心还是基于CSS grid,clip 和 mask来自定义网格形状布局。
CSS网格和自定义形状系列
第3部分(当前文章)
三部分文章不一定要按照顺序阅读,之间没有强制的关联关系,但是可以看看之前的文章,了解作者是如何完成当前的效果,接下来开始我们的第一个案例。
案例一 图片库
以上效果图我们的HTML代码如下:
<div class="gallery"> <img src="..." alt="..."> <img src="..." alt="..."> <img src="..." alt="..."> <img src="..." alt="..."> </div>
本系列的主要挑战都是尽可能使用少量的html标签,后续的示例也是使用相同的html代码,不会有其他的div,嵌套元素等,仅需要如上的代码结构。 然后再来看看CSS代码:
.gallery { --g: 6px; /* the gap */ display: grid; width: 450px; /* the size */ aspect-ratio: 1; /* equal height */ grid: auto-flow 1fr / repeat(3, 1fr); gap: var(--g); } .gallery img:nth-child(2) { grid-area: 1 / 2 / span 2 / span 2; } .gallery img:nth-child(3) { grid-area: 2 / 1 / span 2 / span 2; }
基于上面的CSS是可以看出把整个盒子分为了一个3 ✖️ 3 的网格,第二个和第三个图像设定了相应的网格属性,第一个和最后一个图像将自动围绕布局。
这里的核心代码是 grid: auto-flow 1fr / repeat(3, 1fr);
,这里使用了CSS网格简写属性,相当于以下代码:
grid-template-columns: repeat(3, 1fr); grid-auto-rows: 1fr;
简写属性对应每个属性的值可以通过浏览器DevTools展开看到以下数据。
此同样适用于网格区域属性,当我们定义CSS网格面积为:
grid-area: 1 / 2 / span 2 / span 2;
最终对应的具体属性值如下:
grid-row-start: 1; /* 从第一行开始 */ grid-column-start: 2; /* 从第二列开始 */ grid-row-end: span 2; /* 占据两行 */ grid-column-end: span 2; /* 占据两列 */
基于以上网格区域声明,即得到以下结果:
我们的第二张和第三张图像在中间重叠,这是故意将它们重叠在一起,这样就可以使用 clip-path,从两个图像中裁剪出一部分,得到最终的效果:
们可以使用CSS clip-path 属性裁剪第二张图片(img:nth-child(2))的左下角:
clip-path: polygon(0 0, 100% 0, 100% 100%, calc(50% + var(--g) / 4) 100%, 0 calc(50% - var(--g) / 4))
裁剪第三张图片(img:nth-child(3))的右上角:
clip-path: polygon(0 0, calc(50% - var(--g) / 4) 0, 100% calc(50% + var(--g) / 4), 100% 100%, 0 100%);
如果不理解 clip-path 的用法,我有一篇文章详细介绍如何使用这种技术。
案例二 图片分割效果
接下来我们基于上面用到的裁剪图片技术结合鼠标悬停完成一个不错的交互效果。
这个效果的网格配置没有上一张强烈,因为我们只需要两张重叠的图像:
.gallery { display: grid; } .gallery > img { grid-area: 1 / 1; width: 350px; /* the size */ aspect-ratio: 1; /* equal height */ }
悬停效果依赖于动画剪辑路径。我们将详细剖析第一个图像的代码,看看它是如何实现的,然后用更新的值将相同的代码应用到第二个图像。仔细分析可以得出整个动画过程中出现有三种不同的状态:
- 当图像没有鼠标悬停时,将显示每个图像的一半。
- 当鼠标悬停在第一张图像上时,它会更完全地显示出来,但会保留一个小角。
- 当鼠标悬停在第二张图片上时,第一张图片只有一个小三角形可见。
在每种状态下,我们都有一个三角形。这意味着我们需要一个三点多边形作为clip-path的值。
什么?你可能有疑问,第二个状态不是三角形,而是一个带有切角的正方形。
肉眼看确实是一个带有切角的正方形,但如果我们仔细看,我们可以看到一个“隐藏”的三角形,给图像添加一个方框阴影的效果就比较清晰了。
box-shadow: 0 0 0 200px red;
这是什么原理呢?原理就是 clip-path 接受0%-100%范围之外的值,这将允许我们创建“溢出”元素本身的形状。这样我们只需要使用三个点,而不是使用五个点,就可以完成我们期望的效果。最终图片的 clip-path 属性值如下所示:
.gallery > img:first-child { clip-path: polygon(0 0, calc(100% + var(--_p)) 0 , 0 calc(100% + var(--_p))) } .gallery > img:last-child { clip-path: polygon(100% 100%, 100% calc(0% - var(--_p)), calc(0% - var(--_p)) 100%) }
这里增加了--_p
变量,即两图之间的间隙距离。基于此来优化代码,因为我们添加了悬停过渡。我们不需要更新整个clip-path,而只更新此变量即可以获得动画效果。 最后是增加悬停后的完整代码:
.gallery { --g: 8px; /* the gap */ } .gallery > img { /* etc. */ --_p: calc(-1 * var(--g)); transition: .4s .1s; } .gallery:hover > img:last-child, .gallery:hover > img:first-child:hover{ --_p: calc(50% - var(--g)); } .gallery:hover > img:first-child, .gallery:hover > img:first-child:hover + img { --_p: calc(-50% - var(--g)); }