Essentially I'm working on a slideshow project where each "slide" is loaded dynamically using <svelte:ponent this={currentSelectionponent} />
. Each slide requires custom in and out transitions on a ponent-by-ponent basis. I'd like for the next slide to "wait" while the current slide finishes transitioning, however, as stated in the svelte documentation:
Unlike with transition:, transitions applied with in: and out: are not bidirectional — an in transition will continue to 'play' alongside the out transition, rather than reversing, if the block is outroed while the transition is in progress. If an out transition is aborted, transitions will restart from scratch.
Is there a sensible way to make the next slide "wait" until the current slide is finished with its outro transition?
Toy Example at REPL
Toy code posted for posterity:
//App.svelte
<script>
import RedThing from './RedThing.svelte';
import GreenThing from './GreenThing.svelte';
import BlueThing from './BlueThing.svelte';
const slides = [
RedThing,
BlueThing,
GreenThing
];
let currentComponent = 0;
const prev = () => currentComponent--;
const next = () => currentComponent++;
</script>
<button on:click={prev}>Prev</button><button on:click={next}>Next</button>
<div>
<svelte:ponent this={slides[currentComponent]}/>
</div>
//RedThing.svelte
<script>
import { fly, slide } from 'svelte/transition';
</script>
<style>
div { color: red; }
</style>
<div in:fly out:slide>red thing</div>
//GreenThing.svelte
<script>
import { fade, slide } from 'svelte/transition';
</script>
<style>
div { color: green; }
</style>
<div in:slide out:fade>green thing</div>
//BlueThing.svelte
<script>
import { scale, fade } from 'svelte/transition';
</script>
<style>
div { color: blue; }
</style>
<div in:scale out:fade>blue thing</div>
Edit: I should add a plication – I am driving ponent traversal through sapper anchor tags, which are taking care of ponent creation / destruction. In other words:
<a href={prev} id="previous-button">Previous</a>
<a href={next} id="next-button">Next</a>
<div>
<svelte:ponent this={slides[currentComponent]}/>
</div>
I'm not sure if that makes a difference?
Essentially I'm working on a slideshow project where each "slide" is loaded dynamically using <svelte:ponent this={currentSelection.ponent} />
. Each slide requires custom in and out transitions on a ponent-by-ponent basis. I'd like for the next slide to "wait" while the current slide finishes transitioning, however, as stated in the svelte documentation:
Unlike with transition:, transitions applied with in: and out: are not bidirectional — an in transition will continue to 'play' alongside the out transition, rather than reversing, if the block is outroed while the transition is in progress. If an out transition is aborted, transitions will restart from scratch.
Is there a sensible way to make the next slide "wait" until the current slide is finished with its outro transition?
Toy Example at REPL
Toy code posted for posterity:
//App.svelte
<script>
import RedThing from './RedThing.svelte';
import GreenThing from './GreenThing.svelte';
import BlueThing from './BlueThing.svelte';
const slides = [
RedThing,
BlueThing,
GreenThing
];
let currentComponent = 0;
const prev = () => currentComponent--;
const next = () => currentComponent++;
</script>
<button on:click={prev}>Prev</button><button on:click={next}>Next</button>
<div>
<svelte:ponent this={slides[currentComponent]}/>
</div>
//RedThing.svelte
<script>
import { fly, slide } from 'svelte/transition';
</script>
<style>
div { color: red; }
</style>
<div in:fly out:slide>red thing</div>
//GreenThing.svelte
<script>
import { fade, slide } from 'svelte/transition';
</script>
<style>
div { color: green; }
</style>
<div in:slide out:fade>green thing</div>
//BlueThing.svelte
<script>
import { scale, fade } from 'svelte/transition';
</script>
<style>
div { color: blue; }
</style>
<div in:scale out:fade>blue thing</div>
Edit: I should add a plication – I am driving ponent traversal through sapper anchor tags, which are taking care of ponent creation / destruction. In other words:
<a href={prev} id="previous-button">Previous</a>
<a href={next} id="next-button">Next</a>
<div>
<svelte:ponent this={slides[currentComponent]}/>
</div>
I'm not sure if that makes a difference?
Share Improve this question edited Jan 26, 2020 at 22:47 fish asked Jan 26, 2020 at 14:50 fishfish 1542 silver badges9 bronze badges 1- It’s been quite a while, but my answer might still be of use to you :) – Rodrigo D'Agostino Commented Feb 25, 2023 at 19:50
4 Answers
Reset to default 8I know this thread is a few month old by here is a simple solution. I had also problems with this. The secret? The delay parameter:
The REPL
// RedThing.svelte
<script>
import { fly, slide } from 'svelte/transition';
</script>
<style>
div { color: red; }
</style>
<div in:fly="{{delay: 300, duration: 300}}" out:slide="{{duration: 300}}">red thing</div>
// GreenThing.svelte
<script>
import { fade, slide } from 'svelte/transition';
</script>
<style>
div { color: green; }
</style>
<div in:slide="{{delay: 300, duration: 300}}" out:fade="{{duration: 300}}">green thing</div>
// BlueThing.svelte
<script>
import { scale, fade } from 'svelte/transition';
</script>
<style>
div { background-color: blue; }
</style>
<div in:scale="{{delay: 300, duration: 300}}" out:fade="{{duration: 300}}">blue thing</div>
I've found a semi-workable solution to my issue by adding position: absolute;
to each dynamic ponent's container. This works because transitions append the ining ponent to the dom as a sibling to the old one before destroying it. By making the position absolute, the outgoing and ining ponents inhabit the same position. A bit of fade tweaking makes it look ok. This is not an ideal solution, but it may suffice.
Example:
//RedThing.svelte
<script>
import { fly, slide } from 'svelte/transition';
</script>
<style>
div { color: red; }
</style>
<div style="position:absolute;" transition:fade={{duration: tweaky}}>
<div in:fly out:slide >red thing</div>
</div>
//GreenThing.svelte
<script>
import { fade, slide } from 'svelte/transition';
</script>
<style>
div { color: green; }
</style>
<div style="position:absolute;" transition:fade={{duration: tweaky}}>
<div in:slide out:fade >green thing</div>
</div>
Inspired / stolen by this solution to create sapper crossfades between pages: https://dev.to/buhrmi/svelte-ponent-transitions-5ie
This assumes consistent duration for all transitions. Added delay for intro that matches outro duration in each slide ponent. If you want different durations for each slide transition, you might need to track and match them (may be in a store).
<script>
import { scale, fade } from 'svelte/transition';
</script>
<style>
div { background-color: blue; }
</style>
<div in:scale={{duration: 500, delay: 500}}
out:fade={{duration: 500}}
>
blue thing
</div>
Quite a bit late to the party, but I feel it is still worth sharing what I came up with :)
To begin with, I made use of the delay
property (suggested by MaddEye) to make sure that the animation of the outgoing element would not overlap the ining one’s. That was a good start, but I was experiencing a content reflow during that instant in which these two elements coexist. That’s when I felt that fish’s suggestion could e in handy, but I really wanted to avoid setting a position: absolute
to each element because it could easily cause different kinds of issues.
That’s when I remembered there is another way to achieve the same effect without the drawbacks of position: absolute
. It consists in setting display: grid
on the parent element, and then assigning grid-column
and grid-row
with specific values to the child elements so that they end up sitting in the same position, in the same grid area. Plus you can even align them as you wish thanks to the grid
layout :)
I’ll leave you a demo REPL to better illustrate my explanation ;)