sveltejs / svelte

web development for the rest of us
https://svelte.dev
MIT License
79.5k stars 4.2k forks source link

Flip animation stops and unexpected transform: matrix() style when tab is hidden in Svelte 5 #13599

Open Karosmiy opened 1 week ago

Karosmiy commented 1 week ago

Describe the bug

I'm encountering an issue with the flip animation on a list of elements in Svelte. The list is dynamically updated based on WebSocket messages. Here's what happens:

  1. The flip animation works fine when the tab is active.
  2. When I switch to another tab or minimize the browser, and wait for some time, the flip animation either stops or behaves unexpectedly.
  3. After returning to the tab, I notice that certain elements have incorrect computed styles, such as: transform: matrix(1, 0, 0, 1, 0, 1394). These elements are not positioned correctly.
  4. If I trigger a sort function on the list after returning to the tab, the issue resolves, and everything falls back into place. I suspect the issue might be related to requestAnimationFrame being paused when the tab is inactive, which might interfere with how the flip animation is applied.

Reproduction

Steps to Reproduce:

  1. Switch to another tab or minimize the browser (Safari or Firefox) and wait for some time.
  2. Dynamically update the list via WebSocket messages.
  3. Return to the tab and observe the animation behavior and computed transform style.

Expected Behavior:

  1. The flip animation should continue running smoothly when returning to the tab.
  2. The elements should not have incorrect transform values or be positioned incorrectly.

Actual Behavior:

  1. The flip animation stops or behaves unexpectedly when returning to the tab.
  2. Some elements have incorrect computed styles like transform: matrix(1, 0, 0, 1, 0, 1394, which positions them incorrectly.
  3. Triggering a sort while the tab is active again fixes the issue.

Additional Information:

It seems that requestAnimationFrame might be paused while the tab is inactive, causing this issue. Sorting the list while the tab is active again fixes the problem, but this is not an ideal workaround. Currently, I'm working around this issue by disabling the flip animation when the tab becomes inactive and re-enabling it when the tab becomes active again. However, this is more of a temporary fix.

Logs

No response

System Info

System:
OS: macOS 14.6.1
CPU: (10) arm64 Apple M1 Pro
Memory: 103.28 MB / 16.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 20.10.0 - ~/.nvm/versions/node/v20.10.0/bin/node
Yarn: 1.22.19 - ~/.nvm/versions/node/v20.8.0/bin/yarn
npm: 10.2.3 - ~/.nvm/versions/node/v20.10.0/bin/npm
Browsers:
Safari: 17.6
npmPackages:
svelte: ^5.0.0-next.238 => 5.0.0-next.263

Severity

annoyance

Karosmiy commented 1 week ago

it may be related to #13168

gyzerok commented 1 week ago

It seems like the problem might be indeed in the requestAnimationFrame as it seems to be used for animations. And browsers might throttle or disable it when tabs are in the background.

Looked through the internals a bit a here is what I've found:

  1. Animation does use loop: https://github.com/sveltejs/svelte/blob/main/packages/svelte/src/internal/client/dom/elements/transitions.js#L381
  2. Which in turns uses internal raf: https://github.com/sveltejs/svelte/blob/main/packages/svelte/src/internal/client/loop.js#L34
  3. Which is defined using browser rAF: https://github.com/sveltejs/svelte/blob/main/packages/svelte/src/internal/client/timing.js#L6

Wondering what could be done on Svelte side to avoid this problem?

trueadm commented 1 week ago

AFAIK we only use rAF for tick based transitions and the spring motion. If you're not using these things, then we aren't likely using rAF. You can check by monkey patching globalThis.requestAnimationFrame = () => debugger and seeing if it fires.