[译]如丝般顺滑-使用css3实现60帧的动画

2018-12-03 15:58:55

原文: Smooth as Butter: Achieving 60 FPS Animations with CSS3

在移动端上实现动画很简单。

如果采取我们的建议的话,在移动端正确的实现动画也会比较容易。

虽然现在很多人在手机上运用CSS3动画,但许多人用的都不够恰当。很多应加以考虑的最佳实践常常被忽略,因为仍然有人不明白这些最佳实践的真正意义。

如今有这么多的设备规范,如果还不有针对性地优化你的代码,糟糕的用户体验将让你死无葬身之地。

记住:虽然市场上始终有一些高端的旗舰机在挑战性能极限,但你面对的仍将是和这些性能怪兽相比只是玩具一样的低端设备。

我们想帮助你正确地驾驭 CSS3。首先先要了解几件事。

理解时间轴

当渲染和处理HTML元素时,浏览器做了什么?这个非常简单的时间轴我们称之为 关键渲染路径

想要达到流畅的动画效果,我们需要关注修改属性会对 composite (合成)阶段造成怎样的影响。而不是去关注前面的其他阶段。

1. Styles

浏览器开始计算样式以应用在元素上——重新计算样式

2. Layout

接下来,浏览器会开始为每个元素生成用于布局的形状和位置信息。在该步骤浏览器会设置的页面属性包括 widthheight,还有margin,以及left/top/right/bottom 等。

3. Paint

在该步骤,浏览器开始用像素渲染填充每个元素,此时用到的属性有 box-shadown, border-radius,color, background-color 等。

4. Composite

这步就是你施展拳脚的地方了,浏览器开始在屏幕上渲染所有的元素。

现代浏览器可以使用transformopacity属性很好的实现四种动画。

  • 位置 —— transform: transformX(n) transformY(n) translateZ(n);
  • 缩放 —— transform: scale(n);
  • 旋转 —— rotate(ndeg);
  • 透明 —— opacity:n;

如何达到每秒60帧

想法有了,可以撸起袖子干活了。

首先从HTML开始,创建一个简单的结构,把类名为app-menu的元素放在一个类名为layout的元素中。

<div class="layout">
	<div class="app-menu"></div>
	<div class="header">
		<div class="menu-icon"></div>
	</div>
</div>

用错误的方法来实现这个效果

.app-menu {
	left: -300px;
	transition: left 300ms linear;
}

.app-menu-open .app-menu {
	left: 0px;
	transition: left 300ms linear;
}

看见我们这些我们修改的属性了吗?我们应该避免使用left/top/right/bottom这些属性作为动画。这些属性不能实现流畅的动画,因为他们会让浏览器每次都创建布局,而这会影响他们的子元素。

这样做的结果大概是这样的:

这个动画一点都不流畅。我们使用开发者工具的时间轴来看看发生了什么,结果如下:

这清楚地显示了FPS是不平整的并且表现也很慢。

绿色的条代表FPS,高的条表示动画渲染的帧数达到60FPS。低的条表示小于60帧。所以理想情况下,你想让绿色的长条贯穿整个时间轴。红色的条代表着糟糕的性能,所以另一方面评估程序的方法是消除这些红色的条。

使用 Transform

.app-menu {
	-webkit-transform: translateX(-100%);
			transform: translateX(-100%);
	transition: transform 300ms linear;
}
.app-menu-open .app-menu {
	-webkit-transform: none;
			transform: none;
	transition: transform 300ms linear;
}

transform属性影响Composite(合成)属性。这里我们告诉浏览器布局将被渲染并且已经准备好,当动画开始时。所以让渲染动画时卡顿更少。

时间轴的显示如下:

结果变的好点了,FPS更加规律了,因此动画更顺畅了。

使用GPU运行动画

让我们来上升一个等级,准备好让动画变得如丝般顺滑,我们开始使用GPU来渲染动画。

.app-menu {
	-webkit-transform: translateX(-100%);
			transform: translateX(-100%);
	transition: transform 300ms linear;
	will-change: transform;
}

