svelte
svelte copied to clipboard
Equivalent of Vue's out-in transition mode
For elements that are positioned statically or relatively, intros that happen at the same time as outros can be problematic:
{{#if foo}}
<div transition:fade='{duration:1500}'>yep</div>
{{else}}
<div transition:fade='{duration:1500}'>nope</div>
{{/if}}
Vue has a nice solution to this — transition modes. Since Svelte doesn't have an equivalent of <transition>
, the concept doesn't translate directly, but it would be nice to be able to express something similar without the hacky use of delay
.
Just my 2¢ but this seems too fancy too me, just let people do whatever with CSS. The Vue docs demo of in-out
doesn't even appear to work smoothly without a jump for me in Firefox 57
I'm not 100% sure that the current behaviour feels like a hack:
https://svelte.dev/repl/07465934948446f2b1fe823731225c57?version=3.18.2
Seems fairly reasonable to me. Unless there is specific appetite for improving transitions further?
https://svelte.dev/repl/96ade4f9af6c4bb59dc2ea23c43f72f2?version=3.18.2
@antony this is the problem with that method.
I encounter this problem in all of my Svelte projects- feels like I'm missing something. Fighting it with absolute positioning usually forces me to re-write a lot of CSS multiple times.
Is there is a better way to solve this that I've overlooked? Or would it be better to use an external animation library for animation-heavy projects?
+1 to the appetite for further improving transitions 😁
What do you think of the workaround (position: "absolute"
) mentioned here?
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
I think this is still a valid feature not worthy of an RFC. Reopening.
For anyone else finding this issue, the linked stackoverflow also now suggests a workaround that uses css-grid, and doesn't require the machinery the previous workaround did. It boils down to adding a div
around the transitioning element that needs to share space with a different copy of it, and styling it like this:
.transition-enforcement {
display: grid;
}
.transition-enforcement > * {
grid-column: 1/2;
grid-row: 1/2;
}
It's still would be very nice to have.
I've written an outin.ts utility which like crossfade creates out and in functions:
- The
:out
transition starts immediately - Automaticly calculates the delay of the
in:
- Minimize layout shift by setting
out:
node toposition: absolute
- Abortable & Undoable (Reversed transitions)
<script>
import { fade } from "svelte/transition";
import outin from "$lib/outin";
const [fadeOut, fadeIn] = outin({ transition: fade });
let foo = true
</script>
{{#if foo}}
<div in:fadeIn out:fadeOut>yep</div>
{{else}}
<div in:fadeIn out:fadeOut>nope</div>
{{/if}}
To add duration in the template:
<div in:fadeIn={{duration: 1500}} in:fadeIn={{duration: 1500}} />
or in the script:
outin({ transition: (node, options = {}) => fade(node, { duration: 1500, ...options })});
const [blurOut, flyIn] = outin({ out: blur, in: fly });
I think it's a nice starting point and could serve as inspiration for something we could import from svelte/transtion
I like the utility above but I don't know if it's a bug or not, I still have the layout shift but only when switching back to an element which is higher in the stacking context than the current element, for example:
{{#if foo}}
<div in:fadeIn out:fadeOut>yep</div>
{{else}}
<div in:fadeIn out:fadeOut>nope</div>
{{/if}}
- yep to nope: crossfades smoothly
- nope to yep: crossfades with layout shift
But in my case I have a loop with a condition inside it, maybe that causes a problem..
I ended up using the grid solution as proposed by @SohumB:
<div class="transition-enforcement">
{#each slides as slide, id}
{#if cur === id}
<div in:fade out:fade>
<h4>{slide.name}</h4>
<h5>{slide.title}</h5>
<p>{slide.quote}</p>
</div>
{/if}
{/each}
</div>
<style>
.transition-enforcement {
display: grid;
}
.transition-enforcement > * {
grid-column: 1/2;
grid-row: 1/2;
}
</style>
Any new approaches or developments this? @Rich-Harris All those years by and I just can't find a truly one solution to replicate vue's out-in mode.
It'd be great to have something like
<div transition:fade={{ duration: 300, mode: 'out-in' }}></div>
Or eventually the ability to do with via css classes or something alike...
<div transition:css={{ classes: "fade-in", mode: 'out-in' }}></div>
<div mode:out-in in:fade out:slide></div>
It's a bummer, that this is really an issue - just encountered this. I'm transitioning a calendar page (almost full size) in/out and when I do so, at some point you can see both of them under one another. 😢
Coming from Vue here. Honestly kind of bummed out to see that this isn't a feature in svelte. It gets really annoying to have to figure out layout shifts manually.
I would really love to have the jump controlled by svelte as well. It would be so nice to not have to set up manually with absolute or grid