You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何SVG中图案遮罩(pattern mask)在300单位处被截断?

SVG遮罩中Pattern在viewBox偏移后截断的问题及解决方案

尝试在SVG遮罩里用Pattern实现网格效果,一开始能正常画出带遮罩的彩色圆形,但把viewBox原点改成200后,遮罩突然在300单位处截断了——移除遮罩后圆形显示完整,显然是遮罩和Pattern的坐标转换出了问题。

正常运行的代码(viewBox原点为0)

<svg width="300" height="300" viewBox="0 0 300 300"
    xmlns="http://www.w3.org/2000/svg">
  <defs>  
    <pattern id="checked" width="2" height="2" stroke="white" stroke-linecap="square" stroke-width="1" patternTransform="rotate(45) scale(2)"
        patternUnits="userSpaceOnUse">
      <line x1="0" y1="0" x2="0" y2="2"></line>
      <line x1="0" y1="0" x2="2" y2="0"></line>
    </pattern>
    <mask id="checked-mask" x="0" y="0" width="1" height="1" >
      <rect x="0" y="0" width="1000" height="1000" fill="url(#checked)" />
    </mask>
  </defs>  
  <circle cx="60" cy="60" r="50" stroke="red" mask="url(#checked-mask)" fill="green" />
  <circle cx="120" cy="60" r="50" stroke="blue" mask="url(#checked-mask)" fill="magenta" />
</svg>

这段代码能正常渲染带白色网格遮罩的绿色、品红色圆形。

出现截断的代码(viewBox原点偏移至200)

<svg width="300" height="300" viewBox="200 200 300 300"
  xmlns="http://www.w3.org/2000/svg">
  <defs>  
    <pattern id="checked" width="2" height="2" stroke="white"
      stroke-linecap="square" stroke-width="1" patternTransform="rotate(45) scale(2)"
      patternUnits="userSpaceOnUse">
      <line x1="0" y1="0" x2="0" y2="2" />
      <line x1="0" y1="0" x2="2" y2="0" />
    </pattern>
    <mask id="checked-mask" x="0" y="0" width="1" height="1" >
      <rect x="0" y="0" width="100%" height="100%" fill="url(#checked)" />
    </mask>
  </defs>  
  <circle cx="260" cy="260" r="50" stroke="red" mask="url(#checked-mask)" fill="green" />
  <circle cx="320" cy="260" r="50" stroke="blue" mask="url(#checked-mask)" fill="magenta" />
  <rect x='200' y='200' width='200' height='200' fill='transparent' stroke='red' />
  <rect x='200' y='200' width='100' height='100' fill='transparent' stroke='blue' />
</svg>

把viewBox改成200 200 300 300并移动圆形位置后,遮罩在300单位处被截断(蓝色框标记的是截断范围,红色框验证画布实际能覆盖到400单位)。

问题根因

  1. Pattern的坐标系统没适配viewBox偏移:用了patternUnits="userSpaceOnUse",意味着Pattern的坐标是基于SVG初始的用户空间(原点0,0),而不是偏移后的viewBox(原点200,200)。当遮罩里的矩形覆盖到200-500区域时,Pattern只在0-300区域有内容,300以上就没了,导致遮罩截断。
  2. 遮罩单位默认是元素边界框:遮罩的x="0" y="0" width="1" height="1"默认用的是objectBoundingBox单位,也就是基于被遮罩圆形的边界框,这和Pattern的全局用户空间坐标不匹配,进一步放大了问题。

解决办法

办法1:平移Pattern适配viewBox偏移

给Pattern的patternTransform加上平移参数,匹配viewBox的偏移量,同时让遮罩里的矩形足够大,覆盖所有需要遮罩的区域:

<pattern id="checked" width="2" height="2" stroke="white"
  stroke-linecap="square" stroke-width="1" patternTransform="rotate(45) scale(2) translate(200, 200)"
  patternUnits="userSpaceOnUse">
  <line x1="0" y1="0" x2="0" y2="2" />
  <line x1="0" y1="0" x2="2" y2="0" />
</pattern>
<mask id="checked-mask" x="0" y="0" width="1" height="1" >
  <rect x="200" y="200" width="1000" height="1000" fill="url(#checked)" />
</mask>

办法2:改用元素边界框的Pattern单位

把Pattern的patternUnits改成objectBoundingBox,让Pattern基于被遮罩元素的边界框重复,自动适配元素位置:

<pattern id="checked" width="0.1" height="0.1" stroke="white"
  stroke-linecap="square" stroke-width="0.05" patternTransform="rotate(45)"
  patternUnits="objectBoundingBox">
  <line x1="0" y1="0" x2="0" y2="0.1" />
  <line x1="0" y1="0" x2="0.1" y2="0" />
</pattern>
<mask id="checked-mask" x="0" y="0" width="1" height="1" >
  <rect x="0" y="0" width="1" height="1" fill="url(#checked)" />
</mask>

这里Pattern的尺寸是小数(对应元素边界框的比例),不需要手动平移,每个圆形的遮罩都会自动匹配网格。

办法3:让遮罩内容用全局用户空间坐标

给遮罩加上maskContentUnits="userSpaceOnUse",让遮罩内容的坐标和主SVG保持一致,同时调整遮罩的范围到viewBox的实际区域:

<mask id="checked-mask" x="200" y="200" width="300" height="300" maskContentUnits="userSpaceOnUse" >
  <rect x="200" y="200" width="1000" height="1000" fill="url(#checked)" />
</mask>

这样遮罩内容的坐标和viewBox对齐,Pattern的全局坐标就能正确覆盖所有需要遮罩的区域。


内容的提问来源于stack exchange,提问作者cbradley

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 18:35:58