跳到主要内容

clipPath-mask与marker

三者都是 渲染期效果,不修改 d。导出、布尔运算、节点编辑必须另写几何;命中是否跟着裁切走,也不要靠感觉。

1. clipPath vs mask

clipPathmask
本质硬裁切,按 clip 内容的填充形状按亮度或 alpha 调制,可半透明
软边没有(抗锯齿除外)有,blur / 渐变都能进 mask
成本相对低,仍可能建层离屏缓冲,接近滤镜
单位clipPathUnitsuserSpaceOnUse / objectBoundingBoxmaskUnits + maskContentUnits,同样两套
<clipPath id="hole" clipPathUnits="userSpaceOnUse">
<rect x="10" y="10" width="80" height="80"/>
</clipPath>
<g clip-path="url(#hole)">...</g>

objectBoundingBox 的 clip/mask 同样会在 零宽或零高包围盒 上失败,见 渐变 Pattern 与滤镜

CSS clip-path 可以 url(#id) 指到 SVG clipPath。交互层用 SVG 属性或 CSS 选一条写入通道,序列化进文件时要能还原。

mask-type: luminance(SVG 传统:白显黑隐)和 alpha 不要混。从设计工具拷来的 mask 发灰,先查是不是把 alpha 图当亮度用了。

适用:头像圆裁、地图视窗、淡入边缘。
不适用:想得到「挖洞后的 path 数据」——那是布尔运算,clip 只是看起来挖了。

2. 不要用 clip 当布尔运算

clip-path / mask 之后:

  • d 仍是原路径,getTotalLength 仍是全长。
  • 序列化 SVG 文件,下游打开仍是完整几何 + 一条 clip 引用。
  • 命中:被裁掉的像素通常点不中,但几何 API(isPointInFillgetBBox({ clipped: false }))仍按未裁形状。框选要对齐「数据」还是「可见」,必须显式选 clipped

编辑器要并/交/差:改 path 数据(或存 boolean 树再烘焙)。渲染期 clip 只给预览或装饰开窗。Canvas 侧同类边界见 路径布尔运算

3. marker 坐标系

<marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5"
markerWidth="6" markerHeight="6" orient="auto-start-reverse"
markerUnits="strokeWidth">
<path d="M 0 0 L 10 5 L 0 10 z" fill="context-stroke"/>
</marker>
<path d="M 10 10 L 90 10" stroke="currentColor" marker-end="url(#arrow)"/>
字段含义
markerUnits="strokeWidth"(默认)markerWidth 乘在 被标记元素的线宽
userSpaceOnUsemarkerWidth 直接是用户单位
refX / refY钉在顶点上的局部点
orient="auto"跟切线;起点箭头用 auto-start-reverse

vector-effect: non-scaling-stroke 改变的是 被标记 path 的线宽怎么画,marker 默认仍按 strokeWidth 单位放大。相机缩放后箭头相对线宽是否好看,要自己试;不要假定 marker 也 non-scaling。

marker 是绘制时戳印,不是可点的独立节点。不要对箭头做 elementFromPoint 指望点到 marker 里的 path——命中落在宿主 path 上(还受 pointer-events 约束)。可编辑箭头:把三角形烘焙进几何,或单独一条 path。

context-stroke / context-fill 让 marker 跟宿主走。实现仍有缺口时,回退成 currentColor 或写死并在主题切换时改 fill

4. 失败形态

症状原因
导出后再编辑,洞没了只用了 clip,没有改 d
框选到被裁掉的「幽灵」getBBox / 相交用了 clipped: false
细线上 clip 消失objectBoundingBox 零尺寸
箭头随相机巨大markerUnits=strokeWidth 且线宽写在用户空间未补偿
点不中箭头以为 marker 是 DOM 子树

权威资料

核对日期:2026-08-26