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