浅谈 View Transitions API

新的 View Transitions API 提供了一种更简单的方式来在两个 DOM 状态之间进行动画转换 ------ 即使是在页面加载之间也可以实现。这是一项渐进式的增强功能,可以在今天的浏览器上使用。

过去十年,CSS过渡和动画已经彻底改变了网页效果。但并非所有事情都很容易实现。例如,考虑一个包含10张图片及标题的元素列表,我们想要通过交叉淡化的效果过渡到一个新的元素列表。通常的实现方式是:

  • 保留旧的 DOM 元素
  • 构建新的 DOM 元素,并将其追加到页面中,确保它们处在适当的位置
  • 淡出旧元素集合,同时淡入新元素集合,然后
  • (可选)用新DOM元素替换旧DOM元素

但是在 View Transitions API 中,我们只需要更新 DOM 即可 ------ 这在以前是不可能做到的!

View Transitions API 的工作流程如下:

  • API 对当前页面状态进行快照
  • 我们更新 DOM,添加或删除元素
  • API 对新的页面状态进行快照
  • API 在这两个状态之间进行动画转换,默认为淡入淡出,也可以定义自己的 CSS 动画

我们只需要像以前一样更新 DOM。加上几行代码就可以在 View Transitions API 可用时渐进增强页面,实现类似 PPT 的转换效果。

该 API 目前还在实验阶段,但最新版的 Chromium 内核浏览器都已支持页面内的、单页面文档的DOM转换效果。

针对页面导航的 viewTransition API 在 Chrome 115+ 中也可用,可以实现单页面应用跳转时的动画效果,比如典型的 WordPress 网站。这个用起来更简单,不需要任何 JavaScript。

Firefox 和 Safari 尚未透露会何时支持该 API。 对于不支持 View Transitions API 的浏览器,页面会按正常非动画方式加载,所以可以放心地在今天的网站中添加相关效果。

过时的新技术

对于我们这些见过互联网发展历史的老程序员来说,可能会有一种熟悉的既视感。微软在 IE 4.0(1997年发布)和 IE 5.5(2000年发布)中添加了元素级别和整页的过渡效果。我们可以通过 <meta> 标签添加 PowerPoint式的方框、圆形、翻页、溶解、百叶窗、滑动、条形和螺旋效果:

html 复制代码
<meta http-equiv="Page-Enter" content="progid:DXImageTransform.Microsoft.Iris(Motion='in', IrisStyle='circle')">

<meta http-equiv="Page-Exit" content="progid:DXImageTransform.Microsoft.Iris(Motion='out', IrisStyle='circle')">

奇怪的是,这种技术从未被广泛采用。它不是 Web 标准,但当时 W3C 还在襁褓期 ------ 开发者也确实乐于使用许多其他 IE 专有的技术!

为什么过了25年才出现一个替代方案呢?!

创建页面内的过渡效果

在 Chrome 中查看以下 CodePen 示例,并点击头部的导航以查看两种状态之间的1秒淡入淡出效果。

页面 HTML 包含两个文章元素,ID 为 article1 和 article2:

html 复制代码
<main>
  <div id="articleroot">

    <article id="article1">
      <!-- 文章1内容 --> 
    </article>

    <article id="article2">
     <!-- 文章2内容 -->
    </article>

  </div>
</main>

switchArticle() 函数处理所有 DOM 更新。它通过添加或删除 hidden 属性来显示或隐藏每个文章。在页面加载时,从页面 URL 的 location.hash 确定激活的文章,如果 hash 不存在,则默认为第一个 <article> 元素:

js 复制代码
// 获取页面所有文章 
const articles = document.getElementsByTagName('article');

// 页面加载时显示一个文章
switchArticle(); 

// 显示激活文章
function switchArticle(e) {

  const hash = e?.target?.hash?.slice(1) || location?.hash?.slice(1);

  Array.from(articles).forEach(article => {
    if (article.id === hash || (!hash && !i)) {
      article.removeAttribute('hidden'); 
    }
    else {
      article.setAttribute('hidden', '');
    }
  });

}

事件处理函数监听所有页面点击事件,当用户点击包含 #hash 的链接时,调用 switchArticle():

js 复制代码
// 导航点击事件处理
document.body.addEventListener('click', e => {

  if (!e?.target?.hash) return;

  switchArticle(e);

});

我们现在可以更新这个事件处理器以使用 View Transitions,先判断该 API 是否可用:

js 复制代码
document.body.addEventListener('click', e => {

  if (!e?.target?.hash) return;

  if (document.startViewTransition) {

    // 使用 View Transition 动画效果
    document.startViewTransition(() => switchArticle(e));

  } else {
    
    // View Transition 不可用时正常切换
    switchArticle(e);
  
  }

});

document.startViewTransition() 会拍摄初始状态快照、运行 switchArticle() 更新 DOM、拍摄结束状态快照,最后在两者之间应用默认0.5秒的淡入淡出过渡动画。

我们可以在 CSS 中使用以下选择器针对旧状态和新状态应用样式:

css 复制代码
::view-transition-old(root) {
  /* 动画出效果 */ 
}

::view-transition-new(root) {
  /* 动画入效果 */
}

创建更精细的过渡效果

下面的 CodePen 示例通过 ::view-transition-old(root) 和 ::view-transition-new(root) 选择器添加了更好的动画效果。

