丝滑顺畅:使用CSS3获取60FPS动画
原文链接: Smooth as Butter: Achieving 60 FPS Animations with CSS3
在移动端使用动画元素是很容易的.
如果你能遵循我们的这里的提示, 在移动端适当的使用动画元素, 可以变得更加容易.
在这些天里, 每个人都不会适当的使用CSS3动画. 有些最佳的实践方法, 一直被忽视. 被忽视的主要原因是人们不能真正的理解,这些实践存在的真正原因, 以及为何能得到大力支持.
现在设备的规格非常多, 如果你不能通过仔细思考优化你的代码, 使用顺畅的动画, 你会给大部分的人带来不好的体验.
记住: 尽管一些高端, 旗舰设备不断推动发展, 但这世界上大部分的设备, 和高端机相比差太多, 就像一个带着LCD的算盘.
我们会告诉你如何使用CSS3, 发挥它的最大功效. 为了达成这一点, 我们首先需要学习一点东西.
理解时间线(Timeline)
渲染和使用元素的时候, 浏览器会做些什么? 这是非常简单的一条时间线, 被称之为关键渲染路径(Critical Rendering Path)
我们应该专注于改变影响合成步骤的属性, 而不是增加上一次布局的压力.
1.样式
浏览器开始计算样式, 以便应用到元素上 - 重新计算样式
2.布局
下一个步骤中, 浏览器开始计算模型和各个元素的位置 - 布局. 这是浏览器设置在页面中设置例如宽和高属性的地方, 也包括了外边距, 或者是实例的左/高/右/下.
3.绘制
浏览器开始将每一个元素在像素级别填充到图层中. 我们使用的这些属性包括: box-shadow, border-radius, color, background-color, 等等.
4.合成
这是你需要操作的地方, 因为这是浏览器开始在屏幕绘制所有的图层.
现在浏览器能够产生动画的有四个属性, ,最好使用tansform, 和opacity属性.
- 位置: transform: translateX(n) translateY(n) translateZ(n);
- 扩大/缩放: transform: scale(n);
- 旋转: transform: rotate(ndeg);
- 透明: opacity: n;
如何获得每秒60帧的实现
通过上面这些思考, 让我们开始干吧.
我们通过一个HTML开始. 我们会创建一个非常简单的模型, 然后在.layout
中放置一个app-menu
.
<div class="layout">
<div class="app-menu"></div>
<div class="header">
<div class="menu-icon"></div>
</div>
</div>
开始使用一个错误的方式
Going About It the Wrong Way(下为原文翻译)
.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和这篇文章说的不太一样, 有空翻译下官方文档.
使用 Transform
.app-menu {
transition: transform 300ms linear;
transform: translateX(-100%);
}
.open .app-menu{
transform: none;
}
transform
属性作用到Composite
合成阶段. 这里告诉我们, 只要动画开始, 浏览器所有的图层都渲染完成并准备好了, 所以动画渲染的时候, 间隔非常小.
在实际中的时间线中展示:
现在的结果变得好一些了, FPS能够进行更多渲染, 然后动画更加流畅.
个人第二部分测试
和上一次个人测试相比, 省去了layout阶段, 就是布局的时间.
在GPU中运行动画
那么, 让我把他提高一个等级. 为了保证动画运行的顺畅, 我们是用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
的效果:
同样, 我也是使用这种控制类名的方式实现的动画.
60FPS的顺畅动画
如果我们从视图层外边创建一个区域替代之前的做法呢? 一个隔离的区域, 可以确保影响到的元素, 就是想要进行动画的.
所以, 我们使用下面这种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
丝滑顺畅:使用CSS3获取60FPS动画的更多相关文章
- 使用 CSS3 打造一组质感细腻丝滑的按钮
CSS3 引入了众多供功能强大的新特性,让设计和开发人员能够轻松的创作出各种精美的界面效果.下面这些发出闪亮光泽的按钮,很漂亮吧?把鼠标悬停在按钮上,还有动感的光泽移动效果. 温馨提示:为保证最佳的效 ...
- jQuery和css3控制箭头丝滑旋转
问题: 我们经常会遇见点击一个小三角使之丝滑的旋转180度上下旋转,怎么实现呢,需要css3搭配jq 来处理 如图:1.点击前 2.点击后(效果丝滑旋转) 1.html ...
- 简单的CSS3鼠标滑过图片标题和遮罩层动画特效
此文转自:http://www.cnblogs.com/w2bc/p/5735300.html,仅供本人学习参考,版权归原作者所有! 这是一款使用CSS3制作的简单的鼠标滑过图片标题和遮罩层动画特 ...
- 让你的app体验更丝滑的11种方法!冲击手机应用榜单Top3指日可待
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由WeTest质量开放平台团队发表于云+社区专栏 一款app除了要有令人惊叹的功能和令人发指交互之外,在性能上也应该追求丝滑的要求,这样 ...
- HMS Core Discovery第14期回顾长文|纵享丝滑剪辑,释放视频创作力
HMS Core Discovery第14期直播<纵享丝滑剪辑,释放视频创作力>,已于4月21日圆满结束,本期直播我们同HMS Core视频编辑服务(Video Editor Kit)的产 ...
- CSS3中的动画效果记录
今天要记录的是CSS3中的三种属性transform.transition以及animation,这三个属性大大提升了css处理动画的能力. 一.Transform 变形 CSS中transform ...
- 6种炫酷的CSS3按钮边框动画特效
6种炫酷的CSS3按钮边框动画特效Button border animate 用鼠标滑过下面的按钮看看效果! Draw Draw Meet Center Spin Spin Circle Spin T ...
- 15个超强悍的CSS3圆盘时钟动画赏析
在网页上,特别是个人博客中经常会用到时钟插件,一款个性化的时钟插件不仅可以让页面显得美观,而且可以让访客看到当前的日期和时间.今天我们给大家收集了15个超强悍的圆盘时钟动画,很多都是基于CSS3,也有 ...
- OC语言编写:为视图添加丝滑的水波纹
先看一下最终效果图: 首先我们可以把如此丝滑的水波纹拆分一下下: 一条规律的曲线. 曲线匀速向右移动. 曲线下方的位置用颜色填充. 于是先来一条曲线吧. 对于需要产生波动如此规律的曲线,我们首先想到的 ...
随机推荐
- 分享知识-快乐自己:SpringBoot 使用注解API的方式定义启动端口号
在Spring Boot2.0以上配置嵌入式Servlet容器时EmbeddedServletContainerCustomizer类不存在,经网络查询发现被WebServerFactoryCusto ...
- 分享知识-快乐自己:Hibernate 中的 HQL 语句的实际应用
概要: Hibernate 支持三种查询方式: HQL查询.Criteria查询及原声 SQL (Native SQL)查询. HQL(Hibernate Query Language,Hiberna ...
- Win7系统中用anaconda配置tensorflow运行环境
前言:anaconda是一个python Data Science Platform.安装它的契机是因为要用tensorflow. 安装完后感觉用它来管理python运行环境还是挺方便的,常用的con ...
- ACM学习历程—HDU1719 Friend(数论)
Description Friend number are defined recursively as follows. (1) numbers 1 and 2 are friend number; ...
- BZOJ 1567: [JSOI2008]Blue Mary的战役地图
1567: [JSOI2008]Blue Mary的战役地图 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1011 Solved: 578[Sub ...
- HP SiteScope安装
下载地址以及安装方法见 http://www.jianshu.com/p/fce30e333578 数据库连接URL:jdbc:mysql://mysql_ip:mysql_port/database ...
- 读取web应用下的资源文件(例如properties)
package gz.itcast.b_resource; import java.io.IOException; import java.io.InputStream; import java.ut ...
- 【机器学习】支持向量机SVM
关于支持向量机SVM,这里也只是简单地作个要点梳理,尤其是要注意的是SVM的SMO优化算法.核函数的选择以及参数调整.在此不作过多阐述,单从应用层面来讲,重点在于如何使用libsvm,但对其原理算法要 ...
- HashMap为什么是线程不安全的
HashMap底层是一个Entry数组,当发生hash冲突的时候,hashmap是采用链表的方式来解决的,在对应的数组位置存放链表的头结点.对链表而言,新加入的节点会从头结点加入. 我们来分析一下多线 ...
- jquery中的$.ajax()的源码分析
针对获取到location.href的兼容代码: try { ajaxLocation = location.href; } catch( e ) { // Use the href attribut ...