wake-up-neo.net

Ersetzen von d3.transform in D3 v4

In D3.js v4 wurde die Methode d3.transform entfernt, ohne dass ein Hinweis darauf vorhanden ist, wie sie ersetzt werden soll. Weiß jemand, wie er die folgende D3.js v3-Anweisung ersetzt?

d3.transform(String).translate;

Edit 2016-10-07: Für einen allgemeineren Ansatz siehe Anhang unten.


Laut dem Änderungsprotokoll ist es weg. Es gibt jedoch eine Funktion in transform/decompose.js , die die Berechnungen für den internen Gebrauch durchführt. Leider ist es nicht zur äußeren Anwendung ausgesetzt.

Dies ist jedoch auch ohne D3 möglich:

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)"))

Dadurch wird ein Dummy-Element g für Berechnungszwecke mit Standard-DOM-Methoden erstellt. Das transform-Attribut wird auf die Zeichenfolge gesetzt, die Ihre Transformationen enthält. Sie ruft dann .consolidate() der SVGTransformList - Schnittstelle auf, um die möglicherweise lange Liste der Transformationen zu einem einzigen SVGTransform vom Typ SVG_TRANSFORM_MATRIX zu konsolidieren, der die heruntergefahrene Version aller Transformationen in ihrer matrix-Eigenschaft enthält. Diese SVGMatrix per-Definition enthält die Werte für die Übersetzung in ihren Eigenschaften e und f.

Mit dieser Funktion getTranslation() können Sie Ihre D3 v3-Anweisung umschreiben

d3.transform(transformString).translate;

wie

getTranslation(transformString);

Nachtrag

Da diese Antwort im Laufe der Zeit ein gewisses Interesse gefunden hat, entschied ich mich, eine allgemeinere Methode zusammenzustellen, mit der nicht nur die Übersetzung, sondern auch die Werte aller Transformationsdefinitionen einer Transformationszeichenfolge zurückgegeben werden können. Die grundlegende Vorgehensweise ist die gleiche wie in meinem ursprünglichen Beitrag oben und die Berechnungen aus transform/decompose.js . Diese Funktion gibt ein Objekt mit Eigenschaften für alle Transformationsdefinitionen zurück, ähnlich wie bei der vorherigen d3.transform() did.

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)"));

36
altocumulus

Wenn Sie d3 v4 durch npm ziehen, können Sie die src/transform/parse-Datei direkt importieren und parseSvg aufrufen:

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

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

Bei Elementen mit dem Listener d3.js zoom - normalerweise dem an das Element svg angehängten Element <g> - können Sie diesen Aufruf verwenden, um die Transformationsattribute außerhalb der Zoom-Funktion zu erhalten:

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

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

Dies gibt die gleichen Werte zurück wie beim Aufruf von d3.event.transform in der Zoomereignisfunktion. 

Wenn Sie d3.event.transform außerhalb der Zoomereignisfunktion aufrufen, wird folgender Fehler ausgegeben: Uncaught TypeError: Cannot read property 'transform' of null

Ich muss d3.zoomTransform verwenden, um das Verschieben und Zoomen von Schaltflächen außerhalb des Diagramms zu ermöglichen.

2
NuclearPeon

Ich bin ein wenig zu spät zur Party, aber ich hatte einen Code, der für mich von Vorteil war. Ich hoffe, er hilft dir auch.

Der obige Code von @altocumulus ist ziemlich gründlich und wirkt wie ein Zauber. Es entsprach jedoch nicht ganz meinen Bedürfnissen, da ich die Berechnungen von Hand durchführte und einige Transformationseigenschaften so schmerzlos wie möglich ändern musste. 

Dies ist vielleicht nicht die Lösung für alle, aber es war perfekt für mich.

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(',') + ")"; 
     }, '');
}

Das wirklich Tolle an der ersten Funktion ist, dass ich eine bestimmte Eigenschaft manuell ändern kann (z. B. Rotation) und mich nicht darum kümmern muss, wie sie sich auf das Übersetzen auswirkt, oder auf irgendetwas anderes (beim Drehen um einen Punkt), während ich mich auf das eingebaute Objekt stütze -in oder sogar d3.transform-Methoden konsolidieren alle Eigenschaften in einem Wert. 

Warum ist das cool?

Stellen Sie sich etwas HTML vor 

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


Mit d3.transfrom bekomme ich:

In Objektform

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


In String-Form

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


Was mathematisch korrekt ist, macht es mir jedoch schwer, einfach den Drehwinkel und die Übersetzung zu entfernen, die eingeführt werden musste, um dieses Element um einen bestimmten Punkt zu drehen.


Verwendung meiner _getTokenizedTransformAttributeValue-Funktion

In Objektform

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


In String-Form mit der Funktion _getStringFromTokenizedTransformAttributeObj

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


Was perfekt ist, denn jetzt, wenn Sie die Drehung entfernen, kann Ihr Element dorthin zurückkehren, wo es war

Zugegeben, der Code könnte sauberer sein und die Funktionsnamen sind prägnanter, aber ich wollte das wirklich herausbringen, damit andere davon profitieren können.

0
Samuel Mburu

Ich habe einen Weg gefunden, etwas Ähnliches zu erreichen, indem ich Folgendes verwendet:

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

dadurch erhalten Sie Zugriff auf die x/y-Position und Breite/Höhe Ein Beispiel finden Sie hier: https://bl.ocks.org/mbostock/1160929

0
Gader

Ich habe eine etwas einfachere Lösung gefunden.

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

In dieser Matrix haben Sie die Koordinaten e und f, die x, y entsprechen. (e === x, f === y). Dafür müssen Sie keine eigene Funktion implementieren.

baseVal ist eine Liste von Transformationen des Elements. Sie können das nicht ohne vorherige Transformation für das Objekt verwenden! (Die Liste ist leer) Oder wenn Sie viele Änderungen am Objekt vorgenommen haben, befindet sich die letzte Position unter dem letzten Element der baseVal-Liste.

0
Michał