当前位置:首页 >焦点 >太丝滑了!了解一下原生的视图转换动画 View Transitions API 起到前后过渡的生的视图效果

太丝滑了!了解一下原生的视图转换动画 View Transitions API 起到前后过渡的生的视图效果

2024-06-28 16:58:42 [百科] 来源:避面尹邢网

太丝滑了!太丝了解一下原生的滑解视图转换动画 View Transitions API

作者:XboxYan 开发 前端 Android 里一般称之为共享元素(shareElement)动画,也就是下原动画前后有一个(或多个)相同的元素,起到前后过渡的生的视图效果,可以很清楚的转换看到元素的变化过程,而并不是动画简单的消失和出现。

在原生 APP 中,太丝我们经常会看到那种丝滑又舒适的滑解页面切换动画,比如这样的下原

太丝滑了!了解一下原生的视图转换动画 View Transitions API 起到前后过渡的生的视图效果

太丝滑了!了解一下原生的视图转换动画 View Transitions API 起到前后过渡的生的视图效果

Android 里一般称之为共享元素(shareElement)动画,也就是生的视图动画前后有一个(或多个)相同的元素,起到前后过渡的转换效果,可以很清楚的动画看到元素的变化过程,而并不是太丝简单的消失和出现。

太丝滑了!了解一下原生的视图转换动画 View Transitions API 起到前后过渡的生的视图效果

现在,滑解web 中(Chrome 111+)也迎来了这样一个特性,下原叫做视图转换动画 View Transitions[1],又称“转场动画”,也能很轻松的实现这类效果,一起了解一下吧!

一、快速认识 View Transition

先从一个简单的例子来认识一下。

比如,下面有一个网格列表:

<div class="list" id="list">  <div class="item">1</div>  <div class="item">2</div>  <div class="item">3</div>  <div class="item">4</div>  <div class="item">5</div>  <div class="item">6</div>  <div class="item">7</div>  <div class="item">8</div>  <div class="item">9</div>  <div class="item">10</div></div>

简单修饰后如下:

然后我们实现一个简单交互,点击每个元素,元素就会被删除。

list.addEventListener('click', function(ev){   if (ev.target.className === 'item') {     ev.target.remove()  }})

可以得到这样的效果。

功能正常,就是有点太过生硬了。

现在轮到 View Transitions 出场了!我们只需要在改变状态的地方添加document.startViewTransition,如下:

