d3力场图中事件冲突解决方法

2016-02-01 Alex Sun 更多博文 » 博客 » GitHub »

原文链接 https://syaning.github.io/2016/02/01/d3-force-event-conflict/
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


通过使用d3.js,可以非常方便地创建力场图(force layout graph)。在力场图的交互中,常会涉及到多种事件,例如:缩放,拖动,点击(单击、双击、右键)等。在有些情况下,事件之间会产生冲突。

注:文中所提的node指的是力场图中的节点。

1. zoom和drag

为图表添加缩放功能的代码如下:

var zoom = d3.behavior.zoom()
    .scaleExtent([0.5, 2])
    .on('zoom', function() {
        // ...
    });
svg.call(zoom);

允许力场图的node可拖动的代码如下:

nodes.call(force.drag);

在zoom和drag共存的情况下,拖动node时候,会触发zoom的拖动事件,从而导致node无法被拖动。此时的解决方法是:

force.drag()
    .on('dragstart', function(d) {
        d3.event.sourceEvent.stopPropagation();
        // ...
    });

2. zoom和dblclick

在添加了zoom的情况下,默认双击图像会方法。此时如果想要为node添加双击事件,需要禁止zoom的双击。代码如下:

svg.on('dblclick.zoom', null);

3. drag和click

如果对node同时监听了drag事件和click事件。那么当拖动的时候,会同时触发click事件。此时的解决方法是:

nodes.on('click', function(d) {
    if (d3.event.defaultPrevented) {
        return;
    }
    // ...
});

4. click与dblclick

如果对node同时添加了click与dblclick事件,那么在双击的时候会触发两次click事件。

可以在click事件的处理函数中,判断两次点击的时间差。如果小于某个值,则认为是双击事件,不做处理;否则是单击事件,正常处理。代码如下:

nodes.on('dblclick', function(d) {
        // process double click
        // ...
    })
    .on('click', function(d) {
        if (d._clickid) {
            clearTimeout(d._clickid);
            d._clickid = null;
        } else {
            d._clickid = setTimeout(function() {
                // process simple click
                // ...
                d._clickid = null;
            }.bind(this), 350);
        }
    });

也可以只监听click事件,在click事件的处理函数中,如果判断是双击,则执行双击的处理;否则执行单击的处理。例如:

nodes.on('click', function(d) {
    if (d._clickid) {
        clearTimeout(d._clickid);
        d._clickid = null;
        // process double click
        // ...
    } else {
        d._clickid = setTimeout(function() {
            // process simple click
            // ...
            d._clickid = null;
        }.bind(this), 350);
    }
});

这种情形便是典型的throttle function