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