list.addEventListener('click', function(ev){   if (ev.target.className === 'item') {     document.startViewTransition(() => {  // 开始视图变换      ev.target.remove()    });  }})

当然为了兼容不支持的浏览器,可以做一下判断。

list.addEventListener('click', function(ev){   if (document.startViewTransition) {  // 如果支持就视图变换    document.startViewTransition(() => {  // 开始视图变换      ev.target.remove()    });  } else {  // 不支持就执行原来的逻辑    ev.target.remove()  }})

现在效果如下:

删除前后现在有一个淡入淡出的效果了,也就是默认的动画效果,我们可以把这个动画时长设置大一点,如下:

::view-transition-old(root), /* 旧视图*/::view-transition-new(root) {  /* 新视图*/  animation-duration: 2s;}

这两个伪元素我们后面再做介绍,先看效果:

是不是明显感觉过渡变慢了许多?

但是这种动画还是不够舒服,是一种整体的变化,看不出删除前后元素的位置变化。

接下来我们给每个元素指定一个标识,用来标记变化前后的状态,为了方便控制,可以借助 CSS 变量。

<div class="list" id="list">  <div class="item" style="--i: a1">1</div>  <div class="item" style="--i: a2">2</div>  <div class="item" style="--i: a3">3</div>  <div class="item" style="--i: a4">4</div>  <div class="item" style="--i: a5">5</div>  <div class="item" style="--i: a6">6</div>  <div class="item" style="--i: a7">7</div>  <div class="item" style="--i: a8">8</div>  <div class="item" style="--i: a9">9</div>  <div class="item" style="--i: a10">10</div></div>

这里通过view-transition-name来设置名称。

.item{   view-transition-name: var(--i);}

然后可以得到这样的效果,每个元素在变化前后会自动找到之前的位置,并且平滑的移动过去,如下:

完整代码可以查看

  • view-transition sort (juejin.cn)[2]
  • view-transition sort (codepen.io)[3]

是不是非常丝滑?这就是 View Transitions 的魅力!

二、View Transition 的核心概念

为啥仅仅加了一点点代码就是实现了如此顺畅的动画呢?为啥浏览器可以知道前后的元素位置关系呢?这里简单介绍一下变化原理。

整个 JS 部分只有一行核心代码,也就是document.startViewTransition,表示开始视图变换。

document.startViewTransition(() => {   // 变化操作});

整个过程包括 3 部分

  • 调用document.startViewTransition,浏览器会捕捉当前页面的状态,类似于实时截图,或者“快照”。
  • 执行实际的 dom 变化,再次记录变化后的页面状态(截图)。
  • 触发两者的过渡动画,包括透明度、位移等变化,也可以自定义 CSS 动画。

下面是一个示意图:

在动画执行的过程中,还会在页面根节点自动创建以下伪元素。

::view-transition└─ ::view-transition-group(root)   └─ ::view-transition-image-pair(root)      ├─ ::view-transition-old(root)      └─ ::view-transition-new(root)

下面是控制台的截图。

其中,::view-transition-old表示「旧视图」的状态,也就是变化之前的截图,::view-transition-new表示「新视图」的状态,也就是变化之后的截图。

默认情况下,整个页面root都会作为一个状态,也就是上面的::view-transition-group(root),在切换前后会执行淡入淡出动画,如下:

:root::view-transition-new(root) {   animation-name: -ua-view-transition-fade-in; /*淡入动画*/}:root::view-transition-old(root) {   animation-name: -ua-view-transition-fade-out; /*淡出动画*/}

这也是为什么在使用了document.startViewTransition后整个页面会有淡入淡出的效果了。

为了让每个元素都有自己的过渡状态,这里需要给每个元素都指定名称。

.item{   view-transition-name: item-1;}

这样指定名称后,每个名称都会创建一个::view-transition-group,表示独立的分组。

这样在变化前后view-transition-name相同的部分就会一一自动执行过渡动画了,以第4、5个元素为例(在3删除以后)。

最后就会得到这样的效果。

核心概念就这些了,下面再来看几个例子。

三、不同元素之间的过渡

视图变化其实和元素是否相同没有关联,有关联的只有view-transition-name,浏览器是根据view-transition-name寻找的,也就是相同名称的元素在前后会有一个过渡动画。

比如下面这样一个例子。

每个按钮在打开弹窗时,都可以清楚的看到弹窗是从哪里出现的,如何实现这样的效果呢?

从本质上看,其实就是「按钮到弹窗的视图变换」,按照前面讲到的,可能会想到给前后加上相同的view-transition-name,下面是HTML结构。

<div class="bnt-group" id="group">  <button>按钮1</button>  <button>按钮2</button>  <button>按钮3</button></div><dialog id="dialog">  <form method="dialog">    我是弹窗    <button>关闭</button>  </form></dialog>

尝试一下。

button,dialog{   view-transition-name: dialog;}

然后添加点击打开弹窗事件。

group.addEventListener('click', function(ev){   if (ev.target.tagName === 'BUTTON') {     if (document.startViewTransition) {       document.startViewTransition(() => {         dialog.showModal()      });    } else {       dialog.showModal()    }  }})

这样会有什么问题吗?运行如下:

很明显报错了,意思就是一个页面中不能有相同的view-transition-name。严格来讲,是「不能同时出现」,如果其他元素都是隐藏的,只有一个是显示的,也没有问题。其实仔细想一下,也很好理解,如果同时有两个相同的名称,并且都可见,最后变换的时候该以哪一个为准呢?

所以,在这种情况下,正确的做法应该是动态设置view-transition-name,比如默认不给按钮添加名称,只有在点击的时候才添加,然后在变换结束之后再移除按钮的view-transition-name,实现如下:

group.addEventListener('click', function(ev){   if (ev.target.tagName === 'BUTTON') {     ev.target.style.viewTransitionName = 'dialog' // 动态添加 viewTransitionName    if (document.startViewTransition) {       document.startViewTransition(() => {         ev.target.style.viewTransitionName = '' // 结束后移除 viewTransitionName        dialog.showModal()      });    } else {       dialog.showModal()    }  }})

这样就实现了动态缩放的效果:

大致已经实现想要的效果,不过还有一个小问题,我们把速度放慢一点(把动画时长设置长一点)。

可以清楚的看到,原本的按钮先放大到了弹窗大小,然后逐渐消失。这个过程是我们不需要的,有没有办法去掉呢?

当然也是可以的!原本的按钮其实就是旧视图,也就是点击之前的截图,我们只需要将这个视图隐藏起来就行了。

::view-transition-old(dialog) {   display: none;}

这样就完美实现了从哪里点击就从哪里打开的效果:

完整代码可以查看:

  • view-transition-dialog (juejin.cn)[4]
  • view-transition-dialog (codepen.io)[5]

四、自定义过渡动画

通过前面的例子可以看出,默认情况下,视图转换动画是一种淡入淡出的动画,然后还有如果位置、大小不同,也会平滑过渡。

除此之外,我们还可以手动指定过渡动画。比如下面这个例子。

这是一个黑暗模式的简易模型,实现也非常简单,准备两套主题,这里用color-scheme实现。

.dark{   color-scheme: dark;}

然后通过点击动态给html切换dark类名。

btn.addEventListener('click', function(ev){   document.documentElement.classList.toggle('dark')})

这样就得到了主题切换效果:

接着,我们添加视图转换动画。

btn.addEventListener('click', function(ev){   if (document.startViewTransition) {     document.startViewTransition(() => {       document.documentElement.classList.toggle('dark')    });  } else {     document.documentElement.classList.toggle('dark')  }})

这样就得到了一个默认的淡入淡出的切换效果(为了方便观察,将动画时长延长了)。

图片

你可以把前后变化想象成是两张截图的变化,如果要实现点击出现圆形裁剪动画,其实就是在新视图上执行一个裁剪动画,由于是完全重叠的,所以看着像是一种穿透扩散的效果:

动画很简单,就是一个clip-path动画。

@keyframes clip {   from {     clip-path: circle(0%);  }  to{     clip-path: circle(100%);  }}

我们把这个动画放在::view-transition-new中。

::view-transition-new(root) {   /* mix-blend-mode: normal; */  animation: clip .5s ease-in;  /* animation-duration: 2s; */}

效果如下:

是不是还有点奇怪?这是因为默认的一些样式导致,包括原有的淡出效果,还有混合模式。

所以还需要去除这些影响。

::view-transition-old(root) {   animation: none;}::view-transition-new(root) {   mix-blend-mode: normal;  animation: clip .5s ease-in;}

当然你可以把鼠标点击的位置传递到页面根节点。

btn.addEventListener('click', function(ev){   document.documentElement.style.setProperty('--x', ev.clientX + 'px')  document.documentElement.style.setProperty('--y', ev.clientY + 'px')  if (document.startViewTransition) {     document.startViewTransition(() => {       document.documentElement.classList.toggle('dark')    });  } else {     document.documentElement.classList.toggle('dark')  }})

动画里直接通过CSS 变量获取。

@keyframes clip {   from {     clip-path: circle(0% at var(--x) var(--y));  }  to{     clip-path: circle(100% at var(--x) var(--y));  }}

这样就实现了完美的扩散切换效果:

完整代码可以查看:

  • view transition theme change - (juejin.cn)[6]
  • view transition theme change (codepen.io)[7]

五、其他案例

找了几个有趣的例子。

只要涉及到前后过渡变化的,都可以考虑用这个特性,例如下面的拖拽排序。

Android https://mp.weixin.qq.com/s/v8XwlqLAtCxxYG2FOvatFw。

https://codepen.io/argyleink/pen/rNQZbLr。

再比如这样一个数字过渡动画。

https://mp.weixin.qq.com/s/v8XwlqLAtCxxYG2FOvatFw

https://codepen.io/argyleink/pen/jOQKdeW

还有类似于 APP 的转场动画(多页面跳转)

https://simple-set-demos.glitch.me/6-expanding-image/。

五、总结和说明

总的来说,原生的视图转换动画可以很轻松的实现两种状态的过渡,让 web 也能实现媲美原生 APP 的动画体验,下面再来回顾一下整个变化过程:

  • 调用document.startViewTransition,浏览器会捕捉当前页面的状态,类似于实时截图,或者“快照”。
  • 执行实际的 dom 变化,再次记录变化后的页面状态(截图)。
  • 触发两者的过渡动画,包括透明度、位移等变化,也可以自定义 CSS 动画。
  • 默认情况下是整个页面的淡入淡出变化。
  • ::view-transition-old表示「旧视图」的状态,也就是变化之前的截图,::view-transition-new表示「新视图」的状态,也就是变化之后的截图。
  • 如果需要指定具体元素的变化,可以给该元素指定view-transition-name。
  • 前后变化不一定要同一元素,浏览器是根据view-transition-name寻找的。
  • 同一时间页面上不能出现两个相同view-transition-name的元素,不然视图变化会失效。

另外,视图转换动画应该作为一种「体验增强」的功能,而非必要功能,在使用动画时其实拖慢了页面打开或者更新的速度,并且在动画过程中,页面是完全“冻结”的,做不了任何事情,因此需要衡量好动画的时间,如果页面本身就很慢就更不要使用这些动画了。

[1]View Transitions: https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API

[2]view-transition sort (juejin.cn): https://code.juejin.cn/pen/7268263402853072931

[3]view-transition sort (codepen.io): https://codepen.io/xboxyan/pen/BavBevP

[4]view-transition-dialog (juejin.cn): https://code.juejin.cn/pen/7268262983178911779

[5]view-transition-dialog (codepen.io): https://codepen.io/xboxyan/pen/WNLeBgY

[6]view transition theme change - (juejin.cn): https://code.juejin.cn/pen/7268257573277532219

[7]view transition theme change (codepen.io): https://codepen.io/xboxyan/pen/poqzmLY

责任编辑:姜华 来源: 前端侦探 AndroidCSS

(责任编辑:休闲)

    推荐文章
    • 前10个月安徽省重点项目完成投资15725亿 开工3235个

      前10个月安徽省重点项目完成投资15725亿 开工3235个11月15日,记者从安徽省发改委获悉,今年前10个月,全省重点项目完成投资15725亿;计划内投资完成率、计划内竣工率均超序时进度。省发改委要求,各地及早谋划明年重点项目,持续推进一批战略性、标志性、 ...[详细]
    • 欠多少钱会上失信名单 如何解除失信人名单?

      欠多少钱会上失信名单 如何解除失信人名单?在现在这个社会,信用对于我们每个人都十分重要。可是有些人无视自己信用,借了钱就一直不还,那么就会被称为“失信人”。一旦上了失信名单,那么会有严重后果。于是人们就想知道,欠多少钱 ...[详细]
    • 苹果6相机怎么设置 小技巧有哪些?

      苹果6相机怎么设置 小技巧有哪些?苹果 手机是手机当中偏贵的了,同等性能的手机苹果的价格都高一些,其实高的并不是没有原因,它的硬件或者是软件虽然和别的手机为同一配置,效果却完全不同,都会优质于其它手机,就像它的照相功能,苹果6的后摄像 ...[详细]
    • 工伤休息工资怎么算 工伤期间单位不发工资怎么办?

      工伤休息工资怎么算 工伤期间单位不发工资怎么办?众所周知,工人在职业活动中接触粉尘、放射性物质等有毒有害物质引起的疾病被认定为工伤。也就是说,社会保障中工伤保险认定的伤害是指身体伤害或暴力伤害。那么,工伤休息工资怎么算?如果员工在工伤事故中受伤严重 ...[详细]
    • 信用购关闭后还能开通吗 具体情况是怎样的?

      信用购关闭后还能开通吗 具体情况是怎样的?花呗升级变成“花呗|信用购”后,很多人对新增加的信用购这个消费信用贷款并不感冒,打算把信用购关闭掉,可是考虑以后可能还会用上信用购,就想知道信用购关闭后还能开通吗,这里就给大家 ...[详细]
    • psp2000与3000有什么区别 psp2000与3000区别在哪里?

      psp2000与3000有什么区别 psp2000与3000区别在哪里?psp2000与3000,都是索尼品牌型号不同的携带型游戏机,psp2000上市于2007年,是PSP的第一次改版;而psp3000上市于2008年,是其第二次改版,这两个型号的游戏机还是有很多相似地 ...[详细]
    • 循环经济的目标是什么 原则有哪些?

      循环经济的目标是什么 原则有哪些?循环经济的基本思路是把人类社会的生产方式看成人类与自然界之间的物质能量交换,在人类生产消费中产生的废弃物,仍属于这种物质能量交换中的一个环节,因此废弃物是另一种资源,并将作为资源被利用,废物治理将成为 ...[详细]
    • outlook怎么用?初学者使用outlook的步骤有哪些?

      outlook怎么用?初学者使用outlook的步骤有哪些?outlook怎么用? Outlook不是电子邮箱的提供者,它是Windows操作系统的一个收、发、写、管理电子邮件的自带软件,即收、发、写、管理电子邮件的工具,使用它收发电子邮件十分方便。发信时,可 ...[详细]
    • 信用购怎么变回花呗 操作流程来啦

      信用购怎么变回花呗 操作流程来啦有不少支付宝用户同意花呗服务升级后,发现花呗页面变成了“花呗|信用购”,在原来花呗的基础上还多了一个信用购消费贷款产品,虽说可以和花呗单独使用,可也有的人认为用不着,想知道信用 ...[详细]
    • 房贷成功了为啥一直没有放款下来 原因可能有哪些?

      房贷成功了为啥一直没有放款下来 原因可能有哪些?现在房价很高,很多人都是选择申请贷款,需要向银行提交资料审核,审核通过就能放款了。最近有网友询问,房贷成功了为啥一直没有放款下来?很有可能是这几种情况,跟小编一起去看看吧。如果银行通过了房贷审批,但是 ...[详细]
    热点阅读