技术博客
返回首页

View Transitions API:创建流畅的页面过渡

2024年3月21日10分钟阅读
View Transitions API

传统的网页通常以突兀的页面过渡为特点。当从一个页面导航到另一个页面时,当前页面消失,新页面突然出现。 这种生硬的体验多年来一直是Web平台的一个限制,尤其是与原生应用程序中常见的流畅过渡相比。

View Transitions API的出现改变了这一切——这是一个强大的新浏览器功能,允许开发者在网页的不同状态之间或完全不同的页面之间创建平滑、动画化的过渡。 这一API代表了向Web带来更加精致、类似应用程序体验的重要一步。

什么是View Transitions API?

View Transitions API提供了一种标准化的方式来为Web内容的变化添加动画效果。它允许开发者在以下情况下创建无缝过渡:

  • 在页面之间导航
  • 在单个页面内更改DOM结构
  • 更新UI组件或布局

从本质上讲,该API的工作原理是捕获页面当前状态的快照,捕获新状态的快照,然后在它们之间创建平滑的动画。 这一过程无需开发者手动跟踪和为单个元素添加动画。

浏览器支持

View Transitions API相对较新。截至2024年初,它支持以下浏览器:

  • Chrome(自111版本起)
  • Edge(基于Chromium)
  • Opera(基于Chromium)

Firefox和Safari仍在开发其实现,但今天已经可以使用该API,只需为不支持的浏览器提供适当的回退方案。

基本用法

使用View Transitions API最简单的方法是通过document.startViewTransition()方法:

1if (document.startViewTransition) {
2 document.startViewTransition(() => {
3 // 在这里更新DOM
4 document.body.innerHTML = newHTML;
5 });
6} else {
7 // 对不支持该API的浏览器的回退方案
8 document.body.innerHTML = newHTML;
9}

传递给startViewTransition()的函数应包含更新DOM的代码。浏览器将:

  1. 捕获页面的当前状态
  2. 执行您的更新函数
  3. 捕获页面的新状态
  4. 在两种状态之间创建动画

处理宽高比变化

在使用View Transitions API时,一个常见的问题是当内容的宽高比在过渡期间发生变化时,过渡效果可能看起来不太自然。Jake Archibald在他的博客文章中详细讨论了这个问题及其解决方案。

非故意的宽高比变化

宽高比变化通常是非故意的。例如,当元素的字体大小或位置在过渡期间改变时:

.simple-text {
font-size: 25px;
}
&.toggled {
position: absolute;
bottom: 32px;
right: 32px;
font-size: 9px;
}

当我们切换这个类时,文本元素的宽高比会发生变化,这可能导致过渡动画看起来很奇怪。

btn.onclick = () => {
document.querySelector('.simple-text').classList.toggle('toggled');
}

下面是一个演示,展示了这种宽高比变化的效果:

宽高比变化演示

示例文本

直接改变尺寸会导致宽高比变化,使过渡看起来不自然。

解决方案:使用transform代替直接改变尺寸

解决这个问题的一种方法是使用CSS transform来处理大小变化,而不是直接改变元素的尺寸:

.better-text {
font-size: 25px;
}
&.toggled {
position: absolute;
bottom: 32px;
right: 32px;
transform: scale(0.36); /* 9px / 25px = 0.36 */
transform-origin: bottom right;
}

这种方法保持了元素的原始宽高比,同时通过缩放实现了大小变化,从而使过渡更加平滑。

使用view-transition-name处理特定元素

对于需要特别关注的元素,我们可以使用view-transition-name属性来单独处理它们的过渡:

.image-container {
view-transition-name: my-image;
}
/* 自定义这个特定元素的过渡 */
::view-transition-group(my-image) {
animation-duration: 0.6s;
}
::view-transition-old(my-image),
::view-transition-new(my-image) {
/* 防止内容被裁剪 */
overflow: clip;
}

处理图像宽高比变化

对于图像,宽高比变化是一个特别常见的问题。例如,从缩略图到全尺寸图像的过渡:

