Skip to content

Commit a8f2ac2

Browse files
committed
Limit transform interpolation to transition.attr.
D3’s transform interpolator is designed for SVG transforms; unlike CSS3 transforms, SVG transforms do not have units and are limited to 2D. This commit limits transform interpolation to transition.attr, so that it only applies to SVG transforms and not CSS3 transforms which are assigned via style. Thus, if transition.style is used to interpolate the transform style, the default string interpolator will now be used. Note that string interpolation of CSS3 transforms is still likely to break; you should use CSS3 transitions instead. Fixes d3#1323.
1 parent 2236488 commit a8f2ac2

6 files changed

Lines changed: 122 additions & 134 deletions

File tree

d3.js

Lines changed: 113 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -4891,122 +4891,12 @@ d3 = function() {
48914891
return "#" + d3_rgb_hex(Math.round(ar + br * t)) + d3_rgb_hex(Math.round(ag + bg * t)) + d3_rgb_hex(Math.round(ab + bb * t));
48924892
};
48934893
}
4894-
d3.transform = function(string) {
4895-
var g = d3_document.createElementNS(d3.ns.prefix.svg, "g");
4896-
return (d3.transform = function(string) {
4897-
if (string != null) {
4898-
g.setAttribute("transform", string);
4899-
var t = g.transform.baseVal.consolidate();
4900-
}
4901-
return new d3_transform(t ? t.matrix : d3_transformIdentity);
4902-
})(string);
4903-
};
4904-
function d3_transform(m) {
4905-
var r0 = [ m.a, m.b ], r1 = [ m.c, m.d ], kx = d3_transformNormalize(r0), kz = d3_transformDot(r0, r1), ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0;
4906-
if (r0[0] * r1[1] < r1[0] * r0[1]) {
4907-
r0[0] *= -1;
4908-
r0[1] *= -1;
4909-
kx *= -1;
4910-
kz *= -1;
4911-
}
4912-
this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees;
4913-
this.translate = [ m.e, m.f ];
4914-
this.scale = [ kx, ky ];
4915-
this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0;
4916-
}
4917-
d3_transform.prototype.toString = function() {
4918-
return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")";
4919-
};
4920-
function d3_transformDot(a, b) {
4921-
return a[0] * b[0] + a[1] * b[1];
4922-
}
4923-
function d3_transformNormalize(a) {
4924-
var k = Math.sqrt(d3_transformDot(a, a));
4925-
if (k) {
4926-
a[0] /= k;
4927-
a[1] /= k;
4928-
}
4929-
return k;
4930-
}
4931-
function d3_transformCombine(a, b, k) {
4932-
a[0] += k * b[0];
4933-
a[1] += k * b[1];
4934-
return a;
4935-
}
4936-
var d3_transformIdentity = {
4937-
a: 1,
4938-
b: 0,
4939-
c: 0,
4940-
d: 1,
4941-
e: 0,
4942-
f: 0
4943-
};
4944-
d3.interpolateNumber = d3_interpolateNumber;
4945-
function d3_interpolateNumber(a, b) {
4946-
b -= a = +a;
4947-
return function(t) {
4948-
return a + b * t;
4949-
};
4950-
}
4951-
d3.interpolateTransform = d3_interpolateTransform;
4952-
function d3_interpolateTransform(a, b) {
4953-
var s = [], q = [], n, A = d3.transform(a), B = d3.transform(b), ta = A.translate, tb = B.translate, ra = A.rotate, rb = B.rotate, wa = A.skew, wb = B.skew, ka = A.scale, kb = B.scale;
4954-
if (ta[0] != tb[0] || ta[1] != tb[1]) {
4955-
s.push("translate(", null, ",", null, ")");
4956-
q.push({
4957-
i: 1,
4958-
x: d3_interpolateNumber(ta[0], tb[0])
4959-
}, {
4960-
i: 3,
4961-
x: d3_interpolateNumber(ta[1], tb[1])
4962-
});
4963-
} else if (tb[0] || tb[1]) {
4964-
s.push("translate(" + tb + ")");
4965-
} else {
4966-
s.push("");
4967-
}
4968-
if (ra != rb) {
4969-
if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360;
4970-
q.push({
4971-
i: s.push(s.pop() + "rotate(", null, ")") - 2,
4972-
x: d3_interpolateNumber(ra, rb)
4973-
});
4974-
} else if (rb) {
4975-
s.push(s.pop() + "rotate(" + rb + ")");
4976-
}
4977-
if (wa != wb) {
4978-
q.push({
4979-
i: s.push(s.pop() + "skewX(", null, ")") - 2,
4980-
x: d3_interpolateNumber(wa, wb)
4981-
});
4982-
} else if (wb) {
4983-
s.push(s.pop() + "skewX(" + wb + ")");
4984-
}
4985-
if (ka[0] != kb[0] || ka[1] != kb[1]) {
4986-
n = s.push(s.pop() + "scale(", null, ",", null, ")");
4987-
q.push({
4988-
i: n - 4,
4989-
x: d3_interpolateNumber(ka[0], kb[0])
4990-
}, {
4991-
i: n - 2,
4992-
x: d3_interpolateNumber(ka[1], kb[1])
4993-
});
4994-
} else if (kb[0] != 1 || kb[1] != 1) {
4995-
s.push(s.pop() + "scale(" + kb + ")");
4996-
}
4997-
n = q.length;
4998-
return function(t) {
4999-
var i = -1, o;
5000-
while (++i < n) s[(o = q[i]).i] = o.x(t);
5001-
return s.join("");
5002-
};
5003-
}
50044894
d3.interpolateObject = d3_interpolateObject;
50054895
function d3_interpolateObject(a, b) {
50064896
var i = {}, c = {}, k;
50074897
for (k in a) {
50084898
if (k in b) {
5009-
i[k] = d3_interpolateByName(k)(a[k], b[k]);
4899+
i[k] = d3_interpolate(a[k], b[k]);
50104900
} else {
50114901
c[k] = a[k];
50124902
}
@@ -5021,6 +4911,13 @@ d3 = function() {
50214911
return c;
50224912
};
50234913
}
4914+
d3.interpolateNumber = d3_interpolateNumber;
4915+
function d3_interpolateNumber(a, b) {
4916+
b -= a = +a;
4917+
return function(t) {
4918+
return a + b * t;
4919+
};
4920+
}
50244921
d3.interpolateString = d3_interpolateString;
50254922
function d3_interpolateString(a, b) {
50264923
var m, i, j, s0 = 0, s1 = 0, s = [], q = [], n, o;
@@ -5094,9 +4991,6 @@ d3 = function() {
50944991
while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ;
50954992
return f;
50964993
}
5097-
function d3_interpolateByName(name) {
5098-
return name == "transform" ? d3_interpolateTransform : d3_interpolate;
5099-
}
51004994
d3.interpolators = [ function(a, b) {
51014995
var t = typeof b;
51024996
return (t === "string" ? d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) ? d3_interpolateRgb : d3_interpolateString : b instanceof d3_Color ? d3_interpolateRgb : t === "object" ? Array.isArray(b) ? d3_interpolateArray : d3_interpolateObject : d3_interpolateNumber)(a, b);
@@ -5249,6 +5143,109 @@ d3 = function() {
52495143
return Math.round(a + b * t);
52505144
};
52515145
}
5146+
d3.transform = function(string) {
5147+
var g = d3_document.createElementNS(d3.ns.prefix.svg, "g");
5148+
return (d3.transform = function(string) {
5149+
if (string != null) {
5150+
g.setAttribute("transform", string);
5151+
var t = g.transform.baseVal.consolidate();
5152+
}
5153+
return new d3_transform(t ? t.matrix : d3_transformIdentity);
5154+
})(string);
5155+
};
5156+
function d3_transform(m) {
5157+
var r0 = [ m.a, m.b ], r1 = [ m.c, m.d ], kx = d3_transformNormalize(r0), kz = d3_transformDot(r0, r1), ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0;
5158+
if (r0[0] * r1[1] < r1[0] * r0[1]) {
5159+
r0[0] *= -1;
5160+
r0[1] *= -1;
5161+
kx *= -1;
5162+
kz *= -1;
5163+
}
5164+
this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees;
5165+
this.translate = [ m.e, m.f ];
5166+
this.scale = [ kx, ky ];
5167+
this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0;
5168+
}
5169+
d3_transform.prototype.toString = function() {
5170+
return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")";
5171+
};
5172+
function d3_transformDot(a, b) {
5173+
return a[0] * b[0] + a[1] * b[1];
5174+
}
5175+
function d3_transformNormalize(a) {
5176+
var k = Math.sqrt(d3_transformDot(a, a));
5177+
if (k) {
5178+
a[0] /= k;
5179+
a[1] /= k;
5180+
}
5181+
return k;
5182+
}
5183+
function d3_transformCombine(a, b, k) {
5184+
a[0] += k * b[0];
5185+
a[1] += k * b[1];
5186+
return a;
5187+
}
5188+
var d3_transformIdentity = {
5189+
a: 1,
5190+
b: 0,
5191+
c: 0,
5192+
d: 1,
5193+
e: 0,
5194+
f: 0
5195+
};
5196+
d3.interpolateTransform = d3_interpolateTransform;
5197+
function d3_interpolateTransform(a, b) {
5198+
var s = [], q = [], n, A = d3.transform(a), B = d3.transform(b), ta = A.translate, tb = B.translate, ra = A.rotate, rb = B.rotate, wa = A.skew, wb = B.skew, ka = A.scale, kb = B.scale;
5199+
if (ta[0] != tb[0] || ta[1] != tb[1]) {
5200+
s.push("translate(", null, ",", null, ")");
5201+
q.push({
5202+
i: 1,
5203+
x: d3_interpolateNumber(ta[0], tb[0])
5204+
}, {
5205+
i: 3,
5206+
x: d3_interpolateNumber(ta[1], tb[1])
5207+
});
5208+
} else if (tb[0] || tb[1]) {
5209+
s.push("translate(" + tb + ")");
5210+
} else {
5211+
s.push("");
5212+
}
5213+
if (ra != rb) {
5214+
if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360;
5215+
q.push({
5216+
i: s.push(s.pop() + "rotate(", null, ")") - 2,
5217+
x: d3_interpolateNumber(ra, rb)
5218+
});
5219+
} else if (rb) {
5220+
s.push(s.pop() + "rotate(" + rb + ")");
5221+
}
5222+
if (wa != wb) {
5223+
q.push({
5224+
i: s.push(s.pop() + "skewX(", null, ")") - 2,
5225+
x: d3_interpolateNumber(wa, wb)
5226+
});
5227+
} else if (wb) {
5228+
s.push(s.pop() + "skewX(" + wb + ")");
5229+
}
5230+
if (ka[0] != kb[0] || ka[1] != kb[1]) {
5231+
n = s.push(s.pop() + "scale(", null, ",", null, ")");
5232+
q.push({
5233+
i: n - 4,
5234+
x: d3_interpolateNumber(ka[0], kb[0])
5235+
}, {
5236+
i: n - 2,
5237+
x: d3_interpolateNumber(ka[1], kb[1])
5238+
});
5239+
} else if (kb[0] != 1 || kb[1] != 1) {
5240+
s.push(s.pop() + "scale(" + kb + ")");
5241+
}
5242+
n = q.length;
5243+
return function(t) {
5244+
var i = -1, o;
5245+
while (++i < n) s[(o = q[i]).i] = o.x(t);
5246+
return s.join("");
5247+
};
5248+
}
52525249
function d3_uninterpolateNumber(a, b) {
52535250
b = b - (a = +a) ? 1 / (b - a) : 0;
52545251
return function(x) {
@@ -7506,7 +7503,7 @@ d3 = function() {
75067503
for (value in nameNS) this.attr(value, nameNS[value]);
75077504
return this;
75087505
}
7509-
var interpolate = d3_interpolateByName(nameNS), name = d3.ns.qualify(nameNS);
7506+
var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate, name = d3.ns.qualify(nameNS);
75107507
function attrNull() {
75117508
this.removeAttribute(name);
75127509
}
@@ -7557,14 +7554,13 @@ d3 = function() {
75577554
}
75587555
priority = "";
75597556
}
7560-
var interpolate = d3_interpolateByName(name);
75617557
function styleNull() {
75627558
this.style.removeProperty(name);
75637559
}
75647560
function styleString(b) {
75657561
return b == null ? styleNull : (b += "", function() {
75667562
var a = d3_window.getComputedStyle(this, null).getPropertyValue(name), i;
7567-
return a !== b && (i = interpolate(a, b), function(t) {
7563+
return a !== b && (i = d3_interpolate(a, b), function(t) {
75687564
this.style.setProperty(name, i(t), priority);
75697565
});
75707566
});

d3.min.js

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/interpolate/interpolate.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import "../color/color";
22
import "../color/rgb";
33
import "rgb";
4-
import "transform";
54
import "object";
65
import "array";
76
import "number";
@@ -15,12 +14,6 @@ function d3_interpolate(a, b) {
1514
return f;
1615
}
1716

18-
function d3_interpolateByName(name) {
19-
return name == "transform"
20-
? d3_interpolateTransform
21-
: d3_interpolate;
22-
}
23-
2417
d3.interpolators = [
2518
function(a, b) {
2619
var t = typeof b;

src/interpolate/object.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ function d3_interpolateObject(a, b) {
88
k;
99
for (k in a) {
1010
if (k in b) {
11-
i[k] = d3_interpolateByName(k)(a[k], b[k]);
11+
i[k] = d3_interpolate(a[k], b[k]);
1212
} else {
1313
c[k] = a[k];
1414
}

src/transition/attr.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import "../core/ns";
22
import "../interpolate/interpolate";
3+
import "../interpolate/transform";
34
import "transition";
45
import "tween";
56

@@ -13,7 +14,7 @@ d3_transitionPrototype.attr = function(nameNS, value) {
1314
return this;
1415
}
1516

16-
var interpolate = d3_interpolateByName(nameNS),
17+
var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate,
1718
name = d3.ns.qualify(nameNS);
1819

1920
// For attr(string, null), remove the attribute with the specified name.

src/transition/style.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ d3_transitionPrototype.style = function(name, value, priority) {
2222
priority = "";
2323
}
2424

25-
var interpolate = d3_interpolateByName(name);
26-
2725
// For style(name, null) or style(name, null, priority), remove the style
2826
// property with the specified name. The priority is ignored.
2927
function styleNull() {
@@ -36,7 +34,7 @@ d3_transitionPrototype.style = function(name, value, priority) {
3634
function styleString(b) {
3735
return b == null ? styleNull : (b += "", function() {
3836
var a = d3_window.getComputedStyle(this, null).getPropertyValue(name), i;
39-
return a !== b && (i = interpolate(a, b), function(t) { this.style.setProperty(name, i(t), priority); });
37+
return a !== b && (i = d3_interpolate(a, b), function(t) { this.style.setProperty(name, i(t), priority); });
4038
});
4139
}
4240

0 commit comments

Comments
 (0)