web-dev-qa-db-ja.com

変換後のD3ノードの画面位置の取得

レイアウトがd3.behavior.zoom()によって変換された後、ノードの画面位置を取得しようとしていますが、あまりうまくいきません。レイアウトを変換およびスケーリングした後、ウィンドウ内のノードの実際の位置を取得するにはどうすればよいですか?

mouseOver = function(node) {
  screenX = magic(node.x); // Need a magic function to transform node
  screenY = magic(node.y); // positions into screen coordinates.
};

どんなガイダンスもいただければ幸いです。

編集:上記の「ノード」は強制レイアウトノードであるため、xプロパティとyプロパティはシミュレーションによって設定され、適用される変換のタイプに関係なく、シミュレーションが停止した後も一定のままです。

編集:私がSVGを変換するために使用している戦略は、d3のズーム動作に由来します。概要は次のとおりです: SVGジオメトリックズーム

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
  .append("g")
    .call(d3.behavior.zoom().scaleExtent([1, 8]).on("zoom", zoom))
  .append("g");

svg.append("rect")
    .attr("class", "overlay")
    .attr("width", width)
    .attr("height", height);

svg.selectAll("circle")
    .data(data)
  .enter().append("circle")
    .attr("r", 2.5)
    .attr("transform", function(d) { return "translate(" + d + ")"; });

function zoom() {
  svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}

とても簡単です。 d3のズーム動作は、パンおよびズームイベントをハンドラーに配信します。ハンドラーは、transform属性を使用してコンテナー要素に変換を適用します。

編集:ノードをマウスポインターでホバーしたときのノードの位置に関心があるため、ノード座標ではなくマウス座標を使用して問題を回避しています。それは正確には私が求めている行動ではありませんが、ほとんどの場合機能し、何もないよりはましです。

編集:解決策は、element.getCTM()を使用してsvg要素の現在の変換行列を取得し、それを使用してx座標とy座標を画面相対状態にオフセットすることでした。下記参照。

18
t.888

transformを適用した後、node.getBBox()を試して、ノードシェイプの周りのタイトバウンディングボックスのピクセル位置を取得できます。詳細はこちらをご覧ください: link

編集:

getBBoxは、思ったほどうまく機能しません。四角形は変換された座標空間で定義されるため、常に親<g>を基準としているため、含まれている形状でも常に同じになります。

element.getBoundingClientRect と呼ばれる別の関数があり、これは非常に広くサポートされているようで、ブラウザビューポートの左上を基準としたピクセル位置で長方形を返します。これにより、変換マトリックスを直接操作することなく、目的のものに近づくことができます。

7
Scott Cameron

私の元の質問に対する解決策は次のようになります:

(回転変換をサポートするように更新されました。)

// The magic function.
function getScreenCoords(x, y, ctm) {
    var xn = ctm.e + x*ctm.a + y*ctm.c;
    var yn = ctm.f + x*ctm.b + y*ctm.d;
    return { x: xn, y: yn };
}

var circle = document.getElementById('svgCircle'),
cx = +circle.getAttribute('cx'),
cy = +circle.getAttribute('cy'),
ctm = circle.getCTM(),
coords = getScreenCoords(cx, cy, ctm);
console.log(coords.x, coords.y); // shows coords relative to my svg container

代わりに、これはd3.eventのtranslateプロパティとscaleプロパティを使用して行うこともできます(回転変換が必要ない場合)。

// This function is called by d3's zoom event.
function zoom() {

// The magic function - converts node positions into positions on screen.    
function getScreenCoords(x, y, translate, scale) {
    var xn = translate[0] + x*scale;
    var yn = translate[1] + y*scale;
    return { x: xn, y: yn };
}

// Get element coordinates and transform them to screen coordinates.
var circle = document.getElementById('svgCircle');
cx = +circle.getAttribute('cx'),
cy = +circle.getAttribute('cy'),
coords = getScreenCoords(cx, cy, d3.event.translate, d3.event.scale);
console.log(coords.x, coords.y); // shows coords relative to my svg container

  // ...
}

編集:私は以下の形式の関数が最も便利で汎用的であることがわかりました、そしてそれはgetBoundingClientRectが落ちる場所で立ち上がるようです。より具体的には、D3フォースレイアウトプロジェクトで正確なSVGノードの位置を取得しようとしたときに、getBoundingClientRectが不正確な結果を生成しましたが、以下のメソッドは複数のブラウザーにわたってcircle要素の正確な中心座標を返しました。

(回転変換をサポートするように更新されました。)

// Pass in the element and its pre-transform coords
function getElementCoords(element, coords) {
    var ctm = element.getCTM(),
    x = ctm.e + coords.x*ctm.a + coords.y*ctm.c,
    y = ctm.f + coords.x*ctm.b + coords.y*ctm.d;
    return {x: x, y: y};
};

// Get post-transform coords from the element.
var circle = document.getElementById('svgCircle'),
x = +circle.getAttribute('cx'),
y = +circle.getAttribute('cy'),
coords = getElementCoords(circle, {x:x, y:y});

// Get post-transform coords using a 'node' object.
// Any object with x,y properties will do.
var node = ..., // some D3 node or object with x,y properties.
circle = document.getElementById('svgCircle'),
coords = getElementCoords(circle, node);

この関数は、DOM要素の変換行列を取得し、行列の回転、スケール、および変換情報を使用して、指定されたノードオブジェクトの変換後の座標を返します。

22
t.888