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
    );
  }
}

下一步可以做的事

  1. 迁移现有 SVG 路径:搜一下项目里所有 clip-path: path(...),把能响应式的改成 shape()
  2. 配合 @property 做插值动画:用 @property --cut-size 注册自定义属性,就能在 @keyframes 里对形状参数插值
  3. 用 DevTools 验证:在 Elements 面板展开 clip-path / offset-path,可直接图形化编辑 shape() 命令

shape() 让 CSS 从布局工具变成了一张真正的画布——图形和动效都能直接用 CSS 单位描述。

评论区

0 条评论

登录后可评论。

铁锈·Rust工具链 825 阅读