在写到小程序底部弹出模态框组件的时候希望在其展示和消失的时候有一个弹入-弹出的动画效果。那不可避免地就要用到我们css3的高级特性动画了,正好之前用动画用的比较少,那就手动实现一下吧。
使用的框架是react,页面结构如下所示:
<>
<View className={styles.mask} style={{ display: showBlock ? 'block' : 'none' }} onClick={onCancel}></View>
<View className={`${styles.modal} ${visible ? styles.showModal : styles.closeModal}`} style={{ display: showBlock ? 'block' : 'none' }}>
<View className={styles.topLine}>
<Text className={styles.cancel}>取消</Text>
<Text className={styles.title}>{title}</Text>
<Text className={styles.complete} onClick={onOk}>完成</Text>
</View>
{props.children}
</View>
</>
css:
//定义动画
@keyframes slideUp {
from {
height: 0;
}
to {
height: auto;
}
}
@keyframes slideDown {
from {
height: auto;
}
to {
height: 0;
}
}
//蒙层
.mask {
width: 100%;
height: 100vh;
position: fixed;
display: none;
background-color: rgba(0, 0, 0, 0.5);
top: 0;
left: 0;
}
.modal {
width: 100%;
position: fixed;
bottom: 0;
background-color: #ffffff;
padding: 16px 12px;
border-radius: 12px 12px 0 0;
}
.showModal {
animation: slideUp 0.5s;
}
.closeModal {
animation: slideDown 0.5s;
}
OK,写完代码让我们试一下,效果如下所示
what?为什么这么的卡?
仔细想了一下,卡顿的原因肯定跟浏览器的渲染和绘制有关系,最根本的原因是这几行代码:
//定义动画
@keyframes slideUp {
from {
height: 0;
}
to {
height: auto;
}
}
@keyframes slideDown {
from {
height: auto;
}
to {
height: 0;
}
}
当然我们定义滑入滑出的动画的时候很自然地想到用height来进行过渡,但是这就会带来性能问题。这主要是由于浏览器运行的时候主线程和合成线程调度不合理造成的。浏览器主线程主要执行js脚本,要根据样式计算css布局,而合成线程只调用GPU进行绘制,不涉及计算问题。
在使用height,width,margin,padding作为transition的值时,会造成浏览器主线程的工作量较重,例如从margin-left:-20px渲染到margin-left:0,主线程需要计算样式margin-left:-19px,margin-left:-18px,一直到margin-left:0,而且每一次主线程计算样式后,合成进程都需要绘制到GPU然后再渲染到屏幕上,前后总共进行20次主线程渲染,20次合成线程渲染,20+20次,总计40次计算。
而这个问题的解决方案就是:使用css3的transform来定义动画,将上面定义动画的css改成如下:
//定义动画
@keyframes slideUp {
from {
transform: translateY(100%);
}
to {
transform: translateY(0);
}
}
@keyframes slideDown {
from {
transform: translateY(0);
}
to {
transform: translateY(100%);
}
}
再让我们试验一下效果
果然丝滑了很多有木有,transform为什么有如此神奇的能量呢?
而如果使用transform的话,例如tranform:translate(-20px,0)到transform:translate(0,0),主线程只需要进行一次tranform:translate(-20px,0)到transform:translate(0,0),然后合成线程去一次将-20px转换到0px,这样的话,总计1+20计算。
合成器的优点在于,其工作无关主线程,合成器线程不需要等待样式计算或者 JS 执行,这就是为什么合成器相关的动画 最流畅,如果某个动画涉及到布局或者绘制的调整,就会涉及到主线程的重新计算,自然会慢很多。
说白一点也就是说,用transform不引发回流,不涉及布局和样式的调整,从而不回触发主线程的重新计算,自然就快了
因此,我们在用css写动画效果时,尽量用transform,少用height width margin padding