使用d3.dispatch在d3组件之间广播事件

时间:2014-06-06 13:20:57

标签: javascript svg d3.js

我目前正致力于创建可重复使用的d3组件库,并希望创建一个"交互层"基本上在我的可视化上创建一个svg矩形的组件,然后将其与其交互广播到其他组件来监听这些交互,即。鼠标悬停,鼠标移动,点击等

我一直在关注Mike Bostock关于可重复使用的d3图表(http://bost.ocks.org/mike/chart/)的模式,而我可以在每个组件内部创建一个调度程序来在内部发送事件我还没有能够成功地制作单独的组件来监听另一个组件的调度事件。所以基本上我想为我的库提供一个调度程序,如果使用的话,所有组件都会收听。

我已经做了很多搜索但是没有找到答案,甚至没有提示如何做到这一点,如果有人有任何想法,我会非常感激。

3 个答案:

答案 0 :(得分:6)

这是一种将事件直接与d3组件相关联的方法,而不是强制您注册全局命名空间中的每个事件。

广播事件的组件可能如下所示:

namespace.interactiveLayer = function() {

    dispatch = d3.dispatch('myEvent'); 

    me = function() {
        // ...
        svg.on('click', function() {
          pos = d3.mouse(this);
          dispatch.myEvent(pos);
        });
    };

    me = d3.rebind(me, dispatch, 'on')

    return me;
};

侦听事件的组件可能如下所示:

namespace.listenerLayer = function() {

    me = function() {
        interactive = namespace.interactiveLayer();
        d3.select("body").call(interactive);
        interactive.on('myEvent', function(d) {
            alert('You clicked on ' + d);
        });
    };

    return me;
};

我已经装满了working example here

答案 1 :(得分:1)

所以我意识到我可以通过在我的库命名空间下保存对d3.dispatch的引用并使用我的组件内部来绑定其他事件来实现这一点。

喜欢这个。

namespace = {}
namespace.dispatch = d3.dispatch('mousemove');

//Interactive layer component
namespace.dispatch.mousemove(args)

//Other components listening for events
namespace.dispatch.on('mousemove', function(args){
    //do something here
})

然后在我的交互式图层组件中,我发送事件,在另一个组件中,我听它。

答案 2 :(得分:0)

我遇到了类似的问题,并使用以下设计原则解决了该问题:

  1. 组件之间不能直接对话
  2. 保留一个负责创建和管理组件状态的中央协调器
  3. 每个组件都提供其可以进行的所有调度call的合同
  4. 协调器在创建组件时,还会创建必要的调度,并在实例化组件时设置
  5. 协调器监听事件,并根据需要采取适当的措施(通过调用其他组件的公共功能)

这是一个例子。这个虚拟的“画笔”组件不会产生输出,但是您可以在控制台日志中看到正在发生的事情。

// Here's my re-usable component
var myown = {};
myown.brush = function(){

  // setup private variables
  var height, dispatcher;

  function my(group){
    group.each(function(d,i){
      // here is the code for creating brush
      // note `this` is also available (in addition to d and i), for e.g.
      var b = d3.select(this);

      console.log("inside my each "+height);
      if (dispatcher) {
        dispatcher.call("test1");
      }
    });
  }

  my.move = function (group, position){
    console.log("in move function");

    group.each(function(d,i){
      // here is the code for moving the brush
      // note `this` is also available (in addition to d and i), for e.g.
      var b = d3.select(this), that = this;

      console.log("inside move each");
      console.log(typeof(position));   // note it is function

      // now call that function
      // using "apply" and "arguments"
      let pos = position.apply(that, arguments);
      console.log(pos);
      if (dispatcher) {
        dispatcher.call("test2");
      }
    })

    return my;
  }

  my.height = function(value){
    if(!arguments.length) return value;
    height = +value;
    return my;
  }

  my.dispatcher = function(value){
    if(!arguments.length) return dispatch ? true : false;
    dispatcher = value;
    return my;
  }

  return my;
};

// the orchestrator creates the dispatch, and the callers
var myDispatch = d3.dispatch("test1", "test2");

myDispatch.on("test1", function(){
  console.log("test1 has been called!!!");
  // call the public functions of other components (for e.g. similar to brush.move) here
});

myDispatch.on("test2", function(){
  console.log("and now, test2 has also been called!!!");
  // call the public functions of other components (for e.g. similar to brush.move) here
});

// Okay, now use all that nice code

var data = [
  {id: 1, mv: [100,500]},
  {id: 2, mv: [300,600]},
  {id: 3, mv: [800,1000]}
];

// orchestrator sets the dispatch created during instantiantion
var brush = myown.brush().height(90).dispatcher(myDispatch);

var svg = d3.select("svg");

var g = svg.selectAll("g").data(data).enter()
  .append("g")
  .attr("transform", (d,i)=>"translate(0,"+i*100+")");

g.call(brush).call(brush.move, (d,i)=>d.mv);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<title>Boilerplate for chaining component, and also for testing dispatch events</title>

<svg width=1500 height=1000></svg>

请注意,如果您在实例化过程中未设置调度,即

var brush = myown.brush().height(90); //.dispatcher(myDispatch);

该组件将根本不会拨打电话,因为尚未设置调度程序。