配了八年 MPA,每次跳转动画都只有一个方向——今天 CSS 自己会认路了

写了八年 MPA,每次页面跳转动画只有一个方向——前进就往左滑,后退也往左滑,用户每次都觉得页面在「反着走」。

这件事,今天 CSS 自己会认路了。


问题是:MPA 的过渡动画没有方向感

SPA 靠 document.startViewTransition() 可以随便传 types 参数,告诉浏览器这次是「前进」还是「后退」,CSS 收到之后分别写两套动画规则就行。

但 MPA 不一样——页面跳转是浏览器自己在导航触发的时候创建的,CSS 里只有 @view-transition { navigation: auto; } 这一句,默认所有方向都是同一套淡入淡出。

所以你做了八年的「后退时从左边进来」——其实从来没实现过。


第一步:声明 types,CSS 先把路铺好

在所有参与跳转的页面 CSS 里写:

@view-transition {
navigation: auto;
types: slide;
}

然后在 :root 上关掉全页的默认过渡,只留你想做动画的那块区域:

html:active-view-transition-type(slide) {
:root {
view-transition-name: none;
}
section {
view-transition-name: chapter;
}
&::view-transition-old(chapter) {
animation-name: slide-out-to-left;
}
&::view-transition-new(chapter) {
animation-name: slide-in-from-right;
}
}

这时候所有跳转都会用 slide 这一套动画——但问题是,后退的时候方向还是一样。


第二步:pageswap 事件——JS 动态判断「是前进还是后退」

关键在 pageswap 事件。它在旧页面即将卸载时触发,可以拿到这次导航的出发页和目标页的 URL,拿来比大小就知道方向了:

window.addEventListener(pageswap, (event) => {
if (!event.viewTransition) return;

const from = event.activation.from.url;
const to = event.activation.navigationDestination.url;

const fromNum = parseInt(from.match(/(d+).html$/)?[1] || 0);
const toNum = parseInt(to.match(/(d+).html$/)?[1] || 0);

const type = toNum > fromNum ? forwards : backwards;

event.viewTransition.types.add(type);
});

event.viewTransition.types 是个 Set,直接 .add() 就行。CSS 那边的 :active-view-transition-type(backwards) 会自动感知到这次变化。


第三步:CSS 根据 type 切换动画方向

退出去的动画方向和进来的动画方向要同时换:

html:active-view-transition-type(backwards) {
&::view-transition-old(chapter) {
animation-name: slide-out-to-right;
}
&::view-transition-new(chapter) {
animation-name: slide-in-from-left;
}
}

这样前进时旧页面向左滑走、新页面从右滑进;后退时方向全反过来——用户终于觉得「动画在跟着我的意图走」。

注意:上面这个 pageswap 监听的是旧页面(出发页),新页面(目标页)上还需要一个 pagereveal 事件来设置同样的 type,否则目标页的 CSS 拿不到类型信息:

window.addEventListener(pagereveal, (event) => {
if (!event.viewTransition) return;

const from = navigation.activation.from.url;
const to = navigation.activation.navigationDestination.url;

const fromNum = parseInt(from.match(/(d+).html$/)?[1] || 0);
const toNum = parseInt(to.match(/(d+).html$/)?[1] || 0);

const type = toNum > fromNum ? forwards : backwards;
if (type) {
event.viewTransition.types.add(type);
}
});

两个事件同时配合,CSS 的 :active-view-transition-type() 才能在进出两个页面同时生效。


浏览器支持

@view-transition 的 types 描述符和 :active-view-transition-type() 属于 CSS View Transitions Module Level 2,Chrome 126+、Edge 126+、Safari 18.2+ 支持。Firefox 暂无支持。生产环境用之前记得做特性检测。


总结

配 MPA 跳转动画配了八年,「前进后退方向不一样」这件事要么靠两个独立页面硬写两套 CSS,要么直接放弃——今天 CSS 自己会问了:pageswap 拿方向,types.add() 传给 CSS,:active-view-transition-type() 接住,不同方向跑不同动画,整条链路全是原生 API,不需要任何框架。

下一步:把判断方向的逻辑封装成一个可复用的函数,任何多页面场景都可以直接拿过来用——判断 URL 里数字大小的方式换掉就行,核心逻辑不变。

评论区

0 条评论

登录后可评论。