Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Use transitions without removing the element from the DOM #6336

Open
mckravchyk opened this issue May 17, 2021 · 8 comments

Comments

@mckravchyk
Copy link

mckravchyk commented May 17, 2021

Is your feature request related to a problem? Please describe.

It seems like Svelte transitions require that the element be removed from the dom when it fades out and re-attached when it fades in.

I guess it's probably fine if the element is very simple, but usually, it's not. For example, whenever I just want to show/hide a complex sidebar menu, the dom contents of that menu have to be recreated every time it's displayed (and some other stuff like event listeners, js logic has to re-run, etc.).

Describe the solution you'd like
Extending Svelte transition API to allow to fade an element in/out of view without removing/re-attaching it. So for example, instead of removing the element, set a display: none; style, or control the zIndex. (The latter would also be very nice, while we are at it, as in some extreme scenarios it's better performance if the element is already visible).

The ideal, most universal solution would be to let the user decide what should happen to the element on transitionStart / transitionEnd and allow that the element to stay without being removed.

So for example, this is how it's done now - the element will be removed:

{#if visible}
	<p transition:fly="{{ y: 200, duration: 2000 }}">
		Flies in and out
	</p>
{/if}

This is how it could look like with controlling the display property - which would be done by supplying visible property to transition properties:

<!--Supplying the `visible` property will make the element appear / disappear
      using 'display: none' visibility-->
<p transition:fly="{{ visible, y: 200, duration: 2000 }}">
	Flies in and out
</p>

Advanced usage: define the way in which the element should be shown/hidden.

<script>
const show = (el) => { el.style.zIndex = 10; }
const hide = (el) => { el.style.zIndex = -1; }
</script>

<!--Using show, hide properties means that the visibility of the element
      is to be handled by the user (`display: none;` will not be applied)-->
<p transition:fly="{{ visible, show, hide, y: 200, duration: 2000,  }}">
	Flies in and out
</p>

Describe alternatives you've considered
Doing it manually, perhaps Svelte animations API could help (I haven't gotten to it yet).

How important is this feature to you?
Of medium importance. It means that I cannot use Svelte transitions API (which seems awesome). It also seems to be one of those rare cases where Svelte could use some performance optimization.

@mckravchyk mckravchyk changed the title Transition without removing / re-attaching the element from the DOM Feature Request: Transition without removing / re-attaching the element from the DOM May 17, 2021
@mckravchyk mckravchyk changed the title Feature Request: Transition without removing / re-attaching the element from the DOM Feature Request: Use transitions without removing the element from the DOM May 17, 2021
@Zachiah
Copy link
Contributor

Zachiah commented Jun 5, 2021

An alternative here to what you suggested would be a new construct called {#show} that behaves like v-show in vue. That would support more use cases than just transitions/animations

@Zachiah
Copy link
Contributor

Zachiah commented Jun 5, 2021

I'm trying to create a fork with your solution...

Edit:
There is a major problem with the way you suggested solving this isue. Transition values aren't reactive. Svelte just checks the current value whenever there is a new transition. If you look here https://svelte.dev/repl/b36219cc3d3f4f4b82fe60231aadf033?version=3.38.2 and try editing the value in the number box during a transition and then clicking the checkbox again without waiting for it to finish you will see it uses the old value. So until the current transition is finished it has the old value. What this means is that there isn't an easy way to reactively check when the transition value changes in the svelte compiler, as there isn't a need for that currently.

So, Implementing your solution would require a lot of code that checks reactively when transition values change with invalidation and other things like that from other parts of svelte.

Is there something I'm misunderstanding?

@mckravchyk
Copy link
Author

Hi, thanks for your interest in the issue.

I see. The only dynamic property is visible. How about using a store?

...
import { writable } from 'svelte/store';
const visible = writable(false);
...
<p transition:fly="{{ visible, show, hide, y: 200, duration: 2000,  }}">
...

Could this be relatively easy to make the transition module subscribe to changes of the visible property and allow to change visibility while the transition is in progress?

@Zachiah
Copy link
Contributor

Zachiah commented Jul 22, 2021

@mckravchyk Sorry About my delayed reply I get way too many things in my inbox. I think that using a store might work

@aradalvand
Copy link

I'm in favor of this too, needed this a few times myself, opted for direct CSS animations instead.

@adiguba
Copy link
Contributor

adiguba commented Oct 11, 2022

Hello,

I'm trying to implement a solution to this problem, but I'm not sure of the most appropriate syntax.

I don't think adding parameters to the transition is the most appropriate for some reasons (transitions are not reactive, and the params are passed to the transition method, with the risk of conflict)

But as alternative I don't know if I should opt for a block (ex {#display}) which would include the element(s), or for a directive on the element itself (ex: svelte:display).

{#display} block :

Basically it's the same syntax as an {#if} or {#key}, except that the elements are not removed from the DOM.

<!--Supplying the `visible` property will make the element
    appear / disappear using 'display: none' visibility -->
{#display visible}
	<p transition:fly="{{ y: 200, duration: 2000 }}">
		Flies in and out
	</p>
{/display}

By default it will add a "display:none!important" to hide the elements.
But we can imagine an alternative using class-names : {#display visible ("is-hidden", "is-visible")}
Here, when the element must be hidden we will apply the "is-hidden" class, or "is-visible" in the opposite case.

{svelte:display} directive :

Or we can use a simple directive :

<!--Supplying the `visible` property will make the element
    appear / disappear using 'display: none' visibility -->
<p transition:fly="{{ y: 200, duration: 2000 }}" svelte:display={visible}>
	Flies in and out
</p>

We can use an objet to specify the class-names : svelte:display={{state=open, hidden:'is-hidden', visible:'is-visible'}}
Or an array as alternative syntax : svelte:display={[open,'visible','hidden']}

What do you think ?
What is the most appropriate syntax?

@mckravchyk
Copy link
Author

Hi! Thanks for giving this a shot! I think the latter approach with the display directive feels more natural - using a block would be cumbersome. I really like the idea with using class names by the way, that sounds like an obvious approach, but I missed it.

@adiguba
Copy link
Contributor

adiguba commented Oct 12, 2022

I coded a basic prototype (only with display:none for now) and submitted a PR.
Wait & see...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants