Remplacer d3.transformer en D3 v4

En D3.js v4 le d3.la méthode transform a été retirée, sans aucune indication sur la façon de la remplacer. Personne ne sait comment remplacer la D3.instruction js v3?

d3.transform(String).translate;
23
demandé sur Lukasz Wiktor 2016-07-06 16:08:48

6 réponses

Modifier 2016-10-07: Pour une approche plus générale, voir l'addendum ci-dessous.


selon le changelog, il a disparu. Il y a une fonction dans transform/decompose.js, cependant, qui fait les calculs pour l'usage interne. Malheureusement, il n'est pas exposé à un usage externe.

cela dit, cela se fait facilement, même sans utiliser de 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)"))

Cela crée un mannequin g élément finalités de calcul utilisant les méthodes DOM standard et définit son transform attribut à la chaîne contenant vos transformations. Il appelle ensuite .consolidate()SVGTransformList interface pour consolider la liste peut-être longue de la transformation en un seul SVGTransform de type SVG_TRANSFORM_MATRIX qui contient la version réduite de toutes les transformations dans son matrix propriété. Ce SVGMatrix par définition contient les valeurs de la traduction dans ses propriétés e et f.

utilisant cette fonction getTranslation() vous pouvez réécrire votre déclaration D3 v3

d3.transform(transformString).translate;

getTranslation(transformString);

Addendum

parce que cette réponse a gagné un certain intérêt au fil du temps, j'ai décidé de mettre en place une méthode plus générale capable de retourner non seulement la traduction mais les valeurs de toutes les définitions de transformation d'une chaîne de transformation. L'approche de base est la même, comme stipulé dans mon poste initial ci-dessus plus les calculs effectués à partir de transform/decompose.js. Cette fonction retournera un objet ayant des propriétés pour toutes les définitions de transformation un peu comme la première d3.transform() fait.

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)"));
35
répondu altocumulus 2017-04-27 23:47:32

si vous tirez dans d3 v4 par npm, vous pouvez importer le src/transform/parse le fichier directement et appeler parseSvg:

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

parseSvg("translate(20, 20)");
3
répondu Xiaohan Zhang 2017-02-06 08:59:26

Sur les éléments qui ont le d3.js zoom écouter sur eux -- habituellement le <g> élément ajouté à l'élément svg -- vous pouvez utiliser cet appel pour obtenir la transformation des attributs à l'extérieur de la fonction de zoom:

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

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

renvoie les mêmes valeurs que lors de l'appel d3.event.transform à l'intérieur de la fonction d'événement zoom elle-même.

appel d3.event.transform à l'extérieur de la fonction d'événement de zoom fera une erreur: Uncaught TypeError: Cannot read property 'transform' of null

je dois utiliser d3.zoomTransform pour permettre de panoramique et zoom à partir de boutons à l'extérieur du graphique.

2
répondu NuclearPeon 2017-03-27 20:18:43

je suis un peu en retard à la fête, mais j'avais un code qui m'était bénéfique, j'espère que ça vous aidera aussi.

Le code ci-dessus par @altocumulus est assez complet et fonctionne comme un charme. Cependant, il ne répondait pas tout à fait à mes besoins puisque je faisais les calculs à la main et avait besoin de modifier certaines propriétés de transformation aussi indolore que possible.

Ce n'est pas la solution pour tout le monde, mais c'était parfait pour je.

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

ce qui est vraiment génial avec la première fonction, c'est que je peux modifier manuellement une propriété spécifique (par exemple la rotation), et ne pas avoir à me soucier de la façon dont elle affecte translate ou quoi que ce soit d'autre (lors de la rotation autour d'un point), alors que lorsque je me fie à l'intégré ou même d3.transform méthodes de consolider toutes les propriétés en une seule valeur.

pourquoi c'est cool?

Imaginez un peu de HTML

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



en utilisant d3.transformer j'obtiens:

sous forme d'objet

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



Dans la forme d'une chaîne

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



Ce qui est mathématiquement correct, mais rend difficile pour moi de simplement supprimer l'angle de rotation et la traduction qui a dû être introduite pour faire tourner cet élément autour d'un certain point.





à l'Aide de mon _getTokenizedTransformAttributeValue function

sous forme d'objet

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



Dans la forme d'une chaîne utilisant la fonction _getStringFromTokenizedTransformAttributeObj

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



Ce qui est parfait parce que maintenant quand vous supprimez la rotation, votre élément peut revenir à l'endroit où il était

D'accord, le code pourrait être plus propre et les noms de fonction plus concis, mais je voulais vraiment obtenir ce là-bas alors que d'autres puissent en bénéficier.

0
répondu Samuel Mburu 2017-09-05 12:42:27

j'ai trouvé un moyen de faire parvenir à quelque chose de similaire en utilisant ceci:

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

cela vous donnera accès à la position x/y et la largeur/hauteur Vous pouvez voir un exemple ici: https://bl.ocks.org/mbostock/1160929

0
répondu Gader 2017-09-20 14:24:12

j'ai trouvé une solution un peu plus simple que cela.

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

dans cette matrice, vous avez les cordinates e et F sont équivalents à x, Y. (e === x, f === y). Pas besoin de mettre en œuvre votre propre fonction pour cela.

baseVal est une liste de transformations de l'élément. vous ne pouvez pas utiliser cela pour l'objet sans transformation préalable! (la liste sera vide) Ou si vous avez fait beaucoup de traduction à l'objet de la dernière position sera la dernière élément de baseVal liste.

-1
répondu Michał 2016-11-11 09:37:13