HTML View Transitions API 实战:用原生API实现丝滑的页面切换动画

2026-07-29 0 870

有一个做了两年的后台管理系统,各个页面之间的跳转一直是硬切的——点击侧边栏菜单,页面瞬间消失又瞬间出现,没有任何过渡。产品经理提了好几次说能不能像手机App那样有个平滑的切换效果,但一想到要引入动画库、改写路由逻辑、处理SPA的页面状态管理,就一拖再拖。后来发现浏览器已经内置了View Transitions API,用两行代码就能让页面切换带上淡入淡出效果,再多写几行CSS就能实现类似原生应用的共享元素过渡。不需要任何第三方库,不挑框架,甚至在多页应用里也能用。

这个API在Chrome 111、Edge 111、Safari 18.2、Firefox 134之后都已经支持,覆盖了绝大多数用户。这篇文章用一个完整的多页面切换演示来拆解它的用法,从最基础的全局过渡到精细的共享元素动画,读完就能在项目里用起来。

传统的页面切换有多生硬

回想一下我们平时浏览网页的体验:点击一个链接,当前页面瞬间消失,浏览器白屏或者显示一个空白,然后新页面从上到下逐步渲染出来。这种断裂感让用户丢失了空间上的连续性——列表里的某一个项目点进去,用户需要在详情页重新找到自己的注意力焦点。而在原生应用中,从一个列表项展开到详情页时,列表项的图片通常会平滑地移动到详情页头部的位置,给用户一种“这个元素我还认识”的确定感。

过去在Web上做这种效果要费不少劲。SPA框架里要记录两个页面共有的DOM元素,计算它们在两个页面中的位置差,然后用transform做补间动画。一旦DOM结构不一样,还得写回退逻辑。整套方案写下来,动画代码可能比业务代码还多。

View Transitions API把这件事变成了浏览器层面的内置能力。它的核心思想是:在页面状态变化前后各截一张快照,然后自动在两个快照之间做过渡。对于单页应用,截的是DOM状态的快照。对于多页应用,截的是整个页面的视觉快照。

一行代码开启基础过渡

在SPA里使用View Transitions,最简单的方式是把状态更新包裹在document.startViewTransition()的回调里。浏览器会在这个回调执行前截取当前页面的快照,在回调执行后截取新状态的快照,然后在两者之间做一个默认的交叉淡入淡出。

假设有一个单页面,点击按钮切换内容区域:

function switchPage(newContent) {
    document.startViewTransition(() => {
        // 在这个回调里更新DOM
        document.getElementById('content').innerHTML = newContent;
    });
}

就这么简单。调用之后,浏览器会自动把旧内容以半透明的方式渐出、新内容渐入,整个过渡大约持续300毫秒。不需要写任何CSS动画,不依赖任何库。对于绝大多数页面切换场景来说,这个默认的淡入淡出已经足够让体验上一个台阶。

如果需要对动画做自定义,CSS提供了::view-transition系列伪元素来精确控制。过渡期间,浏览器会创建一个叠加层,包含旧页面的快照和新页面的快照,它们分别对应::view-transition-old::view-transition-new两个伪元素。修改动画时长和缓动函数可以这样写:

::view-transition-old(root),
::view-transition-new(root) {
    animation-duration: 0.4s;
    animation-timing-function: ease-in-out;
}

root换成其他标识符可以控制具体元素的过渡动画,这会在后面共享元素的部分讲到。

多页应用也能用

View Transitions API不局限于SPA。对于传统的多页应用(每个页面都是完整的HTML文档),只需要在<head>里加一个meta标签:

<meta name="view-transition" content="same-origin">

加上这个标签后,同源下的页面跳转都会自动带上默认的交叉淡入淡出过渡。用户在页面之间来回点击时,不会再看到之前的白屏闪动,取而代之的是大约300毫秒的平滑过渡。这个改动成本低到可以忽略不计,但用户体验的提升是立竿见影的。

这里限制在same-origin是出于安全考虑——跨域页面的内容不受当前页面控制,不适合做视觉过渡。如果你的网站内部全在同一个域名下,这个标签几乎可以无条件启用。

共享元素动画:从列表到详情的丝滑过渡

