1

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 already asked this question here and got an answer, which worked perfectly for me until I've run into a case with intersecting rectangulars Click only through holes in svg mask

Is there any simple solution to got area around rectangulars clipped out but not area in intersections? It would be perfect to have something like globalCompositeOperation='destination-out' and I've started to think of how to use canvas instead of svg for my problem, but pointer-events which I use for svg are not yet designed to work correct with canvas as far as I know.

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 90 100 v 240 h 140 v -240 z
          M 200 290 v 80 h 80 v -80 z" />
   </clipPath>
 </defs>
 <rect y="0" x="0" height="400" width="400" class="over" />
</svg>
arsonist
  • 175
  • 1
  • 11
  • what if you make the blue areas the clip path instead ? – G-Cyrillus Jul 22 '20 at 20:00
  • 1
    @G-Cyrillus then I won't be able to disable interaction for black area. Unfortunately I can not make the whole svg clickable (not passing clicks) and then only holes to pass clicks through, to divs that lay below svg. Or I do not understand how I can. – arsonist Jul 22 '20 at 20:06
  • Maybe it is time to step back a bit and describe what the end goal of your setup is. Should this mask of yours have a grafic representation, so the user can see it (like in the example), or is meant to be invisible and just a restriction for pointer events? Why can't you just change the shape of the clickable elements themselves so that they just cover the area where they are supposed to be clickable? Where do the data for the holes come from, and why can they intersect? Are they always rectangular? – ccprog Jul 22 '20 at 21:07
  • @ccprog I am doing some kind of product tour and I try to do it as much independent from content as possible. The holes are actually elements in DOM which I want to highlight and have an ability to interact with whilst not interacting with other elements on page. The holes can vary from circle to rectangular, but the circle is actually a decoration for rectangular. The mask is intended to be either transparent or visible with some not 100% opacity – arsonist Jul 22 '20 at 21:38
  • Doesn't that say you want to interact with a certain _selection_ of all possible elements at a certain time? Here is an idea for you to consider: show a semitransparent overlay over the complete page that blocks interaction with everything below, and then raise only those elements that should be interactive above the overlay with an appropriate `z-index`. Does that fit your goal? – ccprog Jul 23 '20 at 00:04
  • @ccprog the page itself usually has complex z-index logic (it's based on material ui), and I don't want and usually can't change it. Moreover it was intended to be a separate tool, and in general case changing z-index "from outside" for arbitrary elements has strange effects – arsonist Jul 23 '20 at 07:40
  • @arsonist I am also stuck in a similar problem. Were you able to solve it? If yes, how? – Vishwas May 10 '21 at 10:25

1 Answers1

0

In my view, this is a problem of convenience. You would like to be able to use separate paths and treat them the same way whether they interact or not. Unfortunately, because of some limitations in the system, I do not think it is possible to achieve this convenience. However, your quetion itself is easy to answer.

First, a word about what is going on in your example. Your clipPath has a large "outer" path that is drawn clockwise (right, down, left, up). Then you have two "inner" paths drawn counterclockwise (down, right, up, left). The clip works according to what is considered inside and outside the path. The problem is that the intersection is considered outside (see link).

The straightforward solution is to use a single path for every shape, even if it is not a simple rectangle:

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 90 100 v 240 h 110 v 30 h 80 v -80 h -50 v -190 z" />
   </clipPath>
 </defs>
 <rect y="0" x="0" height="400" width="400" class="over" />
</svg>
vqf
  • 2,600
  • 10
  • 16
  • I understand, that my problem has this straightforward solution, and I am trying to discover if I could avoid this somehow, maybe with use of canvas instead of svg. I guess it's rather unpleasant task to find all isolated polygons made of arbitrary number of rectangulars and return paths for contours of all of them. Maybe it's not THAT hard, but it is definitely more difficult that making a union of rectangular (like in mask or in canvas) and than cutting it out. If I do not find any other solution, of course I will try the straightforward one, but I yet don't lose hope. – arsonist Jul 23 '20 at 13:41