Skip to content

Commit

Permalink
fix: properly delay intro transitions
Browse files Browse the repository at this point in the history
WAAPI applies the styles of a delayed animation only when that animation starts. In the case of fade-in transitions that means the element is visible, then goes invisible and fades in. Fix that by never applying a delay on intro transitions, instead add keyframes of the initial state for the duration of the delay.

Fixes #10876
  • Loading branch information
dummdidumm committed Jul 10, 2024
1 parent 9c1371a commit c170e27
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/tricky-ears-shout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': patch
---

fix: properly delay intro transitions
18 changes: 15 additions & 3 deletions packages/svelte/src/internal/client/dom/elements/transitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,8 @@ export function transition(flags, element, get_fn, get_params) {
* @returns {import('#client').Animation}
*/
function animate(element, options, counterpart, t2, callback) {
var is_intro = t2 === 1;

if (is_function(options)) {
// In the case of a deferred transition (such as `crossfade`), `option` will be
// a function rather than an `AnimationConfig`. We need to call this function
Expand All @@ -274,7 +276,7 @@ function animate(element, options, counterpart, t2, callback) {
var a;

queue_micro_task(() => {
var o = options({ direction: t2 === 1 ? 'in' : 'out' });
var o = options({ direction: is_intro ? 'in' : 'out' });
a = animate(element, o, counterpart, t2, callback);
});

Expand Down Expand Up @@ -320,15 +322,25 @@ function animate(element, options, counterpart, t2, callback) {
var keyframes = [];
var n = Math.ceil(duration / (1000 / 60)); // `n` must be an integer, or we risk missing the `t2` value

// In case of a delayed intro, apply the initial style for the duration of the delay;
// else in case of a fade-in for example the element would be visible until the animation starts
if (is_intro && delay > 0) {
let m = Math.ceil(delay / (1000 / 60));
let keyframe = css_to_keyframe(css(0, 1));
for (let i = 0; i <= m; i += 1) {
keyframes.push(keyframe);
}
}

for (var i = 0; i <= n; i += 1) {
var t = t1 + delta * easing(i / n);
var styles = css(t, 1 - t);
keyframes.push(css_to_keyframe(styles));
}

animation = element.animate(keyframes, {
delay,
duration,
delay: is_intro ? 0 : delay,
duration: duration + (is_intro ? delay : 0),
easing: 'linear',
fill: 'forwards'
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { flushSync } from '../../../../src/index-client.js';
import { test } from '../../test';

export default test({
test({ assert, raf, target }) {
const btn = target.querySelector('button');

// in
btn?.click();
flushSync();
assert.htmlEqual(
target.innerHTML,
'<button>toggle</button><p style="opacity: 0;">delayed fade</p>'
);
raf.tick(1);
assert.htmlEqual(
target.innerHTML,
'<button>toggle</button><p style="opacity: 0;">delayed fade</p>'
);

raf.tick(99);
assert.htmlEqual(
target.innerHTML,
'<button>toggle</button><p style="opacity: 0;">delayed fade</p>'
);

raf.tick(150);
assert.htmlEqual(
target.innerHTML,
'<button>toggle</button><p style="opacity: 0.5;">delayed fade</p>'
);

raf.tick(201);
assert.htmlEqual(target.innerHTML, '<button>toggle</button><p style="">delayed fade</p>');

// out
btn?.click();
flushSync();
raf.tick(75);
assert.htmlEqual(target.innerHTML, '<button>toggle</button><p style="">delayed fade</p>');

raf.tick(150);
assert.htmlEqual(
target.innerHTML,
'<button>toggle</button><p style="opacity: 0.5;">delayed fade</p>'
);
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script>
import { fade } from 'svelte/transition';
let visible = $state(false);
</script>

<button onclick={() => (visible = !visible)}>toggle</button>

{#if visible}
<p transition:fade={{ delay: 100, duration: 100 }}>delayed fade</p>
{/if}

0 comments on commit c170e27

Please sign in to comment.