Мелкозернистая обработка событий с помощью щеток D3

У меня есть график рассеяния, который генерируется с использованием D3. Точки (круги SVG) на графике можно выбрать, щелкнув по ним, а области можно выбрать с помощью кисти D3.

Чтобы круги получили событие клика, мне нужно сначала создать кисть, чтобы круги были над ней. К сожалению, это означает, что я не могу перетащить, чтобы создать размер кисти, когда мой курсор находится над точкой в ​​сюжете.

Есть ли способ передать наведения и щелчки события в круги, но обрабатывать связанные с ним события с помощью кисти?

Ответ 1

Это может быть выполнено, но с использованием API-интерфейсов D3 (см. примечание ниже).

Это пример http://bl.ocks.org/4747894, где:

  • Элемент brush находится за кругами
  • Круги реагируют на событие mousedown. (Может также реагировать на другие события.)
  • Элемент brush хорошо себя ведет, даже если перетаскивание начинается из одного из кругов.

Некоторая трассировка и просмотр исходного кода D3 показывает, что extent не является reset правильно, когда mousemove событие запускается из элемента circle поверх кисти. Это можно устранить, сбросив extent для кисти в прослушивателе mousedown для элементов circle:

        circles.on("mousedown", function (d, i) {
            // _svg_ is the node on which the brush has been created
            // x is the x-scale, y is the y-scale
            var xy = d3.mouse(svg.node()),
                xInv = x.invert(xy[0]),
                yInv = y.invert(xy[1]);

            // Reset brush extent
            brush.extent([[xInv, yInv], [xInv, yInv]]);

            // Do other stuff which we wanted to do in this listener
        });

Примечание: В соответствии с API выбор кисти не будет обновлен автоматически при вызове .extent(values). Простое нажатие на круг будет reset extent, но не будет перерисовывать сделанный выбор. Выбор будет отброшен только тогда, когда в circle будет запущен другой выбор, или щелкнув вне кругов и текущий выбор. Это, как я понимаю, из желаемого поведения. Однако это может сломать код, который написан с предположением, что все, что есть extent кисти, будет выбором, видимым на графике.

Ответ 2

Используйте selection.on: http://jsfiddle.net/NH6zD/1

var target,
    dimensions = {width: 200, height: 200},
    svg = d3.select("body").append("svg").attr(dimensions),
    rect = svg.append("rect").attr(dimensions); // Must be beneath circles 
svg
  .on("mousedown", function() {
      target = d3.event.target || d3.event.srcElement;
      if ( target === rect.node() ) {
          /* Brush */
      } else {
          /* Circle */
      }
  })
  .on("mousemove", function() {
      if (!target) return;
      if ( target === svg.rect() ) {
          /* Brush */
      } else {
          var mouse = d3.mouse(svg.node());
          target.attr({x: mouse[0], y: mouse[1]});
      }
  });
(function(exit) {
    for (var i in exit) svg.on(exit[i], function() { target = undefined; });
})(["mouseout", "mouseup"]);