Skip to content

Commit

Permalink
Merge branch 'master' into gh-1425
Browse files Browse the repository at this point in the history
  • Loading branch information
Rich-Harris committed May 6, 2018
2 parents 2b18ab9 + e1db827 commit af32ba6
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 88 deletions.
1 change: 1 addition & 0 deletions src/compile/nodes/EachBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ export default class EachBlock extends Node {
}
}
@transitionManager.groupOutros();
for (; #i < ${iterations}.length; #i += 1) ${outro}(#i);
`
: deindent`
Expand Down
20 changes: 7 additions & 13 deletions src/compile/nodes/Element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -702,16 +702,13 @@ export default class Element extends Node {

block.builders.intro.addBlock(deindent`
#component.root._aftercreate.push(() => {
if (!${name}) ${name} = @wrapTransition(#component, ${this.var}, ${fn}, ${snippet}, true, null);
${name}.run(true, () => {
#component.fire("intro.end", { node: ${this.var} });
});
if (!${name}) ${name} = @wrapTransition(#component, ${this.var}, ${fn}, ${snippet}, true);
${name}.run(1);
});
`);

block.builders.outro.addBlock(deindent`
${name}.run(false, () => {
#component.fire("outro.end", { node: ${this.var} });
${name}.run(0, () => {
${block.outros > 1 ? `if (--#outros === 0) #outrocallback();` : `#outrocallback();`}
${name} = null;
});
Expand All @@ -737,10 +734,8 @@ export default class Element extends Node {

block.builders.intro.addBlock(deindent`
#component.root._aftercreate.push(() => {
${introName} = @wrapTransition(#component, ${this.var}, ${fn}, ${snippet}, true, null);
${introName}.run(true, () => {
#component.fire("intro.end", { node: ${this.var} });
});
${introName} = @wrapTransition(#component, ${this.var}, ${fn}, ${snippet}, true);
${introName}.run(1);
});
`);
}
Expand All @@ -760,9 +755,8 @@ export default class Element extends Node {
// TODO hide elements that have outro'd (unless they belong to a still-outroing
// group) prior to their removal from the DOM
block.builders.outro.addBlock(deindent`
${outroName} = @wrapTransition(#component, ${this.var}, ${fn}, ${snippet}, false, null);
${outroName}.run(false, () => {
#component.fire("outro.end", { node: ${this.var} });
${outroName} = @wrapTransition(#component, ${this.var}, ${fn}, ${snippet}, false);
${outroName}.run(0, () => {
${block.outros > 1 ? `if (--#outros === 0) #outrocallback();` : `#outrocallback();`}
});
`);
Expand Down
2 changes: 2 additions & 0 deletions src/compile/nodes/IfBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ export default class IfBlock extends Node {
const updateMountNode = this.getUpdateMountNode(anchor);

const destroyOldBlock = deindent`
@transitionManager.groupOutros();
${name}.o(function() {
${if_blocks}[ ${previous_block_index} ].d(1);
${if_blocks}[ ${previous_block_index} ] = null;
Expand Down Expand Up @@ -406,6 +407,7 @@ export default class IfBlock extends Node {
// as that will typically result in glitching
const exit = branch.hasOutroMethod
? deindent`
@transitionManager.groupOutros();
${name}.o(function() {
${name}.d(1);
${name} = null;
Expand Down
12 changes: 8 additions & 4 deletions src/shared/await-block.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { assign, isPromise } from './utils.js';
import { transitionManager } from './transitions.js';

export function handlePromise(promise, info) {
var token = info.token = {};
Expand All @@ -14,10 +15,13 @@ export function handlePromise(promise, info) {
if (info.block) {
if (info.blocks) {
info.blocks.forEach((block, i) => {
if (i !== index && block) block.o(() => {
block.d(1);
info.blocks[i] = null;
});
if (i !== index && block) {
transitionManager.groupOutros();
block.o(() => {
block.d(1);
info.blocks[i] = null;
});
}
});
} else {
info.block.d(1);
Expand Down
3 changes: 3 additions & 0 deletions src/shared/keyed-each.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { transitionManager } from './transitions.js';

export function destroyBlock(block, lookup) {
block.d(1);
lookup[block.key] = null;
Expand Down Expand Up @@ -43,6 +45,7 @@ export function updateKeyedEach(old_blocks, component, changed, get_key, dynamic
var did_move = {};

var destroy = has_outro ? outroAndDestroyBlock : destroyBlock;
if (has_outro) transitionManager.groupOutros();

function insert(block) {
block[intro_method](node, next);
Expand Down
153 changes: 82 additions & 71 deletions src/shared/transitions.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,35 @@
import { createElement } from './dom.js';
import { noop } from './utils.js';

export function linear(t) {
return t;
}

export function generateRule(
a,
b,
delta,
duration,
ease,
fn
) {
var keyframes = '{\n';

for (var p = 0; p <= 1; p += 16.666 / duration) {
var t = a + delta * ease(p);
keyframes += p * 100 + '%{' + fn(t) + '}\n';
export function generateRule({ a, b, delta, duration }, ease, fn) {
let keyframes = '{\n';

for (let p = 0; p <= 1; p += 16.666 / duration) {
const t = a + delta * ease(p);
keyframes += p * 100 + `%{${fn(t)}}\n`;
}

return keyframes + '100% {' + fn(b) + '}\n}';
return keyframes + `100% {${fn(b)}}\n}`;
}

// https://github.com/darkskyapp/string-hash/blob/master/index.js
export function hash(str) {
var hash = 5381;
var i = str.length;
let hash = 5381;
let i = str.length;

while (i--) hash = ((hash << 5) - hash) ^ str.charCodeAt(i);
return hash >>> 0;
}

export function wrapTransition(component, node, fn, params, intro, outgroup) {
var obj = fn(node, params);
var duration = obj.duration || 300;
var ease = obj.easing || linear;
var cssText;

// TODO share <style> tag between all transitions?
if (obj.css && !transitionManager.stylesheet) {
var style = createElement('style');
document.head.appendChild(style);
transitionManager.stylesheet = style.sheet;
}
export function wrapTransition(component, node, fn, params, intro) {
const obj = fn(node, params);
const duration = obj.duration || 300;
const ease = obj.easing || linear;
let cssText;

if (intro) {
if (obj.css && obj.delay) {
Expand All @@ -58,13 +45,19 @@ export function wrapTransition(component, node, fn, params, intro, outgroup) {
running: false,
program: null,
pending: null,
run: function(intro, callback) {
var program = {

run(b, callback) {
const program = {
start: window.performance.now() + (obj.delay || 0),
intro: intro,
callback: callback
b,
callback: callback || noop
};

if (!b) {
program.group = transitionManager.outros;
transitionManager.outros.remaining += 1;
}

if (obj.delay) {
this.pending = program;
} else {
Expand All @@ -76,60 +69,67 @@ export function wrapTransition(component, node, fn, params, intro, outgroup) {
transitionManager.add(this);
}
},
start: function(program) {
component.fire(program.intro ? 'intro.start' : 'outro.start', { node: node });

start(program) {
component.fire(`${program.b ? 'intro' : 'outro'}.start`, { node });

program.a = this.t;
program.b = program.intro ? 1 : 0;
program.delta = program.b - program.a;
program.duration = duration * Math.abs(program.b - program.a);
program.end = program.start + program.duration;

if (obj.css) {
if (obj.delay) node.style.cssText = cssText;

program.rule = generateRule(
program.a,
program.b,
program.delta,
program.duration,
ease,
obj.css
);

transitionManager.addRule(program.rule, program.name = '__svelte_' + hash(program.rule));
const rule = generateRule(program, ease, obj.css);
transitionManager.addRule(rule, program.name = '__svelte_' + hash(rule));

node.style.animation = (node.style.animation || '')
.split(', ')
.filter(function(anim) {
// when introing, discard old animations if there are any
return anim && (program.delta < 0 || !/__svelte/.test(anim));
})
.concat(program.name + ' ' + program.duration + 'ms linear 1 forwards')
.filter(anim => anim && (program.delta < 0 || !/__svelte/.test(anim)))
.concat(`${program.name} ${program.duration}ms linear 1 forwards`)
.join(', ');
}

this.program = program;
this.pending = null;
},
update: function(now) {
var program = this.program;

update(now) {
const program = this.program;
if (!program) return;

var p = now - program.start;
const p = now - program.start;
this.t = program.a + program.delta * ease(p / program.duration);
if (obj.tick) obj.tick(this.t);
},
done: function() {
var program = this.program;

done() {
const program = this.program;
this.t = program.b;

if (obj.tick) obj.tick(this.t);
if (obj.css) transitionManager.deleteRule(node, program.name);
program.callback();
program = null;

component.fire(`${program.b ? 'intro' : 'outro'}.end`, { node });

if (!program.b) {
program.group.callbacks.push(() => {
program.callback();
if (obj.css) transitionManager.deleteRule(node, program.name);
});

if (--program.group.remaining === 0) {
program.group.callbacks.forEach(fn => {
fn();
});
}
}

this.program = null;
this.running = !!this.pending;
},
abort: function() {

abort() {
if (obj.tick) obj.tick(1);
if (obj.css) transitionManager.deleteRule(node, this.program.name);
this.program = this.pending = null;
Expand All @@ -145,7 +145,7 @@ export var transitionManager = {
stylesheet: null,
activeRules: {},

add: function(transition) {
add(transition) {
this.transitions.push(transition);

if (!this.running) {
Expand All @@ -154,21 +154,27 @@ export var transitionManager = {
}
},

addRule: function(rule, name) {
addRule(rule, name) {
if (!this.stylesheet) {
const style = createElement('style');
document.head.appendChild(style);
transitionManager.stylesheet = style.sheet;
}

if (!this.activeRules[name]) {
this.activeRules[name] = true;
this.stylesheet.insertRule('@keyframes ' + name + ' ' + rule, this.stylesheet.cssRules.length);
this.stylesheet.insertRule(`@keyframes ${name} ${rule}`, this.stylesheet.cssRules.length);
}
},

next: function() {
next() {
this.running = false;

var now = window.performance.now();
var i = this.transitions.length;
const now = window.performance.now();
let i = this.transitions.length;

while (i--) {
var transition = this.transitions[i];
const transition = this.transitions[i];

if (transition.program && now >= transition.program.end) {
transition.done();
Expand All @@ -189,18 +195,23 @@ export var transitionManager = {
if (this.running) {
requestAnimationFrame(this.bound);
} else if (this.stylesheet) {
var i = this.stylesheet.cssRules.length;
let i = this.stylesheet.cssRules.length;
while (i--) this.stylesheet.deleteRule(i);
this.activeRules = {};
}
},

deleteRule: function(node, name) {
deleteRule(node, name) {
node.style.animation = node.style.animation
.split(', ')
.filter(function(anim) {
return anim.indexOf(name) === -1;
})
.filter(anim => anim.indexOf(name) === -1)
.join(', ');
},

groupOutros() {
this.outros = {
remaining: 0,
callbacks: []
};
}
};
24 changes: 24 additions & 0 deletions test/runtime/samples/transition-css-deferred-removal/_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export default {
data: {
visible: true
},

test(assert, component, target, window, raf) {
component.set({ visible: false });

const outer = target.querySelector('.outer');
const inner = target.querySelector('.inner');

const animations = [
outer.style.animation,
inner.style.animation
];

raf.tick(150);

assert.deepEqual([
outer.style.animation,
inner.style.animation
], animations);
},
};
Loading

0 comments on commit af32ba6

Please sign in to comment.