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

Svelte 5: Transition easing functions aren't reversed during out transition #9937

Closed
aradalvand opened this issue Dec 16, 2023 · 22 comments · Fixed by #10190
Closed

Svelte 5: Transition easing functions aren't reversed during out transition #9937

aradalvand opened this issue Dec 16, 2023 · 22 comments · Fixed by #10190
Assignees

Comments

@aradalvand
Copy link

aradalvand commented Dec 16, 2023

Describe the bug

Take cubicOut as an example; in Svelte 4, this is what it looks like (see REPL):

Rec.0004.mp4

Nice and graceful.

Now, in Svelte 5, however, the same exact code yields a very different "out" transition (see REPL):

Rec.0005.mp4

Which fails to reverse the easing, and thereby creates a jarring look; this is presumably because internally Animation.reverse() is being called for the out transition, which results in the latter type of "reversing". But this never looks good; Svelte 4's way of "reversing" the transition was the "correct" one. The Web Animations API's reverse() method doesn't reverse the easing, which, again, feels jarring and isn't normally what you want; so just calling reverse() isn't sufficient on its own.

There is presumably some way to simulate the former behavior using the Web Animations API, but I haven't looked into that.

It's worth noting that the former behavior is also how plain old CSS transitions (+ toggling classes) work (i.e. they also reverse the easing); so, as I said, Svelte 4 is the one doing the most natural/expected thing here. Svelte 5 should follow suit.

Reproduction

See the REPLs linked above.

Logs

No response

System Info

N/A (see the REPLs)

Severity

blocking an upgrade

@aradalvand aradalvand changed the title Svelte 5: Transition easing functions now work fundamentally differently Svelte 5: Transition easing functions are also reversed during out transition (unlike in Svelte 4) Dec 16, 2023
@aradalvand aradalvand changed the title Svelte 5: Transition easing functions are also reversed during out transition (unlike in Svelte 4) Svelte 5: Transition easing functions aren't reversed during out transition (whereas they were Svelte 4) Dec 16, 2023
@aradalvand aradalvand changed the title Svelte 5: Transition easing functions aren't reversed during out transition (whereas they were Svelte 4) Svelte 5: Transition easing functions aren't reversed during out transition (unlike in Svelte 4) Dec 16, 2023
@aradalvand aradalvand changed the title Svelte 5: Transition easing functions aren't reversed during out transition (unlike in Svelte 4) Svelte 5: Transition easing functions aren't reversed during out transition Dec 16, 2023
@robertadamsonsmith
Copy link

I was wondering if this could be worked around, by creating a custom transition function which dynamically switches the easing function for a reversed one when the out transition is playing.

It appears that isn't possible due to the fix in #8318 not being merged (which would allow the transition function to know the current transition direction), and also because it appears that the easing function cannot be dynamically altered, as used to be possible (which is itself a shame, since that sort of dynamic behaviour is useful).

Repl example here

Switching the transition for separate in and out transitions does somewhat work around the problem, but then means that an interrupted/reversed outro causes a sudden jump instead of a smooth transition (as when transition is used, or the intro is interrupted/reversed), which may itself be an issue.

@aradalvand
Copy link
Author

aradalvand commented Dec 16, 2023

Switching the transition for separate in and out transitions does somewhat work around the problem, but then means that an interrupted/reversed outro causes a sudden jump instead of a smooth transition (as when transition is used, or the intro is interrupted/reversed)

Yeah well, as you noticed, that's not a solution, since the animation couldn't be reversed halfway through when you have separate in and out transitions; which has always been one the key features of Svelte transitions (and also CSS transitions, for that matter).

This seems to me like something that should be fixed by Svelte internally, there's just no possible workaround.

Although the flexibility that something like a direction parameter would bring about would certainly be useful even beyond this particular problem; though I'm not sure how doable that is.

@xamir82
Copy link
Contributor

xamir82 commented Dec 16, 2023

I was recently wondering why transitions felt weird in v5, just found out this is the culprit. I'd like to see this fixed too.

@aradalvand
Copy link
Author

aradalvand commented Dec 16, 2023

After playing with the Web Animations API a little bit, I believe I found out a pretty straightforward way to pull this off:

When the outro animation needs to be played, instead of using the native .reverse() method, Svelte could play a new animation with the same easing function on the element (this in effect reverses the easing since this is now a forwards animation, but the "motion" is outro, if you will), at the end of which, the element is removed.

