2016-05-20 83 views
2

我試圖讓鼠標事件在散點圖中的不同元素之間進行合作。 D3的brush組件爲被調用的元素添加了一些偵聽器(例如svg.call(brush))。我還想顯示SVG上的點,就像散點圖一樣,以及支持mouseover事件(用於工具提示和其他交互)的點。獲取點鼠標交互和畫筆一起工作

A previous solution在調用畫筆之前建議繪圖點,該畫筆支持鼠標懸停在點上,同時允許繪製畫筆並修改範圍。但是,如果筆刷的拖動動作是從一個點開始的(我預計這個點在非常密集的圖形中),則當某個程度段已經處於活動狀態時,畫筆組件會發生錯誤(轉而刷新畫筆的大小)。您可以在this example上試用,上述建議的解決方案已經實施。

Failed solution example

我已經縮小問題的事件是如何在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   

這是什麼樣子目前,與上述解決方案:

     this   eventTarget dragging resizing   
-------------------- -------------- ------------- ---------- ---------------- 
Translating extent brush parent circle  false  circle.datum() 
Resizing extent  brush parent circle  false  circle.datum() 
Redrawing   brush parent circle  false  circle.datum() 

真正的問題是:我怎麼能矇混d3.event.target相匹配的第一個表的來源是什麼?如果我能做到這一點,我可以得到我想要的行爲。謝謝你的幫助!

如果你錯過了,這裏是這個難題的行動bl.ock:http://bl.ocks.org/yelper/d38ddf461a0175ebd927946d15140947

+0

在空刷的情況下正在啓動一個圓,你可能只是做這樣的事情:http://blockbuilder.org/larsenmtl/2862c433899f63456f5279e4a6281f5e,不能解決的舉動從一個圓圈開始。 – Mark

回答

2

這裏有一個快速的黑客,其修正行爲:

.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(), 
 
     \t c = svg.selectAll('circle'); 
 
     \t 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(""); 
 
    }) 
 
\t \t .on('mousedown', function(d){ 
 
    \t \t 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>

+0

太棒了,感謝您的幫助!這絕對是我想要的行爲。 本週末我開始沿着另一條道路前進,最終得到了類似這樣的區塊,但它需要點擊兩下才能工作:https://bl.ocks.org/yelper/dc2bbff489dbf0820ccf8d73537f071c ...我會好奇這個例子中缺少的東西讓它起作用。 – yelper

+1

@yelper,你的方法看起來很合理,但似乎並不像'd3'在你重新發布的mousedown事件中發現。我嘗試使用'd3.event'並將其分發到'rect.background'(而不是創建新事件),但這也不起作用.... – Mark

0

這裏是工作示例: https://jsfiddle.net/paradite/rpqusqdc/2/

基本上我用我以前的編碼範圍選擇工具使用drag事件而不是brushhttp://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; 
    }); 

當然,你可以刪除其邏輯的部分儘可能多的,沒有必要爲你的情況。

+0

感謝您的回覆!但是,它看起來不像是在矩形內的點上懸停,而且也無法拖動(轉換)矩形。 – yelper

+0

我修正了'mouseover'問題,'drag'功能是我的代碼沒有實現的東西,如果你需要它,也許你可以嘗試編碼嗎? – paradite