博客页面切换,标题过渡动画,咋用视图过渡API实现?

2,511字
11–16 分钟
in

这篇内容会手把手拆解视图过渡API在博客场景里的硬核玩法,尤其聚焦列表页到详情页的标题飞入飞出效果。从最基础的那个@view-transition小片段开始,一路聊到咋用PHP动态生成唯一过渡名称,顺带瞅一眼未来那个超香的attr()函数。全程不讲虚的,直接上代码、上实操、上那些踩坑后才知道的小细节。

目录

啥是视图过渡API

视图过渡API说白了就是一套让页面元素在两个独立页面之间丝滑变身的底层机制。传统页面跳转那是硬切,眼睛还没来得及反应就啪一下换了新脸。而这个API允许捕获旧页面的某个元素状态,然后动画过渡到新页面里对应元素的样子。最骚的操作是,只需要给两边的元素塞一个相同的view-transition-name,浏览器就会自动补间动画,连@keyframes都不用写。举个例子,博客列表页里每条标题链接和详情页里的大标题,只要名字对上号,切换时就能让标题像飞过去一样。

实操让标题动起来

开始整活前,先搞定那个最容易被忽略的开关。在全局CSS里丢进下面这段,相当于告诉浏览器“嘿,这俩页面之间的跳转要开特效”:

@view-transition {
  navigation: auto;
}

有了这个开关,接下来就是给参与过渡的元素取名字。假设列表页的标题链接长这样:

<h2 class="post-link"><a href="/post/123">某篇博客</a></h2>

详情页的大标题长这样:

<h1 class="post-single__title">某篇博客</h1>

需要在CSS里让它们共用同一个过渡名:

.post-link { view-transition-name: post-title; }
.post-single__title { view-transition-name: post-title; }

但这里有个大坑——如果列表页同时显示了十篇文章标题,那这十个post-link都会抢着叫post-title,浏览器直接报错罢工。因为每个参与过渡的元素必须持有独一无二的view-transition-name,就像身份证号不能重样。顺带提一嘴,有些访客在系统里设置了“减少动画效果”,为了尊重这类偏好,可以用媒体查询把过渡限定在允许动效的场景:

@media not (prefers-reduced-motion: reduce) {
  .post-link { view-transition-name: post-title; }
  .post-single__title { view-transition-name: post-title; }
}

动态生成唯一过渡名

既然没法手动给每篇新博客编名字,那就让后端动态生成。拿WordPress举例,每个帖子都有专属ID,把这个ID塞进过渡名里就能保证全世界唯一。在详情页模板(single.php)里这么干:

<?php the_title( 
  '<h1 class="post-single__title" style="view-transition-name: post-' . get_the_id() . '">', 
  '</h1>' 
); ?>

在列表页模板(archive.php)里同样操作:

<?php the_title(
  '<h2 class="post-link"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark" style="view-transition-name: post-' . get_the_id() . '">', 
  '</a></h2>' 
); ?>

跑起来的效果就是:列表页里某篇帖子的链接有view-transition-name: post-123,点进去后详情页标题也带着post-123,过渡瞬间配对成功。要是嫌内联style写起来丑,也可以在模板头部塞一个<style>标签,用PHP循环输出规则,两种方式半斤八两,挑顺手的那种。

方法优点缺点
内联style直接明了混合结构和样式
动态style标签样式集中需额外输出块

用PHP生成过渡名

另一种解法是把过渡名的生成逻辑塞进一个自定义函数,然后在模板里调用。比如在functions.php里注册一个helper:

function get_post_transition_name() {
  return 'post-transition-' . get_the_ID();
}

然后在模板里输出:

<h1 class="post-single__title" style="view-transition-name: <?php echo get_post_transition_name(); ?>">
  <?php the_title(); ?>
</h1>

这样以后想改命名规则,只需要动一个函数。注意PHP的get_the_ID()必须在循环内有正确上下文,如果在循环外调用会返回空值,导致所有过渡名都变成post-transition-后面啥也没有,那就又撞车了。一个保底小技巧:在列表页里每个链接生成前先setup_postdata(),完事儿再wp_reset_postdata(),确保ID来源不出岔子。

未来神器attr()函数

浏览器团队正在捣鼓一个叫attr()的增强版函数,以后可以直接从HTML属性里读取值来设置view-transition-name。想象一下这种写法:

<div class="card" id="card-1"></div>
<div class="card" id="card-2"></div>
<div class="card" id="card-3"></div>
.card[id] {
  view-transition-name: attr(id type(<custom-ident>), none);
}

浏览器会自动把card-1card-2card-3分别作为过渡名,彻底告别后端模板的脏活。不过目前这东西还躺在Chrome的实验室里,等所有主流浏览器都跟上估计还得一阵子。眼下最稳当的方案还是上面那套PHP动态生成,虽然土但管用。等attr()真正落地那天,只需要一行CSS就能把整个过渡名生成的逻辑收工,那才叫一个舒坦。