clip-path 调不准?先搞清坐标系与参考框

· 约 4 分钟 CSS 剪切路径

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-boxborder 内缘(padding 外缘)想跟 background 对齐时
content-boxpadding 内缘不含任何 padding 的”实际内容”
margin-boxmargin 外缘几乎不用,部分浏览器还不支持
fill-boxSVG 元素的紧密包围盒SVG 默认
stroke-boxSVG 元素含描边的包围盒带粗描边的 SVG
view-box最近的 SVG viewBoxSVG 元素默认

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 的距离。

修法

  1. 容器 box-sizing: border-box + 0 padding(最直接)
  2. 给 clip-path 显式写 padding-box,对齐手柄系
  3. 手柄外层用 inset: 0 占满 border-box,再用百分比定位

本仓库的 css-clip-path 工具采用方案 1:编辑容器无 padding,手柄百分比 = clip-path 百分比,所以拖到哪 polygon 顶点就跑到哪。

clip-path vs mask

两者都做”切元素”,但本质不同:

维度clip-pathmask
算法布尔(在 / 不在)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(...)
  • 文字镂空背景 → maskbackground-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——三个坑一过就顺了。

❓ 常见问题

clip-path 的百分比是相对什么算的?

默认是元素的 border-box——width 和 height 包含 padding 和 border 但不含 margin。polygon(50% 0, 100% 100%, 0 100%) 的 50% 是 border-box 宽度的一半。可以通过加几何框关键字改变参考:clip-path: polygon(...) padding-boxcontent-boxmargin-boxfill-boxstroke-boxview-box。 SVG 元素上默认是 view-box,HTML 元素上默认是 border-box

clip-path 和 mask 怎么选?

clip-path 是布尔切割——像素要么全在要么全不在,没有中间值,边缘是硬的(除非你用 SVG path + 抗锯齿)。mask 是按 alpha 加权——可以用渐变让边缘羽化,可以用半透明做半显隐。规则三角形 / 多边形 / 圆裁剪用 clip-path,柔边、渐变消失、复杂蒙版用 mask。clip-path GPU 性能更好。

拖拽 / 编辑 clip-path 时手柄位置总跟实际不一致?

90% 是参考框搞错了。手柄通常用 left/top 百分比放在元素里,参考的是元素 width/height(content-box 或 border-box,看 box-sizing);clip-path 默认参考 border-box。如果元素有 padding,两者就错位。修法:要么把 box-sizing 设成 border-box,要么给 clip-path 加 padding-box 参考。本工具里用百分比手柄 + clip-path 默认 border-box,已校准过。

clip-path 影响 box-shadow 和 outline 吗?

影响。被 clip 切掉的部分外阴影、轮廓、border 全部消失——因为它们本质上是元素 border-box 上的"画",clip 把这层画也切了。这是实战中最常见的"为什么三角形按钮没阴影"原因。修法:在被 clip 元素的外层包一个 filter: drop-shadow() 容器——drop-shadow 跟随实际像素轮廓,能给 clip 后的形状加阴影。

打开 CSS 剪切路径 polygon/circle/ellipse/inset·顶点拖拽·星形/箭头/对话气泡预设