菱形遮罩这需求,十有八九是设计师从某个国外酷站上截下来的参考图,原站用的是webgl,你要是照着用canvas硬怼,工作量直接起飞。但如果你只想要"鼠标移上去,图片从一个六边形或者菱形逐渐展开"这个视觉结果,CSS3的clip-path就能干完八成的事,而且代码量少得惊人。这篇文章就把我从零到一实现这个效果的过程、踩过的坑、以及怎么把它扩展成一套可复用的组件,完整记录下来。
先给结论:核心就三个词——clip-path裁剪、transition过渡、transform微动效。文章末尾我会贴一份可以直接拿去改的源码,但建议你先跟着思路走一遍,不然遇到兼容性报错你都不知道去哪排查。
1. 从设计稿到实现:为什么选clip-path而不是canvas
1.1 需求还原:那张让前端头疼的设计稿
拿到的设计稿通常长这样:页面中间有一排商品卡或者团队头像,默认状态下是圆形或者圆角矩形,鼠标悬停上去以后,图片以菱形的轮廓从中心向外"撑开",同时浮出一层半透明的遮罩,上面显示文字说明。有的版本还带一点旋转,看起来像钻石切面在转动。
大部分前端的第一反应是:这得用canvas画吧?或者找个现成的hover动画库?再或者准备八张不同形状的图片做序列帧?这些思路都太重了。canvas的问题在于,图片的响应式布局、懒加载、SEO语义全部要重新处理,为了一个hover效果引入整个渲染管线,性价比极低。动画库的问题在于,你为了一个clip-path效果引入几十KB的依赖,然后发现它内部也是用CSS实现的,何必呢。
clip-path是CSS3里专门干"把元素裁剪成任意形状"这个活的属性。它不会修改元素的DOM结构,也不会影响图片的加载方式,只是在渲染层面把可视区域框定成你指定的多边形。这意味着你可以直接在<img>标签上照着写样式,图片懒加载、alt属性、响应式尺寸全部照常工作,不需要任何额外适配。
1.2 clip-path裁剪逻辑:先把形状搞明白
clip-path最常用的取值是polygon(),它接收一组坐标点,这些点构成一个多边形区域,区域外的部分全部透明隐藏。坐标系的原点在元素的左上角,0% 0%是左上角,100% 100%是右下角。
菱形本质上是四个中点连起来的正方形,所以坐标就是上边中点、右边中点、下边中点、左边中点,按顺时针写就是:
css复制clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
如果你想要一个"竖着的菱形",也就是把尖角朝上和朝下,用上面的坐标就行。如果想要"横着的菱形",也就是尖角朝左朝右,把坐标改成:
css复制clip-path: polygon(0% 50%, 50% 100%, 100% 50%, 50% 0%);
这两种在视觉上的差异非常明显,设计稿里如果画的是"旋转45度的方块",那就要用第一种,因为正方形旋转45度以后,四个尖角确实落在四条边的中点上。如果设计稿里的菱形本来就长得比较扁长,那就直接调坐标百分比,把X轴或者Y轴的距离拉开。
1.3 关键认知:裁剪区域会跟随元素尺寸自动缩放
这里有个小白特别容易忽略的点:polygon()里的百分比是相对于元素自身的宽高计算的。所以同一个clip-path样式,用在300px宽的图片和用在800px宽的图片上,形状比例会自动保持一致。这就是为什么用CSS做这种效果比用canvas画图更方便——你不需要在JavaScript里监听resize事件重新计算坐标,浏览器全部帮你处理好了。
不过这也带来一个反向的坑:如果图片用了object-fit: cover来截取显示区域,那么clip-path裁剪的是<img>这个元素盒子,而不是图片的实际内容。图片内容是居中裁剪的,菱形框出来的区域,实际上是元素盒子落在图片上的那一块。绝大多数情况下这反而更合理,因为你要的是"一块菱形的完整图片",而不是"图片的菱形局部放大"。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 手写第一个菱形悬停效果:从静态裁剪到动态展开
2.1 最简HTML结构:不需要包三层div
很多教程会告诉你hover效果要包三层结构:外层控制溢出隐藏、中间层控制旋转、内层放图片。那是老思路,用transform: rotate()实现菱形才需要那套结构,因为旋转会带动布局溢出。用clip-path完全不需要,一个<a>或者<figure>直接包上<img>就完事。
html复制<div class="card">
<a href="#" class="diamond-wrap">
<img src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=600" alt="山景摄影作品" class="diamond-img" />
<div class="diamond-mask">
<p>群山之巅</p>
<span>点击查看详情</span>
</div>
</a>
</div>
.diamond-mask是悬停以后浮现的文字层,它默认是透明的,hover以后透明度变成1。这里有一个非常关键的布局细节:.diamond-mask需要用绝对定位铺满整个.diamond-wrap,并且设置clip-path和图片保持一致,否则会出现文字层是矩形、图片是菱形的割裂感。
2.2 CSS核心:三个状态讲清楚
css复制.diamond-wrap {
position: relative;
display: block;
width: 100%;
height: 0;
padding-bottom: 100%; /* 关键:用padding撑起正方形区域 */
overflow: hidden;
}
.diamond-img,
.diamond-mask {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
transition: clip-path 0.45s ease, transform 0.45s ease, opacity 0.45s ease;
}
.diamond-mask {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: rgba(20, 24, 30, 0.65);
color: #fff;
opacity: 0;
}
.diamond-wrap用height: 0; padding-bottom: 100%这个经典技巧撑出一个正方形区域。为什么不用aspect-ratio?因为老项目要考虑浏览器兼容,aspect-ratio虽然现在支持度已经很好了,但是在一些老版本移动端浏览器里头还是容易出问题。padding-bottom: 100%是上古时代流传下来的方案,稳如老狗。
悬停状态的样式:
css复制.diamond-wrap:hover .diamond-img,
.diamond-wrap:hover .diamond-mask {
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
}
.diamond-wrap:hover .diamond-mask {
opacity: 1;
}
.diamond-wrap:hover .diamond-img {
transform: scale(1.08);
}
这段代码就是整个效果的核心逻辑。默认状态是菱形,hover以后裁剪区域从菱形变成完整矩形,视觉上就是菱形"撑开"成整张图。同时图片加了一个scale(1.08)的轻微放大,避免形状变化时产生边缘露白的尴尬,也让整个动效更有细节感。文字层则是从opacity: 0渐变到opacity: 1,在形状展开过程中慢慢浮现。
2.3 为什么裁剪形状能平滑过渡:多边形补间动画原理
有人会问:clip-path不是不支持动画吗?这里有个容易混淆的点。clip-path的polygon()在特定条件下是可以过渡的,条件是:裁剪前后多边形的顶点数量必须一致,而且浏览器需要能够建立顶点与顶点之间的对应关系。
从菱形的四个顶点(50% 0%、100% 50%、50% 100%、0% 50%)过渡到矩形的四个顶点(0% 0%、100% 0%、100% 100%、0% 100%),顶点数量都是4个,浏览器会把第一个坐标平滑插值到第一个坐标,第二个到第二个,以此类推。于是你看到的就是菱形逐渐拉开变成矩形的过程。
但是如果你试图从一个三角形(3个顶点)过渡到一个矩形(4个顶点),浏览器没法在3和4之间做插值,过渡就会直接断掉——元素会从三角形瞬间变成矩形,没有中间过程。这个规则同样适用于circle()到polygon()的过渡,也是不行的。凡是顶点数不一致的,一律不能平滑过渡,这是CSS动画里一个很硬性的规则。
2.4 完整源码:可以直接跑的版本
为了方便你对照,我把上面零散的代码整理成一个文件。不需要任何依赖,新建一个HTML文件粘贴进去就能直接在浏览器里打开看效果:
html复制<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CSS3菱形遮罩悬停展示</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
background: #f0f0f0;
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}
.card {
width: 360px;
max-width: 90vw;
}
.diamond-wrap {
position: relative;
display: block;
width: 100%;
height: 0;
padding-bottom: 100%;
overflow: hidden;
border-radius: 8px;
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.15);
}
.diamond-img,
.diamond-mask {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
transition:
clip-path 0.5s cubic-bezier(0.4, 0, 0.2, 1),
transform 0.5s cubic-bezier(0.4, 0, 0.2, 1),
opacity 0.5s ease;
}
.diamond-mask {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 6px;
background: rgba(17, 24, 39, 0.68);
color: #fff;
text-align: center;
opacity: 0;
z-index: 2;
}
.diamond-mask p {
font-size: 22px;
font-weight: 600;
letter-spacing: 2px;
}
.diamond-mask span {
font-size: 14px;
opacity: 0.85;
}
.diamond-wrap:hover .diamond-img,
.diamond-wrap:hover .diamond-mask {
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
}
.diamond-wrap:hover .diamond-mask {
opacity: 1;
}
.diamond-wrap:hover .diamond-img {
transform: scale(1.08);
}
</style>
</head>
<body>
<div class="card">
<a href="#" class="diamond-wrap">
<img
src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=600"
alt="山景摄影作品"
class="diamond-img"
/>
<div class="diamond-mask">
<p>群山之巅</p>
<span>查看详情</span>
</div>
</a>
</div>
</body>
</html>
这段代码在Chrome、Edge、Firefox、Safari的现代版本上都能正常看到完整效果。如果你的浏览器比较老,需要加前缀,往后看兼容性那部分。
3. 从单一卡片到网格画廊:把菱形玩出花
3.1 多卡片布局:避开百分比坐标的陷阱
单一卡片的效果只是开胃菜,真正上项目是要做一整个网格。四张图、六张图、八张图排列,每一张悬停都有菱形展开的效果。这时候你要注意的第一件事,就是padding-bottom: 100%在网格容器里可能会出现意料之外的宽度计算问题。
举个例子,如果你用display: grid; grid-template-columns: repeat(3, 1fr),网格项目会自动拉伸填满列宽,此时.diamond-wrap的宽度由grid决定,padding-bottom: 100%会基于这个宽度撑出等高的正方形,这是正常的。但如果你用flex布局,.diamond-wrap作为flex子项时,width: 100%的解析规则在部分情况下会失效,导致宽度被内容撑开而不是填满容器,这时候正方形比例就乱了。
我的建议是,做网格画廊直接用grid,省心,每列比例完全可控。
3.2 菱形堆叠:默认菱形、悬停展开、再叠一层小菱形
有一种更复杂的变体,设计师会要求:页面默认显示一个菱形,悬停以后菱形稍微放大,同时内部出现一个更小的菱形,小菱形里面是文字。这种一层套一层的效果,用纯CSS也能实现。
思路是:外层容器设置比较大的菱形clip-path,内层再放一个元素,设置成小一号的菱形clip-path,默认状态下小菱形不可见,hover以后小菱形从中心缩放出现。注意,内层元素的clip-path也是百分比坐标,但是它的百分比是相对于它自身的,而它自身的尺寸如果设置成百分比,那就会自动跟外层对齐。
css复制.card-inner {
position: absolute;
inset: 12%; /* 让内层元素比外层小一圈 */
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
background: rgba(255, 255, 255, 0.18);
backdrop-filter: blur(4px);
opacity: 0;
transform: scale(0.65);
transition: opacity 0.4s ease, transform 0.4s ease;
display: flex;
align-items: center;
justify-content: center;
}
.diamond-wrap:hover .card-inner {
opacity: 1;
transform: scale(1);
}
这里有个值得玩味的小细节:clip-path裁剪出来的区域,默认不会影响元素内部的子元素布局,但是如果子元素超出裁剪区域,就会被裁掉。所以内层元素放文字的时候,要保证文字在裁剪区域内部能放下,一般配合text-align: center和padding使用。
3.3 不是只有菱形:多边形坐标变换速查表
很多人学会了菱形就以为clip-path只能做菱形,实际上它还能做三角形、六边形、八角形、斜切角矩形,甚至星形。悬停展示的效果完全可以用在任意形状上。核心逻辑不变:默认状态用某个多边形,hover以后过渡到完整矩形。唯一需要改的就是坐标点。
我整理一份可以直接抄的坐标速查表,你把clip-path的值替换就能得到不同的默认形状。
| 形状 | 坐标值 | 适用场景 |
|---|---|---|
| 竖向菱形 | polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%) |
经典钻石切面、人物头像 |
| 横向菱形 | polygon(0% 50%, 50% 100%, 100% 50%, 50% 0%) |
横向拉长的卡片视觉效果 |
| 等边六边形 | polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%) |
蜂巢排列、团队展示 |
| 三角形 | polygon(50% 0%, 100% 100%, 0% 100%) |
方向指示、页脚装饰 |
| 五边形 | polygon(50% 0%, 100% 38%, 82% 100%, 18% 100%, 0% 38%) |
徽章、勋章样式 |
| 切角矩形 | polygon(0% 0%, 80% 0%, 100% 20%, 100% 100%, 20% 100%, 0% 80%) |
现代感卡片、杂志风格 |
有一点你要有心理准备:不是所有多边形切换到矩形都能保持相同的顶点数。比如六边形有6个顶点,矩形只有4个,直接过渡会断掉。处理办法是给矩形也写6个顶点,把多出来的两个顶点放在边上重合的位置。举例来说,六边形到矩形可以这样写:
css复制/* 默认:六边形 */
clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
/* hover:视觉上接近矩形的六边形 */
clip-path: polygon(0% 0%, 100% 0%, 100% 50%, 100% 100%, 0% 100%, 0% 50%);
仔细看,hover状态六个顶点分别是左上、右上、右中、右下、左下、左中,连起来已经是一个完整的矩形了。浏览器在插值的时候,会把这个"假矩形"和六边形做平滑过渡,视觉效果就是从六边形慢慢变成一个矩形。这种"顶点数对齐"的技巧,是用clip-path做复杂形变动画的底层方法论。
3.4 鼠标跟随光晕:让菱形悬停更高级
纯形状变化看多了会腻,我给自己的项目里加了一个鼠标跟随的径向渐变光晕效果。原理是用background: radial-gradient()实现光晕,然后通过CSS自定义属性来跟踪鼠标位置。
css复制.diamond-wrap {
--pointer-x: 50%;
--pointer-y: 50%;
background: radial-gradient(
240px circle at var(--pointer-x) var(--pointer-y),
rgba(255, 255, 255, 0.25),
transparent 60%
);
}
JS部分在鼠标移动时更新这两个变量:
javascript复制const wrap = document.querySelector('.diamond-wrap');
wrap.addEventListener('pointermove', (e) => {
const rect = wrap.getBoundingClientRect();
const x = ((e.clientX - rect.left) / rect.width) * 100;
const y = ((e.clientY - rect.top) / rect.height) * 100;
wrap.style.setProperty('--pointer-x', `${x}%`);
wrap.style.setProperty('--pointer-y', `${y}%`);
});
这个光晕层会乖乖待在元素底层,菱形裁剪对它同样生效,因为它是写在.diamond-wrap的background上的。悬停时菱形扩大,光晕也跟着扩大,光晕覆盖到图片上,那个质感比普通干巴巴的形状变化高级一个档次。唯一的注意点是,光晕的百分比坐标是基于图片容器的,而鼠标坐标是相对于视口的,两者之间要做一次换算。
4. 这些兼容性坑,我替你们踩过了
4.1 最坑的过渡断点:顶点不一致
我最初踩的第一个坑就是这个。当时想做一个"六边形的图,悬停展开成完整矩形"的效果,写的时候没注意坐标点数量,结果一刷新,hover以后没有过渡,图片直接从六边形跳成了矩形,生硬得像个Bug。排查半天才发现是六边形6个点对矩形4个点,浏览器做不了插值。
这个坑的迷惑性在于,它不会报任何错误,也不会让样式失效,只是静默地跳过过渡动画。如果你的页面里出现了"动画突然失效,但形状切换正常"的情况,优先检查是不是多边形顶点数量不匹配。另外注意,polygon()坐标里逗号后面要有空格,不然在严格模式下可能被解析成非法值,这个问题在压缩CSS以后更容易出现——压缩工具可能把空格去掉,导致解析异常。
4.2 Safari的旧版前缀问题
iOS 15之前的Safari需要通过-webkit-clip-path来使用这个属性。很遗憾,在实际项目里,我遇到过iOS 14的iPhone上clip-path完全不生效,图片还是老实的矩形,hover也没有任何变化。加前缀就正常了。
写兼容样式的时候,标准属性和带前缀的属性要一起写:
css复制-webkit-clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
但是要注意,如果同一段CSS里出现-webkit-clip-path和transition: clip-path,部分旧浏览器可能会把动画也搞丢了。最好把transition也加上-webkit-前缀。我一般直接用Autoprefixer这种构建工具统一加,手写的话太容易漏。
4.3 transform与clip-path同时使用时的层级坑
hover以后图片有一个scale(1.08)的放大效果。在绝大多数浏览器里,transform: scale()和clip-path同时作用于同一个元素,是相安无事的。但某些老版本浏览器里,transform会改变元素的渲染上下文,导致clip-path的百分比坐标系出现偏移,表现为放大以后菱形区域错位,边缘出现明显的裁切偏移。
如果你非要同时用,我建议在元素内部再多包一层:外层管clip-path和transition,内层管transform。这样两个属性互不干扰,最稳。
html复制<div class="diamond-wrap">
<div class="diamond-inner">
<img src="..." alt="..." />
</div>
</div>
css复制.diamond-wrap {
clip-path: polygon(...);
transition: clip-path ...;
}
.diamond-inner {
transform: scale(1);
transition: transform ...;
}
.diamond-wrap:hover .diamond-inner {
transform: scale(1.08);
}
牺牲一层标签,换来全浏览器稳定,划算。
4.4 性能问题:hover动画掉帧
clip-path的动画在低端安卓机上可能会掉帧,尤其是图片尺寸很大、页面同时存在多张图的时候。原因在于,浏览器对clip-path过渡的动画不一定走GPU加速,它可能在主线程里重新计算裁剪区域,导致帧率上不去。
我的优化策略是:
- 避免同时触发多个大图的clip-path动画,尽量错开或者减少同时hover的元素
- 图片尺寸控制一下,不要直接扔原图,用600px到800px的压缩图即可,又没过过渡失真,又省性能
- 给hover动画的transition加上
will-change: clip-path,提前告知浏览器把这个属性提升到独立图层 - 如果还是掉帧,就把过渡时间从0.5s缩短到0.3s,减少主线程计算压力
实测下来,will-change: clip-path对Chrome的帧率提升效果明显,但对Firefox有时候反而会增加内存占用,慎用。如果只是单张卡片的hover,不加也行。
4.5 可访问性:别让键盘用户没有反馈
这个坑是后来做项目的时候被同事提醒的。鼠标hover有动画,键盘用户按下Tab键聚焦到链接上,却什么都没有发生,容易让人觉得这是个坏链接。修复很简单,把:hover的选择器替换成:hover, :focus-visible:
css复制.diamond-wrap:hover .diamond-img,
.diamond-wrap:hover .diamond-mask,
.diamond-wrap:focus-visible .diamond-img,
.diamond-wrap:focus-visible .diamond-mask {
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
}
这样键盘用户Tab聚焦过来,也能看到菱形展开的效果,同时也给屏幕阅读器提供了明确的交互提示。
5. 把菱形悬停嫁接到真实业务:三种实用变体
5.1 产品展示卡片:菱形变圆形的"镜片"效果
电商项目里有一种需求是:默认展示一个圆形的产品图,hover以后圆形扩大并显示产品名和价格,类似"镜头放大特写"的感觉。这个效果几乎不需要改代码,只需要把菱形坐标换成圆形,利用clip-path: circle()就能实现。
css复制/* 默认:居中半径为35%的圆 */
clip-path: circle(35% at center);
/* hover:铺满整个元素 */
clip-path: circle(75% at center);
circle()的过渡相当顺滑,因为两个状态都是圆的半径属性在变。这个变体在珠宝首饰、美妆产品、相机镜头的展示里很吃香,因为它传递出一种"正在被放大观察"的精致感。代码实现上需要注意,circle(35% at center)里的35%是基于元素几何平均值的百分比,不是简单的宽或高的百分比。如果元素是长方形,这个圆的半径会取宽和高的某个加权结果,可能导致圆并不像想象中那么大。最稳妥的方案是保证容器是正方形,或者直接用circle(120px at center)这种像素值。
5.2 团队介绍网格:六边形蜂巢布局
团队页面如果要做六边形头像墙,很多人第一反应是找复杂的菱形平铺插件。其实用clip-path就可以,而且比插件灵活得多。六边形的坐标就是速查表里那6个点,默认状态下不用过渡动画,直接静态显示六边形头像;hover以后六边形平滑过渡成矩形,顺便展开一段个人简介。
考虑到蜂巢布局的排列方式,相邻的六边形会产生锯齿状的缝隙,此时整个容器背景需要跟卡片背景一致,否则缝隙里会露出页面底色,看着像坏掉的拼图。我一般是给所有六边形的父容器设置一个与卡片底色一致的background-color,再把每张卡片的边距设成负值或者重叠,让六边形互相咬合。
5.3 文字遮罩的动态效果:菱形划过标题
还有一种玩法不是用在实际卡片上,而是在大标题上。设计稿里经常出现这种效果:标题文字自带一个菱形的流光扫过,扫过的地方从暗色变成亮色。这个其实是把背景图裁剪成菱形,然后让菱形沿着X轴移动。
css复制.title {
position: relative;
font-size: 48px;
color: #666;
}
.title::after {
content: attr(data-text);
position: absolute;
left: 0;
top: 0;
color: #000;
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
animation: diamond-sweep 3s ease-in-out infinite alternate;
}
@keyframes diamond-sweep {
0% {
clip-path: polygon(90% 0%, 100% 50%, 90% 100%, 80% 50%);
}
100% {
clip-path: polygon(10% 0%, 20% 50%, 10% 100%, 0% 50%);
}
}
这里用了attr(data-text)配合伪元素,复制一份文字当作高亮层,裁剪区域从右往左移动,就产生了一个"菱形探照灯"打亮文字的动效。因为是纯CSS动画,性能开销很小,放在首屏banner里也扛得住。
6. 源码整理与测试结论
最后把我反复验证过的一组生产级代码贴出来,这个版本加了前缀兼容、键盘焦点支持、内层缩放容器,在Chrome 90+、Firefox 88+、Safari 14+、Edge 90+上我都实测过,能稳定复现菱形展开的效果。如果你是复制到自己的Vue或者React项目里,把.diamond-wrap当作组件根节点就行。
css复制/* 兼容前缀版本 */
.diamond-wrap {
position: relative;
display: block;
width: 100%;
height: 0;
padding-bottom: 100%;
overflow: hidden;
background: #1a1d24;
}
.diamond-img,
.diamond-mask {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
-webkit-clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
-webkit-transition:
-webkit-clip-path 0.5s ease,
transform 0.5s ease,
opacity 0.5s ease;
transition:
clip-path 0.5s ease,
transform 0.5s ease,
opacity 0.5s ease;
}
.diamond-mask {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: rgba(15, 23, 42, 0.7);
color: #fff;
opacity: 0;
}
.diamond-wrap:hover .diamond-img,
.diamond-wrap:hover .diamond-mask,
.diamond-wrap:focus-visible .diamond-img,
.diamond-wrap:focus-visible .diamond-mask {
-webkit-clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
}
.diamond-wrap:hover .diamond-mask,
.diamond-wrap:focus-visible .diamond-mask {
opacity: 1;
}
.diamond-wrap:hover .diamond-img,
.diamond-wrap:focus-visible .diamond-img {
transform: scale(1.06);
}
项目里如果用的是构建工具,我建议把这些样式写进一个单独的diamond-card.scss文件里,通过BEM命名规范管理:diamond-wrap是Block,diamond-img和diamond-mask是Element,diamond-wrap--hovered是Modifier,方便以后做更多状态扩展。
实测下来,这个效果的加载性能开销几乎可以忽略——没有任何JavaScript运行时逻辑,所有变化都是纯CSS的样式计算。放在Lighthouse里的Performance得分几乎不掉,CLS布局偏移也是0,因为整个元素的尺寸从始至终没有变过,变的只是可视区域的裁剪形状。
我个人在实际项目里最满意的用法,是把这套菱形遮罩做成了一个小型Vue组件,供整个设计系统复用。组件接收图片地址、标题文字、描述文字三个props,内部维护hover状态,再也不需要每个页面手写一遍样式。组件化以后,设计师新提的需求只要在形状速查表里挑一个坐标,就能快速做出来新的过渡效果。
