页面级可视动画View Transitions API初体验

这篇文章发布于 2024年08月10日,星期六,21:45,归类于 CSS相关, Web综合。 阅读 5776 次, 今日 10 次 3 条评论

 

鸟儿走路听音乐动图 占位图

一、先从最简单的案例开始

本文介绍View Transitions API,可以实现类似Keynote里面神奇移动这样的动画效果,也即是浏览器自动识别场景1和场景2的不同,并让这个不同产生动画效果,特别适合复杂的页面级别的场景切换动画。

先从最简单的案例开始。

一、append图片淡出动画

传统方法实现页面插入一个图片并有动画效果,需要用到CSS3 animation定义,例如:

img {
  animate: fadeIn .2s both;
}
@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}
container.append(img);

而实现View Transitions API实现则是另外的风格,只需要JS代码,这样:

document.startViewTransition(() => {    
  container.append(img);
});

此时图片插入就有淡出效果了,如下视频所示(不动点击播放):

就是这么简单。

眼见为实,您可以狠狠地点击这里:startViewTransition实现图片插入淡入效果demo

二、若要改变动画时间和类型

虽然旧元素淡出,新元素淡入是最常用的动画效果,但是总会遇到需要自定义动画时长和类型的场景。

例如,我们希望图片淡入的时间是1s,或者希望图片append到页面中是放大效果,该如何处理呢?

此时,需要动画CSS属性了,几个专门为View Transitions API设计的CSS伪元素。

例如动画时长1s可以这么设置:

::view-transition-group(root) {
    animation-duration: 1s;
}

或者:

::view-transition-image-pair(root) {
    animation-duration: 1s;
}

或者:

::view-transition-old(root),
::view-transition-new(root) {
    animation-duration: 1s;
}

上面突然出现的4个CSS伪元素估计很多人都看不懂,嗯……我想想,怎么讲才更好理解。

这么说吧,View动画的实现,本质上是新旧快照的diff然后动画。

所以,为了方便设置这些新旧快照的动画特性,就设计了若干全新的CSS伪元素,总共有5个,一起构成了一个伪元素树,其层级关系如下所示:

::view-transition
├─ ::view-transition-group(root)
│  └─ ::view-transition-image-pair(root)
│     ├─ ::view-transition-old(root)
│     └─ ::view-transition-new(root)
│ /* 等等... */

其中,root可以看成是根元素,这个名称是可以自定义的,后面会有说明

然后,动画执行最细枝末节的伪元素就是::view-transition-old::view-transition-new,因此这两个伪元素最为常用,至于其父级的几个伪元素,专门用在设置公共CSS属性的场景下,例如上面设置的动画时长。

动画类型设置

想要改变动画的类型,例如,从淡入效果变成放大效果,很多人会想到使用如下所示的代码:

::view-transition-new(root) {
    animation: scaleUp 1s;
}
@keyframes scaleUp {
  from {
    transform: scale(0.1);
  }
  to {
    transform: scale(1);
  }
}

然而,图片插入到页面后,出现的不是图片放大,而是整个视窗内容一起放大了,如下所示:

视窗整个放大示意

那该如何处理呢?这就需要用到view-transition-name这个CSS属性了。

三、view-transition-name与局部动画

我们可以使用view-transition-name属性给任意的元素设置一个名称,此时,就可以将此名称作为参数,以约束::view-transition-old::view-transition-new的作用范围。

例如:

img {
    view-transition-name: wooo;
}
::view-transition-new(wooo) {
    animation: scaleUp 1s;
}
@keyframes scaleUp {
  from {
    transform: scale(0.1);
  }
  to {
    transform: scale(1);
  }
}

此时IMG插入到页面的时候,就可以看到放大效果了。截图示意:

放大截图示意

您可以狠狠地点击这里:::view-transition-new实现图片放大动画demo

需要注意的是,view-transition-name指定的名称一次只能适用于一个元素,否则会报错,例如下面的错误提示。

名称重复报错

因此,如果动画元素多个且重复,稳妥的方式是使用JS去赋予view-transition-name名称,例如:

img.style.viewTransitionName = 'wooo';
document.startViewTransition(() => {
    this.after(img);
    
    setTimeout(() => {
        img.style.viewTransitionName = '';    
    }, 1000);
});

