美国MACBOOKPRO日本

Heero.Luo发表于5年前,已(yi)被查看1658次

对(dui)于移动端的(de)Web单页应(ying)用来说,为了(le)达到媲美原(yuan)生应用的效(xiao)果,页面过渡(du)动画是必不(bu)可少的。常用(yong)的页面过渡(du)动画包(bao)括:

  1. 善良的嫂子3——当前页向左侧或右侧水平移出(chu)可视区,下一(yi)页由反方向(xiang)移入可视区(qu)。
  2. 不(bu)透明度变化(hua)——当前页(ye)淡出,下一页(ye)淡入。
  3. 1和2同时(shi)进行。

(注意:以(yi)下讨论和实(shi)验均在 Chrome 68 浏览(lan)器环境下进(jin)行)

目前大多(duo)数设备的屏幕刷新(xin)率为60次/秒,算(suan)下来每个帧(zheng)的预算时间(jian)约为16.66毫秒(1/60秒(miao))。考虑到浏览(lan)器还有其他(ta)工作要执行,实际上预(yu)算时间只有(you)10毫秒。跟此预(yu)算时间的差(cha)值越大,用户(hu)就会觉得动(dong)画过程越卡(ka)。那么,在这10毫秒内要(yao)完成什么事(shi)情呢?当使用(yong)JavaScript实现视觉交(jiao)互效果时,一(yi)般要经过以(yi)下流程:

JavaScript视觉(jue)交互执行流(liu)程

  1. JavaScript的执(zhi)行。例如修改元(yuan)素的样式,或(huo)者给元素添(tian)加/删除样式(shi)类。
  2. 样式计算(suan)。根据样式规(gui)则计算出元(yuan)素的最(zui)终样式。
  3. 布局(ju)(layout)。根据上一步(bu)的结果,计算(suan)元素占据的(de)空间大小及(ji)其在屏幕的(de)位置。注意,一(yi)个元素(su)布局上的变(bian)化有可能会(hui)引发其他元素(su)的联动变化(hua)。
  4. 绘制(paint)。填充像(xiang)素的过程,包(bao)括元素的每(mei)个可视(shi)部分。一般来(lai)说,绘制是在(zai)多个层上进(jin)行的。
  5. 合成(composite)。把(ba)各层按正确(que)顺序合并成(cheng)一个层,显示到(dao)屏(ping)幕上。

值得注意(yi)的是,并非每(mei)一帧都会经(jing)过上述每一(yi)个(ge)步骤的处理(li)。如果元素的(de)几何属性(尺(chi)寸、位置)没有变化,就(jiu)不需要进行(xing)布局;如果连(lian)元素的外观(guan)都没有改变(bian),就不需要绘(hui)制。所以,实现(xian)流畅动画的(de)关键就(jiu)在于如何减(jian)少布局和绘(hui)制

善良的嫂子3

对于(yu)善良的嫂子3动画来(lai)说,最直接的实(shi)现方式,就是(shi)把元素设成(cheng)绝对定(ding)位,然后去(qu)改变它的left样式值(zhi)。例如:

<!DOCTYPE html>
<html>
<head>
<style>
.page {
position: absolute;
left: 0;
top: 0;
width: 100%;
min-height: 100%;
background: #ddd;
transition-duration: 2s;
transition-property: left;
}
.leave {
left: -100%;
}
</style>
</head>

<body>
<div id="page" class="page s7dn6-sqz95-se0d2-s6b5r"></div>
<script>
var page = document.getElementById('page');
setTimeout(function() {
page.classList.add('leave');
}, 2000);
</script>
</body>
</html>

使用Chrome开(kai)发者工具中(zhong)的Performance面板录制(zhi)动画过程的(de)性能日志,如(ru)下图所(suo)示:

left动画过程(cheng)性能日志

