如何重用自定义事件

How to reuse custom events

我正在使用具有相当丰富的用户交互的 D3 构建可视化。目前我的背景 canvas 支持以下事件:

我正在尝试管理所有这些事件,但我已经到了开始为应用程序的不同部分重复我的代码的地步。例如,在应用程序中:

  1. 双击不应触发单击操作
  2. 双击也应该忽略用户可能不小心轻微拖动的事实

所以我设计了一种方法来做到这一点。我现在感兴趣的是,是否有一种技术允许我重新使用我创建的事件(有点像你创建一个 d3.behaviour.zoom 并使用 .call)。

所以为了说明


单击 + 双击

我有一些行为可以在单击时启动计时器,这将在超时后触发单击行为。如果再次单击并且计时器仍然存在,则取消它并改为 运行 双击行为。我目前正在这样连接:

d3.select(target)
  .append("circle")
  .on("click", function (d) {
    __onClick(d, this);
  });

然后我这样定义 __onClick

var __onClick = function (d) {
    var self = this;

    if (d3.event.defaultPrevented) return; // ignore drag
    d3.event.stopPropagation();            // prevent event bubbling

    // If we already have a timer then we need to execute
    // a double click behavior event
    if (self.__timer) {
        console.log("dblclick simulated");
        clearTimeout(self.__timer);
        self.__timer = null;
        self.DoADoubleClickThingy();
        return;
    }

    // Start a single click operation
    self.__timer = setTimeout(function () {
        console.log("click simulated");
        self.__timer = null;
        self.DoSingleClickThingy();
    }, 250);
};

所以我想我想知道,是否可以创建如下所示的内容来注册新行为?我目前正在尝试查看 D3 代码,但不太了解它是否可行。

var clicks = d3.behaviours.customDoubleClick
             .clickSpeed(250)
             .on("click", function() { self.DoSingleClickThingy(); })
             .on("dblclick", function() { self.DoADoubleClickThingy(); });

d3.select(target)
      .append("circle")
      .call(clicks);

您可以使用一个工厂函数,给定您想要的行为类型,returns一个安装适当处理程序的函数:

function mkClicks(type) {
  // figure out what "self" should be depending on type
  return function(sel) {
    sel.on("click", function() { self.DoSingleClickThingy(); })
       .on("dblclick", function() { self.DoADoubleClickThingy(); });
  }
}

d3.select(target)
  .append("circle")
  .call(mkClicks(typeA));

再次阅读 github 上 D3zoom 代码后,我意识到它并不像我想象的那么复杂 - 特别是当我意识到 call 实际上只是执行了 zoom 函数。这是我放在一起的解决方案,我希望放在 github 或类似的:

var custom = { };
custom.events = function () {

    var clickTimeout = 3000;        // The timeout that is used to distinguish between a single and double click
    var clickTimer;                 // The timer that is used for a single click event

    var dispatch = d3.dispatch("click", "dblclick");

    function events(g) {

        g.on("click", clicked)
         .on("dblclick", doubleClicked);
    };

    function clicked(d, i) {

        if (d3.event.defaultPrevented) return;      // Ignore if a drag operation is taking place
        d3.event.stopPropagation();                 // Prevent the event going any further

        // If we already have a timer then we need to execute
        // a double click behaviour event
        if (clickTimer) {
            clearTimeout(clickTimer);
            clickTimer = null;
            dispatch.dblclick.call(this, d, i);
            return;
        }

        // Setup the timer for single click
        clickTimer = setTimeout(function () {
            // Trigger a single click
            clickTimer = null;
            dispatch.click.call(this, d, i);
        }, clickTimeout);
    };

    function doubleClicked(d, i) {
        // Suppress the natural double click event
        d3.event.stopPropagation();
    };

    // Return the bound events
    return d3.rebind(events, dispatch, "on");
};