I'm trying to play with d3 transitions - I need to do both translate and rotate.
I have original coordinates:
let data = [
{x: 100, y: 100, rotation: 45},
];
and 2 rectangles. One is doing translate first, second one is doing rotation
first.
This is resulting transform after drawing rectangles:
transform="rotate(45 115 105) translate(100, 100)"
transform="translate(100, 100) rotate(45 115 105)"
they have the same translate and rotate transformations, only thing which differs is order of them.
Then I change data:
data[0].x += 30;
data[0].y += 20;
data[0].rotation += 45;
and I would expect to get some transition ending up with this transformation:
transform="rotate(90 145 125) translate(130, 120)"
transform="translate(130, 120) rotate(90 145 125)"
but what I really get is this:
transform="translate(150, 110) rotate(90)"
transform="translate(400, 100) rotate(90)"
- notice it changes order of translate rotate for the first rectangle.
- it also removes the center of rotation from rotate transformation (does it somehow compute it during transition?)
- how does it get the resulting numbers?
How does the d3 transition work? I need to get some expectable result, since I'm trying to play with some more advanced transitions.
Here is simple example: https://jsfiddle.net/pp6npw4g/2
(click move to start transition)
This is the expected behaviour for D3's standard interpolation of the transform
attribute's value. The transform
attribute is notoriously hard to transition because its value may contain a complex list of transform definitions, which may come in any order and may even appear multiple times within one list. For example:
transform="translate(100) rotate(30, 100, 100) scale(2.5) translate(-10, -50) skewX(20)"
To be able to transition between these values, the transition.attr()
function uses d3.interpolateTransformSvg(a, b)
when it comes to interpolating transform
attributes' values. This does the following:
Within the internal function parseSvg()
of module d3-interpolate the list of transform definitions is boiled down by calling .consolidate()
of interface SVGTransformList
.
Using the private function decompose()
this consolidated transform is then decomposed into its values for translate, rotate, skew and scale.
I have described this process of decomposing the transform list in my answer to "Replacing d3.transform in D3 v4".
During the transition the interpolator returned by d3.interpolateTransformSvg(a, b)
will then interpolate between the respective values of a
and b
for (1) translate
, (2) rotate
, (3) skewX
and (4) scale
in exactly this order.
This leaves you with consolidated values, which may, at first, seem to come unexpected. Furthermore, it explains, why the order of transformations was changed by the transition. To work around this and implement a custom transition, you will have to provide a custom tween function which may be assigned by calling transition.attrTween()
. The implementation of this function largely depends on your needs, though, and is beyond the scope of this answer.