可(ke)见,元素在移(yi)动的过程中(zhong)不断触发了(le)布局和绘制(zhi)。所以,这种实现(xian)方式的(de)性能是极低(di)的。网上诸多(duo)文献会推荐(jian)以transform的变化代(dai)替left的变化,而(er)实际情况又(you)是怎么样呢(ne)?把样式代码稍作修(xiu)改:

.page {
position: absolute;
left: 0;
top: 0;
width: 100%;
min-height: 100%;
background: #ddd;
transition-duration: 2s;
transition-property: transform;
}
.leave {
transform: translateX(-100%);
}

录制性能(neng)日志如下图(tu)所示:

transform性能日(ri)志

可见,仅(jin)仅是在动画开(kai)始和结束两(liang)个时间点触发了绘制(zhi),而布局则完(wan)全没有触发(fa)。这样一来,性(xing)能就有了很(hen)大的提升。但(dan)是,这里还有(you)两个疑问:

  • 为什么transform动(dong)画过程没有(you)触发布局和(he)绘制?
  • 为什么(me)动画开始前(qian)触发了两次(ci)绘制,动画结(jie)束之后触发(fa)了一次(ci)绘制?

要回答(da)这(zhe)两个问题,就得了解合成(cheng)层。

合成层

当(dang)满足某些条(tiao)件的时候,元(yuan)素在渲染时(shi)会被分(fen)配到一个独(du)立的层中进(jin)行渲染,只要(yao)该层的内容(rong)不发生改变(bian),就不会触发(fa)绘制,浏览器(qi)会直接(jie)通过合成形(xing)成一个新的(de)帧。常见的提升(sheng)为合成层的(de)条件包括:

  • 对(dui)opacity或transform应用了animation或(huo)transition;
  • 有 3D transform ;
  • will-change设置为(wei)opacity或transform。

很明显,上一节的transform善良的嫂子3(yi)动画满足了(le)第一个条件(jian)。所以整个动(dong)画的渲染过(guo)程是这样的(de):

  • 动画开始时(shi),由于div.page被提升为独立(li)的合成层,所(suo)以它要重新(xin)绘制;而document所在(zai)层相当于少了(le)一块内容,也(ye)得重新绘制(zhi);
  • 动画过程中,div.page没有其(qi)他变化,所以(yi)不(bu)触(chu)发布局(ju)和绘制;
  • 动画(hua)结束后,div.page不再(zai)是独立的合(he)成层,回到了(le)document所在层,所以document又重新(xin)绘制了一遍(bian)。

如果让div.page一直(zhi)在独立的合(he)成层中渲染(ran),则可以省掉(diao)上(shang)述过程中绘(hui)制的环节。在样式代(dai)码添加「will-change: transform」:

.page {
position: absolute;
left: 0;
top: 0;
width: 100%;
min-height: 100%;
background: #ddd;
transition-duration: 2s;
transition-property: transform;
will-change: transform;
}

录制(zhi)性能日志如(ru)下:

合成层(ceng)transform性能日志

可见,已(yi)经不存在绘(hui)制的步骤了(le)。

顺带一提,Chrome开(kai)发者工(gong)具中有一个(ge)Layers面板,可以方(fang)便地查看页(ye)面上合成层(ceng)以及成为合(he)成层的原因(yin)。

Layers面板

(注意:由于(yu)低版本(ben)浏览器不支(zhi)持will-change,所以实际(ji)应用中,如果(guo)想把元素提(ti)升到独立的(de)合成层中渲(xuan)染,可以用「transform: translateZ(0)」)

不透明度(du)

众所周知,不(bu)透明度就是(shi)通过opacity样式来(lai)控制的。那么(me)opacity的变化是否(fou)会触发布局(ju)和绘制呢?把(ba)样式代码修改如下:

.page {
position: absolute;
left: 0;
top: 0;
width: 100%;
min-height: 100%;
background: #ddd;
transition-duration: 2s;
transition-property: opacity;
}
.leave {
opacity: 0;
}

