【问题标题】:Mousemove event blocked by occluding SVG elements通过遮挡 SVG 元素阻止的 Mousemove 事件
【发布时间】:2017-07-05 00:09:55
【问题描述】:

我有一个带有 mousemove 事件的 SVG 元素,显示指针的坐标。当鼠标移动到遮挡元素上时,mousemove 事件被阻止。我需要使事件传播到原始元素。

我尝试更改pointer-events 属性,但这会影响所有鼠标事件,并且我需要遮挡元素来响应其他事件。有什么建议么。

【问题讨论】:

  • 将鼠标事件监听器放在一个共同的祖先元素中...
  • 您可以将处理程序放在 svg 上,然后使用 getElementFromPoint 临时更改指针事件并重复下一个对象,直到您点击 SVG,然后重新打开指针事件。可能更便宜。没有代码就很难知道。
  • 只是想,如果mousemove是拖动的一部分,也可能有一种方法(即拖动的开始,您关闭其他元素的指针事件并在拖动后将类放回已完成)
  • 嗨,Ian,我要解决的特定问题是在背景矩形上方时以局部坐标显示光标位置,同时在顶部操作对象。这很容易通过在公共连接处捕获事件来实现。我对拖动操作没有问题,因为我只使用“选定”对象和处理程序。对于通过单击或区域矩形进行的选择。您的教程在这里很有用。

标签: javascript svg snap.svg


【解决方案1】:

也许解决此问题的一种方法是将所有事件与 svg 和遮挡元素所在的容器/父节点相关联 - 将鼠标位置记录在该“外部”元素内的“bounding Client Rectangles”属性其中的子元素。

如果问题中没有代码示例,很难确切知道需要什么,但原则可能会遵循这些原则......

<!-- HTML sample -->
<div id="container">
    <div id="elem1" class="occluded">Element #1</div>
    <div id="elem2" class="occluded">Element #2</div>
    <div class="fullWidthHeightOverlay"></div>
</div>
<div id="eventLog"></div>

假设#container 有一个宽度和高度,.occluded 元素在该层内浮动,但在.fullWidthHeightOverly 元素下方(如果我理解您的问题正确,它会扮演您的 SVG 的角色)。 #eventLog 是稍后附加行为的东西。

我们还假设我们想要记录鼠标相对于外部#container 层的位置,以便在鼠标悬停在任一.occluded 元素上时提供“点击”行为。 (我希望这很清楚?)

首先我们需要将一些事件附加到#container - 在这种情况下,一个用于鼠标位置,一个用于点击。

/* SIMPLE SELECTOR - '#id' */
function $(a){
    $.b = $.b || {};
    if (void 0 === $.b[a]) $.b[a] = document.getElementById(a.slice(1));
    return $.b[a];
}

var elem1, elem2;

/* ! */
function addEvents() {
    $('#container').addEventListener('mousemove', logMousePos, false);
    $('#container').addEventListener('click', occludedClick, false);
}

您会看到我还创建了几个变量,我们可以将 .occluded 元素分配给。

接下来我们需要logMousePos()occludedClick() 函数来记录两个.occluded 元素相对于“上方”.fullWidthHeightOverly 层上鼠标位置的位置属性,然后启动一些简单的行为,例如...

function logMousePos(e) {
    var e1 = elem1.getBoundingClientRect(),
        e2 = elem2.getBoundingClientRect(),
        e1CL = elem1.classList,
        e2CL = elem2.classList;

    /* is mouse inside #elem1 ? */
    if (check.X(e.clientX, e1) && check.Y(e.clientY, e1)) {
        /* if no 'over' class add it... */
        if (!e1CL.contains('over')) e1CL.add('over');
    } else {
        /* ...if 'over' class remove it */
        if (e1CL.contains('over')) e1CL.remove('over');
    }

    /* is mouse inside #elem2 ? */
    if (check.X(e.clientX, e2) && check.Y(e.clientY, e2)) {
        if (!e2CL.contains('over')) e2CL.add('over');
    } else {
        if (e2CL.contains('over')) e2CL.remove('over');
    }
}

function occludedClick(e) {
    var e1 = elem1.getBoundingClientRect(),
        e2 = elem2.getBoundingClientRect(),
        e1CL = elem1.classList,
        e2CL = elem2.classList;

    /* was mouse clicked inside #elem1 or #elem2 or not ? */
    if (check.X(e.clientX, e1) && check.Y(e.clientY, e1)) {
        $('#eventLog').textContent = 'Element #1 pseudo-Clicked';
    } else if (check.X(e.clientX, e2) && check.Y(e.clientY, e2)) {
        $('#eventLog').textContent = 'Element #2 pseudo-Clicked';
    } else {
        $('#eventLog').textContent = '';
    }
 }

..你会注意到我指的是一些 check() 对象方法,呃,检查鼠标是否在 .occluded 元素的“boundingClientRect”内。

var check = {
    X: function(x, el) {
        return (x > el.left && x < el.right);
    },
    Y: function(y, el) {
        return (y > el.top && y < el.bottom);
    }
};

那么就只是初始化变量和添加事件监听器了……

(function init() {
    addEvents();
    elem1 = $('#elem1');
    elem2 = $('#elem2');
}());

你可以找到整个事情的 JSFIDDLE HERE

我希望这足以让您能够在您的特定情况下应用该原则。

:)

【讨论】:

    【解决方案2】:

    总结Brian Peacock优秀而详细的建议的关键思想: 似乎问题在于 svg 元素的图形顺序(什么在什么后面)不遵循 DOM 层次结构,因此不能直接使用事件的正常冒泡。因此,必须在一个共同的高点捕捉事件并务实地搜索该点下的元素。

    作为一个一般性的讨论点(请原谅我在这里),应该注意这在许多情况下不是最佳解决方案。例如,当捕捉到快速发生的事件时,例如mousemove,当 SVG 中有很多元素时,当需要快速响应以实现流畅的 GUI 时,该策略可能在计算上过于昂贵。如果 SVG 规范中包含更好的事件处理系统并包含在浏览器中,那就太好了,所有事情都可以由引擎完成。这将显着提高拥有纯 SVG GUI 的能力。

    【讨论】:

    • 确实如此。如果 SVG2 建议的实施不是那么零散就好了。但这是另一天的另一个问题。 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多