Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: react/react
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 056073de
Choose a base ref
...
head repository: react/react
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 540efebc
Choose a head ref
  • 2 commits
  • 16 files changed
  • 1 contributor

Commits on Jan 10, 2025

  1. View Transition Refs (#32038)

    This adds refs to View Transition that can resolve to an instance of:
    
    ```js
    type ViewTransitionRef = {
      name: string,
      group: Animatable,
      imagePair: Animatable,
      old: Animatable,
      new: Animatable,
    }
    ```
    
    Animatable is a type that has `animate(keyframes, options)` and
    `getAnimations()` on it. It's the interface that exists on Element that
    lets you start animations on it. These ones are like that but for the
    four pseudo-elements created by the view transition.
    
    If a name changes, then a new ref is created. That way if you hold onto
    a ref during an exit animation spawned by the name change, you can keep
    calling functions on it. It will keep referring to the old name rather
    than the new name.
    
    This allows imperative control over the animations instead of using CSS
    for this.
    
    ```js
    const viewTransition = ref.current;
    const groupAnimation = viewTransition.group.animate(keyframes, options);
    const imagePairAnimation = viewTransition.imagePair.animate(keyframes, options);
    const oldAnimation = viewTransition.old.animate(keyframes, options);
    const newAnimation = viewTransition.new.animate(keyframes, options);
    ```
    
    The downside of using this API is that it doesn't work with SSR so for
    SSR rendered animations they'll fallback to the CSS. You could use this
    for progressive enhancement though.
    
    Note: In this PR the ref only controls one DOM node child but there can
    be more than one DOM node in the ViewTransition fragment and they are
    just left to their defaults. We could try something like making the
    `animate()` function apply to multiple children but that could lead to
    some weird consequences and the return value would be difficult to
    merge. We could try to maintain an array of Animatable that updates with
    how ever many things are currently animating but that makes the API more
    complicated to use for the simple case. Conceptually this should be like
    a fragment so we would ideally combine the multiple children into a
    single isolate if we could. Maybe one day the same name could be applied
    to multiple children to create a single isolate. For now I think I'll
    just leave it like this and you're really expect to just use it with one
    DOM node. If you have more than one they just get the default animations
    from CSS.
    
    Using this is a little tricky due timing. In this fixture I just use a
    layout effect plus rAF to get into the right timing after the
    startViewTransition is ready. In the future I'll add an event that fires
    when View Transitions heuristics fire with the right timing.
    sebmarkbage authored Jan 10, 2025
    Configuration menu
    Copy the full SHA
    0bf1f39 View commit details
    Browse the repository at this point in the history

Commits on Jan 12, 2025

  1. View Transition Events (#32041)

    This adds five events to `<ViewTransition>` that triggers when React
    wants to animate it.
    
    - `onEnter`: The `<ViewTransition>` or its parent Component is mounted
    and there's no other `<ViewTransition>` with the same name being
    deleted.
    - `onExit`: The `<ViewTransition>` or its parent Component is unmounted
    and there's no other `<ViewTransition>` with the same name being
    deleted.
    - `onLayout`: There are no updates to the content inside this
    `<ViewTransition>` boundary itself but the boundary has resized or moved
    due to other changes to siblings.
    - `onShare`: This `<ViewTransition>` is being mounted and another
    `<ViewTransition>` instance with the same name is being unmounted
    elsewhere.
    - `onUpdate`: The content of `<ViewTransition>` has changed either due
    to DOM mutations or because an inner child `<ViewTransition>` has
    resized.
    
    Only one of these events is fired per Transition. If you want to cover
    all updates you have to listen to `onLayout`, `onShare` and `onUpdate`.
    We could potentially do something like fire `onUpdate` if `onLayout` or
    `onShare` isn't specified but it's a little sketchy to have behavior
    based on if someone is listening since it limits adding wrappers that
    may or may not need it.
    
    Each takes a `ViewTransitionInstance` as an argument so you don't need a
    ref to animate it.
    
    ```js
    <ViewTransition onEnter={inst => inst.new.animate(keyframes, options)}>
    ```
    
    The timing of this event is after the View Transition's `ready` state
    which means that's too late to do any changes to the View Transition's
    snapshots but now both the new and old pseudo-elements are ready to
    animate.
    
    The order of `onExit` is parent first, where as the others are child
    first. This mimics effect mount/unmount.
    
    I implement this by adding to a queue in the commit phase and then call
    it while we're finishing up the commit. This is after layout effects but
    before passive effects since passive effects fire after the animation is
    `finished`.
    sebmarkbage authored Jan 12, 2025
    Configuration menu
    Copy the full SHA
    540efeb View commit details
    Browse the repository at this point in the history
Loading