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-transition.js \
|
||||
src/core/transition-tween.js \
|
||||
src/core/tween.js \
|
||||
src/core/timer.js \
|
||||
src/core/mouse.js \
|
||||
src/core/touches.js \
|
||||
|
|
|
@ -591,11 +591,47 @@
|
|||
return transition;
|
||||
}
|
||||
}
|
||||
function d3_tweenNull(d, i, a) {
|
||||
return a != "" && d3_tweenRemove;
|
||||
function d3_transition_attr(name, b) {
|
||||
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) {
|
||||
return d3.tween(b, d3_interpolateByName(name));
|
||||
function d3_transition_style(name, b, priority) {
|
||||
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() {
|
||||
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));
|
||||
return this;
|
||||
}
|
||||
if (n < 2) return window.getComputedStyle(this.node(), null).getPropertyValue(name);
|
||||
if (n < 2) return getComputedStyle(this.node(), null).getPropertyValue(name);
|
||||
priority = "";
|
||||
}
|
||||
return this.each(d3_selection_style(name, value, priority));
|
||||
|
@ -4371,21 +4407,26 @@
|
|||
};
|
||||
d3_transitionPrototype.attr = function(name, value) {
|
||||
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.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) {
|
||||
function attrTween(d, i) {
|
||||
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));
|
||||
};
|
||||
}
|
||||
function attrTweenNS(d, i) {
|
||||
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));
|
||||
};
|
||||
}
|
||||
|
@ -4397,26 +4438,34 @@
|
|||
if (n < 3) {
|
||||
if (typeof name !== "string") {
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
if (arguments.length < 3) priority = "";
|
||||
return this.tween("style." + name, function(d, i) {
|
||||
var f = tween.call(this, d, i, window.getComputedStyle(this, null).getPropertyValue(name));
|
||||
return f === d3_tweenRemove ? (this.style.removeProperty(name), null) : f && function(t) {
|
||||
var f = tween.call(this, d, i, getComputedStyle(this, null).getPropertyValue(name));
|
||||
return f && function(t) {
|
||||
this.style.setProperty(name, f(t), priority);
|
||||
};
|
||||
});
|
||||
};
|
||||
d3_transitionPrototype.text = function(value) {
|
||||
return this.tween("text", function(d, i) {
|
||||
this.textContent = typeof value === "function" ? value.call(this, d, i) : value;
|
||||
});
|
||||
var id = this.id;
|
||||
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() {
|
||||
return this.each("end.transition", function() {
|
||||
|
@ -4490,17 +4539,6 @@
|
|||
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;
|
||||
d3.timer = function(callback, delay, then) {
|
||||
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.
|
||||
if (n < 2) return window
|
||||
.getComputedStyle(this.node(), null)
|
||||
.getPropertyValue(name);
|
||||
if (n < 2) return getComputedStyle(this.node(), null).getPropertyValue(name);
|
||||
|
||||
// For style(string, string) or style(string, function), use the default
|
||||
// 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
|
||||
// attributes to transition. The values may be functions that are
|
||||
// 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.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) {
|
||||
|
@ -16,17 +19,41 @@ d3_transitionPrototype.attrTween = function(nameNS, tween) {
|
|||
|
||||
function attrTween(d, i) {
|
||||
var f = tween.call(this, d, i, this.getAttribute(name));
|
||||
return f === d3_tweenRemove
|
||||
? (this.removeAttribute(name), null)
|
||||
: f && function(t) { this.setAttribute(name, f(t)); };
|
||||
return f && function(t) { this.setAttribute(name, f(t)); };
|
||||
}
|
||||
|
||||
function attrTweenNS(d, i) {
|
||||
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) { this.setAttributeNS(name.space, name.local, f(t)); };
|
||||
return f && function(t) { this.setAttributeNS(name.space, name.local, f(t)); };
|
||||
}
|
||||
|
||||
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.
|
||||
if (typeof name !== "string") {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -18,15 +18,35 @@ d3_transitionPrototype.style = function(name, value, priority) {
|
|||
}
|
||||
|
||||
// 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) {
|
||||
if (arguments.length < 3) priority = "";
|
||||
return this.tween("style." + name, function(d, i) {
|
||||
var f = tween.call(this, d, i, window.getComputedStyle(this, null).getPropertyValue(name));
|
||||
return f === d3_tweenRemove
|
||||
? (this.style.removeProperty(name), null)
|
||||
: f && function(t) { this.style.setProperty(name, f(t), priority); };
|
||||
var f = tween.call(this, d, i, getComputedStyle(this, null).getPropertyValue(name));
|
||||
return 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) {
|
||||
return this.tween("text", function(d, i) {
|
||||
this.textContent = typeof value === "function"
|
||||
? value.call(this, d, i)
|
||||
: value;
|
||||
});
|
||||
var id = this.id;
|
||||
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); }));
|
||||
};
|
||||
|
||||
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");
|
||||
Sizzle = window.Sizzle;
|
||||
getComputedStyle = window.getComputedStyle;
|
||||
|
||||
process.env.TZ = "America/Los_Angeles";
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче