2016-04-02 16 views
0

私はリスト内のデータに基づいてSVGコンテナ上に緑色の円を描画するのにd3.jsを使用していますmyList。ここで重複する図形からd3.jsマウスイベントを操作するにはどうすればよいですか?

その円の例です。今、私は、次の動作を実装したい

enter image description here

  1. ユーザーのマウスが円の上を通過すると、四角形が表示されます。
  2. 矩形の左上隅が円の中心である必要があります。
  3. のみならマウスが円矩形の境界外にある場合には、矩形が消えなければなりません。

以下は、この問題を解決するために書いたコードです(@ Cyrilのヘルプ、ありがとうございます)。しかし、それは正しく動作しません。マウスポインタが円の上を移動している間、長方形が表示されます。しかし、マウスポインタが東南アジアを矩形に移動すると(円の象限と重なる四角形の一部でも)、円のmouseoutイベントが発生し、矩形が消えます。矩形のmouseoverイベントはまだ発生しません。技術的には、私はまだこれがサークルにあると考えています。しかし、明らかにd3.jsはそうではありません。

これらのマウスイベントの複雑さとそれに伴う微小な違い(および競合条件)を考えれば、この機能をどのように実装できますか?

enter image description here

var myList = [ 
    {"centerX": 200, "centerY": 300, "mouseIn": {"circle":false, "rectangle":false}}, 
    {"centerX": 400, "centerY": 500, "mouseIn": {"circle":false, "rectangle":false}}, 
]; 

var myCircle = self.svgContainer.selectAll(".dots") 
    .data(myList).enter().append("circle") 
    .attr("class", "dots") 
    .attr("cx", function(d, i) {return d.centerX}) 
    .attr("cy", function(d, i) {return d.centerY}) 
    .attr("r", 15) 
    .attr("stroke-width", 0) 
    .attr("fill", function(d, i) {return "Green"}) 
    .style("display", "block"); 

myCircle.on({ 
    "mouseover": function(d) { 
     console.log('\n\nCircle MouseOver ******************************************'); 
     var wasCursorIn = d.mouseIn.circle || d.mouseIn.rectangle; 
     console.log('wasCursorIn = ', JSON.stringify(wasCursorIn)); 
     d.mouseIn.circle = true; 
     console.log('d.mouseIn = ', JSON.stringify(d.mouseIn)); 
     var isCursorIn = d.mouseIn.circle || d.mouseIn.rectangle; 
     console.log('isCursorIn = ', isCursorIn); 
     if ((!wasCursorIn) && isCursorIn) { 
     if (typeof d.rectangle === 'undefined' || d.rectangle === null) 
      d.rectangle = self.svgContainer.append("rect") 
      .attr("x", d.centerX) 
      .attr("y", d.centerY) 
      .attr("width", 100) 
      .attr("height", 50) 
      .attr("stroke-width", 2) 
      .attr("fill", "DimGray") 
      .attr("stroke", "DarkKhaki") 
      .on("mouseover", function(e) { 
       console.log('\n\nRectangle MouseOver ***************************************'); 
       console.log("d = ", JSON.stringify(d)); 
       d.mouseIn.rectangle = true; 
       console.log("d = ", JSON.stringify(d)); 
       } 
      ) 
      .on("mouseout", function(e) { 
       console.log('\n\nRectangle MouseOut ****************************************'); 
       console.log("d = ", JSON.stringify(d)); 
       var wasCursorOut2 = (!d.mouseIn.circle) && (!d.mouseIn.rectangle); 
       console.log('wasCursorOut2 = ', wasCursorOut2); 
       d.mouseIn.rectangle = false; 
       console.log('d.mouseIn = ', JSON.stringify(d.mouseIn)); 
       var isCursorOut2 = (!d.mouseIn.circle) && (!d.mouseIn.rectangle); 
       console.log('isCursorOut2 = ', isCursorOut2); 
       if ((!wasCursorOut2) && isCursorOut2) { 
        d3.select(this).style("cursor", "default"); 
        d.rectangle.remove(); 
        d.rectangle = null; 
       } 
       } 
      ) 
      .style("display", "block"); 
     else 
      d.rectangle.style("display", "block"); 
     } 
    }, 
    "mouseout": function(d) { 
     console.log('\n\nCircle MouseOut *******************************************'); 
     var wasCursorOut = (!d.mouseIn.circle) && (!d.mouseIn.rectangle); 
     console.log('wasCursorOut = ', wasCursorOut); 
     d.mouseIn.circle = false; 
     console.log('d.mouseIn = ', JSON.stringify(d.mouseIn)); 
     var isCursorOut = (!d.mouseIn.circle) && (!d.mouseIn.rectangle); 
     console.log('isCursorOut = ', isCursorOut); 
     if ((!wasCursorOut) && isCursorOut) { 
     if (!(typeof d.rectangle === 'undefined' || d.rectangle === null)) 
      d.rectangle.style("display", "none"); 
     } 
    } 
    } 
); 

答えて

1

SVG要素が重なった場合、マウスイベントは最上位要素の発射。ある要素から別の要素にマウスが移動すると、イベントの順序はmouseoutイベント(マウスが離れる要素)とmouseoverイベント(マウスが入る要素)の順になります。マウスが円と楕円の両方の要素を残しているときだけrect要素を削除したいので、circle要素とrect要素の両方でmouseoutイベントをリッスンし、マウス位置が両方の範囲外にあるときはrect要素を削除する必要があります要素。

以下は、マウスの位置が要素内にあるかどうかを判断するための1つの解決策です。マウスイベントのクライアント座標をsvg座標に変換するには、svgのgetScreenCTM()。inverse()行列を使用します。ポイントを使用して1x1行列を構築します。矩形が要素と交差するかどうかを判断するには、svgのcheckIntersection()を使用します。

次のスニペットは、このソリューションをプレーンなjavascriptで(つまりD3.jsなしで)デモートしています。

var svgNS = "http://www.w3.org/2000/svg"; 
 

 
var svg = document.getElementById("mySvg"); 
 
var circle = document.getElementById("myCircle"); 
 
var rect = null;   
 

 
circle.addEventListener("mouseover", circle_mouseover); 
 
circle.addEventListener("mouseout", circle_mouseout); 
 

 
function circle_mouseover(e) { 
 
    if (!rect) { 
 
     rect = document.createElementNS(svgNS, "rect"); 
 
     rect.setAttribute("x", circle.getAttribute("cx")); 
 
     rect.setAttribute("y", circle.getAttribute("cy")); 
 
     rect.setAttribute("width", 100); 
 
     rect.setAttribute("height", 50); 
 
     rect.setAttribute("style", "fill: gray;"); 
 
     rect.addEventListener("mouseout", rect_mouseout); 
 
     svg.appendChild(rect); 
 
    }  
 
} 
 

 
function circle_mouseout(e) { 
 
    console.log("circle_mouseout"); 
 
    if (rect) { 
 
     var p = svg.createSVGPoint(); 
 
     p.x = e.clientX; 
 
     p.y = e.clientY; 
 
     p = p.matrixTransform(svg.getScreenCTM().inverse()); 
 
     var r = svg.createSVGRect(); 
 
     r.x = p.x; 
 
     r.y = p.y; 
 
     r.width = 1; 
 
     r.height = 1; 
 
     if(!svg.checkIntersection(rect, r)) { 
 
      rect.removeEventListener("mouseout", rect_mouseout); 
 
      svg.removeChild(rect); 
 
      rect = null; 
 
     } 
 
    } 
 
} 
 

 
function rect_mouseout(e) { 
 
    var p = svg.createSVGPoint(); 
 
    p.x = e.clientX; 
 
    p.y = e.clientY; 
 
    p = p.matrixTransform(svg.getScreenCTM().inverse()); 
 
    var r = svg.createSVGRect(); 
 
    r.x = p.x; 
 
    r.y = p.y; 
 
    r.width = 1; 
 
    r.height = 1; 
 
    if(!svg.checkIntersection(circle, r)) { 
 
     rect.removeEventListener("mouseout", rect_mouseout); 
 
     svg.removeChild(rect); 
 
     rect = null; 
 
    } 
 
}
<svg id="mySvg" width="150" height="150"> 
 
    <circle id="myCircle" cx="50" cy="50" r="25" style="fill: green;"/> 
 
</svg>

注:私は、FirefoxはまだcheckIntersection()関数を実装していないと思います。 FireFoxをサポートする必要がある場合は、ポイントと要素の交差をチェックするための別の手段が必要になります。円と長方形だけを扱う場合、交差点をチェックするための独自の関数を書くのは簡単です。

+0

ありがとう@Bobby !!それはとてもうまくいった。私はcheckIntersection()を使用するよりも単純な半径計算を選択し、より多くのブラウザで動作するようにしました。 –

関連する問題