你以为 clip-path 只能靠 SVG?今天 CSS shape() 把这件事彻底原生化了
设计师给了一张带波浪底部的人物剪影图,要你在 Hero 区域实现。配上去之后,在设计师屏幕上完美,但你拿过来一打开——移动端变形了。
这是每一个用过 clip-path: path() 的前端工程师都踩过的坑:SVG path 用的是固定像素坐标,元素一大就崩,一缩也崩,没有任何响应式可言。
shape() 函数解决了这个问题。
旧方案的三重局限
在 shape() 之前,你想给图片加一个曲线裁切,只有三种路,每条都有硬伤。
polygon():接受百分比,是响应式的,但只支持直线段。想画波浪?需要几十个点凑,元素一大这些点之间的距离就全变了,波浪变成了锯齿。
path():接受真实 SVG 贝塞尔曲线命令,能画曲线,但坐标全是固定像素。一个在 800px 宽度下画出来的 path,缩到 375px 就完全走形。
SVG mask:能响应式,但做不到「形状等比缩放但曲线厚度不变」。而且你需要多一个 SVG 文件、一个 viewBox、一段 url(#…) 引用——设计师动一下文件,所有东西都要重新对。
实际项目中,这三个方案的最终结果往往是:要么接一个 resize 监听器让 JS 实时重算 path(性能杀手),要么直接告诉产品和设计师「移动端做不到」,或者发版前一天设计师发现「波浪怎么变形了」。
shape() 是什么
shape() 是 clip-path 的新函数,2026 年 2 月进入 Baseline。
它把 SVG path 的绘图命令直接搬进了 CSS,但关键区别在于:所有坐标都接受 CSS 单位,百分比、rem、calc()、甚至混用。
语法长这样:
.hero {
clip-path: shape(
from 0% 0%,
line to 100% 0%,
line to 100% 80%,
curve to 0% 80% via 50% 105%,
close
);
}
对比一下 path() 版本的固定像素:
.hero {
/* 固定像素,800px 宽度下画,缩放必崩 */
clip-path: path("M 0 0 L 800 0 L 800 360 Q 400 420 0 360 Z");
}
shape() 版本的波浪底部用百分比坐标,元素有多宽,曲线就自动按比例适配——不需要任何 JS。
支持的命令
shape() 的命令和 SVG path 类似,但语法更接近 CSS:
from X% Y%:起点line to X% Y%:直线hline to X%/vline to Y%:水平/垂直直线curve to X% Y% via X1% Y1% [, X2% Y2%]:二次/三次贝塞尔曲线arc to X% Y% of R%:椭圆弧smooth to X% Y% via X1% Y1%:光滑曲线close:闭合路径
每个命令后面可以跟 by(相对坐标)或 to(绝对坐标)。
实战:波浪 Hero 区域
这是生产环境中 shape() 最常见的用法——给区块底部加一个波浪分隔线,替代以前需要切图或者用伪元素叠 PNG 的做法:
.section {
clip-path: shape(
from 0% 0%,
line to 100% 0%,
line to 100% 85%,
curve to 0% 85% via 25% 100%, 75% 70%,
close
);
}
85% 处开始画曲线,左边 25% 处开始向上拱、右边 75% 处回落——这个波浪在任何宽度下形状比例都保持一致。
混用单位:固定厚度曲线
shape() 最厉害的能力:不同坐标轴可以用不同单位,浏览器会分别处理。
想让波浪曲线在元素缩放时,曲线本身的「拱高」保持固定像素值,而不是跟着元素等比缩放:
.hero {
clip-path: shape(
from 0% 20px,
curve to 100% 20px via 25% 0% / 75% 40px,
vline to calc(100% - 20px),
curve to 0% calc(100% - 20px) via 75% 100% / 25% calc(100% - 40px),
close
);
}
20px 的曲线拱高在所有尺寸下都保持不变,宽度方向用百分比响应式——这个能力在 path() 和 SVG mask 里都做不到。
可以动画了
path() 的裁切区域是不能做 CSS transition 的。两个不同的 path 值之间,浏览器直接跳帧。
shape() 改变了这一点——只要两个 shape 的顶点数相同,就可以做平滑过渡:
.card {
clip-path: shape(
from 0% 0%,
line to 100% 0%,
line to 100% 100%,
line to 0% 100%,
close
);
transition: clip-path 0.4s ease;
}
.card:hover {
clip-path: shape(
from 0% 0%,
curve to 100% 0% via 50% -10%,
line to 100% 100%,
curve to 0% 100% via 50% 110%,
close
);
}
卡片悬停时,从直角矩形变成圆角卡片形状,平滑过渡——这个效果以前只能靠 SVG SMIL 或者 JS 动画库来实现。
浏览器支持
Chrome 135+、Firefox 148+、Safari 18.4+、Edge 135+,Baseline 2026。覆盖约 85-96% 全球用户。
渐进增强写法:
.hero {
/* 旧浏览器回退:斜切矩形 */
clip-path: polygon(0 0, 100% 0, 100% 85%, 0 100%);
}
@supports (clip-path: shape(from 0% 0%, line to 100% 0%, close)) {
.hero {
clip-path: shape(
from 0% 0%,
line to 100% 0%,
line to 100% 80%,
curve to 0% 80% via 50% 105%,
close
);
}
}
三个坑
1. Firefox 148 以下不支持:Firefox 在 2026 年初才完整支持 shape(),此前需要 @supports 检测。
2. 不能夸类型过渡:circle() → shape() 这种跨类型过渡不会插值,浏览器直接跳帧。要做形态变换动画,两端都用 shape() 并且顶点数相同。
3. clip-path 裁切不影响 focus 顺序:被裁切掉的区域里的按钮和链接仍然可以被 tab 到,也会被屏幕阅读器读到。裁切交互区域时要同步设置 aria-hidden 或者 inert。
三步下一步
- 查项目里有没有 path():grep “clip-path.*path(” 找到所有固定像素的裁切,这些是 shape() 的直接替换目标
- 从波浪 Hero 切入:找一个有斜切底边的 Hero 或 Section,把 polygon 替换成 shape(),用 curve 命令还原曲线
- 给 shape() 加 @supports:写完 shape() 版本后,外层套一层 @supports 包裹旧 polygon 版本做回退
shape() 解决的不只是「画曲线」的问题,而是把 CSS 的响应式能力真正延伸到了图形裁切领域。在此之前,前端工程师要用 SVG、要用 resize 监听器、要在设计师和移动端之间做妥协——现在,一个函数把这些全收进去了。
评论区
登录后可评论。