好的,所以 上次我们登记入住,我们使用 CSS Grid 并将它们与 CSS 结合 clip-path
和 mask
创建具有花哨形状的网格的技术。
这只是我们一起制作的奇妙网格之一:
准备好第二轮了吗? 我们仍在使用 CSS Grid, clip-path
及 mask
,但在本文结束时,我们将最终以不同的方式在网格上排列图像,包括一些 rad 悬停效果,这些效果可提供真实的交互式体验来查看图片。
你猜怎么着? 我们正在使用 我们上次使用的相同标记. 又是这样:
<div class="gallery">
<img src="..." alt="...">
<img src="..." alt="...">
<img src="..." alt="...">
<img src="..." alt="...">
<!-- as many times as we want -->
</div>
和上一篇一样,我们只需要一个里面有图片的容器。 而已!
嵌套图像网格
上次,我们的网格是典型的图像网格。 除了我们用它们掩盖的整洁形状外,就我们如何在内部放置图像而言,它们是非常标准的对称网格。
让我们尝试将图像嵌套在网格的中心:
我们首先为四个图像设置一个 2✕2 网格:
.gallery {
--s: 200px; /* controls the image size */
--g: 10px; /* controls the gap between images */
display: grid;
gap: var(--g);
grid-template-columns: repeat(2, auto);
}
.gallery > img {
width: var(--s);
aspect-ratio: 1;
object-fit: cover;
}
还没有什么复杂的。 下一步是剪掉我们图像的角落,为嵌套图像创建空间。 我已经有一篇详细的文章了 如何偷工减料 clip-path
和 mask
. 你也可以使用我的 在线生成器 获取用于遮蔽角落的 CSS。
我们这里需要的是以等于的角度切出角 90deg
. 我们可以使用相同的 圆锥梯度技术 从那篇文章开始:
.gallery > img {
mask: conic-gradient(from var(--_a), #0000 90deg, #000 0);
}
.gallery > img:nth-child(1) { --_a: 90deg; }
.gallery > img:nth-child(2) { --_a: 180deg; }
.gallery > img:nth-child(3) { --_a: 0deg; }
.gallery > img:nth-child(4) { --_a:-90deg; }
我们可以使用 clip-path
从同一篇文章中切角的方法,但是在这里使用渐变遮罩更合适,因为我们对所有图像都有相同的配置——我们需要的只是一个旋转(用变量定义 --_a
) 得到效果,所以我们从内部而不是外部边缘进行掩蔽。
现在我们可以将嵌套图像放置在遮罩空间内。 首先,让我们确保在 HTML 中有第五个图像元素:
<div class="gallery">
<img src="..." alt="...">
<img src="..." alt="...">
<img src="..." alt="...">
<img src="..." alt="...">
<img src="..." alt="...">
</div>
我们将依靠良好的绝对定位将其放置在那里:
.gallery > img:nth-child(5) {
position: absolute;
inset: calc(50% - .5*var(--s));
clip-path: inset(calc(var(--g) / 4));
}
inset
属性允许我们使用单个声明将图像放置在中心。 我们知道图像的大小(用变量定义 --s
),我们知道容器的大小等于 100%。 我们做一些数学运算,到每条边的距离应该等于 (100% - var(--s))/2
.
您可能想知道我们为什么要使用 clip-path
完全在这里。 我们将它与嵌套图像一起使用以获得一致的间隙。 如果我们要删除它,您会注意到我们在所有图像之间没有相同的间隙。 这样,我们从第五张图像中剪掉一点,以获得适当的间距。
再次完整代码:
.gallery {
--s: 200px; /* controls the image size */
--g: 10px; /* controls the gap between images */
display: grid;
gap: var(--g);
grid-template-columns: repeat(2, auto);
position: relative;
}
.gallery > img {
width: var(--s);
aspect-ratio: 1;
object-fit: cover;
mask: conic-gradient(from var(--_a), #0000 90deg, #000 0);
}
.gallery > img:nth-child(1) {--_a: 90deg}
.gallery > img:nth-child(2) {--_a:180deg}
.gallery > img:nth-child(3) {--_a: 0deg}
.gallery > img:nth-child(4) {--_a:-90deg}
.gallery > img:nth-child(5) {
position: absolute;
inset: calc(50% - .5*var(--s));
clip-path: inset(calc(var(--g) / 4));
}
现在,你们中的许多人可能还想知道:当我们可以将最后一张图片放在顶部并为其添加边框时,为什么会有这么复杂的东西呢? 这将隐藏嵌套图像下方的图像而没有蒙版,对吗?
这是真的,我们将得到以下信息:
没有 mask
,无 clip-path
. 是的,代码很容易理解,但是有个小缺点:边框颜色需要和主背景一样才能让错觉完美。 这个小缺点足以让我让代码更复杂,以换取独立于背景的真正透明。 我并不是说边界方法是坏的或错误的。 在大多数已知背景的情况下,我会推荐它。 但我们来这里是为了探索新事物,最重要的是,构建不依赖于环境的组件。
这次让我们尝试另一种形状:
这一次,我们将嵌套图像制作为圆形而不是正方形。 这是一个简单的任务 border-radius
但是我们需要使用一个 圆形切口 对于其他图像。 不过,这一次,我们将依靠 radial-gradient()
代替 conic-gradient()
以获得漂亮的圆形外观。
.gallery > img {
mask:
radial-gradient(farthest-side at var(--_a),
#0000 calc(50% + var(--g)/2), #000 calc(51% + var(--g)/2));
}
.gallery > img:nth-child(1) { --_a: calc(100% + var(--g)/2) calc(100% + var(--g)/2); }
.gallery > img:nth-child(2) { --_a: calc(0% - var(--g)/2) calc(100% + var(--g)/2); }
.gallery > img:nth-child(3) { --_a: calc(100% + var(--g)/2) calc(0% - var(--g)/2); }
.gallery > img:nth-child(4) { --_a: calc(0% - var(--g)/2) calc(0% - var(--g)/2); }
所有图像都使用与上一个示例相同的配置,但我们每次都更新中心点。
上图说明了每个圆的中心点。 不过,在实际代码中,您会注意到我还考虑了间隙,以确保所有点都在同一位置(网格的中心),如果我们将它们组合起来,就可以得到一个连续的圆。
现在我们有了布局,让我们来谈谈悬停效果。 如果您没有注意到,很酷的悬停效果会增加嵌套图像的大小并相应地调整其他所有内容。 增加大小是一项相对容易的任务,但更新渐变更复杂,因为默认情况下,渐变不能动画。 为了克服这个问题,我将使用 font-size
hack 能够为径向渐变设置动画。
如果您检查渐变的代码,您可以看到我正在添加 1em
:
mask:
radial-gradient(farthest-side at var(--_a),
#0000 calc(50% + var(--g)/2 + 1em), #000 calc(51% + var(--g)/2 + 1em));
据了解, em
单位是相对于父元素的 font-size
, 所以改变 font-size
的 .gallery
也会改变计算的 em
价值——这是我们使用的技巧。 我们正在制作动画 font-size
从一个值 0
到给定的值,结果,渐变是动画的,使剪切部分更大,跟随嵌套图像的大小越来越大。
这是突出显示悬停效果所涉及部分的代码:
.gallery {
--s: 200px; /* controls the image size */
--g: 10px; /* controls the gaps between images */
font-size: 0; /* initially we have 1em = 0 */
transition: .5s;
}
/* we increase the cut-out by 1em */
.gallery > img {
mask:
radial-gradient(farthest-side at var(--_a),
#0000 calc(50% + var(--g)/2 + 1em), #000 calc(51% + var(--g)/2 + 1em));
}
/* we increase the size by 2em */
.gallery > img:nth-child(5) {
width: calc(var(--s) + 2em);
}
/* on hover 1em = S/5 */
.gallery:hover {
font-size: calc(var(--s) / 5);
}
font-size
如果我们想要动画渐变或其他无法动画的属性,这个技巧很有帮助。 用@property 定义的自定义属性可以解决这样的问题,但是 支持它 在撰写本文时仍然缺乏。
我发现了 font-size
诡计来自 @SelenIT2 在尝试解决的同时 推特上的挑战.
另一种形状? 我们走吧!
这次我们将嵌套图像剪裁成菱形。 我将让您剖析代码作为练习,以弄清楚我们是如何到达这里的。 您会注意到结构与我们的示例中的相同。 唯一的区别是我们如何使用渐变来创建形状。 深入学习!
圆形图像网格
我们可以结合我们在这里和以前的文章中学到的东西来制作一个更令人兴奋的图像网格。 这一次,让我们将网格中的所有图像设为圆形,并在悬停时展开图像以显示整个图像,因为它覆盖了其余的照片。
网格的 HTML 和 CSS 结构在以前并不是什么新鲜事,所以让我们跳过这部分,而是专注于我们想要的圆形和悬停效果。
我们将使用 clip-path
以及 circle()
函数——你猜对了! — 从图像中剪出一个圆圈。
该图说明了 clip-path
用于第一张图片。 左侧显示图像的初始状态,而右侧显示悬停状态。 您可以使用 这个在线工具 播放和可视化 clip-path
值。
对于其他图像,我们可以更新圆心(70% 70%
) 得到以下代码:
.gallery > img:hover {
--_c: 50%; /* same as "50% at 50% 50%" */
}
.gallery > img:nth-child(1) {
clip-path: circle(var(--_c, 55% at 70% 70%));
}
.gallery > img:nth-child(2) {
clip-path: circle(var(--_c, 55% at 30% 70%));
}
.gallery > img:nth-child(3) {
clip-path: circle(var(--_c, 55% at 70% 30%));
}
.gallery > img:nth-child(4) {
clip-path: circle(var(--_c, 55% at 30% 30%));
}
注意我们是如何定义 clip-path
值作为内部的后备 var()
. 这种方式允许我们通过设置 --_c
多变的。 使用时 circle()
, 中心点的默认位置是 50% 50%
,所以我们可以省略它以获得更简洁的代码。 这就是为什么你看到我们只是设置 50%
而不是 50% at 50% 50%
.
然后我们将悬停时图像的大小增加到网格的整体大小,以便我们可以覆盖其他图像。 我们还确保 z-index
在悬停的图像上具有更高的价值,因此它是我们的顶部 堆叠上下文.
.gallery {
--s: 200px; /* controls the image size */
--g: 8px; /* controls the gap between images */
display: grid;
grid: auto-flow var(--s) / repeat(2, var(--s));
gap: var(--g);
}
.gallery > img {
width: 100%;
aspect-ratio: 1;
cursor: pointer;
z-index: 0;
transition: .25s, z-index 0s .25s;
}
.gallery > img:hover {
--_c: 50%; /* change the center point on hover */
width: calc(200% + var(--g));
z-index: 1;
transition: .4s, z-index 0s;
}
.gallery > img:nth-child(1){
clip-path: circle(var(--_c, 55% at 70% 70%));
place-self: start;
}
.gallery > img:nth-child(2){
clip-path: circle(var(--_c, 55% at 30% 70%));
place-self: start end;
}
.gallery > img:nth-child(3){
clip-path: circle(var(--_c, 55% at 70% 30%));
place-self: end start;
}
.gallery > img:nth-child(4){
clip-path: circle(var(--_c, 55% at 30% 30%));
place-self: end;
}
发生了什么事
place-self
财产? 为什么我们需要它,为什么每个图像都有特定的价值?
你还记得我们在上一篇文章中遇到的问题吗? 创建拼图网格? 我们增加了图像的大小以创建溢出,但某些图像的溢出是不正确的。 我们使用 place-self
属性。
这里同样的问题。 我们正在增加图像的大小,因此每个图像都会溢出其网格单元。 但是如果我们什么都不做,它们都会在网格的右侧和底部溢出。 我们需要的是:
- 第一个溢出右下边缘的图像(默认行为),
- 第二张图片溢出左下角,
- 第三张图片溢出右上角,以及
- 溢出左上边缘的第四张图像。
为此,我们需要使用 place-self
属性。
如果你不熟悉 place-self
, 是的简写 justify-self
和 align-self
水平和垂直放置元素。 当它取一个值时,两个对齐使用相同的值。
扩展图像面板
在上一篇文章中,我创建了一个很酷的缩放效果,适用于图像网格,我们可以在其中控制一切:行数、列数、大小、比例因子等。
一个特殊的例子是经典的扩展面板,我们只有一排和一个全宽的容器。
我们将举这个例子并将其与形状结合起来!
在我们继续之前,我强烈建议阅读我的 其他文章 了解我们将要介绍的技巧是如何工作的。 检查一下,我们将继续在这里专注于创建面板形状。
首先,让我们从简化代码和删除一些变量开始
我们只需要一行,列数应该根据图像的数量进行调整。 这意味着我们不再需要行数的变量(--n
) 和列 (--m
) 但我们需要使用 grid-auto-flow: column
,允许网格在我们添加新图像时自动生成列。 我们会为我们的容器考虑一个固定的高度; 默认情况下,它将是全角的。
让我们将图像剪辑成倾斜的形状:
再一次,每个图像都包含在其网格单元中,因此图像之间的空间比我们想要的要大:
我们需要增加图像的宽度以创建重叠。 我们更换 min-width:
100%
min-width: calc(100% + var(--s))
,其中 --s
是一个控制形状的新变量。
现在我们需要修复第一张和最后一张图片,这样它们就会从页面上流下来而没有间隙。 换句话说,我们可以去除第一张图像左侧的倾斜和最后一张图像右侧的倾斜。 我们需要一个新的 clip-path
专门针对这两个图像。
我们还需要纠正溢出。 默认情况下,所有图像都会在两侧溢出,但是对于第一个图像,我们需要在右侧溢出,而对于最后一个图像,我们需要向左溢出。
.gallery > img:first-child {
min-width: calc(100% + var(--s)/2);
place-self: start;
clip-path: polygon(0 0,100% 0,calc(100% - var(--s)) 100%,0 100%);
}
.gallery > img:last-child {
min-width: calc(100% + var(--s)/2);
place-self: end;
clip-path: polygon(var(--s) 0,100% 0,100% 100%,0 100%);
}
最终结果是一个很好的扩展倾斜图像面板!
我们可以根据需要添加任意数量的图像,并且网格会自动调整。 另外,我们只需要控制一个值来控制形状!
我们可以使用 flexbox 进行相同的布局,因为我们正在处理单行元素。 这是 我的实现.
当然,倾斜的图像很酷,但是锯齿形图案呢? 我已经在 最后一篇文章的结尾.
我在这里所做的只是替换 clip-path
mask
… 你猜怎么着? 我已经有一篇详细的文章了 创造那个之字形 - 更不用说在线 生成器获取代码. 看看所有东西是如何结合在一起的?
这里最棘手的部分是确保之字形完全对齐,为此,我们需要为每个 :nth-child(odd)
图像元素。
.gallery > img {
mask:
conic-gradient(from -135deg at right, #0000, #000 1deg 89deg, #0000 90deg)
100% calc(50% + var(--_p, 0%))/51% calc(2*var(--s)) repeat-y,
conic-gradient(from 45deg at left, #0000, #000 1deg 89deg, #0000 90deg)
0% calc(50% + var(--_p, 0%))/51% calc(2*var(--s)) repeat-y;
}
/* we add an offset to the odd elements */
.gallery > img:nth-child(odd) {
--_p: var(--s);
}
.gallery > img:first-child {
mask:
conic-gradient(from -135deg at right, #0000, #000 1deg 89deg, #0000 90deg)
0 calc(50% + var(--_p, 0%))/100% calc(2*var(--s));
}
.gallery > img:last-child {
mask:
conic-gradient(from 45deg at left, #0000, #000 1deg 89deg, #0000 90deg)
0 calc(50% + var(--_p, 0%)) /100% calc(2*var(--s));
}
注意使用 --_p
变量,它将回退到 0%
但将等于 --_s
对于奇怪的图像。
这是一个演示该问题的演示。 悬停以查看偏移量 - 由 --_p
— 正在修复对齐方式。
另外,请注意我们如何为第一张和最后一张图像使用不同的蒙版,就像我们在前面的示例中所做的那样。 我们只需要在第一张图像的右侧和最后一张图像的左侧有一个之字形。
为什么不圆边? 我们开始做吧!
我知道代码可能看起来很吓人且难以理解,但所发生的一切都是我们在这篇文章和我已经分享的其他文章中介绍的不同技巧的组合。 在这种情况下,我使用与之字形和倾斜形状相同的代码结构。 将其与这些示例进行比较,您会发现没有区别! 这些都是相同的技巧 我之前关于缩放效果的文章. 然后,我正在使用我的 其他写作 和 我的在线生成器 获取创建这些圆形形状的蒙版的代码。
如果您还记得我们为之字形所做的事情,我们对所有图像都使用了相同的蒙版,但随后必须为奇数图像添加偏移量以创建完美的重叠。 在这种情况下,我们需要为奇数图像使用不同的掩码。
第一个面具:
mask:
linear-gradient(-90deg,#0000 calc(2*var(--s)),#000 0) var(--s),
radial-gradient(var(--s),#000 98%,#0000) 50% / calc(2*var(--s)) calc(1.8*var(--s)) space repeat;
第二个:
mask:
radial-gradient(calc(var(--s) + var(--g)) at calc(var(--s) + var(--g)) 50%,#0000 98% ,#000)
calc(50% - var(--s) - var(--g)) / 100% calc(1.8*var(--s))
我在这里所做的唯一努力是更新第二个掩码以包含间隙变量(--g
) 在图像之间创建该空间。
最后一步是修复第一个和最后一个图像。 与前面的所有示例一样,第一个图像需要一个直的左边缘,而最后一个图像需要一个直的右边缘。
对于第一张图像,我们总是知道它需要具有的掩码,如下所示:
.gallery > img:first-child {
mask:
radial-gradient(calc(var(--s) + var(--g)) at right, #0000 98%, #000) 50% / 100% calc(1.8 * var(--s));
}
对于最后一张图像,它取决于元素的数量,因此该元素是否是重要的 :nth-child(odd)
or :nth-child(even)
.
.gallery > img:last-child:nth-child(even) {
mask:
linear-gradient(to right,#0000 var(--s),#000 0),
radial-gradient(var(--s),#000 98%,#0000) left / calc(2*var(--s)) calc(1.8*var(--s)) repeat-y
}
.gallery > img:last-child:nth-child(odd) {
mask:
radial-gradient(calc(var(--s) + var(--g)) at left,#0000 98%,#000) 50% / 100% calc(1.8*var(--s))
}
就这样! 三种不同的布局,但每次使用相同的 CSS 技巧:
- 创建缩放效果的代码结构
- 用于创建形状的蒙版或剪辑路径
- 在某些情况下为奇数元素单独配置,以确保我们有完美的重叠
- 第一个和最后一个图像的特定配置,以仅在一侧保持形状。
这是一个大型演示,所有这些都放在一起。 您只需要添加一个类来激活您想要查看的布局。
这是具有 Flexbox 实现的那个
结束了
哦,我们完成了! 我知道在这篇文章和上一篇文章之间有很多 CSS 技巧和示例,更不用说我在其他文章中引用的所有其他技巧了。 我花了一些时间把所有东西放在一起,而且你不必一下子理解所有东西。 一次阅读将使您对所有布局有一个很好的概述,但您可能需要多次阅读这篇文章并专注于每个示例以掌握所有技巧。
您是否注意到除了标记中的图像数量之外,我们根本没有触及 HTML? 我们制作的所有布局都共享相同的 HTML 代码,它只是一个图像列表。
在我结束之前,我将给你留下最后一个例子。 这是两个具有酷炫悬停效果的动漫角色之间的“对战”。
那你呢? 你能根据你学到的东西创造一些东西吗? 它不需要很复杂——想象一些很酷或有趣的东西,就像我对那个动漫对决所做的那样。 这对你来说可能是一个很好的练习,我们可能会在评论部分以一个优秀的集合结束。