javascript - examples - Reemplazando d3.transform en D3 v4
github d3 (6)
En D3.js v4, se ha eliminado el método d3.transform, sin ninguna pista sobre cómo reemplazarlo. ¿Alguien sabe cómo reemplazar la siguiente instrucción D3.js v3?
d3.transform(String).translate;
Encontré una solución un poco más simple que eso.
selection.node().transform.baseVal[0].matrix
En esta matriz, tienes cordinates e y f son equivalentes a x, y. (e === x, f === y). No es necesario implementar su propia función para eso.
baseVal es una lista de transformaciones del elemento. ¡No puedes usar eso para el objeto sin una transformación anterior! (la lista estará vacía) O si realizó muchas transformaciones al objeto, la última posición estará debajo del último elemento de la lista baseVal.
Si inserta d3 v4 a npm, puede importar el archivo src/transform/parse
directamente y llamar a parseSvg
:
// using es2015 modules syntax
import { parseSvg } from "d3-interpolate/src/transform/parse";
parseSvg("translate(20, 20)");
En los elementos que tienen el detector de zoom
d3.js en ellos, generalmente el elemento <g>
agregado al elemento svg, puede usar esta llamada para obtener los atributos de transformación fuera de la función de zoom:
var self = this;
var t = d3.zoomTransform(self.svg.node());
// t = {k: 1, x: 0, y: 0} or your current transformation values
Esto devuelve los mismos valores que cuando se llama a d3.event.transform
dentro de la función de evento de zoom.
Al llamar a d3.event.transform
fuera de la función de evento de zoom, se producirá un error: Uncaught TypeError: Cannot read property ''transform'' of null
Tengo que usar d3.zoomTransform
para permitir la panorámica y el zoom desde botones fuera del gráfico.
Editar 2016-10-07: Para un enfoque más general, ver el apéndice a continuación.
De acuerdo con el registro de cambios, se ha ido. Sin embargo, hay una función en transform/decompose.js
, que hace los cálculos para uso interno. Lamentablemente, no está expuesto para uso externo.
Dicho esto, esto se hace fácilmente incluso sin utilizar 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)"))
Esto crea un elemento g
ficticio para fines de cálculo utilizando métodos DOM estándar y establece su atributo de transform
en la cadena que contiene sus transformaciones. Luego llama a .consolidate()
de la interfaz SVGTransformList
para consolidar la posible lista larga de transformación en una sola SVGTransform
de tipo SVG_TRANSFORM_MATRIX
que contiene la versión reducida de todas las transformaciones en su propiedad de matrix
. Esta SVGMatrix
por definición contiene los valores de la traducción en sus propiedades e
y f
.
Usando esta función getTranslation()
podrías reescribir tu declaración D3 v3
d3.transform(transformString).translate;
como
getTranslation(transformString);
Apéndice
Debido a que esta respuesta ha ganado interés con el tiempo, decidí juntar un método más general capaz de devolver no solo la traducción sino también los valores de todas las definiciones de transformación de una cadena de transformación. El enfoque básico es el mismo que se describe en mi publicación original anterior más los cálculos tomados de transform/decompose.js
. Esta función devolverá un objeto que tiene propiedades para todas las definiciones de transformación, muy parecido al antiguo 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)"));
Llego un poco tarde a la fiesta, pero tenía un código que era beneficioso para mí, espero que te ayude también.
El código de arriba de @altocumulus es bastante minucioso y funciona como un amuleto. Sin embargo, no satisfacía mis necesidades ya que estaba haciendo los cálculos a mano y necesitaba modificar algunas propiedades de transformación de la forma más sencilla posible.
Esta podría no ser la solución para todos, pero fue perfecto para mí.
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('','') + ")";
}, '''');
}
Lo mejor de la primera función es que puedo alterar manualmente una propiedad específica (por ejemplo, rotación), y no tener que preocuparme de cómo afecta la traducción o cualquier otra cosa (cuando se rota alrededor de un punto), mientras que cuando confío en la construcción -en o incluso métodos d3.transform
, consolidan todas las propiedades en un solo valor.
¿Por qué es genial?
Imagina un poco 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>
Usando d3.transfrom obtengo:
En forma de objeto
jr {rotate: 59.99999999999999, translate: [577.8600589984691, -37.88141544673796], scale: [1, 1], skew: 0, toString: function}
En forma de cadena
"translate(577.8600589984691,-37.88141544673796)rotate(59.99999999999999)skewX(0)scale(1,1)"
Lo cual es correcto matemáticamente, pero me dificulta simplemente eliminar el ángulo de rotación y la traducción que se tuvo que introducir para rotar este elemento alrededor de un punto dado.
Usando mi función _getTokenizedTransformAttributeValue
En forma de objeto
{translate: ["542.8228777985075", "0"], rotate: ["60", " 50.324859619140625", " 011.402383210764288"]}
En forma de cadena usando la función _getStringFromTokenizedTransformAttributeObj
"translate(542.8228777985075,0)rotate(60, 50.324859619140625, 011.402383210764288)"
Lo cual es perfecto porque ahora cuando quitas la rotación, tu elemento puede volver a donde estaba
De acuerdo, el código podría ser más limpio y los nombres de las funciones más concisos, pero realmente quería sacar esto a la luz para que otros pudieran beneficiarse de él.
Encontré una forma de lograr algo similar al usar esto:
d3.select(this).node().getBBox();
esto le dará acceso a la posición x / y y al ancho / alto. Puede ver un ejemplo aquí: https://bl.ocks.org/mbostock/1160929