|
import {dispatch} from "d3-dispatch"; |
|
import {timer, timeout} from "d3-timer"; |
|
|
|
var emptyOn = dispatch("start", "end", "cancel", "interrupt"); |
|
var emptyTween = []; |
|
|
|
export var CREATED = 0; |
|
export var SCHEDULED = 1; |
|
export var STARTING = 2; |
|
export var STARTED = 3; |
|
export var RUNNING = 4; |
|
export var ENDING = 5; |
|
export var ENDED = 6; |
|
|
|
export default function(node, name, id, index, group, timing) { |
|
var schedules = node.__transition; |
|
if (!schedules) node.__transition = {}; |
|
else if (id in schedules) return; |
|
create(node, id, { |
|
name: name, |
|
index: index, |
|
group: group, |
|
on: emptyOn, |
|
tween: emptyTween, |
|
time: timing.time, |
|
delay: timing.delay, |
|
duration: timing.duration, |
|
ease: timing.ease, |
|
timer: null, |
|
state: CREATED |
|
}); |
|
} |
|
|
|
export function init(node, id) { |
|
var schedule = get(node, id); |
|
if (schedule.state > CREATED) throw new Error("too late; already scheduled"); |
|
return schedule; |
|
} |
|
|
|
export function set(node, id) { |
|
var schedule = get(node, id); |
|
if (schedule.state > STARTED) throw new Error("too late; already running"); |
|
return schedule; |
|
} |
|
|
|
export function get(node, id) { |
|
var schedule = node.__transition; |
|
if (!schedule || !(schedule = schedule[id])) throw new Error("transition not found"); |
|
return schedule; |
|
} |
|
|
|
function create(node, id, self) { |
|
var schedules = node.__transition, |
|
tween; |
|
|
|
|
|
|
|
schedules[id] = self; |
|
self.timer = timer(schedule, 0, self.time); |
|
|
|
function schedule(elapsed) { |
|
self.state = SCHEDULED; |
|
self.timer.restart(start, self.delay, self.time); |
|
|
|
|
|
if (self.delay <= elapsed) start(elapsed - self.delay); |
|
} |
|
|
|
function start(elapsed) { |
|
var i, j, n, o; |
|
|
|
|
|
if (self.state !== SCHEDULED) return stop(); |
|
|
|
for (i in schedules) { |
|
o = schedules[i]; |
|
if (o.name !== self.name) continue; |
|
|
|
|
|
|
|
|
|
if (o.state === STARTED) return timeout(start); |
|
|
|
|
|
if (o.state === RUNNING) { |
|
o.state = ENDED; |
|
o.timer.stop(); |
|
o.on.call("interrupt", node, node.__data__, o.index, o.group); |
|
delete schedules[i]; |
|
} |
|
|
|
|
|
else if (+i < id) { |
|
o.state = ENDED; |
|
o.timer.stop(); |
|
o.on.call("cancel", node, node.__data__, o.index, o.group); |
|
delete schedules[i]; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
timeout(function() { |
|
if (self.state === STARTED) { |
|
self.state = RUNNING; |
|
self.timer.restart(tick, self.delay, self.time); |
|
tick(elapsed); |
|
} |
|
}); |
|
|
|
|
|
|
|
self.state = STARTING; |
|
self.on.call("start", node, node.__data__, self.index, self.group); |
|
if (self.state !== STARTING) return; |
|
self.state = STARTED; |
|
|
|
|
|
tween = new Array(n = self.tween.length); |
|
for (i = 0, j = -1; i < n; ++i) { |
|
if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) { |
|
tween[++j] = o; |
|
} |
|
} |
|
tween.length = j + 1; |
|
} |
|
|
|
function tick(elapsed) { |
|
var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1), |
|
i = -1, |
|
n = tween.length; |
|
|
|
while (++i < n) { |
|
tween[i].call(node, t); |
|
} |
|
|
|
|
|
if (self.state === ENDING) { |
|
self.on.call("end", node, node.__data__, self.index, self.group); |
|
stop(); |
|
} |
|
} |
|
|
|
function stop() { |
|
self.state = ENDED; |
|
self.timer.stop(); |
|
delete schedules[id]; |
|
for (var i in schedules) return; |
|
delete node.__transition; |
|
} |
|
} |
|
|