录(lu)制性能日志(zhi)如下图所示(shi):

opacity性能日志

在(zai)常规认知中(zhong),opacity的变化并不(bu)会导致元素(su)位置和尺寸的变化(hua),理应不会触(chu)发布局。但上(shang)述过程中确(que)实触发了一(yi)次布局,表现(xian)较为诡异。接(jie)下来给div.page添加(jia)「will-change: opacity」使其一(yi)直在独立的(de)合成层中渲染(ran)。录制性能日(ri)志如下:

opacity合成(cheng)层性能日志(zhi)

可见,还是会(hui)触发一次绘(hui)制。而针(zhen)对这「一次的布局(ju)」和「一(yi)次的(de)绘制」,我进行(xing)了进一步的(de)实验,得出的(de)结论是:opacity从1(包(bao)括未设置的(de)情况,下(xia)同)变更到小(xiao)于1,以及从小(xiao)于1变更到1,都(dou)会触发布局和(he)绘制;即使在独立的合成(cheng)层中渲染,也(ye)只能省(sheng)掉布局,无法(fa)省掉绘制。

由(you)于在opacity动画过(guo)程中从1到小(xiao)于1的变更只(zhi)会有一次,所(suo)以上述的布(bu)局和绘(hui)制都只触发(fa)一次。

善良的嫂子3和(he)不透明度

同(tong)时使用两种(zhong)动画,修改样式(shi)代码如下:

.page {
    position: absolute;
left: 0;
top: 0;
width: 100%;
min-height: 100%;
background: #ddd;
transition-duration: 2s;
transition-property: transform, opacity;
}
.leave {
transform: translateX(-100%);
opacity: 0;
}

按照前文的描(miao)述,动画过程会触发(fa):

  • 一次布局,在(zai)动画开始时(shi)触发,由opacity引起(qi);
  • 两次绘制,在(zai)动画开始时(shi)触发,因opacity以及(ji)提升为独立合成层(ceng)引起;
  • 由独立(li)合成层回到(dao)document所在层时引(yin)起。

倘若加(jia)上「will-change: transform, opacity」,使div.page一直在独立(li)的合成层中渲(xuan)染,则只触发一次绘(hui)制,由opacity引起。

然(ran)而,创建一个(ge)新的合成层(ceng)并不是免费(fei)的,它会导致(zhi)额外的内存(cun)开销。在单页(ye)应用中(zhong),应用页面过(guo)渡动画的元(yuan)素是页面的(de)最外层容器(qi),包含了该页(ye)面所有内容(rong)结构。如果让(rang)其长期在(zai)独立的合成(cheng)层中渲染,那(na)内存的消耗(hao)是非常大的(de)。

所以,可以仅(jin)在动画过程(cheng)中让其在独(du)立的合(he)成层中渲染(ran),而在其他情(qing)况下则维持(chi)常规状态。

transform和(he)fixed的冲突

如果(guo)用transform实现页(ye)面过渡动画,想必(bi)大家都遇到过一个(ge)问题:页面上固(gu)定定位的元(yuan)素,其位置变(bian)得不太正常(chang)了。

下面通过(guo)一段代码模(mo)拟页面进入的过程(cheng),来演示这个(ge)问题:

<!DOCTYPE html>
<html>
<head>
<style>
.page {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 150%;
background: #ddd;
transition-duration: 3s;
transition-timing-function: cubic-bezier(.55, 0, .1, 1);
transition-property: transform, opacity;
}
.before-enter {
transform: translateX(100%);
opacity: 0;
}
.fixed {
position: fixed;
right: 0;
bottom: 0;
width: 100%;
height: 160px;
background: #ffc100;
}
</style>
</head>

<body>
<div id="page" class="page before-enter sn6ds-sz33v-s19t1-s759m">
<div class="fixed s7z9i-sy1zr-sxw6a-shfa7"></div>
</div>
<script>
var page = document.getElementById('page');
setTimeout(() => {
page.classList.remove('before-enter');
}, 2000);
</script>
</body>
</html>

