likes
comments
collection
share

SVG奇淫巧技(二):关于交互那点事

作者站长头像
站长
· 阅读数 52

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第2篇文章。

SVG毕竟也是DOM中的一员,所以除了单纯的展示之外,挂点事件之类的操作也是分内之事,不过,SVG在实践交互的过程中,总是会出现一些奇奇怪怪的现象,今天我们就来聊聊关于SVG交互的那些事。

三、SVG的交互事件

先来举个栗子,一个非填充的path元素绘制的三角形,想实现一个hover时颜色填满的功能,脑补一下怎么实现呢?

  1. 先来用path实现一个fill="none"的三角形:

    SVG奇淫巧技(二):关于交互那点事

    <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>
    
  2. 在CSS中对path添加hover时修改fill的样式:

    path:hover {
      fill: #cd0000;
      cursor: pointer;
    }
    

嗯,颅内渲染了一下,看起来没什么问题,让我们看下实践的效果:

SVG奇淫巧技(二):关于交互那点事

可以看到,鼠标hover到三角形内部时,三角形并没有换色,这和我们最初的想法不一致,难道说SVG压根不支持hover事件?要知道,作为DOM不支持事件,那可以算是妥妥的大逆不道了啊,这明显不符合常识,让我们稍安勿躁,再仔细观察一下:

SVG奇淫巧技(二):关于交互那点事

可以看到,当我们小心翼翼的将鼠标hoverpathstroke上时,三角形被填满了红色。目的实现,看来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,且鼠标指针在元素内部边界时,元素才会成为鼠标事件的目标,fillstroke属性的值不影响事件处理。

painted: 元素只有在以下情况才会成为鼠标事件的目标:

• 鼠标指针在元素内部,且fill属性指定了none之外的值

• 鼠标指针在元素边界上,且stroke属性指定了none之外的值

visibility属性的值不影响事件处理。

fill: 只有鼠标指针在元素内部时,元素才会成为鼠标事件的目标,fillvisibility属性的值不影响事件处理。

stroke: 只有鼠标指针在元素边界上时,元素才会成为鼠标事件的目标,strokevisibility属性的值不影响事件处理。

all: 只有鼠标指针在元素内部边界时,元素才会成为鼠标事件的目标fillstrokevisibility属性的值不影响事件处理。

我承认概念有点多,所以我整理了下面的表格:

属性响应范围visibilityfillstroke
visiblePainted内部 or 边界visible非none非none
visibleStroke边界visible--不限制
visibleFill内部visible不限制--
visible内部 or 边界visible不限制不限制
painted内部 or 边界不限制非none非none
fill内部不限制不限制--
stroke边界不限制--不限制
all内部 or 边界不限制不限制不限制

OK,这下简单易懂多了吧,现在我们对另一半只适用于SVG的可选值也有了解了,那么解决方案也应该相出来了:

  1. 不涉及visibility隐藏/显示的解决方案且边框是否响应无所谓的情况:

    pointer-events: visibleFill / visible / fill / all;
    
  2. 不涉及visibility隐藏/显示的解决方案且边框需要响应的情况:

    pointer-events: visible / all;
    
  3. 涉及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交互那点事也不过如此了。

总结一下:

遇到只渲染边框的SVG交互时,通用解决方案为:

  1. 填充透明色,fill="transparent";
  2. 修改CSS属性,pointer-events: visible;

以上这些不知道大家学会了没有,没学会就扔进收藏夹吃灰吧,毕竟收藏就等于学会了嘛。不过,SVG的交互与fillstroke的值息息相关,这样的话,我们下一篇就来撸撸这俩属性吧。

转载自:https://juejin.cn/post/7140092589725876231
评论
请登录