淡入淡出只是开胃菜。View Transitions真正让人兴奋的能力是共享元素过渡。简单说,就是让前一个页面中的某个元素“变身”为后一个页面中的对应元素,浏览器自动计算两者之间的位置和大小差异,生成一个平滑的补间动画。

实现共享元素只需要两步:给前后两个页面对应的元素设置相同的view-transition-name,然后确保它们在过渡期间能被浏览器识别到。下面是一个完整的列表到详情的案例。

列表页的HTML结构:

<div class="product-list">
    <a href="/product/1" rel="external nofollow"  class="product-card">
        <img src="product1.jpg" style="view-transition-name: product-image-1">
        <h3>产品名称</h3>
    </a>
    <a href="/product/2" rel="external nofollow"  class="product-card">
        <img src="product2.jpg" style="view-transition-name: product-image-2">
        <h3>产品名称</h3>
    </a>
</div>

详情页的对应元素:

<div class="product-detail">
    <img src="product1-large.jpg" style="view-transition-name: product-image-1">
    <h1>产品名称</h1>
    <p>产品描述...</p>
</div>

关键点是两个页面中参与过渡的元素必须使用相同的view-transition-name值。浏览器在截取快照时会记录这个名称对应的元素的位置和大小,然后在两帧之间自动计算transform动画。列表页的小缩略图会平滑地放大并移动到详情页大图的位置,整个过程由浏览器原生处理,不需要手动计算坐标。

需要注意一个规则:在同一个页面上,view-transition-name必须是唯一的。如果在列表页里每个产品卡片都叫product-image,浏览器就不知道该用哪个了。所以上面每个产品图片用了带ID的命名。通常的做法是在点击某个产品时动态设置它的view-transition-name,或者用模板语法给每个列表项生成不同的名称。

完整代码:一个带过渡的商品列表

下面是一个可以直接运行的多页应用示例,包含商品列表页和商品详情页。页面切换有默认淡入淡出,商品图片有共享元素过渡。先看列表页index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="view-transition" content="same-origin">
    <title>商品列表</title>
</head>
<body>
    <h1>商品列表</h1>
    <div class="grid">
        <article>
            <a href="detail.html?id=1" rel="external nofollow" >
                <img src="images/product1.jpg" 
                     style="view-transition-name: product-1"
                     width="200" height="200">
                <h2>复古台灯</h2>
                <p>¥299</p>
            </a>
        </article>
        <article>
            <a href="detail.html?id=2" rel="external nofollow" >
                <img src="images/product2.jpg"
                     style="view-transition-name: product-2"
                     width="200" height="200">
                <h2>无线耳机</h2>
                <p>¥599</p>
            </a>
        </article>
        <article>
            <a href="detail.html?id=3" rel="external nofollow" >
                <img src="images/product3.jpg"
                     style="view-transition-name: product-3"
                     width="200" height="200">
                <h2>机械键盘</h2>
                <p>¥899</p>
            </a>
        </article>
    </div>
</body>
</html>

详情页detail.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="view-transition" content="same-origin">
    <title>商品详情</title>
</head>
<body>
    <a href="index.html" rel="external nofollow" >← 返回列表</a>
    <div class="detail">
        <img src="images/product1-large.jpg"
             style="view-transition-name: product-1"
             width="400" height="400">
        <h1>复古台灯</h1>
        <p>工业风格金属台灯,适合书房和卧室使用。</p>
        <span>¥299</span>
    </div>
    <script>
        // 根据URL参数决定显示哪个产品的详情
        // 实际项目中这里会动态设置view-transition-name和内容
    </script>
</body>
</html>

这里为了演示固定写了product-1,实际项目中需要在详情页根据URL参数动态生成对应的view-transition-name。比如用JavaScript读取location.search拿到id,然后设置图片的style.viewTransitionName

列表页里每个产品图片都有唯一的view-transition-name,当用户点击“复古台灯”进入详情页时,详情页中对应的大图也使用了相同的view-transition-name,浏览器会自动在两个页面之间构建这个图片的位移和缩放动画。用户看到的效果就是小图平滑地放大移动到详情页大图的位置,视觉焦点保持连续。

自定义过渡动画的节奏

默认的过渡是淡入淡出,时长约300毫秒。如果想做得更有层次感,可以用CSS微调。比如让旧页面先缩小再淡出,新页面从右侧滑入:

