@fantaghiro
2015-06-04T07:47:44.000000Z
字数 8990
阅读 1728
翻译
图灵
js
动画
前言
在网络刚起步时,动画(animation)主要是在开发新手实在没有其他办法时才会使用,为的是强调页面上的重要部分以吸引人们的注意。即使他们想要让动画突破限制而发挥更大作用,也做不到:这是因为浏览器(以及电脑)的速度太慢,无法流畅地呈现基于网络的动画效果。
我们从闪烁的横幅广告、滚动的跑马灯新闻和flash介绍视频的旧时光一路走来,取得长足发展。时至今日,iOS以及Android中叹为观止的动态设计(motion design)不仅没有降低用户体验,反而使其巨幅提升。最优秀的网站和应用的开发人员利用动画来提升用户界面的感觉和直觉性。动画在设计开发中的重要性明显提升,这不仅仅是因为硬件的处理能力提高了,更体现了网路开发群体对于最佳实践有了更深的理解。现如今,人们普遍认为最终用户体验的质量比开发网站用什么工具更加重要。尽管这是个看似明显的结论,但事实却并非总是如此。
那么,究竟是什么偏偏让动画变得这么有用?不论是内容块之间的过渡、复杂加载次序的设计还是对用户下一步操作的提示,动画都是文字和布局的有效补充,强化了网站的预期行为、彰显了个性、丰富了视觉体验。你的内容究竟是要以友好的方式弹跳出现呢,还是要在屏幕上猛然闪过?这正是动态设计研究的问题。同时,你的选择将会决定应用的总体感觉。
当用户将你的应用推荐给他人的时候,他们经常会试着用“顺滑”或“精致”这样的字眼来形容。但他们没有意识到,他们描述的大多是界面上的动态设计。作为外行人,他们没法明确区分应用和应用的动态设计,而这正是优秀的用户界面(UI)设计师孜孜以求的效果:用动画来加强页面所要达到的目标,但同时又不分散用户的注意。
本书为你提供了一些必备的知识。掌握了它,你就可以自信地实现动画效果,同时不仅视觉上效果震撼而且技术上也易于维护。一方面要通过动态设计丰富页面体验;另一方面又要避免累赘的花哨。本书自始至终都努力在这两者之间达到平衡。
为什么所说的这些都如此重要?为什么值得花费时间去优化过渡和缓动效果?以上这些问题的答案也正是设计师花费几个小时优化字体和颜色的原因:只是因为使产品越来越完美,这种感觉棒极了。是他们让用户交头接耳、啧啧称赞:“哇,这简直太酷了,”然后马上转头对朋友叫道:“你可得看看这个!”
注意:如果你不熟悉基本的CSS属性,那就需要先抓来一本介绍HTML和CSS的书看看,然后再来读这一本。
第一章 JavaScript动画的优势
在本章中,我们将会对比CSS动画和JavaScript动画的优劣,同时介绍JavaScript动画的特点和工作流方面的优势。
简而言之,我们提供所需的背景知识,帮助你理解即将在本书中学到的JavaScript的任何知识。
JavaScript动画 vs. CSS动画
在网页开发业界有一种误解,那就是认为CSS动画是网络中唯一可以高性能实现动画的方法。这种误解使很多开发人员干脆放弃了用JavaScript实现动画,而这迫使他们必须:
- 要在样式表中管理有关用户界面(UI)互动的所有内容,很快使代码难以维护。
- 牺牲了实时的动画定时控制,而这只能通过JavaScript实现。(在移动应用中会看到需要响应用户拖拽操作的UI。在为这些UI设计动画时,必须使用定时控制。)
- 放弃基于物理的动态仿真设计,无法让页面上的元素好像真实世界的物体一样运动。
- 失去旧浏览器版本的支持,尽管陈旧的浏览器在世界范围内仍大量使用。
实际上基于JavaScript的动画与基于CSS的动画一样快。之所以人们错误地认为CSS动画在性能上有显著优势,那是因为人们通常拿它与jQuery的动画性能对比,后者确实非常慢。然而,另外一些彻底绕开jQuery的JavaScript动画库通过与页面的顺畅交互表现出非凡的性能。
注意:一个著名的动画库——也是本书自始至终使用的——就是Velocity.js。这是个轻量级的库,但是功能却异常丰富。另外,它与jQuery的动画语法类似,能够大幅降低学习曲线。
当然,CSS非常适合实现悬停状态的动画效果(例如:当鼠标位于链接上方时,链接变成蓝色)——这也是通常情况下基本的网页所包含的动画实现的程度。CSS过渡效果可以直接在已有的样式表中实现,使开发人员避免使用不需要的JavaScript库而让页面臃肿。另外,CSS动画不费吹灰之力就可以呈现上佳表现。
但是,本书将会说明为什么JavaScript对于动画来说经常是更好的选择,简单的悬停效果除外。
不要将JavaScript与jQuery混为一谈。
强大的性能
JavaScript与jQuery被错误地混为一谈。JavaScript动画很快,是jQuery让它慢了下来。尽管jQuery非常强大,但它并未被设计成高性能动画引擎。它没有避免“布局颠簸”的机制,这使浏览器在动画处理过程中,过分忙于布局处理的工作。
另外,由于jQuery的代码库除了实现动画以外还有许多其他目的,因此它的内存消耗会在浏览器内触发垃圾回收,导致动画在不可预知的情况下卡壳。最后,由于jQuery团队奉行一个崇高的追求,那就是帮助新手避免用坏代码毁了他们的UI,因此jQuery放弃了推荐做法,拒不使用requestAnimation方法——而浏览器竞相支持该方法,因为它可以为网络动画大幅提升帧率(frame rate)。
一些彻底绕开jQuery的JavaScript动画库通过与页面的顺畅交互表现出非凡的性能。其中之一是著名的动画库——也是本书自始至终使用的——Velocity.js。这是个轻量级的库,但是功能却异常丰富。另外,它与jQuery的动画语法类似,能够大幅降低学习曲线。
有关性能的问题,我们会在第七章“动画性能”深入探讨。通过学习浏览器渲染性能的微妙差异,你能够打下坚实的基础,为所有的浏览器和设备创建可靠的动画效果,不论它们各自的处理能力如何。
特性
追求速度当然不是使用JavaScript的唯一理由——它还具有一大堆其他同等重要的特性。让我们大致看几个JavaScript所独有的值得关注的动画特性。
页面滚动
页面滚动是基于JavaScript的动画最为流行的应用之一。最近,网页设计的趋势是创建很长的页面。随着页面向下滚动,让每一部分新的内容自动滚到可视区域中来。
例如Velocity这样的JavaScript动画库为滚动元素至可视区域提供了简单的方法:
$element.velocity(“scroll”, 1000);
以上代码通过使用Velocity的scroll命令让浏览器用1000毫秒的时间滚动至
动画反转
动画反转是还原某个元素前一个动画的使用快捷方式。通过触发反转命令,你会使一个元素运动至上一个动画开始之前的值。反转的常见用途是动态显示一个模态对话框后,在用户点击“关闭”后再将其隐藏。
未经优化的反转工作流要跟踪记录每一个元素上次运动的特定属性,以备后续反转动画之用。不幸的是,在UI代码中跟踪之前的动画状态很快变得笨重不堪。与之形成鲜明对比的是Velocity,通过reverse命令就可以帮你记住一切。
模仿Velocity的scroll命令,reverse命令通过将“reverse”作为Velocity的第一个参数传入即可:
// 第一个动画:将一个元素的opacity属性动态变化至0
$element.velocity({ opacity: 0 });
// 第二个动画:将该元素的opacity属性动态变回至原来的1
$element.velocity(“reverse”);
当谈到JavaScript的动画定时控制时,就不仅仅是反转那么简单了:JavaScript还允许你对全部正在运行的动画进行全局减速或加速。在第四章“动画工作流”中,你会学到这一强大特性的相关知识。
基于物理的仿真动画
将物理原理应用于动态设计,这反映了你的网站成就优秀的用户体验(UX)的核心原则:那就是伴随着用户的输入,界面出现自然的反应。换言之,界面设计尊重物体在真实世界中的运动规律。
例如在Velocity中有一个简单且强大的入门级物理仿真动画,即基于弹簧运动原理的缓动效果(easing)。(我们会在下一章详细探讨缓动效果。)在常规的缓动配置参数中,你传入的是一个已经定义好的缓动曲线(例如:”ease“或”easeInOutSine“)的字符串。但是在模拟弹簧运动的缓动动画中,接受一个含有两个值的数组作为参数。
//模仿500张力单位和20摩擦力单位下的弹簧运动,将元素的宽度运动至“500px”
$element.velocity({ width: “500px” }, { easing: [ 500, 20 ] });
在缓动参数的数组中,第一个值代表模拟弹簧的张力;第二个代表摩擦力。张力值越大,动画的速度和弹动幅度就越大。摩擦力越低,动画尾部振动的速度就越高。通过调整这些数值,你可以为页面上每一个动画实现独特的运动效果,有助于强化不同元素的不同行为。
易维护的工作流
设计动画是一个不断试验的过程,需要反复修改时间和缓动参数才能在页面上达到和谐统一的效果。然而无可避免的是,就当你已经将设计优化到最佳时,客户跳出来要求你大改。这种情况下,易于维护的代码就变得至关重要了。
基于JavaScript来解决这一工作流难题是非常优雅的,这将会在第四章“动画工作流”进行详细阐述。在这里先简要解释一下:有技术可以将一个个JavaScript动画链接起来——而且每个动画都有各自不同的持续时间和缓动效果等——这样其中一个动画的定时不会影响其他动画。这就意味着你不用重新计算,就可以把某个动画的持续时间改来改去,还能轻易将动画设置为同时进行或是顺次进行。
封装
当使用CSS设计动画时,你肯定要受限于CSS规范所提供的那些特性。但JavaScript是一门编程语言。由于编程语言的天然属性,第三方程序库可以对动态设计进行无限的逻辑控制。动画引擎利用这一点来提供强大的功能,不仅可以大幅优化工作流,还能拓展交互动态设计的可能性。而这正是本书的目的所在:用尽可能高效的方法设计优美的动画效果。
下一章解释了怎样使用本书选择的JavaScript动画引擎:Velocity.js。通过掌握Velocity.js,你将会了解如何使用我们前面已经介绍的一些特性以及其他更多内容。
第二章 使用Velocity.js制作动画
本章中,你会学到Velocity.js所提供的功能、命令和配置参数。如果你已经对基于jQuery的动画比较熟悉,那你就已经明白该如何使用Velocity了;它的方法与jQuery的$.animate()方法基本一模一样。
但不论你是否已具备相关知识,由于本章对Velocity进行了功能分解,你还将会了解到动画引擎行为的细微差异。掌握这些细节会帮助你从新手荣升为专家。即使你对JavaScript动画和Velocity都已经非常熟悉,那也浏览一下本章内容,就当帮自己个小忙。你肯定会发现一些始料未及的东西。
JavaScript动画库的种类
JavaScript动画库有很多种类。有一些是在浏览器中再现物理交互;有一些使WebGL和Canvas动画更易于维护;有一些专注SVG动画;还有一些旨在提升UI动画——本书的重点正是这最后一种。
有两个流行的UI动画库,它们是GSAP(请到GreenSock.com下载)和Velocity(请到VelocityJS.org下载)。在使用本书期间,你可以免费使用Velocity,因为它采用的是MIT许可证(GSAP要根据网站的商业形式收取许可费用)。此外,令Velocity引以为傲的是利用它惊人强大的功能,可以写出干净并出色的代码。很多流行网站都采用了这个引擎,例如Tumblr、Gap和Scribd。
噢对了,Velocity就是本书作者编出来的!
安装jQuery和Velocity
你可以在jQuery.com下载jQuery;在VelocityJS.org下载Velocity。要想将它们使用到页面上——就像其他任何JavaScript库一样——只需要在标签前面添加指向对应库的标签。如果你想链接至在线版本(而不是位于自己电脑上的本地副本),那么代码可能会像下面这样:
<html>
<head>My Page</head>
<body>
My content.
<script src=”//code.jquery.com/jquery-2.1.1.min.js”></script>
<script src=”//cdn.jsdelivr.net/velocity/1.1.0/velocity.min.js”></script>
</body>
</html>
当同时使用jQuery和Velocity时,将添加jQuery的script标签放到Velocity的前面。
就这样!现在你可以折腾起来了。
使用Velocity:基础知识
为了能让你入门,我们先从基本组成开始讲起:参数、属性、值和链式操作。既然jQeury是这样无处不在,来看一下Velocity与它的关系也挺重要。
Velocity和jQuery
Velocity的方法是独立于jQuery的,但两者可以结合使用。通常这么做的好处是可以利用jQuery的链式操作:当你先用jQuery选择了一个元素后,就可以用这个元素去调用.velocity()为它添加动画效果,例如:
// 将一个变量赋给一个jQuery元素对象
var $div = $(“div”);
// 使用Velocity为该元素添加动画
$div.velocity({ opacity: 0 });
☆ 该句语法与jQuery自有的动画语法相同: $div.animate({ opacity: 0 });
本书中所有例子都是Velocity结合jQuery使用的,因此也都使用该语法。
参数
Velocity接收多个参数。第一个参数是一个对象,用于将CSS属性映射到最终的期望数值。各个属性以及它们接受的数值类型与在CSS中使用的直接对应(如果你不熟悉基础的CSS属性,请先阅读一本介绍HTML和CSS的书,然后再来读本书):
// 使一个元素的width属性值运动至“500px”;opacity属性值运动至1。
$element.velocity({ width: “500px”, opacity: 1 });
小窍门:在JavaScript中,如果你提供的属性值包含字母(不仅仅是整数),那么将它们用半角引号括起来。
你可以把一个用来配置动画的对象作为第二个参数传入:
$element.velocity({ width: “500px”, opacity: 1 }, { duration: 400, easing: “swing” });
也有一种参数简写语法:那就是没有在第二个参数位置传入配置动画的对象,而是使用分号分隔参数的语法。这需要列举出动画的持续时间(接受一个整数值),缓动形式(一个字符串)和动画执行完毕后触发的回调函数(一个函数),以上这些参数可以以任何顺序用半角逗号分隔。(你马上就会学到这些配置参数都是做什么用的。)
// 动画持续1000毫秒(隐含使用默认的缓动参数“swing”)
$element.velocity({ top: 50 }, 1000);
// 动画持续1000毫秒,缓动参数为“ease-in-out”
$element.velocity({ top: 50 }, 1000, “ease-in-out”);
// 动画的缓动参数为“ease-out”(隐含使用默认持续时间400毫秒)
$element.velocity({ top: 50 }, “ease-out”);
// 动画持续时间1000毫秒,动画执行完毕触发回调函数
$element.velocity({ top: 50 }, 1000, function() { alert(“Complete.”) });
当你只需要配置基本参数时(持续时间、缓动参数、回调函数),简写语法是传入参数的快捷方式。如果除了以上三个基本参数外,还要配置其他参数,你就必须按照对象的语法来写左右配置参数。因此,如果你想要配置一个延迟参数,就需要更改下面的语法:
$element.velocity({ top: 50 }, 1000, “ease-in-out”);
改为:
// 重写上面动画的配置参数为对象形式,添加设置延迟时间为500毫秒
$element.velocity({ top: 50 }, { duration: 1000, easing: “ease-in-out”, delay: 500 });
你不能这么写:
// 错误:一部分参数用逗号分隔的语法;一部分用对象的语法
$element.velocity({ top: 50 }, 1000, { easing: “ease-in-out”, delay: 500 });
属性
基于CSS与基于JavaScript的属性动画有两点区别。
首先,不像在CSS里面,Velocity针对每一个CSS属性,只接受唯一一个数值。
因此,你可以这样传参数:
$element.velocity({ padding: 10 });
或者
$element.velocity({ paddingLeft: 10, paddingRight: 10 });
但不能写成这样:
// 错误:针对一个CSS属性传入了多个数值。
$element.velocity({ padding: “10 10 10 10” });
如果你想运动所有四个padding值(top、right、bottom和left),那么就作为单独属性一个个列举出来:
// 正确
$element.velocity({
paddingTop: 10,
paddingRight: 10,
paddingBottom: 10,
paddingLeft: 10
});
CSS有一些其他常见的多值属性,包括:margin、transform、text-shadow和box-shadow。
在实现动画效果时,将这些复合属性分拆成它们的子属性有助于加强对缓动值的控制。在CSS中,你可以为一个复合属性设置一个缓动值,那么举例来说,这个padding父属性下面的多个子属性就都会运动起来。在JavaScript中,你可以为每一个子属性设置缓动值——这么做的优势在本章后续讨论CSS的transform属性动画时就变得尤为明显了。
把各自独立的子属性都列举出来也会使你的动画代码更易读也更易维护。
基于CSS与基于JavaScript的属性动画第二个不同点在于:JavaScript的属性名称中,单词之间的连接号去掉了,除第一个单词外,其余单词都首字母大写。例如:padding-left变成了paddingLeft;background-color变成了backgroundColor。另外还要注意,JavaScript的属性名称不能用引号括起来:
// 正确
$element.velocity({ paddingLeft: 10 });
// 错误:使用了连接号,而且第二个单词首字母没有大写
$element.velocity({ padding-left: 10 });
// 错误:将JavaScript格式的属性名称用引号括起来了
$element.velocity({ “paddingLeft”: 10 });
值
Velocity支持px、em、rem、%、deg、vw和vh这些单位。如果你没有为数值提供单位,那么就会根据CSS属性类型自动指派一个单位给它。对于大多数属性,px是默认单位。但是,有些属性可能表示旋转的角度,例如rotateZ,Velocity会自动将deg单位指派给它:
$element.velocity({
top: 50, // 默认使用px单位
left: “50%”, // 手动设置了百分号
rotateZ: 25 // 默认使用deg单位
});
为所有属性值清晰指明单位会让你的代码更易读,因为当你快速浏览代码时,px单位与其他单位就更易区分。
Velocity胜过CSS的另一优势在于它四个值运算符:+、-、*和/,可以选择其中一个加到数值前面。这些运算符的作用与JavaScript中的数学运算符一致。你也可以将这些值运算符与等号(=)组合使用,来进行相应的数值运算。请参考下面的行内注释:
$element.velocity({
top: “50px”, // 没有加运算符,预计运动到50
left: “-50”, // 负号运算符,预计运动到-50
width: “+=5rem”, // Convert the current width value into its rem equivalent and add 5 more units.
height: “-10rem”, // Convert the current height value into its rem
p equivalent and subtract 10 units.
paddingLeft: “*=2” // Double the current paddingLeft value.
paddingRight: “/=2” // Divide the current paddingLeft value into two.
});
Velocity的简写特性——例如使用运算符——可以在动画引擎内完全保留动画逻辑。无需再进行手工运算,这使代码更加简洁。除此以外,通过告诉Velocity你打算如何运动元素,性能也得到提升。在Velocity里运行的逻辑越多,Velocity就能越好地优化代码,达到更高的帧率。
链式操作
当一个元素(或一系列元素)链式调用多个Velocity方法,它们就会自动排成队列。这意味着前一个动画一结束,后一个动画就马上开始。
$element
// 运动width和height属性
.velocity({ width: “100px”, height: “100px” })
// 当width和height属性运动完毕后,运动top属性
.velocity({ top: “50px” });