clip-path 的参数本身简单,但调出来效果总跟预期差一点——大概率是坐标系没搞清。理解参考框、单位和与 mask 的边界,能省下大量”为什么不对”的时间。
五种基础形状
.shape-polygon { clip-path: polygon(50% 0, 100% 100%, 0 100%); } /* 三角形 */
.shape-circle { clip-path: circle(40% at 50% 50%); }
.shape-ellipse { clip-path: ellipse(50% 30% at 50% 50%); }
.shape-inset { clip-path: inset(10% 20% 10% 20% round 12px); } /* 内嵌矩形 */
.shape-path { clip-path: path('M 0 0 L 100 0 L 100 100 Z'); } /* 任意 SVG 路径 */
path() 接收 SVG path 语法,精度最高但坐标是绝对像素——元素尺寸变了 path 不会跟着变,做响应式得算。其他四种都接受百分比,自动适配。
参考框:百分比相对什么
clip-path 后面可以跟一个几何框关键字,决定百分比相对的”框”:
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%) padding-box;
| 关键字 | 含义 | 默认场景 |
|---|---|---|
border-box | 包含 border 的外缘 | HTML 元素默认 |
padding-box | border 内缘(padding 外缘) | 想跟 background 对齐时 |
content-box | padding 内缘 | 不含任何 padding 的”实际内容” |
margin-box | margin 外缘 | 几乎不用,部分浏览器还不支持 |
fill-box | SVG 元素的紧密包围盒 | SVG 默认 |
stroke-box | SVG 元素含描边的包围盒 | 带粗描边的 SVG |
view-box | 最近的 SVG viewBox | SVG 元素默认 |
HTML 元素默认 border-box。如果元素有 padding,polygon(0 0, 100% 0, ...) 的 100% 包含 padding 区域。想让 clip 的百分比对齐”内容框”,写 content-box:
.card {
padding: 20px;
clip-path: inset(0) content-box; /* 切到 padding 内缘 */
}
手柄拖拽时常见的”对不上”
工具里常用百分比手柄:
.handle { position: absolute; left: 50%; top: 0; }
left/top 百分比相对包含块的 padding-box 之内(即 content-box + padding,等于 padding-box)。如果元素 box-sizing: content-box 且有 padding,手柄百分比是 padding-box,clip-path 默认是 border-box——错开一圈 padding 的距离。
修法:
- 容器
box-sizing: border-box+ 0 padding(最直接) - 给 clip-path 显式写
padding-box,对齐手柄系 - 手柄外层用
inset: 0占满 border-box,再用百分比定位
本仓库的 css-clip-path 工具采用方案 1:编辑容器无 padding,手柄百分比 = clip-path 百分比,所以拖到哪 polygon 顶点就跑到哪。
clip-path vs mask
两者都做”切元素”,但本质不同:
| 维度 | clip-path | mask |
|---|---|---|
| 算法 | 布尔(在 / 不在) | alpha 加权(每像素 0-100% 不透明度) |
| 边缘 | 硬边(除非 SVG path 抗锯齿) | 可羽化、可渐变 |
| 输入 | 形状关键字 / SVG path | 图片 / SVG / 渐变 |
| 性能 | GPU 友好,强 | 大遮罩较重 |
| 阴影 / 边框 | 一并切掉 | 可保留(取决于实现) |
典型场景对照:
- 头像剪成圆 →
clip-path: circle(50%)或border-radius: 50%,不要用 mask - 图片淡出消失 →
mask: linear-gradient(to right, black, transparent) - 三角形指示器 →
clip-path: polygon(...) - 文字镂空背景 →
mask或background-clip: text - 复杂剪裁(星形、对话气泡) →
clip-path: polygon(...)或path()
阴影和边框为什么没了
.btn {
clip-path: polygon(50% 0, 100% 100%, 0 100%);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); /* 看不见 */
border: 2px solid red; /* 三角内部反而出现红线 */
}
box-shadow 是元素 border-box 外延的”图层”,被 clip 一并切了——三角形外侧空空如也。border 是 border-box 内侧的”圈”,三角形把这个圈也切了,剩余的边只在三角形内部。
修法是用外层包裹 + drop-shadow:
<div class="btn-wrap">
<div class="btn">提交</div>
</div>
.btn-wrap {
filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.2));
}
.btn {
clip-path: polygon(50% 0, 100% 100%, 0 100%);
background: var(--primary);
}
drop-shadow 跟随实际像素轮廓,能正确给三角形描边阴影。代价是 GPU 成本比 box-shadow 高。
边框可以模拟成内嵌阴影或叠两层 clip-path(外层是大三角,内层是小三角),但都不优雅——clip-path 后的元素就别想要 1px 边了。
动画
clip-path 形状之间能不能 transition / animate,取决于形状是否同型。
.box { clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); transition: clip-path .3s; }
.box:hover { clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%); } /* 顶点数同,可平滑 */
条件:
- polygon → polygon:顶点数相同时插值,否则
discrete(直接跳变) - circle → circle:r 和 at 都能 tween
- inset → inset:四边和 round 都能 tween
- 跨形状(polygon → circle):直接跳变
变形过程顶点数不一致时,把简单形状补到等顶点数即可:
/* 三角形 → 矩形:把三角写成 4 个顶点 */
.tri { clip-path: polygon(50% 0, 100% 100%, 50% 100%, 0 100%); }
.square { clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); }
性能
clip-path 在合成线程里跑,GPU 友好。例外:
path()复杂路径每帧重算- 动画过程中顶点数变化(discrete)会触发重排
- 大量元素同时动 clip-path 仍要小心 paint 成本
绝大多数场景比 mask 快。要做”按钮飞入”之类的过渡,clip-path 是首选。
一句话总结
clip-path 调不准查参考框,要羽化就换 mask,丢了阴影就外层加 drop-shadow——三个坑一过就顺了。