CSS Grid布局搭配clip-path和mask遮罩,能整出不少花里胡哨的图片网格效果。之前玩过基础版,这次咱们继续深挖,搞点嵌套图片、圆形裁剪、悬停缩放的高级活儿。全程只用同一个HTML结构:一个div容器里塞一堆img标签,别的啥也不用。
纯CSS Grid加遮罩技术,让普通图片网格变身嵌套中心图、圆形互叠、斜切扩展面板等炫酷形态。通过
clip-path和mask裁剪边角,结合position: absolute居中放置第五张图,用font-size动画实现渐变动态变化。圆形网格利用circle()函数和place-self控制溢出方向,实现悬停放大覆盖全组。斜切面板系列通过grid-auto-flow: column自动生成列,用min-width制造重叠,首尾图片单独处理边缘。所有案例共用同一套HTML,代码可复制粘贴直接玩。
网格里套张小图
四个角图裁掉内角,中间再塞个第五张图。先搭个2×2网格:
.gallery {
--size: 200px; /* 图片尺寸 */
--gap: 10px; /* 间隙 */
display: grid;
gap: var(--gap);
grid-template-columns: repeat(2, auto);
position: relative;
}
.gallery > img {
width: var(--size);
aspect-ratio: 1;
object-fit: cover;
}裁剪四张图的角落,用conic-gradient做遮罩。每个图的旋转角度不一样:
.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; } /* 左下角 */第五张图用绝对定位甩到正中间。计算位置:上下左右各留出(100% - 图片宽)/2的距离。inset属性一步到位:
.gallery > img:nth-child(5) {
position: absolute;
inset: calc(50% - 0.5 * var(--size));
clip-path: inset(calc(var(--gap) / 4));
}为啥还要加clip-path?如果不加,第五张图和周围四张图的间隙对不齐,看着像贴歪了。用inset切掉一圈小边边,间隙就统一了。这招比给第五张图加边框更干净——边框颜色得跟背景色一致才不穿帮,但用遮罩就完全透明,换个深色背景照样完美。
把中间图变成圆形的
把第五张图改成圆形,四个角的图也要配合挖出圆弧边。这时用radial-gradient做遮罩:
.gallery > img {
mask: radial-gradient(farthest-side at var(--_a),
#0000 calc(50% + var(--gap)/2), #000 calc(51% + var(--gap)/2));
}
.gallery > img:nth-child(1) { --_a: calc(100% + var(--gap)/2) calc(100% + var(--gap)/2); }
.gallery > img:nth-child(2) { --_a: calc(0% - var(--gap)/2) calc(100% + var(--gap)/2); }
.gallery > img:nth-child(3) { --_a: calc(100% + var(--gap)/2) calc(0% - var(--gap)/2); }
.gallery > img:nth-child(4) { --_a: calc(0% - var(--gap)/2) calc(0% - var(--gap)/2); }每个图圆的中心点分别指向四个角,组合起来正好是一个大圆。悬停时让中间图放大,四周图的圆弧也跟着扩大。但渐变不能直接做动画,咋办?用font-size hack。把遮罩里的尺寸加上1em,然后悬停时改变父容器的font-size:
.gallery {
font-size: 0;
transition: 0.5s;
}
.gallery > img {
mask: radial-gradient(farthest-side at var(--_a),
#0000 calc(50% + var(--gap)/2 + 1em), #000 calc(51% + var(--gap)/2 + 1em));
}
.gallery > img:nth-child(5) {
width: calc(var(--size) + 2em);
}
.gallery:hover {
font-size: calc(var(--size) / 5);
}1em的值从0变成size/5,渐变范围就跟着变大了。这招比@property兼容性好多了,不用等浏览器更新。
四图互叠圆形网格
这回所有图片都是圆形,鼠标滑过哪张,哪张就放大盖住其他三张。先搭网格,每格尺寸固定:
.gallery {
--size: 200px;
--gap: 8px;
display: grid;
grid: auto-flow var(--size) / repeat(2, var(--size));
gap: var(--gap);
}
.gallery > img {
width: 100%;
aspect-ratio: 1;
cursor: pointer;
z-index: 0;
transition: 0.25s, z-index 0s 0.25s;
}用clip-path: circle()切出圆形,每个图的圆心位置不同:
.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%)); }悬停时把圆心统一改成50%(即circle(50%)),同时图片宽度变成200%加间隙,z-index拉高:
.gallery > img:hover {
--_c: 50%;
width: calc(200% + var(--gap));
z-index: 1;
transition: 0.4s, z-index 0s;
}这里有个大坑:图片放大后会溢出网格,但默认全部往右下角溢。第一张图需要往右下溢,第二张往左下,第三张往右上,第四张往左上。用place-self控制每个图的对齐方式:
.gallery > img:nth-child(1) { place-self: start; } /* 左上角,溢右下 */
.gallery > img:nth-child(2) { place-self: start end; } /* 右上角,溢左下 */
.gallery > img:nth-child(3) { place-self: end start; } /* 左下角,溢右上 */
.gallery > img:nth-child(4) { place-self: end; } /* 右下角,溢左上 */place-self是justify-self和align-self的合体。一个值管两个方向。这么一搞,悬停时图片就能准确覆盖相邻格子,不跑偏。
一行斜切扩展面板
做一行图片,每张切成平行四边形,鼠标滑过时展开。这回用grid-auto-flow: column让列数自动适配图片数量:
.gallery {
display: grid;
grid-auto-flow: column;
gap: 0; /* 间隙靠重叠制造 */
height: 300px;
}
.gallery > img {
width: 100%;
object-fit: cover;
clip-path: polygon(10% 0%, 100% 0%, 90% 100%, 0% 100%);
transition: 0.5s;
cursor: pointer;
}为了制造重叠,把图片最小宽度改成calc(100% + var(--slant)),--slant控制斜切偏移量。然后悬停时放大宽度:
.gallery > img:hover {
width: 200%;
}第一张图的左边不能切,最后一张图的右边不能切,否则边缘会有缺口。单独写规则:
.gallery > img:first-child {
min-width: calc(100% + var(--slant)/2);
place-self: start;
clip-path: polygon(0 0, 100% 0, calc(100% - var(--slant)) 100%, 0 100%);
}
.gallery > img:last-child {
min-width: calc(100% + var(--slant)/2);
place-self: end;
clip-path: polygon(var(--slant) 0, 100% 0, 100% 100%, 0 100%);
}place-self: start让第一张图左对齐,溢出向右;place-self: end让最后一张右对齐,溢出向左。这样边缘就齐整了,不会露出一截空白。
锯齿边和圆凸边面板
把斜切换成锯齿形,用mask而不是clip-path。锯齿形状需要两张图交替偏移才能完美咬合:
.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;
}
.gallery > img:nth-child(odd) {
--_p: var(--s);
}奇数张图加一个垂直偏移--_p,让上下锯齿错开,拼起来严丝合缝。首尾图同样单独处理,只保留一侧锯齿。
圆凸边面板更复杂,需要radial-gradient画半圆凸起。偶数图和奇数图用不同的mask:
/* 偶数图 */
.gallery > img:nth-child(even) {
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;
}
/* 奇数图 */
.gallery > img:nth-child(odd) {
mask:
radial-gradient(calc(var(--s) + var(--gap)) at calc(var(--s) + var(--gap)) 50%, #0000 98%, #000)
calc(50% - var(--s) - var(--gap)) / 100% calc(1.8*var(--s));
}首图左边平直,末图右边平直,且末图的奇偶性影响具体mask写法。代码虽然长,但套路都一样:基础网格+遮罩形状+首尾特调+奇偶偏移。
整套玩下来,HTML纹丝不动,全是CSS在变戏法。这种搞法就像乐高积木,同一个底座换不同的拼装说明书,就能变出完全不一样的造型。试试改改--size和--gap,或者把斜切角度从10度改成30度,图片墙的气质立马不一样。
