refactor bidirectional transitions

pull/1971/head
Richard Harris 7 years ago
parent 765309388e
commit 4cb1470c05

@ -593,24 +593,18 @@ export default class ElementWrapper extends Wrapper {
const fn = component.qualify(intro.name);
block.builders.intro.addConditional(`@intros.enabled`, deindent`
if (${name}) ${name}.invalidate();
@add_render_callback(() => {
if (!${name}) ${name} = @create_bidirectional_transition(${this.var}, ${fn}, ${snippet}, true);
${name}.run(1, () => {
${name} = null;
});
${name}.run(1);
});
`);
block.builders.outro.addBlock(deindent`
if (!${name}) ${name} = @create_bidirectional_transition(${this.var}, ${fn}, ${snippet}, false);
${name}.run(0, () => {
${name} = null;
});
${name}.run(0);
`);
block.builders.destroy.addConditional('detach', `if (${name}) ${name}.abort();`);
block.builders.destroy.addConditional('detach', `if (${name}) ${name}.end();`);
}
else {

@ -52,6 +52,7 @@ export function updateKeyedEach(old_blocks, component, changed, get_key, dynamic
var did_move = {};
function insert(block) {
console.log(`inserting`, block.key);
block[intro_method](node, next);
lookup[block.key] = block;
next = block.first;

@ -41,8 +41,6 @@ export function create_rule(a, b, duration, ease, fn) {
}
export function delete_rule(node, name) {
console.log(`delete ${name} from ${node.textContent}`, { active });
node.style.animation = node.style.animation
.split(', ')
.filter(anim => anim.indexOf(name) < 0)
@ -55,7 +53,6 @@ export function clear_rules() {
requestAnimationFrame(() => {
if (active) return;
let i = stylesheet.cssRules.length;
console.log(`clear_rules ${i}`);
while (i--) stylesheet.deleteRule(i);
current_rules = {};
});

@ -173,156 +173,126 @@ export function create_out_transition(node, fn, params) {
export function create_bidirectional_transition(node, fn, params, intro) {
let config = fn(node, params);
let ready = !intro;
let t = intro ? 0 : 1;
let running = false;
let running_program = null;
let pending_program = null;
let animation_name = null;
function clear_animation() {
if (animation_name) delete_rule(node, animation_name);
animation_name = null;
}
function start(program, duration, easing) {
node.dispatchEvent(new window.CustomEvent(`${program.b ? 'intro' : 'outro'}start`));
program.a = t;
program.d = program.b - t;
program.duration = duration * Math.abs(program.d);
program.end = program.start + program.duration;
if (config.css) {
clear_animation();
animation_name = create_rule(t, program.b, program.duration, easing, config.css);
node.style.animation = (node.style.animation ? ', ' : '') + `${animation_name} ${program.duration}ms linear 1 forwards`;
}
running_program = program;
pending_program = null;
}
function done() {
const program = running_program;
running_program = null;
t = program.b;
if (config.tick) config.tick(t, 1 - t);
node.dispatchEvent(new window.CustomEvent(`${program.b ? 'intro' : 'outro'}end`));
if (!program.b && !program.invalidated) {
program.group.callbacks.push(() => {
program.callback();
clear_animation();
});
if (--program.group.remaining === 0) {
program.group.callbacks.forEach(run);
}
} else {
clear_animation();
}
running = !!pending_program;
function init(program, duration) {
const d = program.b - t;
duration *= Math.abs(d);
return {
a: t,
b: program.b,
d,
duration,
start: program.start,
end: program.start + duration,
group: program.group
};
}
function go(b, callback) {
function go(b) {
const {
delay = 0,
duration = 300,
easing = linear
easing = linear,
tick = noop,
css
} = config;
const program = {
start: window.performance.now() + delay,
b,
callback
b
};
if (!ready) {
if (config.css && delay) {
// TODO can we just use the normal rule, but delay it?
animation_name = create_rule(0, 1, 1, linear, () => config.css(0, 1));
node.style.animation = (node.style.animation ? ', ' : '') + `${animation_name} 9999s linear 1 both`;
}
if (config.tick) config.tick(0, 1);
ready = true;
}
if (!b) {
program.group = outros;
outros.remaining += 1;
}
if (delay) {
if (running_program) {
pending_program = program;
} else {
start(program, duration, easing);
}
// if this is an intro, and there's a delay, we need to do
// and initial tick and/or apply CSS animation immediately
if (css) {
animation_name = create_rule(t, b, duration, easing, css);
node.style.animation = (node.style.animation ? `${node.style.animation}, ` : '') + `${animation_name} ${duration}ms linear ${delay}ms 1 both`;
}
if (!running) {
running = true;
if (b) tick(0, 1);
running_program = init(program, duration);
node.dispatchEvent(new window.CustomEvent(`${running_program.b ? 'intro' : 'outro'}start`));
loop(now => {
if (running_program && now >= running_program.end) {
done();
}
if (pending_program && now > pending_program.start) {
running_program = init(pending_program, duration);
pending_program = null;
node.dispatchEvent(new window.CustomEvent(`${running_program.b ? 'intro' : 'outro'}start`));
if (pending_program && now >= pending_program.start) {
start(pending_program, duration, easing);
if (css) {
clear_animation();
animation_name = create_rule(t, running_program.b, running_program.duration, easing, config.css);
node.style.animation = (node.style.animation ? ', ' : '') + `${animation_name} ${running_program.duration}ms linear 1 forwards`;
}
}
if (running) {
if (running_program) {
if (running_program) {
if (now >= running_program.end) {
tick(t = running_program.b, 1 - t);
node.dispatchEvent(new window.CustomEvent(`${running_program.b ? 'intro' : 'outro'}end`));
if (!pending_program) {
// we're done
if (running_program.b) {
// intro — we can tidy up immediately
clear_animation();
} else {
// outro — needs to be coordinated
if (!--running_program.group.remaining) run_all(running_program.group.callbacks);
}
}
running_program = null;
}
else if (now >= running_program.start) {
const p = now - running_program.start;
t = running_program.a + running_program.d * easing(p / running_program.duration);
if (config.tick) config.tick(t, 1 - t);
tick(t, 1 - t);
}
return true;
}
return !!(running_program || pending_program);
});
}
}
return {
run(b, callback = noop) {
run(b) {
if (typeof config === 'function') {
wait().then(() => {
config = config();
go(b, callback);
go(b);
});
} else {
go(b, callback);
go(b);
}
},
abort(reset) {
if (reset) {
// if an outro was aborted by an intro, we need
// to reset the node to its initial state
if (config.tick) config.tick(1, 0);
}
end() {
clear_animation();
running_program = pending_program = null;
running = false;
},
invalidate() {
// invalidation happens when a (bidirectional) outro is interrupted by an
// intro — callbacks should not fire, as that would cause the nodes to
// be removed from the DOM
if (running_program) {
running_program.invalidated = true;
}
}
};
}

@ -4,8 +4,10 @@ export default {
const div = target.querySelector('div');
raf.tick(25);
component.visible = false;
raf.tick(26);
assert.ok(~div.style.animation.indexOf('25ms'));
},
};

Loading…
Cancel
Save