web-dev-qa-db-ja.com

D3 v4でのd3.transformの置き換え

D3.js v4では、d3.transformメソッドは削除されましたが、置換方法についてのヒントはありません。次のD3.js v3命令を置き換える方法を知っている人はいますか?

d3.transform(String).translate;

2016-10-07を編集:より一般的なアプローチについては、以下の補遺を参照してください。


変更ログによると、それはなくなっています。 _transform/decompose.js_ には、内部使用のための計算を行う関数があります。悲しいことに、外部使用のために公開されていません。

ただし、これは使用するD3を配置しなくても簡単に実行できます。

_function getTranslation(transform) {
  // Create a dummy g for calculation purposes only. This will never
  // be appended to the DOM and will be discarded once this function 
  // returns.
  var g = document.createElementNS("http://www.w3.org/2000/svg", "g");
  
  // Set the transform attribute to the provided string value.
  g.setAttributeNS(null, "transform", transform);
  
  // consolidate the SVGTransformList containing all transformations
  // to a single SVGTransform of type SVG_TRANSFORM_MATRIX and get
  // its SVGMatrix. 
  var matrix = g.transform.baseVal.consolidate().matrix;
  
  // As per definition values e and f are the ones for the translation.
  return [matrix.e, matrix.f];
}

console.log(getTranslation("translate(20,30)"))  // simple case: should return [20,30]
console.log(getTranslation("rotate(45) skewX(20) translate(20,30) translate(-5,40)"))_

これにより、標準のDOMメソッドを使用して計算用のダミーg要素が作成され、そのtransform属性が変換を含む文字列に設定されます。次に、 SVGTransformList インターフェイスの.consolidate()を呼び出して、変換の可能性のある長いリストを単一の SVGTransform に統合します。タイプSVG_TRANSFORM_MATRIX_のmatrixプロパティにすべての変換の要約バージョンが含まれています。この SVGMatrix 定義ごとに、そのプロパティeおよびfに翻訳の値が保持されます。

この関数getTranslation()を使用すると、D3 v3ステートメントを書き換えることができます

_d3.transform(transformString).translate;
_

として

_getTranslation(transformString);
_

補遺

この回答は時間の経過とともにある程度の関心を集めたため、変換だけでなく、変換文字列のすべての変換定義の値を返すことができる、より一般的な方法をまとめることにしました。基本的なアプローチは、上記の最初の投稿で説明したものに加えて、 _transform/decompose.js_ からの計算と同じです。この関数は、以前の d3.transform() のように、すべての変換定義のプロパティを持つオブジェクトを返します。

_function getTransformation(transform) {
  // Create a dummy g for calculation purposes only. This will never
  // be appended to the DOM and will be discarded once this function 
  // returns.
  var g = document.createElementNS("http://www.w3.org/2000/svg", "g");
  
  // Set the transform attribute to the provided string value.
  g.setAttributeNS(null, "transform", transform);
  
  // consolidate the SVGTransformList containing all transformations
  // to a single SVGTransform of type SVG_TRANSFORM_MATRIX and get
  // its SVGMatrix. 
  var matrix = g.transform.baseVal.consolidate().matrix;
  
  // Below calculations are taken and adapted from the private function
  // transform/decompose.js of D3's module d3-interpolate.
  var {a, b, c, d, e, f} = matrix;   // ES6, if this doesn't work, use below assignment
  // var a=matrix.a, b=matrix.b, c=matrix.c, d=matrix.d, e=matrix.e, f=matrix.f; // ES5
  var scaleX, scaleY, skewX;
  if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX;
  if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX;
  if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY;
  if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX;
  return {
    translateX: e,
    translateY: f,
    rotate: Math.atan2(b, a) * 180 / Math.PI,
    skewX: Math.atan(skewX) * 180 / Math.PI,
    scaleX: scaleX,
    scaleY: scaleY
  };
}

console.log(getTransformation("translate(20,30)"));  
console.log(getTransformation("rotate(45) skewX(20) translate(20,30) translate(-5,40)"));_
40
altocumulus

Npmを介してd3 v4を取り込む場合、src/transform/parseファイルを直接呼び出して、parseSvgを呼び出します。

// using es2015 modules syntax
import { parseSvg } from "d3-interpolate/src/transform/parse";

parseSvg("translate(20, 20)");
5
Xiaohan Zhang

