Web: 一个跨页面动画的 API
Web: 一个跨页面动画的 API
John DoeWeb: 一个跨页面动画的 API
我目前知道的跨页面动画需要用到 JS。通过 FLIP思想来做。
现在看到了一个新的 API。copy 过来给大家看看
https://developer.chrome.com/docs/web-platform/view-transitions/#api-reference
Smooth and simple transitions with the View Transitions API
Published on Tuesday, August 17, 2021 • Updated on Tuesday, November
22, 2022
Jake Archibald Jake Archibald Human boy working on web standards at
Google
Website Twitter This feature was previously called “Shared Element
Transitions”, and is sometimes referred to as “page transitions”.
The View Transition API makes it easy to change the DOM in a single
step, while creating an animated transition between the two states.
It’s available in Chrome 111+, currently in beta.
Transitions created with the View Transition API. Try the demo site –
Requires Chrome 111+. #Why do we need this feature? Page transitions not
only look great, they also communicate direction of flow, and make it
clear which elements are related from page to page. They can even happen
during data fetching, leading to a faster perception of performance.
But, we already have animation tools on the web, such as CSS
transitions, CSS animations, and the Web Animation API, so why do we
need a new thing to move stuff around?
The truth is, state transitions are hard, even with the tools we
already have.
Even something like a simple cross-fade involves both states being
present at the same time. That presents usability challenges, such as
handling additional interaction on the outgoing element. Also, for users
of assistive devices, there’s a period where both the before and after
state are in the DOM at the same time, and things may move around the
tree in a way that’s fine visually, but can easily cause reading
position and focus to be lost.
Handling state changes is particularly challenging if the two states
differ in scroll position. And, if an element is moving from one
container to another, you can run into difficulties with overflow:
hidden and other forms of clipping, meaning you have to restructure your
CSS to get the effect you want.
It isn’t impossible, it’s just really hard.
View Transitions give you an easier way, by allowing you to make your
DOM change without any overlap between states, but create a transition
animation between the states using snapshotted views.
Additionally, although the current implementation targets single page
apps (SPAs), this feature will be expanded to allow for transitions
between full page loads, which is currently impossible.
#Standardization status The feature is being developed within the W3C
CSS Working Group as a draft specification.
Once we’re happy with the API design, we’ll start the processes and
checks required to ship this feature to stable.
Developer feedback is really important, so please file issues on
GitHub with suggestions and questions.
#The simplest transition: A cross-fade The default View Transition is
a cross-fade, so it serves as a nice introduction to the API:
function spaNavigate(data) { // Fallback for browsers that don’t
support this API: if (!document.startViewTransition) {
updateTheDOMSomehow(data); return; }
// With a transition: document.startViewTransition(() =>
updateTheDOMSomehow(data)); } Where updateTheDOMSomehow changes the DOM
to the new state. That can be done however you want: Add/removing
elements, changing class names, changing styles… it doesn’t matter.
And just like that, pages cross-fade:
The default cross-fade. Minimal demo. Source. Ok, a cross-fade isn’t
that impressive. Thankfully, transitions can be customized, but before
we get to that, we need to understand how this basic cross-fade
worked.
Demo links in this article are less visually impressive, but also
have a tiny codebase. The goal is to make it easier to read and
understand all of the code, without knowing any particular SPA
framework.
#How these transitions work Taking the code sample from above:
document.startViewTransition(() => updateTheDOMSomehow(data));
When .startViewTransition() is called, the API captures the current
state of the page. This includes taking a screenshot.
Once that’s complete, the callback passed to .startViewTransition()
is called. That’s where the DOM is changed. Then, the API captures the
new state of the page.
Once the state is captured, the API constructs a pseudo-element tree
like this:
::view-transition └─ ::view-transition-group(root) └─
::view-transition-image-pair(root) ├─ ::view-transition-old(root) └─
::view-transition-new(root) The ::view-transition sits in an overlay,
over everything else on the page. This is useful if you want to set a
background color for the transition.
::view-transition-old(root) is a screenshot of the old view, and
::view-transition-new(root) is a live representation of the new view.
Both render as CSS ‘replaced content’ (like an
).
The old view animates from opacity: 1 to opacity: 0, while the new
view animates from opacity: 0 to opacity: 1, creating a cross-fade.
All of the animation is performed using CSS animations, so they can
be customized with CSS.
The old and new views also have mix-blend-box: plus-lighter, and the
::view-transition-image-pair has isolation: isolate. This is required to
create a correct cross-fade. If you’re interested in why, here’s far too
many words on the topic.
#Simple customization All of the pseudo-elements above can be
targeted with CSS, and since the animations are defined using CSS, you
can modify them using existing CSS animation properties. For
example:
::view-transition-old(root), ::view-transition-new(root) {
animation-duration: 5s; } With that one change, the fade is now really
slow:
Long cross-fade. Minimal demo. Source. Ok, that’s still not
impressive. Instead, let’s implement Material Design’s shared axis
transition:
@keyframes
fade-in { from { opacity: 0; } }
@keyframes
fade-out { to { opacity: 0; } }
@keyframes
slide-from-right { from { transform: translateX(30px); } }
@keyframes
slide-to-left { to { transform: translateX(-30px); } }
::view-transition-old(root) { animation: 90ms cubic-bezier(0.4, 0, 1,
- both fade-out, 300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
::view-transition-new(root) { animation: 210ms cubic-bezier(0, 0,
0.2, 1) 90ms both fade-in, 300ms cubic-bezier(0.4, 0, 0.2, 1) both
slide-from-right; } And here’s the result:
Shared axis transition. Minimal demo. Source. In this example, the
animation always moves from right to left, which doesn’t feel natural
when clicking the back button. How to change the animation depending on
the direction of navigation is covered later in the article.
#Transitioning multiple elements In the previous demo, the whole page
is involved in the shared axis transition. That works for most of the
page, but it doesn’t seem quite right for the heading, as it slides out
just to slide back in again.
To avoid this, you can extract the header from the rest of the page
so it can be animated separately. This is done by assigning a
view-transition-name to the element.
.main-header { view-transition-name: main-header; } The value of
view-transition-name can be whatever you want (except for none, which
means there’s no transition name). It’s used to uniquely identify the
element across the transition.
view-transition-name must be unique. If two rendered elements have
the same view-transition-name at the same time, the transition will be
skipped.
And the result of that:
Shared axis transition with fixed header. Minimal demo. Source. Now
the header stays in place and cross-fades.
That CSS declaration caused the pseudo-element tree to change:
::view-transition ├─ ::view-transition-group(root) │ └─
::view-transition-image-pair(root) │ ├─ ::view-transition-old(root) │ └─
::view-transition-new(root) └─ ::view-transition-group(main-header) └─
::view-transition-image-pair(main-header) ├─
::view-transition-old(main-header) └─ ::view-transition-new(main-header)
There are now two transition groups. One for the header, and another for
the rest. These can be targeted independently with CSS, and given
different transitions. Although, in this case main-header was left with
the default transition, which is a cross-fade.
Well, ok, the default transition isn’t just a cross fade, the
::view-transition-group also transitions:
Position and transform (via a transform) Width Height That hasn’t
mattered until now, as the header is the same size and position both
sides of the DOM change. But we can also extract the text in the
header:
.main-header-text { view-transition-name: main-header-text; width:
fit-content; } fit-content is used so the element is the size of the
text, rather than stretching to the remaining width. Without this, the
back arrow reduces the size of the header text element, whereas we want
it to be the same size in both pages.
So now we have three parts to play with:
::view-transition ├─ ::view-transition-group(root) │ └─ … ├─
::view-transition-group(main-header) │ └─ … └─
::view-transition-group(main-header-text) └─ … But again, just going
with the defaults:
Sliding header text. Minimal demo. Source. Now the heading text does
a little satisfying slide across to make space for the back button.
View transitions use a flat structure. In the real DOM, the heading
text was in the header. But, during the transition, their respective
::view-transition-groups are siblings. This is really handy when
animating items from one container to another, as you don’t need to
worry about clipping from parent elements.
#Debugging transitions Since View Transitions are built on top of CSS
animations, the animations panel in Chrome Dev Tools is great for
debugging transitions.
Using the animations panel, you can pause the next animation, then
scrub back and forth through the animation. During this, the transition
pseudo-elements can be found in the elements panel.
Debugging View Transitions with Chrome Dev Tools. #Transitioning
elements don’t need to be the same DOM element So far we’ve used
view-transition-name to create separate transition elements for the
header, and the text in the header. These are conceptually the same
element before and after the DOM change, but you can create transitions
where that isn’t the case.
For instance, the main video embed can be given a
view-transition-name:
.full-embed { view-transition-name: full-embed; } Then, when the
thumbnail is clicked, it can be given the same view-transition-name,
just for the duration of the transition:
thumbnail.onclick = async () => {
thumbnail.style.viewTransitionName = ‘full-embed’;
document.startViewTransition(() => {
thumbnail.style.viewTransitionName = ’’; updateTheDOMSomehow(); }); };
And the result:
One element transitioning to another. Minimal demo. Source. The
thumbnail now transitions into the main image. Even though they’re
conceptually (and literally) different elements, the transition API
treats them as the same thing because they shared the same
view-transition-name.
The real code for this is a little more complicated than the simple
example above, as it also handles the transition back to the thumbnail
page. See the source for the full implementation.
#Custom entry and exit transitions Look at this example:
Entering and exiting sidebar. Minimal demo. Source. The sidebar is
part of the transition:
.sidebar { view-transition-name: sidebar; } But, unlike the header in
the previous example, the sidebar doesn’t appear on all pages. If both
states have the sidebar, the transition pseudo-elements look like
this:
::view-transition ├─ …other transition groups… └─
::view-transition-group(sidebar) └─
::view-transition-image-pair(sidebar) ├─ ::view-transition-old(sidebar)
└─ ::view-transition-new(sidebar) However, if the sidebar is only on the
new page, the ::view-transition-old(sidebar) pseudo-element won’t be
there. Since there’s no ‘old’ image for the sidebar, the image-pair will
only have a ::view-transition-new(sidebar). Similarly, if the sidebar is
only on the old page, the image-pair will only have a
::view-transition-old(sidebar).
In the demo above, the sidebar transitions differently depending on
whether it’s entering, exiting, or present in both states. It enters by
sliding from the right and fading in, it exits by sliding to the right
and fading out, and it stays in place when it’s present in both
states.
To create specific entry and exit transitions, you can use the
:only-child pseudo-class to target the old/new pseudo-element when it’s
the only child in the image-pair:
/* Entry transition */ ::view-transition-new(sidebar):only-child {
animation: 300ms cubic-bezier(0, 0, 0.2, 1) both fade-in, 300ms
cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right; }
/* Exit transition */ ::view-transition-old(sidebar):only-child {
animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out, 300ms
cubic-bezier(0.4, 0, 0.2, 1) both slide-to-right; } In this case,
there’s no specific transition for when the sidebar is present in both
states, since the default is perfect.
Support for :only-child on transition pseudo-elements was added in
Chrome 110.
#Async DOM updates, and waiting for content The callback passed to
.startViewTransition() can return a promise, which allows for async DOM
updates, and waiting for important content to be ready.
document.startViewTransition(async () => { await something; await
updateTheDOMSomehow(); await somethingElse; }); The transition won’t be
started until the promise fulfills. During this time, the page is
frozen, so delays here should be kept to a minimum. Specifically,
network fetches should be done before calling .startViewTransition(),
while the page is still fully interactive, rather than doing them as
part of the .startViewTransition() callback.
If you decide to wait for images or fonts to be ready, be sure to use
an aggressive timeout:
const wait = ms => new Promise(r => setTimeout(r, ms));
document.startViewTransition(async () => {
updateTheDOMSomehow();
// Pause for up to 100ms for fonts to be ready: await
Promise.race([document.fonts.ready, wait(100)]); }); However, in some
cases it’s better to avoid the delay altogether, and use the content you
already have.
#Making the most of content you already have In the case where the
thumbnail transitions to a larger image:
The default transition is to cross-fade, which means the thumbnail
could be cross-fading with a not-yet-loaded full image.
One way to handle this is to wait for the full image to load before
starting the transition. Ideally this would be done before calling
.startViewTransition(), so the page remains interactive, and a spinner
can be shown to indicate to the user that things are loading. But in
this case there’s a better way:
::view-transition-old(full-embed), ::view-transition-new(full-embed)
{ /* Prevent the default animation, so both views remain opacity:1
throughout the transition / animation: none; / Use normal
blending, so the new view sits on top and obscures the old view */
mix-blend-mode: normal; } Now the thumbnail doesn’t fade away, it just
sits underneath the full image. This means if the new view hasn’t
loaded, the thumbnail is visible throughout the transition. This means
the transition can start straight away, and the full image can load in
its own time.
This wouldn’t work if the new view featured transparency, but in this
case we know it doesn’t, so we can make this optimization.
#Handling changes in aspect ratio Conveniently, all the transitions
so far have been to elements with the same aspect ratio, but that won’t
always be the case. What if the thumbnail is 1:1, and the main image is
16:9?
One element transitioning to another, with an aspect ratio change.
Minimal demo. Source. In the default transition, the group animates from
the before size to the after size. The old and new views are 100% width
of the group, and auto height, meaning they keep their aspect ratio
regardless of the group’s size.
This is a good default, but it isn’t what we want in this case.
So:
::view-transition-old(full-embed), ::view-transition-new(full-embed)
{ /* Prevent the default animation, so both views remain opacity:1
throughout the transition / animation: none; / Use normal
blending, so the new view sits on top and obscures the old view /
mix-blend-mode: normal; / Make the height the same as the group,
meaning the view size might not match its aspect-ratio. / height:
100%; / Clip any overflow of the view */ overflow: clip; }
/* The old view is the thumbnail /
::view-transition-old(full-embed) { / Maintain the aspect ratio of
the view, by shrinking it to fit within the bounds of the element */
object-fit: contain; }
/* The new view is the full image /
::view-transition-new(full-embed) { / Maintain the aspect ratio of
the view, by growing it to cover the bounds of the element */
object-fit: cover; } This means the thumbnail stays in the center of the
element as the width expands, but the full image ‘un-crops’ as it
transitions from 1:1 to 16:9.
Animating width and height, as happens here on the
::view-transition-group, is generally frowned upon in web performance
circles as it runs layout per frame. However, for View Transitions, we
plan to optimize it so it can run off the main thread in most cases.
This optimization hasn’t been implemented yet.
#Changing the transition depending on device state You may want to
use different transitions on mobile vs desktop, such as this example
which performs a full slide from the side on mobile, but a more subtle
slide on desktop:
One element transitioning to another. Minimal demo. Source. This can
be achieved using regular media queries:
/* Transitions for mobile */ ::view-transition-old(root) { animation:
300ms ease-out both full-slide-to-left; }
::view-transition-new(root) { animation: 300ms ease-out both
full-slide-from-right; }
@media (min-width:
500px) { /* Overrides for larger displays. This is the shared axis
transition from earlier in the article. */ ::view-transition-old(root) {
animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out, 300ms
cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left; }
::view-transition-new(root) { animation: 210ms cubic-bezier(0, 0,
0.2, 1) 90ms both fade-in, 300ms cubic-bezier(0.4, 0, 0.2, 1) both
slide-from-right; } } You may also want to change which elements you
assign a view-transition-name depending on matching media queries.
#Reacting to the ‘reduced motion’ preference Users can indicate they
prefer reduced motion via their operating system, and that preference is
exposed via CSS.
You could chose to prevent any transitions for these users:
@media
(prefers-reduced-motion) { ::view-transition-group(),
::view-transition-old(), ::view-transition-new(*) { animation: none
!important; } } However, a preference for ‘reduced motion’ doesn’t mean
the user wants no motion. Instead of the above, you could choose a more
subtle animation, but one that still expresses the relationship between
elements, and the flow of data.
#Changing the transition depending on the type of navigation
Sometimes a navigation from one particular type of page to another
should have a specifically tailored transition. Or, a ‘back’ navigation
should be different to a ‘forward’ navigation.
Different transitions when going ‘back’. Minimal demo. Source. The best
way to handle these cases is to set a class name on
, also known as the document element:
if (isBackNavigation) {
document.documentElement.classList.add(‘back-transition’); }
const transition = document.startViewTransition(() =>
updateTheDOMSomehow(data) );
try { await transition.finished; } finally {
document.documentElement.classList.remove(‘back-transition’); } This
example uses transition.finished, a promise that resolves once the
transition has reached its end state. Other properties of this object
are covered in the API reference.
The above doesn’t define how isBackNavigation is determined, as that
depends on how the navigation is performed. The Navigation API is really
useful here, and that’s what’s used in the minimal demo.
Now you can use that class name in your CSS to change the
transition:
/* ‘Forward’ transitions */ ::view-transition-old(root) { animation:
90ms cubic-bezier(0.4, 0, 1, 1) both fade-out, 300ms cubic-bezier(0.4,
0, 0.2, 1) both slide-to-left; }
::view-transition-new(root) { animation: 210ms cubic-bezier(0, 0,
0.2, 1) 90ms both fade-in, 300ms cubic-bezier(0.4, 0, 0.2, 1) both
slide-from-right; }
/* Overrides for ‘back’ transitions */
.back-transition::view-transition-old(root) { animation-name: fade-out,
slide-to-right; }
.back-transition::view-transition-new(root) { animation-name:
fade-in, slide-from-left; } As with media queries, the presence of these
classes could also be used to change which elements get a
view-transition-name.
#Transitioning without freezing other animations Take a look at this
demo of a video transitioning position:
Video transition. Minimal demo. Source. Did you see anything wrong
with it? Don’t worry if you didn’t. Here it is slowed right down:
Video transition, slower. Minimal demo. Source. During the
transition, the video appears to freeze, then the playing version of the
video fades in. This is because the ::view-transition-old(video) is a
screenshot of the old view, whereas the ::view-transition-new(video) is
a live image of the new view.
You can fix this, but first, ask yourself if it’s worth fixing. If
you didn’t see the ‘problem’ when the transition was playing at its
normal speed, I wouldn’t bother changing it.
If you really want to fix it, then don’t show the
::view-transition-old(video); switch straight to the
::view-transition-new(video). You can do this by overriding the default
styles and animations:
::view-transition-old(video) { /* Don’t show the frozen old view */
display: none; }
::view-transition-new(video) { /* Don’t fade the new view in */
animation: none; } And that’s it!
Video transition, slower. Minimal demo. Source. Now the video plays
throughout the transition.
#Animating with JavaScript So far, all the transitions have been
defined using CSS, but sometimes CSS isn’t enough:
Circle transition. Minimal demo. Source. A couple of parts of this
transition can’t be achieved with CSS alone:
The animation starts from the click location. The animation ends with
the circle having a radius to the farthest corner. Although, hopefully
this will be possible with CSS in future. Thankfully, you can create
transitions using the Web Animation API!
let lastClick; addEventListener(‘click’, event => (lastClick =
event));
function spaNavigate(data) { // Fallback for browsers that don’t
support this API: if (!document.startViewTransition) {
updateTheDOMSomehow(data); return; }
// Get the click position, or fallback to the middle of the screen
const x = lastClick?.clientX ?? innerWidth / 2; const y =
lastClick?.clientY ?? innerHeight / 2; // Get the distance to the
furthest corner const endRadius = Math.hypot( Math.max(x, innerWidth -
x), Math.max(y, innerHeight - y) );
// With a transition: const transition =
document.startViewTransition(() => { updateTheDOMSomehow(data);
});
// Wait for the pseudo-elements to be created:
transition.ready.then(() => { // Animate the root’s new view
document.documentElement.animate( { clipPath: [circle(0 at ${x}px ${y}px),circle(${endRadius}px at ${x}px ${y}px), ], }, { duration:
500, easing: ‘ease-in’, // Specify which pseudo-element to animate
pseudoElement: ‘::view-transition-new(root)’, } ); }); } This example
uses transition.ready, a promise that resolves once the transition
pseudo-elements have been successfully created. Other properties of this
object are covered in the API reference.
#Transitions as an enhancement The View Transition API is designed to
‘wrap’ a DOM change and create a transition for it. However, the
transition should be treated as an enhancement, as in, your app
shouldn’t enter an ‘error’ state if the DOM change succeeds, but the
transition fails. Ideally the transition shouldn’t fail, but if it does,
it shouldn’t break the rest of the user experience.
In order to treat transitions as an enhancement, take care not to use
transition promises in a way that would cause your app to throw if the
transition fails.
Don’t
async function switchView(data) { // Fallback for browsers that don’t
support this API: if (!document.startViewTransition) { await
updateTheDOM(data); return; }
const transition = document.startViewTransition(async () => {
await updateTheDOM(data); });
await transition.ready;
document.documentElement.animate( { clipPath:
[inset(50%), inset(0)], }, { duration: 500,
easing: ‘ease-in’, pseudoElement: ‘::view-transition-new(root)’, } ); }
The problem with this example is that switchView() will reject if the
transition cannot reach a ready state, but that doesn’t mean that the
view failed to switch. The DOM may have successfully updated, but there
were duplicate view-transition-names, so the transition was skipped.
Instead:
Do
async function switchView(data) { // Fallback for browsers that don’t
support this API: if (!document.startViewTransition) { await
updateTheDOM(data); return; }
const transition = document.startViewTransition(async () => {
await updateTheDOM(data); });
animateFromMiddle(transition);
await transition.updateCallbackDone; }
async function animateFromMiddle(transition) { try { await
transition.ready;
1 | document.documentElement.animate( |
} catch (err) { // You might want to log this error, but it shouldn’t
break the app } } This example uses transition.updateCallbackDone to
wait for the DOM update, and to reject if it fails. switchView no longer
rejects if the transition fails, it resolves when the DOM update
completes, and rejects if it fails.
If you want switchView to resolve when the new view has ‘settled’, as
in, any animated transition has completed or skipped to the end, replace
transition.updateCallbackDone with transition.finished.
updateCallbackDone was previously domUpdated. It was renamed in
Chrome 111.
#Not a polyfill, but… I don’t think this feature can be polyfilled in
any useful way, but I’m happy to be proven wrong!
However, this helper function makes things much easier in browsers
that don’t support view transitions:
function transitionHelper({ skipTransition = false, classNames = [],
updateDOM, }) { if (skipTransition || !document.startViewTransition) {
const updateCallbackDone = Promise.resolve(updateDOM()).then(() =>
{});
1 | return { |
}
document.documentElement.classList.add(…classNames);
const transition = document.startViewTransition(updateDOM);
transition.finished.finally(() =>
document.documentElement.classList.remove(…classNames) );
return transition; } And it can be used like this:
function spaNavigate(data) { const classNames = isBackNavigation ?
[‘back-transition’] : [];
const transition = transitionHelper({ classNames, updateDOM() {
updateTheDOMSomehow(data); }, });
// … } In browsers that don’t support View Transitions, updateDOM
will still be called, but there won’t be an animated transition.
You can also provide some classNames to add to
during the transition, making it easier to change the transition
depending on the type of navigation.
You can also pass true to skipTransition if you don’t want an
animation, even in browsers that support View Transitions. This is
useful if your site has a user preference to disable transitions.
#Working with frameworks If you’re working with a library or
framework that abstracts away DOM changes, the tricky part is knowing
when the DOM change is complete. Here’s a set of examples, using the
helper above, in various frameworks.
React—the key here is flushSync, which applies a set of state changes
synchronously. Yes, there’s a big warning about using that API, but Dan
Abramov assures me it’s appropriate in this case. As usual with React
and async code, when using the various promises returned by
startViewTransition, take care that your code is running with the
correct state. Vue.js—the key here is nextTick, which fulfills once the
DOM has been updated. Svelte—very similar to Vue, but the method to
await the next change is tick. Lit—the key here is the
this.updateComplete promise within components, which fulfills once the
DOM has been updated. #API reference const viewTransition =
document.startViewTransition(updateCallback) Start a new
ViewTransition.
updateCallback is called once the current state of the document is
captured.
Then, when the promise returned by updateCallback fulfills, the
transition begins in the next frame. If the promise returned by
updateCallback rejects, the transition is abandoned.
Instance members of ViewTransition:
viewTransition.updateCallbackDone A promise that fulfills when the
promise returned by updateCallback fulfills, or rejects when it
rejects.
The View Transition API wraps a DOM change and creates a transition.
However, sometimes you don’t care about the success/failure of the
transition animation, you just want to know if and when the DOM change
happens. updateCallbackDone is for that use-case.
updateCallbackDone was previously domUpdated. It was renamed in
Chrome 111.
viewTransition.ready A promise that fulfills once the pseudo-elements
for the transition are created, and the animation is about to start.
It rejects if the transition cannot begin. This can be due to
misconfiguration, such as duplicate view-transition-names, or if
updateCallback returns a rejected promise.
This is useful for animating the transition pseudo-elements with
JavaScript.
viewTransition.finished A promise that fulfills once the end state is
fully visible and interactive to the user.
It only rejects if updateCallback returns a rejected promise, as this
indicates the end state wasn’t created.
Otherwise, if a transition fails to begin, or is skipped during the
transition, the end state is still reached, so finished fulfills.
viewTransition.skipTransition() Skip the animation part of the
transition.
This won’t skip calling updateCallback, as the DOM change is separate
to the transition.
#Default style and transition reference ::view-transition The root
pseudo-element which fills the viewport and contains each
::view-transition-group.
::view-transition-group Absolutely positioned.
Transitions width and height between the ‘before’ and ‘after’
states.
Transitions transform between the ‘before’ and ‘after’ viewport-space
quad.
::view-transition-image-pair Absolutely positioned to fill the
group.
Has isolation: isolate to limit the effect of the plus-lighter blend
mode on the old and new views.
::view-transition-new and ::view-transition-old Absolutely positioned
to the top-left of the wrapper.
Fills 100% of the group width, but has an auto height, so it will
maintain its aspect ratio rather than filling the group.
Has mix-blend-mode: plus-lighter to allow for a true cross-fade.
The old view transitions from opacity: 1 to opacity: 0. The new view
transitions from opacity: 0 to opacity: 1.
#Feedback Developer feedback is really important at this stage, so
please file issues on GitHub with suggestions and questions.
Updated on Tuesday, November 22, 2022 • Improve article