这样,每一个append的图片都可以有放大显示的效果。

四、看似复杂的动画变得简单了

单个图片的动画并不能体现视区动画的强大,下面通过几个例子演示View Transitions API擅长且推荐做的事情。

案例1:倒序后列表的移动

在过去,在改变列表的顺序的时候,我一直希望可以有动画效果,这样用户才能够直观地看到变化的过程。

可实操下来,太复杂了,除非所有元素都是绝对定位,然后动态修改每一个元素的left,top值。

现在有了View Transitions API,事情就简单多了,因此视图动画他无需设置元素的具体的定位值,它是根据新旧快照前后的位置变化实现的动画效果。

所以事情就变成了:

  1. 给所有列表元素设置一个唯一的view-transition-name
  2. 执行startViewTransition()然后改变位置;

结束了,就这么两步,非常简单。

无论是DOM位置变化实现的倒序,还是CSS属性在视觉上实现的倒序都可以有动画效果。

如下GIF录屏所示:

倒序排序效果

眼见为实,您可以狠狠地点击这里:View Transition实现列表倒序动画demo

其中实现的关键要点就是给每个列表设置独立的视图动画名称,此时,浏览器会给每一个元素创建独立的动画组,执行的动画就不是页面级的,而是元素之间的。

案例2:DOM元素remove淡出

在过去,删除某个列表元素,执行DOM.remove()方法的时候,元素删除效果很生硬,“bang”地一下就没了,一不留神,都没注意刚才删除的是哪个列表。

jQuery里面有个fadeOut()方法很好用,淡出后元素隐藏。

但是现在都是数据驱动了,数据一变,哗,列表一变,硬邦邦的,fadeOut()效果想用都用不上。

现在有了View Transitions API,一切都简单了,数据变化(或DOM手动移除),startViewTransition一下,就可以看到元素移出加淡出的效果了,老卵了。

实现也非常简单:

第一步:列表元素设置唯一的view-transition-name
第二步:执行类似下面的JS代码。

document.startViewTransition(() => {
    dom.remove();
});

就有如下所示的删除动效了,简单高效效果还好。

移除删除动画效果示意

眼见为实,您可以狠狠地点击这里:View Transition实现DOM移除淡出动画demo

五、@view-transition与页面间动画

View Transitions API除了可以用在单页中,也可以用在多页面跳转场景中,实现步骤如下。

页面插入这么一段CSS语句:

@view-transition {
  navigation: auto;
}

然后设置页面之间切换的动画类型,例如:

/* 创建自定义动画 */
@keyframes move-out {
  from {
    transform: translateX(0%);
  }
  to {
    transform: translateX(-100%);
  }
}

@keyframes move-in {
  from {
    transform: translateX(100%);
  }
  to {
    transform: translateX(0%);
  }
}

/* 给新旧快照应用动画效果 */
::view-transition-old(root) {
  animation: 0.4s ease-in both move-out;
}

::view-transition-new(root) {
  animation: 0.4s ease-in both move-in;
}

此时页面间切换,就会像单页页面一样,有滑来滑去的效果了。

页面滑来滑去示意

就是这么神奇,眼见为实:传统跳转页面变得单页一样滑来滑去demo

六、其他一些说明

View Transitions API还包括pagereveal和pageswap两个事件方法。

前者文档首次呈现时触发,无论是从网络加载新文档还是激活文档(从反向/正向缓存(bfcache)或预渲染器),后者在文档因导航而即将卸载时触发。

可以用来实现一些自定义行为。

好,暂时就说这么多吧,之后有其他新的东西再补充。

感谢阅读,欢迎

尾部占位图,银月小可爱

(本篇完)

分享到:


发表评论(目前3 条评论)

  1. mrfangge说道:

    可惜移动端不太支持

  2. codehz说道:

    这玩意目前就chrome在正式版有这个功能,而且之前还在iframe里显示出现过bug(指iframe的view transition不能和外部的view transition同时进行)
    safari 目前只有18-TP有这个,效果来看还有点bug

  3. mmm说道:

    虽然但是,类似功能早在25年前就被 IE 实现了(逃)……

    毕竟,用 HTML 实现应用(而不是“页面”)的想法由来已久(但是 IE 在各方面惨败)…… 但直到近年来才变为现实。