原文连接: Smooth as Butter: Achieving 60 FPS Animations with CSS3css
在移动端使用动画元素是很容易的.html
若是你能遵循咱们的这里的提示, 在移动端适当的使用动画元素, 能够变得更加容易.css3
在这些天里, 每一个人都不会适当的使用CSS3动画. 有些最佳的实践方法, 一直被忽视. 被忽视的主要缘由是人们不能真正的理解,这些实践存在的真正缘由, 以及为什么能获得大力支持.web
如今设备的规格很是多, 若是你不能经过仔细思考优化你的代码, 使用顺畅的动画, 你会给大部分的人带来很差的体验.chrome
记住: 尽管一些高端, 旗舰设备不断推进发展, 但这世界上大部分的设备, 和高端机相比差太多, 就像一个带着LCD的算盘.浏览器
咱们会告诉你如何使用CSS3, 发挥它的最大功效. 为了达成这一点, 咱们首先须要学习一点东西.app
渲染和使用元素的时候, 浏览器会作些什么? 这是很是简单的一条时间线, 被称之为关键渲染路径(Critical Rendering Path)chrome-devtools
咱们应该专一于改变影响合成步骤的属性, 而不是增长上一次布局的压力.函数
浏览器开始计算样式, 以便应用到元素上 - 从新计算样式布局
下一个步骤中, 浏览器开始计算模型和各个元素的位置 - 布局. 这是浏览器设置在页面中设置例如宽和高属性的地方, 也包括了外边距, 或者是实例的左/高/右/下.
浏览器开始将每个元素在像素级别填充到图层中. 咱们使用的这些属性包括: box-shadow, border-radius, color, background-color, 等等.
这是你须要操做的地方, 由于这是浏览器开始在屏幕绘制全部的图层.
如今浏览器可以产生动画的有四个属性, ,最好使用tansform, 和opacity属性.
经过上面这些思考, 让咱们开始干吧.
咱们经过一个HTML开始. 咱们会建立一个很是简单的模型, 而后在.layout
中放置一个app-menu
.
<div class="layout"> <div class="app-menu"></div> <div class="header"> <div class="menu-icon"></div> </div> </div>
.app-menu { transition: left 300ms linear; left: -60%; } .open .app-menu { left: 0; }
看到被咱们改变的属性了吗? 你应该避免使用transition
上面的top/right/bottom/left
属性. 那并不会产生一个流畅的动画. 由于他们让浏览器一直在建立layout
s, 这会影响到他全部的子组件.
他的结果就像这样:
这个动画彻底不畅. 咱们经过使用DevTools Timeline
来看看底层发生了什么, 这是他的结果:
这清楚的展示了FPS的不规则, 而且性能不好.
"绿条表示FPS. 他上面的条表示动画在60FPS如何渲染. 下面的条表示低于60FPS. 因此, 理想状况下, 你但愿绿条能在整个时间线保持较高的水平. 红条也能表示出闪避
jank
(避开了渲染时间?), 因此, 还能够经过消除红条, 来表示你性能的进步." 感谢Kayce Basques指出.
Dev Tools
官方文档: Timeline
已经再也不使用, 下面是使用Performance
这是一个疑问句? 如今我发现, 官方文档上所说的CRP和这篇文章说的不太同样, 有空翻译下官方文档.
.app-menu { transition: transform 300ms linear; transform: translateX(-100%); } .open .app-menu{ transform: none; }
transform
属性做用到Composite
合成阶段. 这里告诉咱们, 只要动画开始, 浏览器全部的图层都渲染完成并准备好了, 因此动画渲染的时候, 间隔很是小.
在实际中的时间线中展现:
如今的结果变得好一些了, FPS可以进行更多渲染, 而后动画更加流畅.
和上一次我的测试相比, 省去了layout阶段, 就是布局的时间.
那么, 让我把他提升一个等级. 为了保证动画运行的顺畅, 咱们是用GPU开始渲染动画.
.app-menu { transition: transform 300ms linear; transform: translateX(-100%); /* transform: translate3d(-100%, 0, 0); */ will-change: transform; } .open .app-menu{ transform: none; }
尽管一些浏览器依旧须要使用translateZ()
和translate3d()
做为备选方案, will-change
CSS will-change
- how to use it, how it works才是之后的趋势. 这样作, 能够把元素提高到另外一层上, 因此, 浏览器不须要考虑布局的渲染和绘制.
可以看出他的顺畅吗? 渲染路径会证明这一点.
动画的FPS是很是连续的, 而且动画的渲染是很是快速的. 可是依旧有一个红框在渲染的时候. 那只是在开始的时候, 一个小瓶颈.
记住刚开始时建立的HTML的结构. 让咱们使用JavaScript在结构中控制一个app-menu
div.
function toggleClassMenu() { var layout = document.querySelector('.layout') if (!layout.classList.contains('app-menu-open')) { layout.classList.add('app-menu-open') } else { layout.classList.remove('app-menu-open') } }
这个问题是: 咱们给layout
这个div添加了类名, 让浏览器再一次计算了样式, 影响了渲染性能.
首先是, 使用translate3d的效果:
这是使用了will-change
的效果:
一样, 我也是使用这种控制类名的方式实现的动画.
若是咱们从视图层外边建立一个区域替代以前的作法呢? 一个隔离的区域, 能够确保影响到的元素, 就是想要进行动画的.
因此, 咱们使用下面这种HTML结构.
<div class="menu"> <div class="app-menu"></div> </div> <div class="layout"> <div class="header"> <div class="menu-icon"></div> </div> <a href="www.baidu.com">baidu</a> </div>
如今咱们可使用稍微不一样的方式控制menu的状态了. 当动画结束的时候, 咱们使用JavaScript中的transitionend
函数, 删除还有动画的类名.
function toggleClassMenu() { myMenu.classList.add("menu--animatable"); if (!myMenu.classList.contains("menu--visible")) { myMenu.classList.add("menu--visible"); } else { myMenu.classList.remove('menu--visible'); } } function OnTransitionEnd() { myMenu.classList.remove("menu--animatable"); } var myMenu = document.querySelector(".menu"); var oppMenu = document.querySelector(".menu-icon"); myMenu.addEventListener("transitionend", OnTransitionEnd, false); // 只在动画期间添加动画函数 oppMenu.addEventListener("click", toggleClassMenu, false); myMenu.addEventListener("click", toggleClassMenu, false);
让咱们所有结合起来, 而后检查结果.
下面是完整, 可使用CSS3的例子, 每一处都使用了最正确的方式.
body { margin: 0; padding: 0; } .menu { position: fixed; left: 0; top: 0; width: 100%; height: 100%; overflow: hidden; pointer-events: none; /* 这个属性表示, 即便是上面有一层, 也不影响, 下面元素的使用 */ z-index: 150; } .menu--visible { pointer-events: auto; /* 遮盖了, 也就不让用了 */ } .app-menu { background-color: #fff; color: #fff; position: relative; max-width: 400px; width: 90%; height: 100%; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5); -webkit-transform: translateX(-103%); transform: translateX(-103%); display: flex; flex-direction: column; will-change: transform; z-index: 160; pointer-events: auto; /* 这是咱们的侧边栏, 打开的时候, 不让用下面的元素 */ } .menu--visible .app-menu { -webkit-transform: none; transform: none; } .menu--animatable .app-menu { /* 消失的时候, 先慢后快 */ transition: all 130ms ease-in; } .menu--visible.menu--animatable .app-menu { /* 出现的时候, 先快, 后慢 */ transition: all 330ms ease-out; } .menu:after { content: ''; display: block; position: absolute; left: 0; top: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.4); opacity: 0; will-change: opacity; pointer-events: none; transition: opacity 0.3s cubic-bezier(0, 0, 0.3, 1); } .menu--visible.menu:after { opacity: 1; pointer-events: auto; } /* aux */ body { margin: 0; } .layout { width: 375px; height: 667px; background-color: #f5f5f5; position: relative; } .header { background-color: #ccc; } .menu-icon { content: "Menu"; color: #fff; background-color: #666; width: 40px; height: 40px; } .app-menu { width: 300px; height: 667px; box-shadow: none; background-color: #ddd; } .menu:after { width: 375px; height: 667px; }
让咱们看下Timeline展现给咱们的?
看到了吗? 很是流畅.
发现, 性能提高主要在Event中, 其余未能看出提高, 并进行了一个名为Fire Idle Callback
.还须要深刻了解下. 下图为实操图片:
关键渲染路径
究竟是什么. 文章和官网说的不同.will-change
和pointer-events