获得点鼠标交互和笔刷一起工作

时间:2016-05-20 19:23:29

标签: javascript d3.js javascript-events

我正在尝试让鼠标事件在散点图中的不同元素之间进行协作。 D3的 this.Configuration.LazyLoadingEnabled = false; 组件为被调用元素添加了一些侦听器(例如brush)。我还希望显示绑定在SVG上的点,就像散点图一样,以及那些支持svg.call(brush)事件的点(用于工具提示和其他交互)。

A previous solution建议在调用画笔之前绘制点,它支持在点上鼠标悬停,同时允许绘制画笔并修改范围。但是,如果画笔的拖动动作开始于一个点(我预计在非常密集的图形中),当画笔组件已经处于活动状态时(画笔的大小调整大小),画笔组件会出现异常。您可以在this example上进行试用,其中已实施上述建议的解决方案。

Failed solution example

我已将问题范围缩小到如何在mouseover组件内部的d3的brushstart()函数中处理事件。这是刷子正确工作时相关变量的样子。

d3.svg.brush

这是目前的情况,使用上述解决方案:

                     this           eventTarget                           dragging   resizing  
                     -------------- ------------------------------------- ---------- ---------- 
Translating extent   brush parent   rect.extent                           true       0         
Resizing extent      brush parent   rect (invisible rects for resizing)   false      e.g. "e"  
Redrawing            brush parent   rect.background                       false      0         

真正的问题是:我如何捏造d3.event.target的来源以匹配第一个表?如果我能做到这一点,我就可以得到我想要的行为。谢谢你的帮助!

如果你错过了,这就是这个难题的重点:http://bl.ocks.org/yelper/d38ddf461a0175ebd927946d15140947

2 个答案:

答案 0 :(得分:3)

这是一个快速破解纠正行为的行为:

.on('mousedown', function(d){
    var e = brush.extent(),
      m = d3.mouse(svg.node()), // pointer position with respect to g
      p = [x.invert(m[0]), y.invert(m[1])]; // position in user space

  if ( brush.empty() || // if there is no brush
       (e[0][0] > d[0] || d[0] > e[1][0]
       || e[0][1] > d[1] || d[1] > e[1][1] ) // or our current circle is outside the bounds of the brush
  ) {
    brush.extent([p,p]); // set brush to current position
  } else {
    d3.select(this).classed('extent', true); // else we are moving the brush, so fool d3 (I got this from looking at source code, it's how d3 determines a drag)
  }
});

下面的工作代码,更新了阻止here



<!DOCTYPE html>
<meta charset="utf-8">
<style>
    .axis path,
    .axis line {
        fill: none;
        stroke: #000;
        shape-rendering: crispEdges;
    }
    
    .hidden {
        opacity: 0.3;
    }
    
    
    .extent {
        fill: #000;
        fill-opacity: .125;
        stroke: #fff;
    }
</style>
<body>
<script src="//d3js.org/d3.v3.js"></script>
<script>
    
var margin = {top: 20, right: 50, bottom: 30, left: 50},
    width = 960 - margin.left - margin.right,
    height = 350 - margin.top - margin.bottom;
    
var x = d3.scale.linear()
    .range([0, width])
    .domain([0, 10]);
    
var y = d3.scale.linear()
    .range([height, 0])
    .domain([0, 10]);
    
var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom");

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left");
    
var curPt = d3.select('body')
    .append('p')
    .html("Current point: ")
    .append('span')
        .attr('id', 'curPt');
    
var svg = d3.select('body').insert('svg', 'p')
    .attr('width', width + margin.left + margin.right)
    .attr('height', height + margin.top + margin.bottom)
  .append('g')
    .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
    
svg.append('g')
    .attr('class', 'x axis')
    .attr('transform', 'translate(0,'+height+')')
    .call(xAxis);
    
svg.append('g')
    .attr('class', 'y axis')
    .call(yAxis);
    
var brush = d3.svg.brush()
    .x(x)
    .y(y)
    .on("brush", function() {
        var e = brush.extent(),
        	  c = svg.selectAll('circle');
      	c.classed('extent', false);
        c.classed('hidden', function(d) {
            return e[0][0] > d[0] || d[0] > e[1][0]
                || e[0][1] > d[1] || d[1] > e[1][1];
            }
        );
    })
    .on("brushend", function() {
        if (brush.empty()) svg.selectAll('circle').classed('hidden', false);
    });
    
svg.call(brush);
    
var data = d3.range(50).map(function() { return [Math.random() * 10, Math.random() * 10]; });

svg.append('g') 
    .attr('class', 'points')
    .selectAll('circle')
    .data(data).enter()
  .append('circle')
    .attr('cx', function(d) { return x(d[0]); })
    .attr('cy', function(d) { return y(d[1]); })
    .attr('r', 10)
    .style('fill', 'steelblue')
    .on('mouseover', function(d) {
        curPt.html("[" + d[0] + ", " + d[1] + "]");
    })
    .on('mouseout', function(d) {
        curPt.html("");
    })
		.on('mousedown', function(d){
  		var e = brush.extent(),
          m = d3.mouse(svg.node()),
          p = [x.invert(m[0]), y.invert(m[1])];
       
      if ( brush.empty() || 
           (e[0][0] > d[0] || d[0] > e[1][0]
           || e[0][1] > d[1] || d[1] > e[1][1] )   
      ) {
        brush.extent([p,p]);
      } else {
        d3.select(this).classed('extent', true);
      }
    });

    
</script>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

以下是工作示例: https://jsfiddle.net/paradite/rpqusqdc/2/

基本上,我使用drag事件而不是brush使用我之前编码的范围选择工具:http://bl.ocks.org/paradite/71869a0f30592ade5246

它不会干扰您的圈子。因此,您只需获取当前的rect维度并相应地更新您的圈子:

  // select your points here
  var e = selectionRect.getCurrentAttributes();
  svg.selectAll('circle').classed('hidden', function(d) {
    return e.x1 > x(d[0]) || x(d[0]) > e.x2 || e.y1 > y(d[1]) || y(d[1]) > e.y2;
  });

当然,你可以删除部分逻辑,因为你的情况不需要它。