SVG奇淫巧技(二):关于交互那点事
我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第2篇文章。
SVG
毕竟也是DOM
中的一员,所以除了单纯的展示之外,挂点事件之类的操作也是分内之事,不过,SVG
在实践交互的过程中,总是会出现一些奇奇怪怪的现象,今天我们就来聊聊关于SVG
交互的那些事。
三、SVG的交互事件
先来举个栗子,一个非填充的path
元素绘制的三角形,想实现一个hover
时颜色填满的功能,脑补一下怎么实现呢?
-
先来用
path
实现一个fill="none"
的三角形:<svg width="200" height="100" viewBox="0 0 200 100" xmlns="http://www.w3.org/2000/svg"> <path d="M 100 10 L 190 90 H 10 Z" stroke="#cd0000" fill="none"> </svg>
-
在CSS中对
path
添加hover
时修改fill
的样式:path:hover { fill: #cd0000; cursor: pointer; }
嗯,颅内渲染了一下,看起来没什么问题,让我们看下实践的效果:
可以看到,鼠标hover
到三角形内部时,三角形并没有换色,这和我们最初的想法不一致,难道说SVG
压根不支持hover
事件?要知道,作为DOM
不支持事件,那可以算是妥妥的大逆不道了啊,这明显不符合常识,让我们稍安勿躁,再仔细观察一下:
可以看到,当我们小心翼翼的将鼠标hover
到path
的stroke
上时,三角形被填满了红色。目的实现,看来SVG
不支持hover
事件是多虑了,但是新的问题又来了,stroke
那么小的交互区域,就算能被hover
也是于事无补啊,鼠标触发都如此的捉襟见肘,这要是放在手指交互的移动端,我都能脑补出用户为了复现那电光火石般的hover
事件手指在屏幕上搓出的火星。
不过,SVG
当然不会这么“傻X”的只给用户这么一点点的交互区域,但是在解决这个问题之前,我们需要先了解,为什么一个非填充的<path>
只有stroke
那么一丁点的区域支持事件交互呢?
这就要从一个CSS
属性说起。
CSS有一个属性 pointer-events
,它经常被用来实现事件穿透的效果,即pointer-events: none;
。
不过,我们今天的主角不是事件穿透的实现,而是pointer-events
属性本身,如果现在提问这个属性有几个可选值,相信大部分小伙伴都能答出来一些,例如:
/* 属性值 */
pointer-events: auto;
pointer-events: none;
/* Global values */
pointer-events: inherit;
pointer-events: initial;
pointer-events: unset;
这些可选值,除了none
是常用的之外,auto
作为默认值,其余3个都是全局可选值,几乎适用大部分CSS
属性。
好,我们已经答出了pointer-events
这个属性一半的可选值了,还有一半的可选值分别是:
pointer-events: visiblePainted; /* 只适用于 SVG */
pointer-events: visibleFill; /* 只适用于 SVG */
pointer-events: visibleStroke; /* 只适用于 SVG */
pointer-events: visible; /* 只适用于 SVG */
pointer-events: painted; /* 只适用于 SVG */
pointer-events: fill; /* 只适用于 SVG */
pointer-events: stroke; /* 只适用于 SVG */
pointer-events: all; /* 只适用于 SVG */
先别管这些值分别代表了什么意思,只看后面的备注就很明显了,这些值都只是适应于SVG
元素的,那么问题应该就出现在他们身上,我们现在来看下MDN
上对于这些值的定义:
visiblePainted
:元素只有在以下情况才会成为鼠标事件的目标:•
visibility
属性值为visible
,且鼠标指针在元素内部
,且fill
属性非none
。•
visibility
属性值为visible
,鼠标指针在元素边界
上,且stroke
属性非none
。
visibleStroke
: 只有在元素visibility
属性值为visible
,且鼠标指针在元素边界
时,元素才会成为鼠标事件的目标,stroke
属性的值不影响事件处理。
visibleFill
: 只有在元素visibility
属性值为visible
,且鼠标指针在元素内部
时,元素才会成为鼠标事件的目标,fill
属性的值不影响事件处理。
visible
: 只有在元素visibility
属性值为visible
,且鼠标指针在元素内部
或边界
时,元素才会成为鼠标事件的目标,fill
和stroke
属性的值不影响事件处理。
painted
: 元素只有在以下情况才会成为鼠标事件的目标:• 鼠标指针在元素
内部
,且fill
属性指定了none
之外的值• 鼠标指针在元素边
界上
,且stroke
属性指定了none
之外的值
visibility
属性的值不影响事件处理。
fill
: 只有鼠标指针在元素内部
时,元素才会成为鼠标事件的目标,fill
和visibility
属性的值不影响事件处理。
stroke
: 只有鼠标指针在元素边界
上时,元素才会成为鼠标事件的目标,stroke
和visibility
属性的值不影响事件处理。
all
: 只有鼠标指针在元素内部
或边界
时,元素才会成为鼠标事件的目标fill
、stroke
和visibility
属性的值不影响事件处理。
我承认概念有点多,所以我整理了下面的表格:
属性 | 响应范围 | visibility | fill | stroke |
---|---|---|---|---|
visiblePainted | 内部 or 边界 | visible | 非none | 非none |
visibleStroke | 边界 | visible | -- | 不限制 |
visibleFill | 内部 | visible | 不限制 | -- |
visible | 内部 or 边界 | visible | 不限制 | 不限制 |
painted | 内部 or 边界 | 不限制 | 非none | 非none |
fill | 内部 | 不限制 | 不限制 | -- |
stroke | 边界 | 不限制 | -- | 不限制 |
all | 内部 or 边界 | 不限制 | 不限制 | 不限制 |
OK,这下简单易懂多了吧,现在我们对另一半只适用于SVG
的可选值也有了解了,那么解决方案也应该相出来了:
-
不涉及visibility
隐藏/显示的解决方案且边框是否响应无所谓
的情况:pointer-events: visibleFill / visible / fill / all;
-
不涉及visibility
隐藏/显示的解决方案且边框需要响应
的情况:pointer-events: visible / all;
-
涉及visibility
隐藏/显示的解决方案且边框需要响应
的情况:pointer-events: visible;
现在,我们已经有了解决方案,最通用的无非就是visbible
了,我们只需要在CSS
中修改这个属性值即可;
path {
pointer-events: visible;
}
不过,虽然问题解决了,却并没有解惑,回到最初的问题,我们没有修改pointer-events
的值,为什么SVG
只响应边框而内部却不响应呢?
难道是默认的auto
做了什么手脚?我们再来看下关于auto
的描述:
auto
: 与pointer-events
属性未指定时的表现效果相同,对于SVG
内容,该值与visiblePainted
效果相同。
疑惑解开了,当 pointer-events
为默认值时,其表现与 visiblePainted
相同,我们再来看下 visiblePainted
的定义:
visiblePainted
:元素只有在以下情况才会成为鼠标事件的目标:•
visibility
属性值为visible
,且鼠标指针在元素内部
,且fill
属性非none
。•
visibility
属性值为visible
,鼠标指针在元素边界
上,且stroke
属性非none
。
所以,三角形的内部不响应是因为默认为 visiblePainted
的前提下,我们只指定了stroke
的颜色,而fill="none"
。
那我们应该也有了另一种解决方案,将fill
设置为transparent
,即透明的填充色是不是也能起到一样的效果呢?
<svg width="200" height="100" viewBox="0 0 200 100" xmlns="http://www.w3.org/2000/svg">
<path d="M 100 10 L 190 90 H 10 Z" stroke="#cd0000" fill="transparent">
</svg>
果然可行,看来SVG
交互那点事也不过如此了。
总结一下:
遇到只渲染边框的SVG
交互时,通用解决方案为:
- 填充透明色,
fill="transparent"
; - 修改CSS属性,
pointer-events: visible
;
以上这些不知道大家学会了没有,没学会就扔进收藏夹吃灰吧,毕竟收藏就等于学会了嘛。不过,SVG
的交互与fill
和stroke
的值息息相关,这样的话,我们下一篇就来撸撸这俩属性吧。
转载自:https://juejin.cn/post/7140092589725876231