CSS 中定义了淡入淡出和旋转的 transition-out 和 transition-in 动画:

css 复制代码
::view-transition-old(root) {
  animation: 1s transition-out 0s ease;
}

::view-transition-new(root) {
  animation: 1s transition-in 0s ease; 
}

@keyframes transition-out {
  from {
    opacity: 1; 
    translate: 0;
    rotate: 0;
  }
  to {
    opacity: 0;
    translate: -3rem -5rem; 
    rotate: -10deg;
  }
}

@keyframes transition-in {
  from {
    opacity: 0;
    translate: 3rem 5rem;
    rotate: -10deg;
  }
  to {
    opacity: 1;
    translate: 0; 
    rotate: 0;
  }
}

这些动画会应用到整个页面,包括 <header> 元素,看起来不太正常。我们可以通过设置 view-transition-name 为特定值的方式给不同元素应用动画效果:

css 复制代码
header {
  view-transition-name: header; 
}

然后可以针对该元素应用不同的动画:

css 复制代码
::view-transition-old(header) {
  /* ... */
}

::view-transition-new(header) {
  /* ... */
}  

在这个例子中,我们不想 header 有任何动画效果,所以不需要定义任何样式。::view-transition-old(root) 和 ::view-transition-new(root) 选择器现在仅应用于不包含 header 的其他元素。

因为我们在 CSS 中定义了动画效果,可以在开发者工具的动画面板中更详细地检查和调试动画。

使用 Web Animations API

使用 Web Animations API 可以在 JavaScript 中进行更细致的动画效果和时间控制。

document.startViewTransition() 返回一个对象,其ready属性是一个Promise,在过渡的旧状态和新状态伪元素可用时resolve:

js 复制代码
// 使用 Web Animations API
const transition = document.startViewTransition(doDOMUpdate);

transition.ready.then(() => {

  document.documentElement.animate(
    [
      { rotate: '0deg' },
      { rotate: '360deg' } 
    ],
    {
      duration: 1000,
      easing: 'ease',
      pseudoElement: '::view-transition-new(root)' 
    }
  );

});

创建多页面导航过渡效果

我们也可以在多页面应用(比如典型的WordPress网站)的页面加载之间使用 View Transitions。这被称为 viewTransition API for navigations,需要在 Chrome 115(当前开发者版Canary)中启用 chrome://flags/ 实验标志来开启。

启用只需要在 HTML head 中添加一个 meta 标签:

html 复制代码
<meta name="view-transition" content="same-origin" />

然后我们可以定义与页面内过渡相同的 ::view-transition-old 和 ::view-transition-new 选择器。不需要任何 JavaScript,除非想要使用 Web Animations API。

该导航 API 是否默认启用还不确定。但我们今天就可以使用该技术,因为不支持的浏览器会退化为正常的非动画页面加载。

禁用动画

对于部分遭受运动障碍的用户来说,动画可能会引起不适。大多数操作系统提供禁用效果的用户偏好设置。我们可以使用 CSS 的 prefers-reduced-motion 媒体查询检测这一设置并相应关闭动画:

css 复制代码
@media (prefers-reduced-motion) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

总结

View Transitions API简化了改变元素状态时的动画转换,无论是页面内还是页面之间的导航。这之前也可以实现类似的过渡效果,但需要大量 JavaScript 代码,还需要注意不要影响浏览器的正常导航操作如后退按钮。

该 API 还非常新。不能保证它会保持不变,成为 W3C 标准,或在 Firefox 和 Safari 中实现。但是我们今天就可以使用该 API,因为它是渐进增强的。在不支持该 API 的浏览器上,页面还是可以正常工作,只是没有动画效果。API 可能会有变动的风险,但即使需要维护,旧代码也不应该对网站造成影响,更新工作应该也很小。

该 API 的一个缺点是可能会在 Web 上出现大量令人讨厌的冗长和夸张的动画效果,因为站长认为这很符合自己的"品牌形象"!理想情况下,动画应该快速和微妙地突出 UI 的变化。少即是多。

参考资料:

相关推荐
[seven]5 小时前
React Router TypeScript 路由详解:嵌套路由与导航钩子进阶指南
前端·react.js·typescript
无我Code5 小时前
前端-2025年末个人总结
前端·年终总结
文刀竹肃5 小时前
DVWA -SQL Injection-通关教程-完结
前端·数据库·sql·安全·网络安全·oracle
LYFlied5 小时前
【每日算法】LeetCode 84. 柱状图中最大的矩形
前端·算法·leetcode·面试·职场和发展
Bigger5 小时前
Tauri(21)——窗口缩放后的”失焦惊魂”,游戏控制权丢失了
前端·macos·app
Bigger6 小时前
Tauri (20)——为什么 NSPanel 窗口不能用官方 API 全屏?
前端·macos·app
bug总结6 小时前
前端开发中为什么要使用 URL().origin 提取接口根地址
开发语言·前端·javascript·vue.js·html
一招定胜负7 小时前
网络爬虫(第三部)
前端·javascript·爬虫
Shaneyxs7 小时前
从 0 到 1 实现CloudBase云开发 + 低代码全栈开发活动管理小程序(13)
前端
半山烟雨半山青7 小时前
微信内容emoji表情包编辑器 + vue3 + ts + WrchatEmogi Editor
前端·javascript·vue.js