Here's a very basic demo.

This achieves the desired effect:

Rec.0008.mp4

@robertadamsonsmith
Copy link

Looking at @aradalvand 's idea, I had a go at improving the behaviour by having the first keyframe state be determined via getComputedStyle (a bit like transition/fly does) when there is a transition already in progress. This makes the transition animation very robust, and tolerant of multiple cancellations and restarts for either the intro or outro transition without glitching.

REPL link to demo

You should be able to hammer on the animate button, and have everything work as expected.

Whether this sort of approach is suitable for incorporating into Svelte is another matter though (I don't know the specifics of how transitions are now implemented, other than the general move to using the animation API).

@aradalvand
Copy link
Author

aradalvand commented Dec 17, 2023

Thanks @robertadamsonsmith, that's nice, good job. I think that's the way to go.

Also, it just occurred to me that this very approach could be used to make separate in and out transitions reversible as well, even improving upon Svelte 4 in this regard.

Waiting for the maintainers now to tell us what they think.

@robertadamsonsmith
Copy link

It looks like Svelte 5 is still building keyframes from the css callback returned by transition function, and since the fly function is already starting from the current style via getComputedStyle, I'm not actually sure why things are misbehaving - this seems like something that should be fixable, but I don't follow the logic in svelte/packages/svelte/src/internal/client/transitions.js well enough to understand exactly what is going on.

@trueadm trueadm self-assigned this Dec 17, 2023
@trueadm
Copy link
Contributor

trueadm commented Dec 17, 2023

Thanks, I'll take a look when I'm back after the holidays :)

I do wonder if this is actually a bug in Web Animations API though. I would have expected reverse to apply the easing in reverse too. I wonder if there's some configuration we're missing to make that work as expected? However if it turns out we can't use this API then we can likely do something like mentioned above, however the downside is that this won't play as smoothly when applying it to an existing transition that is active (Svelte 4 is janky with this on low end devices, Web Animations are smoother here).

@denlukia
Copy link

Wait, am I the only one here for who the very first video of this issue looks nothing like "Nice and graceful."?

And the second one looks like "Yep, thats the correctly reversed outro".

It's not perfect. But to me it's the correct outro at least for the shown intro.

It's rather expectations question – "When we are specifying easing for the transition, are we expecting easing to be reversed on outro together with transition".

Personally I think of an easing as an inseparable stylistic description of a transition, when transition behaves reversed it behaves reversed in its whole – with easing

@xamir82
Copy link
Contributor

xamir82 commented Dec 18, 2023

am I the only one here

Yes.

@aradalvand
Copy link
Author

aradalvand commented Dec 18, 2023

But to me it's the correct outro at least for the shown intro.

No, it's not. The "correct" outro involves also reversing the easing, again, that's how CSS transitions have always worked — although you could also describe the same thing as "not reversing the easing", depending on which perspective you choose to look at it, it's a bit of a semantic quibble, which is why I struggled with titling this issue properly at first, but the underlying behavior being described remains the same.

Think of it this way: When you say "ease-out", for example, what you're basically saying, as the name implies, is "I want the beginning of the animation to be fast, and the ending to be slow"; the current implementation (+ Animation.reverse()) fails to accomplish this for the outro transition, where it starts the animation slowly, and ends it fast, which is, of course, the opposite of what you wanted (i.e. "ease-out"). Hopefully that makes it clear.

The REPLs might not make this immediately obvious since they're meant to be minimal repros, but having tried this in a real-world app, I can assure you it does look very awkward (e.g. a modal that slides up and down with a highly curved easing function like cubicOut looks like it's stuck for like half a second when you try to close it).

@aradalvand
Copy link
Author

aradalvand commented Jan 13, 2024

@trueadm Any movement on this one, Dominic?

@trueadm
Copy link
Contributor

trueadm commented Jan 15, 2024

This turned out to be super complex. I don't think we can ever get it perfectly like Svelte 4 either – as web animations just work differently. We can reverse the easing on the keyframes and re-apply them, but only once the transition has finished. Doing it during a transition and trying to reverse it causes far too many issues to be reliable. So it's better than what we have now, but is likely the best we can do with web animations – unless someone knows how we can apply custom easing separately from the keyframes.

@aradalvand
Copy link
Author

