IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

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

    张 鑫旭发表于 2024-08-10 13:45:31
    love 0

    by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11308
    本文可全文转载,独立域名个人网站无需授权,但需要保留原作者、出处以及文中链接,任何网站均可摘要聚合,商用请联系授权。

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

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

    本文介绍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)或预渲染器),后者在文档因导航而即将卸载时触发。

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

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

    感谢阅读,欢迎。

    尾部占位图,银月小可爱

    本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
    本文地址:https://www.zhangxinxu.com/wordpress/?p=11308

    (本篇完)



沪ICP备19023445号-2号
友情链接