svelte icon indicating copy to clipboard operation
svelte copied to clipboard

Equivalent of Vue's out-in transition mode

Open Rich-Harris opened this issue 7 years ago • 13 comments

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}}

yep-nope

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.

Rich-Harris avatar May 01 '17 20:05 Rich-Harris

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

aubergene avatar Dec 03 '17 23:12 aubergene

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?

antony avatar Feb 14 '20 18:02 antony

https://svelte.dev/repl/96ade4f9af6c4bb59dc2ea23c43f72f2?version=3.18.2

@antony this is the problem with that method.

Blazzike avatar Mar 16 '20 14:03 Blazzike

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 😁

braebo avatar Sep 06 '20 20:09 braebo

What do you think of the workaround (position: "absolute") mentioned here?

TylerRick avatar Nov 11 '20 01:11 TylerRick

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.

stale[bot] avatar Jun 26 '21 22:06 stale[bot]

I think this is still a valid feature not worthy of an RFC. Reopening.

pngwn avatar Jul 11 '21 11:07 pngwn

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;
}

SohumB avatar Sep 07 '21 04:09 SohumB

It's still would be very nice to have.

gyurielf avatar Nov 26 '21 15:11 gyurielf

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 to position: absolute
  • Abortable & Undoable (Reversed transitions)

REPL

<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

bfanger avatar Jan 08 '22 19:01 bfanger

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>

craxrev avatar Jan 21 '22 17:01 craxrev

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>

tiagoapp avatar Aug 17 '22 19:08 tiagoapp

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. 😢

divStar avatar Sep 11 '22 17:09 divStar

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.

ImpossibleReality avatar Mar 18 '23 01:03 ImpossibleReality

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

Garth619 avatar Nov 20 '23 18:11 Garth619