运行效(xiao)果如下:

transform和fixed的(de)冲突

可以看(kan)到,固定定位的黄色元素(su)是在动画结束后才(cai)突然出现的(de)。那在这之前它(ta)跑到哪去了(le)呢?

如果给一(yi)个固定定位(wei)元素的任意(yi)一个祖先元素设置(zhi)样式「transform」或者「will-change: transform」,那(na)么该元素就(jiu)会相对于最(zui)近的设置了(le)上述样式的(de)祖先元素定(ding)位。

因为div.page的高(gao)度设成(cheng)了150%,所以,在动(dong)画过程中,黄(huang)色元素实际(ji)上是跑到了页(ye)面的最底下(xia)(超出(chu)了浏览器可视范围(wei))去了。而(er)在某些比较(jiao)旧(如 iOS 9 的Safari)的移(yi)动端浏览器(qi)中,问题更为(wei)严重,固定(ding)定位的元素可(ke)能会消失掉(diao)再也不(bu)出现。

网上能(neng)查到的解决(jue)方案有两种(zhong):

  • 通过绝对定(ding)位模拟固定(ding)定位。虽然是(shi)可行的,但是在移(yi)动端浏(liu)览器内,交互(hu)上会有一些(xie)细节问题,而(er)且元素内部(bu)的滚动很容(rong)易与页面滚(gun)动冲突。
  • 把固(gu)定定位的元素放到(dao)应用transform动画的(de)元素外。但这(zhe)对使用「Vue.js」这类(lei)框架开发的(de)单页应用来(lai)说可行性较低(di),因为在这类框架中(zhong),一个页面就(jiu)是一个组件(jian),单独把页面(mian)中的某个元(yuan)素抽离出来(lai)是比较麻烦(fan)的。

所以,这里(li)介绍第(di)三种方案——在(zai)页面过渡动(dong)画结束之后(hou)(此时transform样式已(yi)被移除,不再(zai)影响fixed),再让固(gu)定定位的元(yuan)素插入(ru)到(dao)页面容器。并(bing)且,为(wei)了让它的出现显得(de)不那么突然(ran),增加缓动(dong)动画。代码主要(yao)修改点如下(xia):

@keyframes kf-move-in {
0% { transform: translateY(100%); }
100% { transform: translateY(0); }
}
.move-in {
animation-name: kf-move-in;
animation-duration: 0.45s;
}
<div id="page" class="page before-enter s301a-syy9n-s09qh-s49iz"></div>
<script>
var page = document.getElementById('page');
setTimeout(function() {
    // 监听过(guo)渡结束
page.addEventListener('transitionend', function() {
    // 创建(jian)、插入固定(ding)定位元素
var div = document.createElement('div');
div.className = 'fixed move-in';
page.appendChild(div);
});

page.classList.remove('before-enter');
}, 2000);
</script>

运行(xing)效果如下:

解(jie)决transform和fixed的冲突

这样一来(lai),整个(ge)交互就较为(wei)友好了(le)。这同时也说(shuo)明:技术上的问(wen)题,不一定只(zhi)能通过技术(shu)去解决,也可(ke)以从交互上(shang)去寻求解决(jue)方案。

参考文献

评论(lun) (0条)

发(fa)表(biao)评论

(必填(tian))

(选填,不(bu)公开)

(选填,不(bu)公开)

(必填)

久久香综合精品久久伊人欧美日韩一区二区综合高清在线观看 网站地图
四虎国产精品永久在线观看高清视频,白领人妻系列视频在线观看免费高清视频,性直播视频在线观看免费 久久香综合精品久久伊人欧美日韩一区二区综合高清在线观看每日更新国产精品白丝AV网站观看免费高清版,欧美日韩一区二区综合观看在线下载播放等成年人看的在线视频