.thumbnail {
width: 100px;
height: 100px;
object-fit: cover;
view-transition-name: selected-image;
}
.full-image {
width: 100%;
height: auto;
view-transition-name: selected-image;
}
/* 自定义图像过渡 */
::view-transition-group(selected-image) {
animation-duration: 0.5s;
}
::view-transition-old(selected-image),
::view-transition-new(selected-image) {
/* 使用 'contain' 而不是默认的 'cover' */
object-fit: contain;
}

高级技巧:使用伪元素保持宽高比

对于更复杂的情况,我们可以使用伪元素和CSS变量来保持宽高比:

.advanced-container {
position: relative;
view-transition-name: my-element;
}
.advanced-container::before {
content: '';
display: block;
padding-top: var(--aspect-ratio, 56.25%); /* 默认16:9比例 */
}
.advanced-container .content {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
/* 在JavaScript中设置CSS变量 */
// element.style.setProperty('--aspect-ratio', '75%'); // 4:3比例

单页应用程序过渡

对于SPA,您可以将View Transitions API与路由器集成,以在视图之间创建平滑过渡:

// 使用假设的路由器的示例
router.beforeNavigate((to, from, next) => {
if (document.startViewTransition) {
document.startViewTransition(() => {
next();
});
} else {
next();
}
});

多页应用程序过渡

对于传统的多页应用程序,您可以将API与Navigation API一起使用:

1document.addEventListener('click', (e) => {
2 // 只处理链接点击
3 if (e.target.tagName !== 'A') return;
4
5 // 只处理同源导航
6 const href = e.target.href;
7 if (!href || !href.startsWith(window.location.origin)) return;
8
9 // 阻止默认导航
10 e.preventDefault();
11
12 // 如果可用,使用View Transitions API
13 if (document.startViewTransition) {
14 document.startViewTransition(async () => {
15 // 获取新页面
16 const response = await fetch(href);
17 const text = await response.text();
18
19 // 提取我们想要显示的内容
20 const parser = new DOMParser();
21 const newDocument = parser.parseFromString(text, 'text/html');
22 const newContent = newDocument.querySelector('main').innerHTML;
23
24 // 更新当前页面
25 document.querySelector('main').innerHTML = newContent;
26
27 // 更新URL
28 window.history.pushState({}, '', href);
29 });
30 } else {
31 // 回退到正常导航
32 window.location.href = href;
33 }
34});

与Next.js集成

Next.js 15.2引入了对React中View Transitions API的实验性支持。要启用此功能,请在您的next.config.js文件中添加以下内容 [^1]:

module.exports = {
experimental: {
viewTransition: true,
},
};

此功能高度实验性,可能在未来的版本中发生变化 [^2]。它建立在View Transitions的原生浏览器实现之上,允许您在Next.js应用程序的不同视图和组件之间创建平滑过渡。

无障碍性考虑

在实现视图过渡时,请记住无障碍性:

  • 使用prefers-reduced-motion媒体查询尊重用户对减少动画的偏好
  • 确保在过渡期间内容保持可访问
  • 为动态内容更改提供适当的ARIA属性
// 检查减少动画偏好
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!prefersReducedMotion && document.startViewTransition) {
document.startViewTransition(() => {
// 更新DOM
});
} else {
// 不带动画的直接更新
}

结论

View Transitions API代表了Web动画能力的重大进步。它提供了一种标准化、高性能的方式来创建平滑过渡,这在以前没有复杂的JavaScript库是很难或不可能实现的。

处理宽高比变化是使用View Transitions API时的一个关键考虑因素。通过使用本文中讨论的技术,如CSS transform、适当的object-fit属性和伪元素,您可以创建更加自然和专业的过渡效果。

随着浏览器支持的不断增长,我们可以期待看到更多的网站采用这一API来创建更具吸引力、类似应用程序的体验。与React和Next.js等框架的集成将使开发者更容易访问它。

虽然仍在发展中,但View Transitions API已经可以在今天进行实验性使用,只需提供适当的回退方案。这是一个开始探索这一新功能并思考它如何增强您的Web项目的激动人心的时刻。