::view-transition-old(root) {
    animation: 0.3s ease-out both shrink-out;
}
::view-transition-new(root) {
    animation: 0.3s ease-out both slide-in;
}

@keyframes shrink-out {
    from { transform: scale(1); opacity: 1; }
    to   { transform: scale(0.95); opacity: 0; }
}
@keyframes slide-in {
    from { transform: translateX(30px); opacity: 0; }
    to   { transform: translateX(0); opacity: 1; }
}

这些样式只需要写一次,全局生效。当然前提是页面里不能没有<style>标签——CSS需要放在独立的样式文件里通过<link>引入,这和现代前端工程化的做法一致。

SPA路由切换的封装

如果你的项目用的Vue、React或者原生JS做SPA,可以把路由跳转统一封装一层,让每次页面切换都自动带上过渡效果。以原生JS做一个极简路由为例:

function navigateTo(url) {
    document.startViewTransition(async () => {
        // 更新浏览器历史记录
        history.pushState({}, '', url);
        // 重新渲染页面内容
        await renderPage(url);
    });
}

// 监听浏览器前进后退
window.addEventListener('popstate', () => {
    document.startViewTransition(async () => {
        await renderPage(location.pathname);
    });
});

这样无论是点击链接跳转还是浏览器前进后退,所有的页面切换都会经过View Transitions。如果页面渲染很快(几十毫秒内完成),过渡依然流畅,因为浏览器是在渲染完成之后才开始播动画。

过渡期间的状态处理

过渡进行中时,页面处于一种半冻结状态:旧页面内容已经被截成快照,但新页面的交互还没有完全生效。如果用户在动画播放期间快速连续点击,可能会导致状态混乱。startViewTransition返回的对象的finishedready属性可以提供一些控制:

const transition = document.startViewTransition(() => updateDOM());

// transition.finished 是一个Promise,过渡完全结束后resolve
await transition.finished;
console.log('过渡完成');

// transition.ready 在快照截取完毕、动画即将开始时resolve
await transition.ready;
console.log('动画已准备好');

利用finished可以在过渡结束后做一些清理工作,比如移除不再需要的view-transition-name。利用ready可以在动画开始前做一些最后的微调,比如测量新元素的位置。

另外,过渡过程中如果有滚动条,用户依然可以滚动页面,可能会看到快照和实际内容的错位。在过渡前先锁住滚动(比如给bodyoverflow: hidden),过渡结束后解除,可以避免这个问题。

浏览器不支持怎么办

View Transitions API在较新的浏览器中都有支持,但如果要兼容一些老版本,可以做一个简单的特性检测:

if (document.startViewTransition) {
    document.startViewTransition(() => updateDOM());
} else {
    // 回退到直接更新DOM,没有动画
    updateDOM();
}

如果不支持的浏览器占比很小,直接不做动画也可以接受。多页应用里的meta标签在不支持的浏览器中会被忽略,页面跳转退回到原有的硬切行为,不会有负面影响。

小结

View Transitions API把页面过渡动画从“需要精心编排的复杂特性”变成了“开箱即用的浏览器能力”。两行JS代码实现全局淡入淡出,一个CSS属性实现共享元素过渡,这些在过去需要几百行代码和额外库支持的效果,现在浏览器原生就提供了。

对于后台管理系统、电商网站、内容平台这些页面切换频繁的场景,加上过渡动画不仅提升视觉效果,更重要的是给用户提供空间上的连续性——他们能直观地感知到自己从列表进入了详情,而不是被丢到了一个全新的页面。如果你的用户用的是现代浏览器,几乎没有任何理由不用它。

HTML View Transitions API 实战:用原生API实现丝滑的页面切换动画
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

版权声明:
本站资源有的来自互联网收集整理,本站纯免费分享提供学习使用,如果侵犯了您的合法权益,请联系本站我们会及时删除。
本站资源仅供研究、学习交流之用,免费开源项目不代表完全可商用,若商业用途请先咨询开发企业能否商用,否则产生的一切后果将由下载用户自行承担。
原创板块未经允许不得转载,否则将追究法律责任。

淘吗网 html HTML View Transitions API 实战:用原生API实现丝滑的页面切换动画 https://www.taomawang.com/web/html/2453.html

常见问题

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务