8000 [css-view-transitions-2] Allow synchronous snapshots · Issue #9400 · w3c/csswg-drafts · GitHub
[go: up one dir, main page]

Skip to content

[css-view-transitions-2] Allow synchronous snapshots #9400

@mattgperry

Description

@mattgperry

Problem

Currently, view transitions are triggered asynchronously.

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

console.log(2)

// 2, 1

Every example of view library integration shows startViewTransition wrapping some kind of state update function.

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.

Screenshot 2023-09-23 at 10 31 43

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.

class Component {
  snapshot() {
    this.transition = document.snapshotViewTransition()
  }
  
  updated() {
    this.transition.notifyUpdateComplete()
  }
}
Screenshot 2023-09-23 at 10 39 09

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?

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0