虽然一些浏览器仍然需要 translateZ()translate3d() 作为备选方案,但will-change 被广泛支持已经是势不可挡了。它的功能是把元素提升到另一个层中,这样浏览器就不必关心布局渲染或者绘制了。

看见有多顺畅了吗? 时间轴目前是这样的:

动画的FPS更稳定了,渲染也更快了。但是有一帧仍然渲染得很久。在开始处还有一点点瓶颈。

还记得开始时创建的HTML结构吗?让我们来通过JavaScript来控制类名为app-menu的元素。

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");
	}
}
var oppMenu = document.querySelector(".menu-icon");
oppMenu.addEventListener("click", toggleClassMenu, false);

这儿的问题是给布局中的div元素增加了类名,这样使得浏览器多了一次重新计算样式的步骤,因此影响了渲染表现。

如丝般顺滑的60帧动画解决方案

如果我们在视窗外的区域创建menu元素呢?在脱离主区域的地方这么做,可以确保影响仅限于你想赋予动画的元素。

因此,我们打算使用下面的HTML结构:

<div class="menu">
	<div class="app-menu"></div>
</div>
<div class="layout">
	<div class="header">
		<div class="menu-icon"></div>
	</div>
</div>

现在我们可以以略微不同的方式来控制菜单的状态。我们可以通过使用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代码示例:

.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;
}

那么时间轴表现如何呢?

是不是如丝般顺滑呢?

翻译总结:

目前在给手机页面通过CSS3实现简单动画时,一般很少考虑流畅问题。因为在开发阶段使用的PC机性能都极高,很难主要到这一点。如我目前使用的Mac Pro(i7,16g)。即便使用文中提到的错误示例(第一种方案,通过left控制动画),通过审查工具分析时,其帧数依旧如诗般顺滑...,然而应该当考虑到实际的使用场景是移动端,且就目前而言,确实仍有一大批人在只用较为低端的安卓机。所以采用最佳实践很有必要,并且实际使用的动画会比菜单滑动要复杂。所以是否采取最佳实践方案在实际的设备中运行会出现较大差异。

Mac Photoshop 2018(Adobe全家桶)下载破解

请支持正版软件!!! 适用于Adobe所有软件的安装破解,以 Photoshop 2018 为例。根据电脑语言为中文/英文,安装完的软件自动为中文/英文。 1、下载安装 Adobe creative cloud 下载地址:https://www.adobe.com/creativecloud/desktop-app.html 可以理解为Adobe的下载器,可以使用这个下载器下载所有Adobe产品。 下载完成后,选择登陆/注册Adobe账户,然后你就可以看到如下页面: 而事实上,从官网介绍来看,我们需要的是这样的(Apps页面可下载Adobe的产品): 猜测可能是因为注册的账户是天朝或者所在地为天朝的原因,好多功能被屏蔽了,大天朝还真是一枝独秀,且看我手势 🖕 解决方法: macOS修改 Adobe creative cloud 的配置文件:false 改为 true: sudo vi /Library/Application

图床从七牛迁移至腾讯COS

前言(吐槽): 之前收到邮件,七牛要回收什么测试域名,想着没啥影响。因为我绑定了备案域名。最近突然发现博客好多图片挂了。一看我备案了快五年的域名被取消备案了???而且七牛直接把测试域名删了... 我当初注册的就是个人性质博客类网站,五年时间网站一直正常运行,且内容性质从未改变。空壳网站?备案信息不准确?真是睿智 🖕 概览 以下操作在macOS下进行。所有命令的文档请参考qshell命令列表 主要流程: 下载七牛中的所有资源,上传至腾讯COS。由于七牛的测试域名已失效,原空间的资源无法直接下载,所以先新建一个空间(新空间测试域名30天有效期),将旧空间的资源全部转移至新空间,然后从新空间下载所有资源。 一、七牛资源从就空间转移至新空间 1、下载七牛的命令行工具qshell 2、解压、重命名、赋予qshell运行权限 前往目录 cd /Users/ludis/Downloads/qshell-v2.3.4 && ll ➜ qshell-v2.3.4 ll