使用指针事件属性管理 SVG 交互
已发表: 2022-03-10pointer-events
属性来塑造SVG 图像的交互性——即控制文档的哪些部分可以接收点击、触摸或点击。尝试单击或点击下面的 SVG 图像。 如果您将指针放在正确的位置(阴影路径),那么您应该在新的浏览器选项卡中打开 Smashing Magazine 的主页。 如果您尝试单击一些空白区域,您可能会感到非常困惑。
这是我在最近的一个包含 SVG 图像中的链接的项目中面临的困境。 有时当我单击图像时,链接有效。 其他时候没有。 令人困惑,对吧?
我转向 SVG 规范以了解更多关于可能发生的事情以及 SVG 是否提供修复的信息。 答案: pointer-events
。
不要与 DOM(文档对象模型)指针事件混淆, pointer-events
既是CSS属性又是SVG元素属性。 有了它,我们可以管理 SVG 文档或元素的哪些部分可以接收来自指针设备(如鼠标、触控板或手指)的事件。
关于术语的说明:“指针事件”也是与设备无关的、用于用户输入的 Web 平台功能的名称。 然而,在本文中——出于pointer-events
属性的目的——短语“指针事件”也包括鼠标和触摸事件。
开箱即用:SVG 的“形状模型”
将 CSS 与 HTML 结合使用会在 HTML 上强加一个盒子布局模型。 在盒子布局模型中,每个元素都会围绕其内容生成一个矩形。 该矩形可能是内联、内联级、原子内联级或块,但它仍然是一个具有四个直角和四个边的矩形。 当我们向元素添加链接或事件侦听器时,交互区域与矩形的尺寸相匹配。
注意:向交互元素添加clip-path
会改变其交互边界。 换句话说,如果您将六边形clip-path
路径添加到a
元素,则只有剪辑路径中的点是可点击的。 同样,添加倾斜变换会将矩形变成菱形。
SVG 没有盒子布局模型。 您会看到,当 HTML 文档包含 SVG 文档时,在 CSS 布局中,根 SVG 元素遵循盒子布局模型。 它的子元素没有。 因此,大多数与 CSS 布局相关的属性不适用于 SVG。
因此,SVG 拥有我称之为“形状模型”的东西。 当我们向 SVG 文档或元素添加链接或事件侦听器时,交互区域不一定是矩形。 SVG 元素确实有一个边界框。 边界框被定义为:与该元素的用户坐标系的轴对齐的最紧密的矩形,完全包围它及其后代。
但最初,SVG 文档的哪些部分是交互式的,取决于哪些部分是可见的和/或绘制的。
绘制与可见元素
SVG 元素可以“填充”,但也可以“抚摸”。 填充是指形状的内部。 笔画是指它的轮廓。
“填充”和“描边”一起是绘制操作,将元素渲染到屏幕或页面(也称为画布)。 当我们谈论绘制元素时,我们的意思是元素具有填充和/或描边。 通常,这意味着该元素也是可见的。
但是,可以在不可见的情况下绘制 SVG 元素。 如果visible
属性值或 CSS 属性被hidden
或display
为none
,则会发生这种情况。 元素在那里并占据理论空间。 我们只是看不到它(辅助技术可能无法检测到它)。
也许更令人困惑的是,一个元素也可以是可见的——也就是说,有一个计算visibility
的visible
性值——而不被绘制。 当元素同时缺少描边和填充时,就会发生这种情况。
注意:具有 alpha 透明度的颜色值(例如rgba(0,0,0,0)
)不会影响元素是否被绘制或可见。 换句话说,如果一个元素有一个 alpha 透明填充或描边,即使看不到它也会被绘制。
了解元素何时被绘制、可见或两者都不被绘制对于理解每个pointer-events
值的影响至关重要。
全有或无或介于两者之间:价值观
pointer-events
既是 CSS 属性又是 SVG 元素属性。 它的初始值为auto
,这意味着只有绘制的和可见的部分会接收指针事件。 大多数其他值可以分为两组:
- 要求元素可见的值; 和
- 没有的值。
painted
、 fill
、 stroke
, all
属于后一类。 它们依赖于可见性的对应物—— visiblePainted
、 visibleFill
、 visibleStroke
和visible
—— 属于前者。
SVG 2.0 规范还定义了bounding-box
值。 当pointer-events
的值为bounding-box
时,元素周围的矩形区域也可以接收到指针事件。 在撰写本文时,只有 Chrome 65+ 支持bounding-box
值。
none
也是一个有效值。 它防止元素及其子元素接收任何指针事件。 pointer-events
CSS 属性也可以用于 HTML 元素。 但是当与 HTML 一起使用时,只有auto
和none
是有效值。
由于pointer-events
值的演示比解释更好,让我们看一些演示。
在这里,我们有一个应用了填充和描边的圆圈。 它既是画的又是可见的。 整个圈子都可以接收到指针事件,但是圈子外面的区域不能。
禁用填充,使其值为none
。 现在,如果您尝试悬停、单击或点击圆圈的内部,则不会发生任何事情。 但是如果你对笔画区域做同样的事情,指针事件仍然会被调度。 将fill
值更改为none
意味着该区域可见,但未绘制。
让我们对标记做一个小改动。 我们将添加pointer-events="visible"
到我们的circle
元素,同时保持fill=none
。
现在被笔画包围的未绘制区域可以接收指针事件。
增加 SVG 图像的可点击区域
让我们回到本文开头的那张图。 我们的“紫水晶”是一个path
元素,而不是一组多边形,每个多边形都有一个stroke
和fill
。 这意味着我们不能只添加pointer-events="all"
就结束了。
相反,我们需要扩大点击区域。 让我们使用我们所知道的关于绘制和可见元素的知识。 在下面的示例中,我在图像标记中添加了一个矩形。
即使这个矩形是看不见的,它在技术上仍然是可见的(即visibility: visible
)。 然而,它没有填充意味着它没有被绘制。 我们的图像看起来一样。 事实上,它的工作原理仍然相同——单击空白仍然不会触发导航操作。 我们仍然需要为我们a
元素添加一个pointer-events
属性。 使用visible
或all
值将在这里工作。
现在整个图像可以接收指针事件。
使用bounding-box
将消除对幻像元素的需要。 边界框内的所有点都将接收指针事件,包括路径所包围的空白区域。 但同样: pointer-events="bounding-box"
没有得到广泛支持。 在此之前,我们可以使用未绘制的元素。
在混合 SVG 和 HTML 时使用pointer-events
pointer-events
另一种情况:在 HTML 按钮中使用 SVG。
在大多数浏览器中——Firefox 和 Internet Explorer 11 是例外—— event.target
的值将是一个 SVG 元素,而不是我们的 HTML 按钮。 让我们将pointer-events="none"
添加到打开的 SVG 标记中。
现在,当用户单击或点击我们的按钮时, event.target
将引用我们的button
。
精通 DOM 和 JavaScript 的人会注意到,使用function
关键字而不是箭头函数和this
而不是event.target
可以解决这个问题。 但是,使用pointer-events="none"
(或 CSS 中的pointer-events: none;
)意味着您不必将特定的 JavaScript 怪癖提交到内存中。
结论
SVG 支持与 HTML 相同的交互方式。 我们可以使用它来创建响应点击或点击的图表。 我们可以创建不符合 CSS 和 HTML 框模型的链接区域。 通过添加pointer-events
,我们可以改进 SVG 文档响应用户交互的方式。
浏览器对 SVG pointer-events
的支持非常强大。 每个支持 SVG 的浏览器都支持 SVG 文档和元素的属性。 当与 HTML 元素一起使用时,支持的健壮性稍差。 它在 Internet Explorer 10 或其前身或任何版本的 Opera Mini 中均不可用。
在这篇文章中,我们刚刚触及了pointer-events
的表面。 如需更深入的技术处理,请通读 SVG 规范。 MDN(Mozilla 开发者网络)Web Docs 为pointer-events
提供了更多对 Web 开发者友好的文档,并附有示例。