D3.js zoomリスナーを持つ要素(通常はsvg要素に追加される<g>要素)で、この呼び出しを使用してズーム機能の外部で変換属性を取得できます。

var self = this;
var t = d3.zoomTransform(self.svg.node()); 

// t = {k: 1, x: 0, y: 0} or your current transformation values

これは、ズームイベント関数自体の中でd3.event.transformを呼び出すときと同じ値を返します。

ズームイベント関数の外でd3.event.transformを呼び出すと、エラーが発生します:Uncaught TypeError: Cannot read property 'transform' of null

d3.zoomTransformを使用して、グラフ外のボタンからパンおよびズームできるようにする必要があります。

3
NuclearPeon

私はパーティーに少し遅れていますが、私にとって有益なコードがいくつかありました。あなたにも役立つことを願っています。

@altocumulusによる上記のコードは非常に徹底的で、魅力のように機能します。しかし、手作業で計算を行っており、変換のプロパティを可能な限り簡単に変更する必要があるため、私のニーズを完全には満たしていませんでした。

これは誰にとっても解決策ではないかもしれませんが、私にとっては完璧でした。

function _getTokenizedTransformAttributeValue(transformStr) {
    var cleanedUpTransformAttrArr = transformStr.split(')', ).slice(0,-1);

    return cleanedUpTransformAttrArr.reduce(function(retObj, item) { 
        var transformPair = item.split('(');

        retObj[transformPair[0]] = transformPair[1].split(',');

        return retObj;
    }, {});
}

function _getStringFromTokenizedTransformAttributeObj(transformAttributeObj) {
    return _.keys(transformAttributeObj).reduce(function(finalStr, key) {
        // wrap the transformAttributeObj[key] in array brackets to ensure we have an array
        // join will flatten the array first and then do the join so [[x,y]].join(',') -> "x,y"
        return finalStr += key + "(" + [transformAttributeObj[key]].join(',') + ")"; 
     }, '');
}

最初の機能の本当に素晴らしい点は、特定のプロパティ(回転など)を手動で変更できることです。また、変換やその他(点を中心に回転する場合)に与える影響を心配する必要はありません。 -inまたはd3.transformメソッドは、すべてのプロパティを1つの値に統合します。

なぜこれがクールなのですか?

HTMLを想像してください

<g class="tick-label tick-label--is-rotated" transform="translate(542.8228777985075,0) rotate(60, 50.324859619140625, 011.402383210764288)" style="visibility: inherit;"></g>


D3.transfromを使用すると、次のようになります。

オブジェクト形式で

jr {rotate: 59.99999999999999, translate: [577.8600589984691, -37.88141544673796], scale: [1, 1], skew: 0, toString: function} 


文字列形式

"translate(577.8600589984691,-37.88141544673796)rotate(59.99999999999999)skewX(0)scale(1,1)"


これは数学的には正しいのですが、単純に回転角度を削除するのが難しくなりますおよびこの要素を特定の点を中心に回転させるために導入しなければならなかった平行移動。


私の_getTokenizedTransformAttributeValue 関数

オブジェクト形式で

{translate: ["542.8228777985075", "0"],  rotate: ["60", " 50.324859619140625", " 011.402383210764288"]}


文字列形式で関数_getStringFromTokenizedTransformAttributeObj

"translate(542.8228777985075,0)rotate(60, 50.324859619140625, 011.402383210764288)"


これは完璧です。回転を削除すると、要素は元の場所に戻ることができるからです

確かに、コードはより簡潔になり、関数名はより簡潔になる可能性がありますが、他の人がそれを利用できるように、私は本当にこれを実現したかったです。

0
Samuel Mburu

私はこれを使用して同様の何かを達成する方法を見つけました:

d3.select(this).node().getBBox();

これにより、x/yの位置と幅/高さにアクセスできます。ここに例を見ることができます。 https://bl.ocks.org/mbostock/1160929

0
Gader

それよりも少し簡単な解決策を見つけました。

selection.node().transform.baseVal[0].matrix

このマトリックスでは、xとyに等しい座標eとfがあります。 (e === x、f === y)。そのための独自の機能を実装する必要はありません。

baseValは、要素の変換のリストです。 前の変換なしでオブジェクトに使用することはできません!(リストは空になります)またはオブジェクトに多くの変換を行った場合、最後の位置はbaseValリストの最後の要素の下になります。

0
Michał