SVG path 的坐标是死的,CSS shape() 的坐标能用 calc 算——同一个斜切角,一个 resize 就崩,一个跟着容器自适应(Chrome 129+ / Safari 18.2)
写过一个斜切角的卡片,你的第一个冲动是什么?
大多数人的做法:打开设计工具导出一段 SVG,把路径字符串贴进 clip-path: path(...)。
这个方案有三个坑。
第一,SVG 路径坐标是死的。你写的是 M 10 20 L 100 30,不管容器多宽,它都卡在那个像素位置。想让斜切角跟着元素缩放?得靠 JS 动态算,或者把数字全换成百分比——然后发现 SVG 路径不支持百分比。
第二,路径字符串是一次性格式。你没法像写普通 CSS 那样,用变量控制某一段弧度,或者加 calc() 让长度随容器弹性伸缩。
第三,如果还想让一个元素沿着同一条路径做动画,path() 只服务 clip-path,offset-path 里的路径语法又不完全一样,两套写法维护。
CSS shape() 函数就是来解决这三个问题的。
shape() 是什么
shape() 是 CSS Basic Shapes Module Level 4 新增的函数,用于给 clip-path 和 offset-path 定义形状。它的语法不是 SVG 路径字符串,而是一系列可组合的绘图命令:
.clip-card {
clip-path: shape(
from 0 0,
line to 100% 0,
line to 100% 85%,
line to 60% 85%,
line to 50% 100%,
line to 40% 85%,
line to 0 85%,
close
);
}
from 0 0 是起点,之后每个命令都从上一个命令的终点继续,close 自动闭合。
命令分几类:
- 移动:
move by 10px 5px(相对)/move to 10px 5px(绝对) - 直线:
line by 20px 40%/line to 80% 80% - 水平 / 垂直:
hline by 50px/vline to 5rem - 贝塞尔曲线:
curve to 80px 80px with 160px 1px / 20% 16px(控制点 + 切线) - 平滑曲线:
smooth to 100px 50pt - 圆弧:
arc to 80px 1pt of 10% ccw large rotate 25deg
关键在于:所有坐标都能用 CSS 长度单位,包括百分比和 calc()。这正是 SVG 路径做不到的。
响应式斜切角:Before → After
Before:写死像素,resize 就崩
.clip-old {
/* 只能在固定宽度下正常显示 */
clip-path: path('M 0 0 L 300 0 L 300 250 L 200 300 L 100 250 L 0 250 Z');
}
After:百分比 + calc(),任意宽度自适应
.clip-new {
clip-path: shape(
from 0 0,
line to 100% 0,
line to 100% calc(100% - 2rem), /* calc 保留底部留白 */
line to 60% calc(100% - 2rem),
line to 50% 100%,
line to 40% calc(100% - 2rem),
line to 0 calc(100% - 2rem),
close
);
}
无论容器多宽,斜切角始终落在底部 2rem 处,角度随宽度自动保持。同一份代码,320px 视口和 1440px 视口都对。
动效路径:让元素沿自定义路线飞
shape() 另一个杀手场景是 offset-path。配合 offset-distance 做动画:
.spark {
offset-path: shape(
from 0% 80%,
curve to 50% 20% with 20% 10%,
curve to 100% 80% with 80% 10%
);
offset-distance: 0%;
animation: travel 4s linear infinite;
}
@keyframes travel {
to { offset-distance: 100%; }
}
加上 offset-rotate: auto,元素还能自动转向、匹配路径切线方向:
.arrow {
offset-path: shape(from 0% 50%, curve to 100% 50% with 50% 0);
offset-rotate: auto; /* 跟随路径方向旋转 */
}
之前做这种动效,要么用 GSAP + MotionPath 插件,要么自己写 JS 逐帧计算位置。现在一段 CSS 搞定。
与 polygon() / path() 对比
| 能力 | polygon() | path() | shape() |
|---|---|---|---|
| 坐标单位 | 百分比 / pc | 像素(固定) | 任意 CSS 单位(含 calc) |
| 曲线支持 | 仅多边形 | 贝塞尔 | curve / smooth / arc |
| 响应式 | 有限 | 不支持 | 原生支持 |
| offset-path | 不支持 | 支持 | 支持 |
| 语法可读性 | 中 | 低(SVG 字符串) | 高(命令式) |
/* polygon() 只能画直线段 */
clip-path: polygon(0 0, 100% 0, 80% 100%, 0 100%);
/* shape() 能加曲线 */
clip-path: shape(
from 0 0,
line to 100% 0,
curve to 80% 100% with 30% 0,
close
);
浏览器支持与降级
支持情况(2026-09):
- Chrome 129+
- Edge 129+
- Firefox 137+
- Safari 18.2+
- Baseline 2026-02(新进可用)
Safari 18.2 是最后一个跟上的版本,目前覆盖率约 78%,仍在上升。老项目可以叠一层 @supports 降级:
.badge {
/* 通用降级 */
clip-path: polygon(0 0, 100% 0, 90% 100%, 0 100%);
}
@supports (clip-path: shape(from 0 0, line to 100% 0, close)) {
.badge {
clip-path: shape(
from 0 0,
line to 100% 0,
curve to 90% 100% with 20% 80%,
close
);
}
}
下一步可以做的事
- 迁移现有 SVG 路径:搜一下项目里所有
clip-path: path(...),把能响应式的改成shape() - 配合 @property 做插值动画:用
@property --cut-size注册自定义属性,就能在@keyframes里对形状参数插值 - 用 DevTools 验证:在 Elements 面板展开
clip-path/offset-path,可直接图形化编辑 shape() 命令
shape() 让 CSS 从布局工具变成了一张真正的画布——图形和动效都能直接用 CSS 单位描述。
评论区
登录后可评论。