Click only through holes in svg mask

笑着哭i 提交于 2020-08-07 09:51:46

问题


I have svg mask which determines holes in rectangular. Behind svg mask I have some clickable elements and I would like to pass events to them, but only through holes. I've experimented with pointer-events values, but I can only make either whole mask to pass events or whole mask to capture them. For one hole it can be simply done using clip-path, just determining outer part of the hole, but several holes make things more difficult. Is there any possibility to avoid using clip-path? I also tried pointer-events: visiblePainted and pointer-events: painted, but had no success.

.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>

回答1:


There are several aspects to this problem. First, you are right the behavior of masks and clip-paths is different in relation to hit-testing.

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.

Second, a clip-path is a geometric shape, but just like all paths, it might contain holes. Instead of three <rect>s, you can use one <path> with three subpaths, as long as the clip-rule makes sure the subpaths inside get cut out of the surrounding shape.

Third, if the pointer-events property is applied to an <svg> element in a HTML context, its behavior becomes...strange. Any other value than pointer-events: none on the <svg> element lead to the whole bounding box receiving events - a behavior proposed for HTML elements, but currently not part of any spec.

The solution here is to set pointer-events: none on the <svg> element, and then to reverse that with pointer-events: painted on the child <rect> element.

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>



回答2:


Clip masks are useful for cropping parts out of complicated objects, but if you're just working with blocks of solid colour then maybe it would be just as easy to create shapes that already have holes in them.

I've added an example below. Does this help?

<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>


来源:https://stackoverflow.com/questions/63004101/click-only-through-holes-in-svg-mask

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!