仅通过 svg 蒙版中的孔单击

Click only through holes in svg mask

我有 svg 掩码,它可以确定矩形中的孔。在 svg mask 后面我有一些可点击的元素,我想将事件传递给它们,但只能通过孔。我已经尝试过 pointer-events 值,但我只能制作整个掩码来传递事件或制作整个掩码来捕获它们。对于一个孔,可以简单地使用 clip-path 来完成,只需确定孔的外部,但是多个孔会使事情变得更加困难。有没有可能避免使用剪辑路径?我也尝试了 pointer-events: visiblePaintedpointer-events: painted,但没有成功。

.background {
  width: 400px;
  height: 400px;
  background: red;
  cursor: pointer;
}

.svg {
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none;
}
<button class="background">
</button>
<svg width="400" height="400" xmlns="http://www.w3.org/2000/svg" class="svg">
  <defs>
     <mask id="mask">
       <rect
         x="0"
         y="0"
         width="400"
         height="400"
         fill="white"
       />  
       <rect
          x="20"
          y="20"
          width="40"
          height="40"
          fill="black"
       />
       <rect
          x="290"
          y="290"
          width="40"
          height="40"
          fill="black"
       />  
     </mask>
  </defs>   
  <rect
    x="0"
    y="0"
    width="400"
    height="400"
    fill="black"
    opacity="0.5"
    mask="url(#mask)"
    pointer-events="auto"
   />
</svg>

剪贴蒙版对于从复杂的对象中裁剪出部分很有用,但如果您只是处理纯色块,那么创建已经有孔的形状可能同样容易。

我在下面添加了一个示例。这有帮助吗?

<svg width="400" heoght="200" viewBox="0 0 400 200">
  <text x="100" y="100" text-anchor="middle"
    alignment-baseline="middle" onclick="alert('Hello!')"
    style="cursor:pointer">Click me</text>
  <text x="300" y="100" text-anchor="middle"
    alignment-baseline="middle" onclick="alert('Hello?')"
    style="cursor:pointer">Not me</text>
  <path d="M20 20 180 20 180 180 20 180ZM60 60 60 140 140
    140 140 60Z" fill="#3a6" fill-opacity="0.7"
     fill-rule="nonzero"/>
  <path d="M220 20 380 20 380 180 220 180Z" fill="#f20"
    fill-opacity="0.7"/>
</svg>

这个问题有几个方面。首先,你是对的,掩码和剪辑路径的行为与命中测试相关different

A clip path is a geometric boundary, and a given point is clearly either inside or outside that boundary; thus, pointer events must be captured normally over the rendered areas of a clipped element, but must not be captured over the clipped areas... By contrast, a mask is not a binary transition, but a pixel operation, and different behavior for fully transparent and almost-but-not-fully-transparent may be confusingly arbitrary; as a consequence, for elements with a mask applied, pointer events must still be captured even in areas where the mask goes to zero opacity.

其次,剪辑路径是一种几何形状,但就像所有路径一样,它可能包含孔洞。您可以使用一个 <path> 和三个子路径,而不是三个 <rect>,只要 clip-rule 确保内部的子路径被切出周围的形状即可。

第三,如果 pointer-events 属性 应用于 HTML 上下文中的 <svg> 元素,其行为会变得……奇怪。 <svg> 元素上除 pointer-events: none 之外的任何其他值都会导致整个边界框接收事件 - HTML 元素的行为 proposed,但目前不属于任何规范。

这里的解决方案是在 <svg> 元素上设置 pointer-events: none,然后在子 <rect> 元素上用 pointer-events: painted 反转它。

button, svg {
  position:absolute;
  width:400px;
  height:400px
}
button {
  background: #0000ff;
  cursor: pointer; 
}
button:hover {
  background: #008800; 
}
svg {
  pointer-events: none;
}
.over {
  fill: #000;
  clip-path: url(#clip);
  pointer-events: painted;
}
<button></button>
<svg xmlns="http://www.w3.org/2000/svg" height="400" width="400">
 <defs>
   <clipPath id="clip" clip-rule="evenodd">
 <path d="M 20 20 h 360 v 360 h -360 z
          M 40 40 v 40 h 40 v -40 z
          M 200 290 v 40 h 40 v -40 z" />
   </clipPath>
 </defs>
 <rect y="0" x="0" height="400" width="400" class="over" />
</svg>