aradalvand commented Jan 16, 2024

@trueadm What issues did this solution cause? It seemed to work perfectly reliably.

@trueadm
Copy link
Contributor

trueadm commented Jan 16, 2024

@aradalvand It doesn't work at a core level, unfortunately. Making that change causes hundreds of unit tests to fail.

@aradalvand
Copy link
Author

aradalvand commented Jan 17, 2024

It doesn't work at a core level... Making that change causes hundreds of unit tests to fail.

@trueadm The fact that it causes the unit tests as they currently are to fail does not indicate that it shouldn't done though, does it? It's the unit tests that need modifying if fixing this issue makes them fail. And you didn't elaborate on what "not working at a core level" actually means, apart from it not matching the current unit tests.

I'm very curious what specifically you were referring to when you said "Doing it during a transition and trying to reverse it causes far too many issues to be reliable". What are some concrete examples of those "far too many issues"?

@dummdidumm
Copy link
Member

Svelte 5 uses the web animations API which Svelte 4 did not. There are some tricky edge cases which make them work differently/ make certain things easier but others harder. To get more details I suggest you dig into the code yourself

@trueadm
Copy link
Contributor

trueadm commented Jan 17, 2024

@aradalvand I'm not saying this is a solved story right now. We still have work to do, however it's complicated. It turns out that replacing keyframes of a non-idle web animation in progress causes flicker between different browsers. So you can't simply just replace the keyframes with new ones with the easing reversed without there being some issues. We can reverse them once the transition has finished and the web animation is idle however, and that's what my PR did.

As for why we can't just apply what was mentioned above – well that's not how transition work in Svelte. For example the fly transition, already applies any existing transforms on the keyframes, but that doesn't solve the issue here. Furthermore, if you have many combined transitions (like an animation) then going too far down this route can cause those animations to break too – which was why I was seeing unit tests fail (which were taken from Svelte 4 and shouldn't need fundamental changing).

What can really help me fix things is REPL cases showing the oddities in what we have right now. :)

@aradalvand
Copy link
Author

aradalvand commented Jan 17, 2024

Svelte 5 uses the web animations API which Svelte 4 did not. There are some tricky edge cases which make them work differently/ make certain things easier but others harder.

@dummdidumm I'm well aware of that. @robertadamsonsmith and I, however, seemed to achieve the desired result with the Web Animations API as demonstrated in the linked REPLs like this one; which is why I'm finding it surprising to hear that it somehow wasn't a feasible approach.

What can really help me fix things is REPL cases showing the oddities in what we have right now. :)

@trueadm Well, the only oddity left is that an outro transition that starts before the intro transition is completed now looks different in terms of easing than one that begins after the intro transition is completed. As you pointed out yourself. So, this issue has only been half-fixed right now.
I'm going to have to create a new issue specifically for that then (although this one did represent it, IMO, but it's been closed).

I build animation/transition-heavy web apps and this particular issue and this regression, so to speak, in the behavior of Svelte 5 transitions would be an absolutely massive bummer for me. I'd really want to see it fixed.

@robertadamsonsmith
Copy link

@aradalvand The REPL I posted isn't directly applicable to how Svelte transitions work. I was relying on the browser interpolating between the current computed style and the first/last frame of the transition animation and using CSS easing, whereas Svelte transitions (such as fly) assume that the current computed style is to be maintained, and then builds a series of frames using the custom easing function, in a way that doesn't make it possible to simply resume a transition from the current style.

I'd be interested to investigate the flicker issue that @trueadm mentioned though. I've not been able to reproduce any flicker after checking a few Windows and Android web browsers (with the REPL I posted). Are there specific browsers/devices that are problematic?

@trueadm
Copy link
Contributor

trueadm commented Jan 18, 2024

@robertadamsonsmith I wasn't seeing issues with your approach above, but more with trying to apply keyframes on a non-idle transition using the animation.current.setKeyframes API. It's most apparent on Webkit iOS FWIW.

@aradalvand can you link me to the other issue you mentioned? I'd like to tackle that next.

@aradalvand
Copy link
Author

aradalvand commented Jan 18, 2024

@aradalvand can you link me to the other issue you mentioned? I'd like to tackle that next.

@trueadm Sure, I just created it: #10219
Thanks in advance.

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

Successfully merging a pull request may close this issue.

6 participants