Greedy evaluation of transition.{style,attr,text}.
Rather than computing the ending value when the transition starts, the ending value is computed when the transition is scheduled. This gives more predictable behavior and makes it easier to debug evaluation errors since they occur immediately (during user code) rather than inside a d3_timer callback. The behavior of attrTween and styleTween are unchanged, since the interpolator can only be constructed once the starting value is known. This commit also removes d3.tween; I may add this back in a future commit, but I think there is probably a better way to specify an interpolator for transitions.
This commit is contained in:
Родитель
0794607c39
Коммит
3af91c35e4
1
Makefile
1
Makefile
|
@ -123,7 +123,6 @@ d3.core.js: \
|
||||||
src/core/transition-each.js \
|
src/core/transition-each.js \
|
||||||
src/core/transition-transition.js \
|
src/core/transition-transition.js \
|
||||||
src/core/transition-tween.js \
|
src/core/transition-tween.js \
|
||||||
src/core/tween.js \
|
|
||||||
src/core/timer.js \
|
src/core/timer.js \
|
||||||
src/core/mouse.js \
|
src/core/mouse.js \
|
||||||
src/core/touches.js \
|
src/core/touches.js \
|
||||||
|
|
|
@ -591,11 +591,47 @@
|
||||||
return transition;
|
return transition;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function d3_tweenNull(d, i, a) {
|
function d3_transition_attr(name, b) {
|
||||||
return a != "" && d3_tweenRemove;
|
function attrNull() {
|
||||||
|
this.removeAttribute(name);
|
||||||
|
}
|
||||||
|
function attrNullNS() {
|
||||||
|
this.removeAttributeNS(name.space, name.local);
|
||||||
|
}
|
||||||
|
function attrString() {
|
||||||
|
var a = this.getAttribute(name), i;
|
||||||
|
return a !== b && (i = interpolate(a, b), function(t) {
|
||||||
|
this.setAttribute(name, i(t));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function attrStringNS() {
|
||||||
|
var a = this.getAttributeNS(name.space, name.local), i;
|
||||||
|
return a !== b && (i = interpolate(a, b), function(t) {
|
||||||
|
this.setAttributeNS(name.space, name.local, i(t));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var interpolate;
|
||||||
|
name = d3.ns.qualify(name);
|
||||||
|
return b == null ? name.local ? attrNullNS : attrNull : (b += "", interpolate = d3_interpolateByName(name), name.local ? attrStringNS : attrString);
|
||||||
}
|
}
|
||||||
function d3_tweenByName(b, name) {
|
function d3_transition_style(name, b, priority) {
|
||||||
return d3.tween(b, d3_interpolateByName(name));
|
function styleNull() {
|
||||||
|
this.style.removeProperty(name);
|
||||||
|
}
|
||||||
|
function styleString() {
|
||||||
|
var a = getComputedStyle(this, null).getPropertyValue(name), i;
|
||||||
|
return a !== b && (i = interpolate(a, b), function(t) {
|
||||||
|
this.style.setProperty(name, i(t), priority);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var interpolate;
|
||||||
|
return b == null ? styleNull : (b += "", interpolate = d3_interpolateByName(name), styleString);
|
||||||
|
}
|
||||||
|
function d3_transition_text(value) {
|
||||||
|
if (value == null) value = "";
|
||||||
|
return function() {
|
||||||
|
this.textContent = value;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
function d3_timer_step() {
|
function d3_timer_step() {
|
||||||
var elapsed, now = Date.now(), t1 = d3_timer_queue;
|
var elapsed, now = Date.now(), t1 = d3_timer_queue;
|
||||||
|
@ -4052,7 +4088,7 @@
|
||||||
for (priority in name) this.each(d3_selection_style(priority, name[priority], value));
|
for (priority in name) this.each(d3_selection_style(priority, name[priority], value));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
if (n < 2) return window.getComputedStyle(this.node(), null).getPropertyValue(name);
|
if (n < 2) return getComputedStyle(this.node(), null).getPropertyValue(name);
|
||||||
priority = "";
|
priority = "";
|
||||||
}
|
}
|
||||||
return this.each(d3_selection_style(name, value, priority));
|
return this.each(d3_selection_style(name, value, priority));
|
||||||
|
@ -4371,21 +4407,26 @@
|
||||||
};
|
};
|
||||||
d3_transitionPrototype.attr = function(name, value) {
|
d3_transitionPrototype.attr = function(name, value) {
|
||||||
if (arguments.length < 2) {
|
if (arguments.length < 2) {
|
||||||
for (value in name) this.attrTween(value, d3_tweenByName(name[value], value));
|
for (value in name) this.attr(value, name[value]);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return this.attrTween(name, d3_tweenByName(value, name));
|
var id = this.id;
|
||||||
|
return d3_selection_each(this, typeof value === "function" ? function(node, i, j) {
|
||||||
|
node.__transition__[id].tween.set("attr." + name, d3_transition_attr(name, value.call(node, node.__data__, i, j)));
|
||||||
|
} : (value = d3_transition_attr(name, value), function(node) {
|
||||||
|
node.__transition__[id].tween.set("attr." + name, value);
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
d3_transitionPrototype.attrTween = function(nameNS, tween) {
|
d3_transitionPrototype.attrTween = function(nameNS, tween) {
|
||||||
function attrTween(d, i) {
|
function attrTween(d, i) {
|
||||||
var f = tween.call(this, d, i, this.getAttribute(name));
|
var f = tween.call(this, d, i, this.getAttribute(name));
|
||||||
return f === d3_tweenRemove ? (this.removeAttribute(name), null) : f && function(t) {
|
return f && function(t) {
|
||||||
this.setAttribute(name, f(t));
|
this.setAttribute(name, f(t));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function attrTweenNS(d, i) {
|
function attrTweenNS(d, i) {
|
||||||
var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local));
|
var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local));
|
||||||
return f === d3_tweenRemove ? (this.removeAttributeNS(name.space, name.local), null) : f && function(t) {
|
return f && function(t) {
|
||||||
this.setAttributeNS(name.space, name.local, f(t));
|
this.setAttributeNS(name.space, name.local, f(t));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -4397,26 +4438,34 @@
|
||||||
if (n < 3) {
|
if (n < 3) {
|
||||||
if (typeof name !== "string") {
|
if (typeof name !== "string") {
|
||||||
if (n < 2) value = "";
|
if (n < 2) value = "";
|
||||||
for (priority in name) this.styleTween(priority, d3_tweenByName(name[priority], priority), value);
|
for (priority in name) this.style(priority, name[priority], value);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
priority = "";
|
priority = "";
|
||||||
}
|
}
|
||||||
return this.styleTween(name, d3_tweenByName(value, name), priority);
|
var id = this.id;
|
||||||
|
return d3_selection_each(this, typeof value === "function" ? function(node, i, j) {
|
||||||
|
node.__transition__[id].tween.set("style." + name, d3_transition_style(name, value.call(node, node.__data__, i, j), priority));
|
||||||
|
} : (value = d3_transition_style(name, value, priority), function(node) {
|
||||||
|
node.__transition__[id].tween.set("style." + name, value);
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
d3_transitionPrototype.styleTween = function(name, tween, priority) {
|
d3_transitionPrototype.styleTween = function(name, tween, priority) {
|
||||||
if (arguments.length < 3) priority = "";
|
if (arguments.length < 3) priority = "";
|
||||||
return this.tween("style." + name, function(d, i) {
|
return this.tween("style." + name, function(d, i) {
|
||||||
var f = tween.call(this, d, i, window.getComputedStyle(this, null).getPropertyValue(name));
|
var f = tween.call(this, d, i, getComputedStyle(this, null).getPropertyValue(name));
|
||||||
return f === d3_tweenRemove ? (this.style.removeProperty(name), null) : f && function(t) {
|
return f && function(t) {
|
||||||
this.style.setProperty(name, f(t), priority);
|
this.style.setProperty(name, f(t), priority);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
d3_transitionPrototype.text = function(value) {
|
d3_transitionPrototype.text = function(value) {
|
||||||
return this.tween("text", function(d, i) {
|
var id = this.id;
|
||||||
this.textContent = typeof value === "function" ? value.call(this, d, i) : value;
|
return d3_selection_each(this, typeof value === "function" ? function(node, i, j) {
|
||||||
});
|
node.__transition__[id].tween.set("text", d3_transition_text(value.call(node, node.__data__, i, j)));
|
||||||
|
} : (value = d3_transition_text(value), function(node) {
|
||||||
|
node.__transition__[id].tween.set("text", value);
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
d3_transitionPrototype.remove = function() {
|
d3_transitionPrototype.remove = function() {
|
||||||
return this.each("end.transition", function() {
|
return this.each("end.transition", function() {
|
||||||
|
@ -4490,17 +4539,6 @@
|
||||||
node.__transition__[id].tween.set(name, tween);
|
node.__transition__[id].tween.set(name, tween);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
d3.tween = function(b, interpolate) {
|
|
||||||
function tweenFunction(d, i, a) {
|
|
||||||
var v = b.call(this, d, i);
|
|
||||||
return v == null ? a != "" && d3_tweenRemove : a != v && interpolate(a, v + "");
|
|
||||||
}
|
|
||||||
function tweenString(d, i, a) {
|
|
||||||
return a != b && interpolate(a, b);
|
|
||||||
}
|
|
||||||
return typeof b === "function" ? tweenFunction : b == null ? d3_tweenNull : (b += "", tweenString);
|
|
||||||
};
|
|
||||||
var d3_tweenRemove = {};
|
|
||||||
var d3_timer_id = 0, d3_timer_byId = {}, d3_timer_queue = null, d3_timer_interval, d3_timer_timeout;
|
var d3_timer_id = 0, d3_timer_byId = {}, d3_timer_queue = null, d3_timer_interval, d3_timer_timeout;
|
||||||
d3.timer = function(callback, delay, then) {
|
d3.timer = function(callback, delay, then) {
|
||||||
if (arguments.length < 3) {
|
if (arguments.length < 3) {
|
||||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -13,9 +13,7 @@ d3_selectionPrototype.style = function(name, value, priority) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// For style(string), return the computed style value for the first node.
|
// For style(string), return the computed style value for the first node.
|
||||||
if (n < 2) return window
|
if (n < 2) return getComputedStyle(this.node(), null).getPropertyValue(name);
|
||||||
.getComputedStyle(this.node(), null)
|
|
||||||
.getPropertyValue(name);
|
|
||||||
|
|
||||||
// For style(string, string) or style(string, function), use the default
|
// For style(string, string) or style(string, function), use the default
|
||||||
// priority. The priority is ignored for style(string, null).
|
// priority. The priority is ignored for style(string, null).
|
||||||
|
|
|
@ -4,11 +4,14 @@ d3_transitionPrototype.attr = function(name, value) {
|
||||||
// For attr(object), the object specifies the names and values of the
|
// For attr(object), the object specifies the names and values of the
|
||||||
// attributes to transition. The values may be functions that are
|
// attributes to transition. The values may be functions that are
|
||||||
// evaluated for each element.
|
// evaluated for each element.
|
||||||
for (value in name) this.attrTween(value, d3_tweenByName(name[value], value));
|
for (value in name) this.attr(value, name[value]);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.attrTween(name, d3_tweenByName(value, name));
|
var id = this.id;
|
||||||
|
return d3_selection_each(this, typeof value === "function"
|
||||||
|
? function(node, i, j) { node.__transition__[id].tween.set("attr." + name, d3_transition_attr(name, value.call(node, node.__data__, i, j))); }
|
||||||
|
: (value = d3_transition_attr(name, value), function(node) { node.__transition__[id].tween.set("attr." + name, value); }));
|
||||||
};
|
};
|
||||||
|
|
||||||
d3_transitionPrototype.attrTween = function(nameNS, tween) {
|
d3_transitionPrototype.attrTween = function(nameNS, tween) {
|
||||||
|
@ -16,17 +19,41 @@ d3_transitionPrototype.attrTween = function(nameNS, tween) {
|
||||||
|
|
||||||
function attrTween(d, i) {
|
function attrTween(d, i) {
|
||||||
var f = tween.call(this, d, i, this.getAttribute(name));
|
var f = tween.call(this, d, i, this.getAttribute(name));
|
||||||
return f === d3_tweenRemove
|
return f && function(t) { this.setAttribute(name, f(t)); };
|
||||||
? (this.removeAttribute(name), null)
|
|
||||||
: f && function(t) { this.setAttribute(name, f(t)); };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function attrTweenNS(d, i) {
|
function attrTweenNS(d, i) {
|
||||||
var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local));
|
var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local));
|
||||||
return f === d3_tweenRemove
|
return f && function(t) { this.setAttributeNS(name.space, name.local, f(t)); };
|
||||||
? (this.removeAttributeNS(name.space, name.local), null)
|
|
||||||
: f && function(t) { this.setAttributeNS(name.space, name.local, f(t)); };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween);
|
return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function d3_transition_attr(name, b) {
|
||||||
|
var interpolate;
|
||||||
|
|
||||||
|
name = d3.ns.qualify(name);
|
||||||
|
|
||||||
|
// For attr(string, null), remove the attribute with the specified name.
|
||||||
|
function attrNull() {
|
||||||
|
this.removeAttribute(name);
|
||||||
|
}
|
||||||
|
function attrNullNS() {
|
||||||
|
this.removeAttributeNS(name.space, name.local);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For attr(string, string), set the attribute with the specified name.
|
||||||
|
function attrString() {
|
||||||
|
var a = this.getAttribute(name), i;
|
||||||
|
return a !== b && (i = interpolate(a, b), function(t) { this.setAttribute(name, i(t)); });
|
||||||
|
}
|
||||||
|
function attrStringNS() {
|
||||||
|
var a = this.getAttributeNS(name.space, name.local), i;
|
||||||
|
return a !== b && (i = interpolate(a, b), function(t) { this.setAttributeNS(name.space, name.local, i(t)); });
|
||||||
|
}
|
||||||
|
|
||||||
|
return b == null
|
||||||
|
? (name.local ? attrNullNS : attrNull)
|
||||||
|
: (b += "", interpolate = d3_interpolateByName(name), name.local ? attrStringNS : attrString);
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ d3_transitionPrototype.style = function(name, value, priority) {
|
||||||
// specifies the priority.
|
// specifies the priority.
|
||||||
if (typeof name !== "string") {
|
if (typeof name !== "string") {
|
||||||
if (n < 2) value = "";
|
if (n < 2) value = "";
|
||||||
for (priority in name) this.styleTween(priority, d3_tweenByName(name[priority], priority), value);
|
for (priority in name) this.style(priority, name[priority], value);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,15 +18,35 @@ d3_transitionPrototype.style = function(name, value, priority) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, a name, value and priority are specified, and handled as below.
|
// Otherwise, a name, value and priority are specified, and handled as below.
|
||||||
return this.styleTween(name, d3_tweenByName(value, name), priority);
|
var id = this.id;
|
||||||
|
return d3_selection_each(this, typeof value === "function"
|
||||||
|
? function(node, i, j) { node.__transition__[id].tween.set("style." + name, d3_transition_style(name, value.call(node, node.__data__, i, j), priority)); }
|
||||||
|
: (value = d3_transition_style(name, value, priority), function(node) { node.__transition__[id].tween.set("style." + name, value); }));
|
||||||
};
|
};
|
||||||
|
|
||||||
d3_transitionPrototype.styleTween = function(name, tween, priority) {
|
d3_transitionPrototype.styleTween = function(name, tween, priority) {
|
||||||
if (arguments.length < 3) priority = "";
|
if (arguments.length < 3) priority = "";
|
||||||
return this.tween("style." + name, function(d, i) {
|
return this.tween("style." + name, function(d, i) {
|
||||||
var f = tween.call(this, d, i, window.getComputedStyle(this, null).getPropertyValue(name));
|
var f = tween.call(this, d, i, getComputedStyle(this, null).getPropertyValue(name));
|
||||||
return f === d3_tweenRemove
|
return f && function(t) { this.style.setProperty(name, f(t), priority); };
|
||||||
? (this.style.removeProperty(name), null)
|
|
||||||
: f && function(t) { this.style.setProperty(name, f(t), priority); };
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function d3_transition_style(name, b, priority) {
|
||||||
|
var interpolate;
|
||||||
|
|
||||||
|
// For style(name, null) or style(name, null, priority), remove the style
|
||||||
|
// property with the specified name. The priority is ignored.
|
||||||
|
function styleNull() {
|
||||||
|
this.style.removeProperty(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For style(name, string) or style(name, string, priority), set the style
|
||||||
|
// property with the specified name, using the specified priority.
|
||||||
|
function styleString() {
|
||||||
|
var a = getComputedStyle(this, null).getPropertyValue(name), i;
|
||||||
|
return a !== b && (i = interpolate(a, b), function(t) { this.style.setProperty(name, i(t), priority); });
|
||||||
|
}
|
||||||
|
|
||||||
|
return b == null ? styleNull : (b += "", interpolate = d3_interpolateByName(name), styleString);
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
d3_transitionPrototype.text = function(value) {
|
d3_transitionPrototype.text = function(value) {
|
||||||
return this.tween("text", function(d, i) {
|
var id = this.id;
|
||||||
this.textContent = typeof value === "function"
|
return d3_selection_each(this, typeof value === "function"
|
||||||
? value.call(this, d, i)
|
? function(node, i, j) { node.__transition__[id].tween.set("text", d3_transition_text(value.call(node, node.__data__, i, j))); }
|
||||||
: value;
|
: (value = d3_transition_text(value), function(node) { node.__transition__[id].tween.set("text", value); }));
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function d3_transition_text(value) {
|
||||||
|
if (value == null) value = "";
|
||||||
|
return function() { this.textContent = value; };
|
||||||
|
}
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
d3.tween = function(b, interpolate) {
|
|
||||||
|
|
||||||
function tweenFunction(d, i, a) {
|
|
||||||
var v = b.call(this, d, i);
|
|
||||||
return v == null
|
|
||||||
? a != "" && d3_tweenRemove
|
|
||||||
: a != v && interpolate(a, v + "");
|
|
||||||
}
|
|
||||||
|
|
||||||
function tweenString(d, i, a) {
|
|
||||||
return a != b && interpolate(a, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
return typeof b === "function" ? tweenFunction
|
|
||||||
: b == null ? d3_tweenNull
|
|
||||||
: (b += "", tweenString);
|
|
||||||
};
|
|
||||||
|
|
||||||
var d3_tweenRemove = {};
|
|
||||||
|
|
||||||
function d3_tweenNull(d, i, a) {
|
|
||||||
return a != "" && d3_tweenRemove;
|
|
||||||
}
|
|
||||||
|
|
||||||
function d3_tweenByName(b, name) {
|
|
||||||
return d3.tween(b, d3_interpolateByName(name));
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
require("../env");
|
|
||||||
|
|
||||||
var vows = require("vows"),
|
|
||||||
assert = require("assert");
|
|
||||||
|
|
||||||
var suite = vows.describe("d3.tween");
|
|
||||||
|
|
||||||
suite.addBatch({
|
|
||||||
"tween": {
|
|
||||||
"coerces constants to strings before interpolating": function() {
|
|
||||||
var t = d3.tween({toString: function() { return "#fff"; }}, d3.interpolate),
|
|
||||||
i = t(null, 0, "red");
|
|
||||||
assert.strictEqual(i(0), "#ff0000");
|
|
||||||
assert.strictEqual(i(.5), "#ff8080");
|
|
||||||
assert.strictEqual(i(1), "#ffffff");
|
|
||||||
},
|
|
||||||
"coerces function return values to strings before interpolating": function() {
|
|
||||||
var t = d3.tween(function(d) { return {toString: function() { return d; }}; }, d3.interpolate),
|
|
||||||
i = t("#fff", 0, "red");
|
|
||||||
assert.strictEqual(i(0), "#ff0000");
|
|
||||||
assert.strictEqual(i(.5), "#ff8080");
|
|
||||||
assert.strictEqual(i(1), "#ffffff");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
suite.export(module);
|
|
|
@ -5,6 +5,7 @@ CSSStyleDeclaration = window.CSSStyleDeclaration;
|
||||||
|
|
||||||
require("../lib/sizzle/sizzle");
|
require("../lib/sizzle/sizzle");
|
||||||
Sizzle = window.Sizzle;
|
Sizzle = window.Sizzle;
|
||||||
|
getComputedStyle = window.getComputedStyle;
|
||||||
|
|
||||||
process.env.TZ = "America/Los_Angeles";
|
process.env.TZ = "America/Los_Angeles";
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче