CSS View Transitions API跨文档过渡动画与SPA路由切换实战

View Transitions API跨文档导航工作原理

View Transitions API是浏览器原生的页面过渡动画方案,从Chrome 111开始支持。核心机制是在页面切换时自动捕获新旧状态的截图,生成一对pseudo-element树(旧状态和新状态),通过CSS动画实现两者的平滑过渡。与SPA中手动管理的过渡动画不同,View Transitions API无需维护复杂的挂载/卸载生命周期。

跨文档导航(Cross-Document View Transitions)是Chrome 126引入的重要扩展,允许MPA(多页应用)在页面跳转时也享有原生过渡效果。以前只有单页应用通过document.startViewTransition()才能使用,现在传统的全页面刷新导航也支持了。

/* 启用跨文档View Transitions - 最简配置 */
@view-transition {
  navigation: auto;
}

/* 默认过渡动画 - 300ms淡入淡出 */
::view-transition-old(root) {
  animation: 300ms ease-out both fade-out;
}
::view-transition-new(root) {
  animation: 300ms ease-in both fade-in;
}

@keyframes fade-out {
  to { opacity: 0; }
}
@keyframes fade-in {
  from { opacity: 0; }
}

navigation: auto声明后,浏览器在同源页面间的前进/后退/点击链接等导航行为自动触发View Transition,不需要任何JavaScript代码。对于大多数内容型网站,仅此一条CSS规则就足够了。

SPA路由切换中的startViewTransition用法

单页应用的路由切换需要手动调用document.startViewTransition()。这个API接收一个回调函数,回调中执行DOM更新操作,浏览器在回调前后分别截图,生成过渡动画。

// Vue Router + View Transitions
const router = createRouter({
  history: createWebHistory(),
  routes: [...]
});

router.beforeEach(async (to, from) => {
  if (!document.startViewTransition) return;
  
  // 返回false阻止默认导航,由View Transition接管
  return new Promise((resolve) => {
    document.startViewTransition(async () => {
      resolve();
      await nextTick(); // 等待Vue完成DOM更新
    });
  });
});

React框架下需要配合useTransition或flushSync使用。关键点在于确保DOM更新在startViewTransition回调内同步完成,避免异步更新导致截图时捕获到中间状态。

// React Router + View Transitions
function App() {
  const [isTransitioning, startTransition] = useTransition();
  
  const handleNavigate = (path) => {
    if (!document.startViewTransition) {
      navigate(path);
      return;
    }
    
    document.startViewTransition(() => {
      flushSync(() => {
        navigate(path);
      });
    });
  };
}

View Transition命名与独立元素动画

默认情况下,View Transitions对整个页面做统一的淡入淡出。通过view-transition-name属性,可以指定特定元素独立参与过渡,实现更精细的动画效果——例如列表页的缩略图滑入详情页的大图位置。

/* 列表页缩略图 */
.card-image {
  view-transition-name: hero-image;
}

/* 详情页大图 */
.detail-hero {
  view-transition-name: hero-image;
}

/* 命名元素的过渡动画 */
::view-transition-old(hero-image) {
  animation: 400ms ease-in-out both scale-down;
}
::view-transition-new(hero-image) {
  animation: 400ms ease-in-out both scale-up;
}

@keyframes scale-down {
  to { transform: scale(0.8); opacity: 0; }
}
@keyframes scale-up {
  from { transform: scale(1.2); opacity: 0; }
}

新旧页面中相同view-transition-name的元素会被浏览器自动配对,生成从旧位置/大小到新位置/大小的补间动画。这个特性被称为共享元素过渡(Shared Element Transition),是实现Page Swap效果的基石。

命名元素必须满足唯一性约束:同一时刻页面中不能有两个相同view-transition-name的元素,否则浏览器会跳过该元素的过渡。动态列表场景下需要用JavaScript在导航时动态设置view-transition-name。

降级方案与浏览器兼容性处理

View Transitions API的浏览器支持截至2026年仍不完整:Chrome/Edge 111+完整支持,Firefox和Safari仅部分实现跨文档导航。生产环境必须提供降级方案。

// 特征检测 + 降级
function navigateWithTransition(path) {
  if (document.startViewTransition) {
    document.startViewTransition(() => {
      window.location.href = path;
    });
  } else {
    // 降级:CSS class过渡或直接跳转
    document.body.classList.add('page-leaving');
    setTimeout(() => {
      window.location.href = path;
    }, 300);
  }
}

/* CSS降级过渡 */
.page-leaving {
  opacity: 0;
  transition: opacity 300ms ease;
}

prefers-reduced-motion媒体查询也需要处理。部分用户设置了减少动画的系统偏好,View Transitions应尊重这一设置:

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation-duration: 0.01ms !important;
  }
}

将动画时长设为极小值而非0,确保过渡逻辑仍被执行(避免状态不一致),但视觉上几乎瞬间完成,兼顾了无障碍访问需求。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/cssviewtransitionsapi-kua-wen-dang-guo-du-dong-hua-yu-spa/

(0)
小编小编
上一篇 18小时前
下一篇 18小时前

相关推荐