sveltejs / svelte

Cybernetically enhanced web apps
https://svelte.dev
MIT License
77.56k stars 4.04k forks source link

Uneasy bouncing in `transition:slide` #12003

Open qupig opened 1 month ago

qupig commented 1 month ago

Describe the bug

Check the REPL. This issues can be reproduced in Svelte4/5.

source code ```svelte
{#if visible1}

slide in and out 1-1

slide in and out 1-2

{/if}

slide in and out 1-3

slide in and out 1-4

{#if visible2}

slide in and out 2-1

{/if}

slide in and out 2-2

slide in and out 2-3

{#if visible3}

slide in and out 3-1

slide in and out 3-2

{/if}

slide in and out 3-3

slide in and out 3-4

```
demo video https://github.com/sveltejs/svelte/assets/61939856/9b209e6f-99dc-4478-a18c-16e05f51ba3d

Reproduction

REPL

Logs

No response

System Info

Svelte compiler version 4.2.18
Svelte compiler version 5.0.0-next.153

Severity

annoyance

Neptunium1129 commented 1 month ago

Please save 'svelte'...

henrikvilhelmberglund commented 1 month ago

There needs to be an offset so the slide ends up at the correct position.

https://github.com/sveltejs/svelte/blob/2491eb7c8503c08693a7e72b152557668d19f54b/packages/svelte/src/transition/index.js#L132 changing this line to ${primary_property}: ${t * primary_property_value + t * 16}px; with a hardcoded offset value fixes the issue for the top transition but I'm not too sure how to get the actual value needed. It's still a bit ugly too with the background but not sure if it's fixable.

Someone mentioned in an old issue that jquery had a version of this that works so maybe checking that out could help: https://github.com/sveltejs/svelte/issues/2957

Hopefully someone smarter than me can fix this. Here's the transition before and after adding the offset:

Before ![before](https://github.com/sveltejs/svelte/assets/110549389/39714bbf-d223-4216-b0b4-a5c492251b4f)
After ![after](https://github.com/sveltejs/svelte/assets/110549389/f9172686-f594-479d-a86c-3cade6bb4c44)

Edit: after playing around and looking at 7nik's example below I have no idea anymore, it doesn't seem like a single offset at least. Adding an offset with the margin of the child element makes the child text stay in place but the background of the transitioning element looks wrong. Ideally the text should stay in place and the background should transition smoothly too.

7nik commented 1 month ago

Basically, it's caused by margin collapsing, but its rules are complicated. Moreover, margin collapsing may involve more than two elements, which may be non-adjacent siblings or non-direct parent-child, and all of it leads to a wide variety of tricky cases.

I tried to fix the first example, but you can see that inner content still jumps.

7nik commented 1 month ago

I tried to do it, but I don't know how reliable it is. Plus, it ignores writing-mode and white-space. Likely something else isn't taken into account.

henrikvilhelmberglund commented 1 month ago

I tried to do it, but I don't know how reliable it is. Plus, it ignores writing-mode and white-space. Likely something else isn't taken into account.

It looks good but isn't reversible in the in-out direction, eg. when you enable->disable or spam click it gets a bit glitchy.

7nik commented 1 month ago

Updated version (v4). I'll blame Svelte's transition API for being limiting. I find Vue's transition API much better.

henrikvilhelmberglund commented 1 month ago

Updated version (v3). I'll blame Svelte's transition API for being limiting. I find Vue's transition API much better.

Is there anything specific that's missing or could be improved? Looks good now except if you double click from enabled->disabled->enabled it disappears instead of showing.

7nik commented 1 month ago

Is there anything specific that's missing or could be improved?

Some CSS like white-space: preserve and clear: both may still break things. In some edge cases, the condition of margin collapsibility or finding an adjacent margin may be wrong. I read specs, but it's hard to comprehend them.

if you double click from enabled->disabled->enabled it disappears instead of showing

Looks like a bug in all transitions.

qupig commented 4 weeks ago

In fact, even using Flexbox, you will encounter bouncing when there is a gap:

source code ```svelte

slide in and out 3-1

{#if visible}

slide in and out 3-2

{/if}

slide in and out 3-3

slide in and out 3-4

```

REPL

It would be worse if there was a height or max-height there:

REPL

For example in a scroll-box:

REPL or REPL

qupig commented 4 weeks ago

Additional, I have to correct that the distinction in my original REPL as bounce on “in” / bounce on “out” was wrong.

In fact if you look at the video frame by frame, you will find that it actually exists in both directions, it is just more prominent in one direction due to visual effects.

7nik commented 4 weeks ago

I believe this entire issue will be classified as "won't fix" — margin collapse is a really complex topic (my incomplete solution is already bigger 5-6 times, and yesterday I looked at clear and found a case that can be resolved only by animating multiple elements, which is impossible with css fn, only with tick fn). Plus, the user's CSS can interfere with the animation styles, which can barely be anything. Here is another example of messing with transition.

In all these cases, there are only two solutions:

  1. change your CSS and/or DOM to remove the CSS interference;
  2. write your own transition function that takes into account your case.

Writing a universal transition function isn't worth it — the code quickly becomes too large and heavy, while the goal of the built-in transition function is just covering common cases.

qupig commented 4 weeks ago

@7nik Pretty much agree with what you said.

But really, I think these examples are basic use cases, not complex situations.

This makes the built-in transition function probably almost worthless. (cause it's easy for people to hit the wall)

And it's necessary to explicitly mention these limitations and workarounds in the documentation.