Замена d3.transform в D3 v4
В D3.js v4 метод d3.transform был удален без каких-либо подсказок о том, как его заменить. Кто-нибудь знает, как заменить следующую инструкцию D3.js v3?
d3.transform(String).translate;
6 ответов
Редактировать 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)"))
Это создает пустышку g
элемент для целей расчета с использованием стандартных методов DOM и устанавливает его transform
атрибут строки, содержащей ваши преобразования. Затем звонит .consolidate()
из SVGTransformList
интерфейс для консолидации возможно длинного списка преобразования в единый 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)"));
Если вы потянете d3 v4 через npm, вы можете импортировать src/transform/parse
подать прямо и позвонить parseSvg
:
// using es2015 modules syntax
import { parseSvg } from "d3-interpolate/src/transform/parse";
parseSvg("translate(20, 20)");
На элементах, которые имеют d3.js zoom
слушатель на них - обычно <g>
элемент, добавленный к элементу svg - вы можете использовать этот вызов, чтобы получить атрибуты преобразования вне функции масштабирования:
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
чтобы позволить панорамирование и масштабирование от кнопок за пределами графика.
Я нашел немного более простое решение, чем это.
selection.node().transform.baseVal[0].matrix
В этой матрице у вас есть кординаты e и f, которые эквивалентны x, y. (е === х, е === у). Для этого не нужно реализовывать свои собственные функции.
baseVal - это список преобразований элемента. Вы не можете использовать это для объекта без предварительного преобразования! (список будет пустым) Или, если вы сделали много преобразований для объекта, последняя позиция будет под последним элементом списка baseVal.
Я немного опоздал на вечеринку, но у меня был какой-то код, который был полезен для меня, надеюсь, он вам тоже поможет.
Код выше @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(',') + ")";
}, '');
}
Что действительно важно в первой функции, так это то, что я могу вручную изменять определенное свойство (например, вращение), и мне не нужно беспокоиться о том, как оно влияет на перемещение или что-либо еще (при вращении вокруг точки), тогда как когда я полагаюсь на построенное в или даже d3.transform
методы они объединяют все свойства в одно значение.
Почему это круто?
Вообразите некоторый 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)"
Что идеально, потому что теперь, когда вы убираете вращение, ваш элемент может вернуться туда, где он был
Конечно, код может быть чище, а имена функций - более краткими, но я действительно хотел, чтобы это было так, чтобы другие могли извлечь из этого пользу.
Я нашел способ добиться чего-то похожего с помощью этого:
d3.select(this).node().getBBox();
это даст вам доступ к положению x/y и ширине / высоте. Вы можете увидеть пример здесь: https://bl.ocks.org/mbostock/1160929