[csswg-drafts] [css-view-transitions-2] Allow synchronous snapshots (#9400)

mattgperry has just created a new issue for https://github.com/w3c/csswg-drafts:

== [css-view-transitions-2] Allow synchronous snapshots ==
# Problem

Currently, view transitions are triggered asynchronously.

```javascript
document.startViewTransition(() => {
  console.log(1)
})

console.log(2)

// 2, 1
```

Every [example of view library integration](https://developer.chrome.com/docs/web-platform/view-transitions/#working-with-frameworks) shows `startViewTransition` wrapping some kind of state update function.

```javascript
document.startViewTransition(() => {
  flushSync(() => setState(newState))
})
```

The work performed within this state change is often heavy, notably when changing everything on a page (a common usecase for this API). It includes all the code within a render function, everything the view library does to compute the required DOM updates (diffing etc), the update itself, any layout effects etc.

So we are required to freeze the website (animations in most cases, scroll and other interactions) throughout all this work.

The vast majority of this work is, at least in React and probably in other libraries, usually interruptable. But not when we've frozen everything for a view transition.  

<img width="1229" alt="Screenshot 2023-09-23 at 10 31 43" src="https://github.com/w3c/csswg-drafts/assets/7850794/e6b80d46-2557-48b9-81cf-b6a744bbab35">

# Proposed solution

A synchronous alternative to snapshotting would allow any view library that supports before/after commit lifecycles to snapshot *just* before the DOM is updated, and start the animation just after it's commited. Moving all the calculation, diffing etc to before the snapshot. 

Although the snapshot itself is now (optionally!) synchronous, moving all this work to before the snapshot means that view libraries like React that support time chunking and interruption will remain responsive. Likewise we will only snapshot changes that are actually going to happen.

```javascript
class Component {
  snapshot() {
    this.transition = document.snapshotViewTransition()
  }
  
  updated() {
    this.transition.notifyUpdateComplete()
  }
}
```

<img width="1367" alt="Screenshot 2023-09-23 at 10 39 09" src="https://github.com/w3c/csswg-drafts/assets/7850794/9db7929c-6335-4d6c-bc4c-6852ce335116">

# Alternatives

It is of course possible that all view libraries that exist or could exist make it possible for the snapshot lifecycle to defer commit until a returned promise is resolved. Something like this?

```javascript
class Component {
  snapshot() {
    let snapshotReady
    this.updated = new Promise()
    const promise = new Promise(resolve => {
      resolve = resolve
    })
    this.transition = document.startViewTransition(() => {
      snapshotReady()
      await this.updated
    })
    
    return promise
  }
  
  updated() {
    resolve this.updated
  }
}
```

But it's unlikely any will implement this just for the View Transitions API and practically by offering a synchronous alternative to the current API we can solve this across all view libraries right now.

Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/9400 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Saturday, 23 September 2023 08:47:26 UTC