animate while switching dynamic element

pull/6898/head
tanhauhau 4 years ago
parent f4a8e799ff
commit 0ef1dfca7a

@ -48,6 +48,7 @@ export default class Block {
hydrate: Array<Node | Node[]>;
mount: Array<Node | Node[]>;
measure: Array<Node | Node[]>;
restore_measurements: Array<Node | Node[]>;
fix: Array<Node | Node[]>;
animate: Array<Node | Node[]>;
intro: Array<Node | Node[]>;
@ -96,6 +97,7 @@ export default class Block {
hydrate: [],
mount: [],
measure: [],
restore_measurements: [],
fix: [],
animate: [],
intro: [],
@ -326,6 +328,12 @@ export default class Block {
${this.chunks.measure}
}`;
if (this.chunks.restore_measurements.length) {
properties.restore_measurements = x`function #restore_measurements(#measurement) {
${this.chunks.restore_measurements}
}`;
}
properties.fix = x`function #fix() {
${this.chunks.fix}
}`;
@ -379,6 +387,7 @@ export default class Block {
m: ${properties.mount},
p: ${properties.update},
r: ${properties.measure},
s: ${properties.restore_measurements},
f: ${properties.fix},
a: ${properties.animate},
i: ${properties.intro},

@ -135,6 +135,8 @@ const events = [
}
];
const CHILD_DYNAMIC_ELEMENT_BLOCK = 'child_dynamic_element';
export default class ElementWrapper extends Wrapper {
node: Element;
fragment: FragmentWrapper;
@ -161,11 +163,11 @@ export default class ElementWrapper extends Wrapper {
) {
super(renderer, block, parent, node);
if (node.is_dynamic_element && block.type !== 'child_dynamic_element') {
if (node.is_dynamic_element && block.type !== CHILD_DYNAMIC_ELEMENT_BLOCK) {
this.child_dynamic_element_block = block.child({
comment: create_debugging_comment(node, renderer.component),
name: renderer.component.get_unique_name('create_dynamic_element'),
type: 'child_dynamic_element'
type: CHILD_DYNAMIC_ELEMENT_BLOCK
});
renderer.blocks.push(this.child_dynamic_element_block);
this.child_dynamic_element = new ElementWrapper(
@ -344,9 +346,14 @@ export default class ElementWrapper extends Wrapper {
block.chunks.destroy.push(b`if (${this.var}) ${this.var}.d(detaching)`);
if (this.node.animation) {
block.chunks.measure.push(b`${this.var}.r()`);
block.chunks.fix.push(b`${this.var}.f()`);
block.chunks.animate.push(b`${this.var}.a()`);
const measurements = block.get_unique_name('measurements');
block.add_variable(measurements);
block.chunks.measure.push(b`${measurements} = ${this.var}.r()`);
block.chunks.fix.push(b`${this.var}.f();`);
block.chunks.animate.push(b`
${this.var}.s(${measurements});
${this.var}.a()
`);
}
}
@ -983,6 +990,11 @@ export default class ElementWrapper extends Wrapper {
${rect} = ${this.var}.getBoundingClientRect();
`);
if (block.type === CHILD_DYNAMIC_ELEMENT_BLOCK) {
block.chunks.measure.push(b`return ${rect}`);
block.chunks.restore_measurements.push(b`${rect} = #measurement;`);
}
block.chunks.fix.push(b`
@fix_position(${this.var});
${stop_animation}();

@ -0,0 +1,105 @@
let originalDivGetBoundingClientRect;
let originalSpanGetBoundingClientRect;
let originalParagraphGetBoundingClientRect;
export default {
skip_if_ssr: true,
props: {
things: [
{ id: 1, name: 'a' },
{ id: 2, name: 'b' },
{ id: 3, name: 'c' },
{ id: 4, name: 'd' },
{ id: 5, name: 'e' }
],
tag: 'div'
},
html: `
<div>a</div>
<div>b</div>
<div>c</div>
<div>d</div>
<div>e</div>
`,
before_test() {
originalDivGetBoundingClientRect =
window.HTMLDivElement.prototype.getBoundingClientRect;
originalSpanGetBoundingClientRect =
window.HTMLSpanElement.prototype.getBoundingClientRect;
originalParagraphGetBoundingClientRect =
window.HTMLParagraphElement.prototype.getBoundingClientRect;
window.HTMLDivElement.prototype.getBoundingClientRect =
fakeGetBoundingClientRect;
window.HTMLSpanElement.prototype.getBoundingClientRect =
fakeGetBoundingClientRect;
window.HTMLParagraphElement.prototype.getBoundingClientRect =
fakeGetBoundingClientRect;
function fakeGetBoundingClientRect() {
const index = [...this.parentNode.children].indexOf(this);
const top = index * 30;
return {
left: 0,
right: 100,
top,
bottom: top + 20
};
}
},
after_test() {
window.HTMLDivElement.prototype.getBoundingClientRect =
originalDivGetBoundingClientRect;
window.HTMLSpanElement.prototype.getBoundingClientRect =
originalSpanGetBoundingClientRect;
window.HTMLParagraphElement.prototype.getBoundingClientRect =
originalParagraphGetBoundingClientRect;
},
async test({ assert, component, target, raf }) {
// switch tag and things at the same time
await component.update('p', [
{ id: 5, name: 'e' },
{ id: 2, name: 'b' },
{ id: 3, name: 'c' },
{ id: 4, name: 'd' },
{ id: 1, name: 'a' }
]);
const ps = document.querySelectorAll('p');
assert.equal(ps[0].dy, 120);
assert.equal(ps[4].dy, -120);
raf.tick(50);
assert.equal(ps[0].dy, 60);
assert.equal(ps[4].dy, -60);
raf.tick(100);
assert.equal(ps[0].dy, 0);
assert.equal(ps[4].dy, 0);
await component.update('span', [
{ id: 1, name: 'a' },
{ id: 2, name: 'b' },
{ id: 3, name: 'c' },
{ id: 4, name: 'd' },
{ id: 5, name: 'e' }
]);
const spans = document.querySelectorAll('span');
assert.equal(spans[0].dy, 120);
assert.equal(spans[4].dy, -120);
raf.tick(150);
assert.equal(spans[0].dy, 60);
assert.equal(spans[4].dy, -60);
raf.tick(200);
assert.equal(spans[0].dy, 0);
assert.equal(spans[4].dy, 0);
}
};

@ -0,0 +1,26 @@
<script>
export let things;
export let tag;
function flip(node, animation, params) {
const dx = animation.from.left - animation.to.left;
const dy = animation.from.top - animation.to.top;
return {
duration: 100,
tick: (t, u) => {
node.dx = u * dx;
node.dy = u * dy;
}
};
}
export function update(new_tag, new_things) {
things = new_things;
tag = new_tag;
}
</script>
{#each things as thing (thing.id)}
<svelte:element this={tag} animate:flip>{thing.name}</svelte:element>
{/each}

@ -1,5 +1,4 @@
export default {
solo: true,
html: '',
test({ component, target, assert }) {
component.tag = 'h1';

Loading…
Cancel
Save