jQuery2-动画技术入门指南-全-
jQuery2 动画技术入门指南(全)
原文:
zh.annas-archive.org/md5/71BE345FA56C4A075E859338F3DCA6DA译者:飞龙
序言
jQuery 是一个跨浏览器的 JavaScript 库,旨在简化 HTML 的客户端脚本编写,并且是当今最流行的 JavaScript 库。利用 jQuery 提供的功能,开发人员能够创建动态网页。这本书将作为您在 Web 应用程序中创建动画和高级特效的资源,通过遵循其中易于理解的步骤。
jQuery 2.0 动画技术初学者指南 将使您能够掌握 jQuery 中的动画技术,以生成响应您访问者交互的流畅且具有吸引力的界面。您将学会使用 jQuery 创建引人入胜和有效的网页动画所需的一切。该书使用许多示例,并解释了如何使用简单的、逐步的初学者指南方法创建动画。
本书提供了各种示例,逐渐增强读者在使用 jQuery API 中创建令人惊叹的动画方面的知识和实践经验。该书首先解释了动画如何使您的用户界面具有交互性和吸引力。它解释了用于使正在动画化的元素出现或消失的各种方法。它提供了一套步骤,以创建简单的动画并显示淡入淡出的动画。
你可以之后学习如何通过链接不同的效果来制作复杂的动画,以及如何停止当前正在运行的动画。你将了解如何滑动动画元素,并学会创建复杂和专业化的自定义动画。
您将学习如何获取和设置 jQuery UI——jQuery 的官方用户界面库。本书将告诉您如何为页面的背景图像设置动画,并教您如何根据鼠标指针的移动使图像以特定方向和速度滚动。
本书涵盖的内容
第一章,入门,涵盖了包括下载 jQuery 和设置开发区域在内的基础知识,Web 上动画的简要历史,何时何地不要使用动画,动画如何增强界面以及 jQuery 提供的动画方法。还介绍了动画的基本示例。
第二章,图像动画,使用简单的方法创建图像幻灯片。然后我们在幻灯片中构建功能,留下一个值得您下一个开发项目使用的脚本。
第三章,背景动画,带领我们穿越创建动态背景图像和背景颜色的旅程,当用户向下滚动我们的网站时。这种非常细微的动画为网站增添了许多审美吸引力。
第四章,导航动画,介绍了在网站导航中添加动画的创造性方法。我们将网页背景颜色渐隐,并实现对页面上点击链接的平滑滚动。
第五章,表格和输入动画,专注于由我们的用户与表单交互触发的动画。我们将通过表单动画引导用户进行表单验证,并提供更好的整体体验。
第六章,使用 jQuery UI 扩展动画,介绍了 jQuery UI 增加的额外效果,这是建立在 jQuery 之上的官方 UI 库。我们会介绍其中的 14 种效果,以及库内置的缓动函数。
第七章,自定义动画,侧重于animate()方法,jQuery 提供此方法用于创建未预定义的自定义动画。这个非常强大的方法允许我们几乎可以动画化任何 CSS 样式属性,轻松创建复杂和吸引人的动画。
第八章,其他流行动画,介绍了网页上一些常见类型的动画,包括鼠标指针触发的近距离动画、动画标题以及现代版的走马灯元素。
第九章,CSS3 动画,介绍了如何使用 CSS3 基于最新的 CSS 变换创建吸引人的动画,以及如何使用 jQuery 使这个过程更加简单。
第十章,画布动画,展示了 HTML5 画布元素的用法,说明了如何在不使用 Flash 或其他专有技术的情况下创建令人惊叹的动画。书的结尾通过深入的示例介绍了如何只使用 HTML 和 JavaScript 创建交互式游戏。
本书的要求
要充分利用本书,你应该具备一定的前端开发知识,最好包括 JavaScript。有 jQuery 的经验也比较理想,但并非必需,因为书中涉及到的所有技术都会有详细讨论。
你需要一台能运行最新浏览器的计算机,最好有互联网连接。拥有一个代码编辑开发软件包将会有所帮助,但同样也不是必需的,只要你有某种文本编辑器即可。
本书适合谁
本书适合具有良好 HTML 和 CSS 知识的网页设计师和前端开发人员。虽然不是必需条件,但对 jQuery 或 JavaScript 的一些经验将会有所帮助。如果你想学习如何使用 jQuery 为你的 Web 应用程序添加用户界面动画,那么这本书就适合你。
习惯用法
在本书中,你会经常看到一些标题。
为了清晰地说明如何完成一个过程或任务,我们使用:
行动时间 - 标题
-
行动 1
-
行动 2
-
行动 3
指示通常需要一些额外的解释以使其有意义,因此它们后面跟着:
刚刚发生了什么?
这个标题解释了你刚刚完成的任务或指示的工作方式。
书中还包含其他一些学习辅助工具,包括:
突发提问时间 - 标题
这些是短的多项选择题,旨在帮助你测试自己的理解。
自我挑战时间 - 标题
这些实际挑战为你提供了尝试所学内容的想法。
你也会发现一些不同类型信息的文本样式。以下是一些示例和它们的含义解释。
文本中的代码示例如下:"使用 jQuery 执行最简单的动画的方法为fadeIn()和fadeOut()"
代码块设置如下:
$("#next").click(function(event) {
activeSlide++;
rotate();
event.preventDefault();
});
当我们希望引起你对代码块的特定部分的注意时,相关行或项目会以粗体显示:
$("#slider, #prev, #next").hover(function() {
clearInterval(timer);
pause = true;
}, function() {
timer = setInterval(rotate, speed);
pause = false;
});
新 术语 和 重要 词语 以粗体显示。屏幕上看到的文字,如菜单或对话框中的文字等,会出现在文本中,如:"在这种情况下,我们清除整个画布,移除飞船和任何幸存的外星人,并在画布中央打印文本 GAME OVER!"。
注意
警告或重要提示以如下方式出现在框中。
提示
贴士和技巧如下所示。
第一章:入门
欢迎来到 jQuery 2.0 动画技术初学者指南。在本书中,我们将研究 jQuery JavaScript 库中可用的每一种产生或控制动画的方法。我们将看到这些方法的使用方式,它们能接受的参数以及它们产生的不同行为。我们还将研究如何使用一系列配套资源,包括精选的 jQuery 插件和 jQuery UI 库。
在这个介绍性的章节中,我们将讨论以下主题:
-
网络动画简史
-
为什么动画您的 UI 很重要
-
jQuery 提供的动画方法
-
每个示例使用的模板文件
-
基本动画示例
网络动画
1989 年,CompuServe 发布了 GIF89a,这是流行的 GIF 图像格式的增强版本,它允许将一系列帧存储为单个图像,并由支持的软件播放。
GIF 格式在那些年已经在被称为互联网的东西上非常流行了(记住,万维网直到 1991 年才存在),因为它的文件大小小,无损压缩,并且得到了广泛的支持。增强版本使得任何人都可以使用支持的软件创建动画,很快就变得流行起来。
除了动画 GIF 之外,浏览器厂商还添加了对原生处理动画的专有 HTML 元素的支持,例如<blink>和<marquee>元素,它们为文本添加了不同的动画效果。
这两个元素都不是特别吸引人或成功的,W3C 以及领先的行业无障碍性和可用性专家建议在大多数情况下不要使用它们。当时的不同浏览器支持其中一个或另一个元素,但不能同时支持。这两个元素都是由各自的供应商作为原始浏览器战争的一部分添加的。
在 1990 年代末,流行的浏览器增加了对一种称为动态 HTML(DHTML)的技术的支持,该技术允许脚本语言在页面加载后修改页面的内容。DHTML 不是任何单一技术,而是一组技术(JavaScript,CSS,DOM 等),它们共同作用以实现基本的互动和/或动画。
实际上,DHTML 使得创建相当先进的动画成为可能,但是早期实现所需技术的限制,以及极其不同的浏览器支持使得 DHTML 变得棘手起来。
这个时代还见证了 Flash 的发布和崛起(以及 Shockwave,一种最终被 Macromedia 吞并的竞争技术),这是一种矢量和光栅图形格式,允许音频和视频流,逐帧动画,以及一系列其他功能。Flash 迅速成为流行,并且在撰写本文时仍然是基于网络的视频,基于浏览器的游戏和广告的首选格式。
浏览器中的 DOM 逐渐标准化(大部分),以及 JavaScript 库(如 jQuery)的兴起,这些库抽象化了浏览器之间仍然存在的差异,使得动画对比以往更多的人开放。如今很少使用 DHTML 这个术语,因为它与浏览器之间的支持不佳有关,但是许多交互式和动画网站的基本原理和技术仍然相似。
如今,除了 JavaScript 库可能实现的动画外,我们还有更加新颖和令人兴奋的可能性,比如 CSS3 和本机 HTML 元素(如 <canvas> 元素),后者提供了对页面区域的完全像素级控制。我们将在本书的后面更详细地介绍一些 CSS3 动画技术,以及 <canvas> 元素。基于 Flash 的动画首次在本世纪出现下降趋势,新技术正在地平线上崛起。
动画化用户界面的力量
现代操作系统不断地使用动画来吸引用户,创造更引人入胜的计算体验。在正确的使用方式下,动画为系统的用户提供帮助,引导他们完成不同的任务,提供上下文或反馈,并加强积极的行为。
一个很好的例子是在 Windows 7 或 OS X 中最小化应用程序的方式——应用程序似乎压缩到任务栏/停靠栏上的图标中,这向用户显示了他们想要返回到应用程序时应该去哪里。正是这样的简单细节最有效。
良好的动画可以赋予界面一种时尚的专业感,使其显得更先进或更现代。苹果的 iPhone(或 iPad)就是一个完美的例子——在操作系统及其应用程序中无缝地使用微妙的动画和过渡,使用户能够以一种深刻满意和沉浸式的方式与设备连接。任何出现或消失的内容都会平滑地淡入淡出,菜单和内容面板会从顶部或侧面滑入或滑出。突然的事件可能会使用户感到不安或分心,但是适时的动画可以帮助他们意识到即将发生的事情。
但是需要警告的是,执行不好、笨拙或过于无意义的动画可能会产生相反的效果,使您的界面显得基本、设计不佳或劣质。没有动画总比糟糕的动画好。即使您的应用程序运行完美,过多的动画也可能使用户感到沮丧,导致他们放弃使用您的应用程序或网站。
桌面电脑以及日益增长的移动和手持设备的计算能力已经足够强大,能够处理相当复杂的动画,并且随着集成硬件加速和更加精细的 CSS3 和 HTML5 进入最新的浏览器,网络上可以实现的可能性呈指数级增长。
何时使用动画
在以下情况下,动画可以留下深刻印象并增强用户体验:
-
当显示或隐藏窗口、弹出窗口和内容面板时
-
当某物被移动到窗口或页面的其他区域时
-
当用户的操作导致页面上某个内容发生了状态变化时
-
引导用户执行特定的行动或者引起他们对重要事项的注意
不适合使用动画的情况
在不必要的地方进行过多的动画可能会有害。在以下情况下,请尽量避免动画,或者至少认真考虑:
-
当用户需要非常频繁地重复某个操作时
-
已知使用该系统的设备可能无法充分显示动画的情况下
-
对于时间敏感的操作或过程
注意
请记住,这些只是指南,而不是必须始终遵守的法则,它们当然也不是绝对的。几乎没有任何情况下动画绝对不应该被使用,也几乎没有任何情况下动画一定要被使用。
使用您的判断力来确定动画是否适用于您的应用程序或页面及其预期的受众。如果可能的话,请让用户有机会根据自己的个人喜好启用或禁用动画。
动画检查表
在我们的页面或应用程序中实现动画之前,请考虑以下问题清单:
-
动画是否适用于您的目标用户?
-
动画是否实用?
-
动画是否增加了价值或者增强了用户体验?
-
设备上是否会以适当的速度运行动画,这些设备很可能会被使用?
如果您能回答以上所有问题都是肯定的,那么该动画可能是一个积极的特征。如果您对其中任何问题的回答是否定的,您可能需要停下来思考一下您试图通过添加动画来实现什么,以及是否可以以其他方式更好地实现它。
使用 jQuery 进行动画
jQuery (jquery.com) 在本地提供了一系列动画方法,无需使用额外的效果库或插件。然而,许多插件都是由在线社区贡献的,包括 jQuery UI (jqueryui.com),它是官方的 jQuery UI 库,扩展了 jQuery 的动画能力。本地,jQuery 提供了一些方法,只需最小的配置就能添加滑动和淡出效果,并且能够跨浏览器工作。它还提供了与管理动画队列相关的方法,并提供了一种创建几乎适用于所有数字 CSS 样式的自定义动画的方法。在本书的过程中,我们将详细介绍库中包含的每个动画方法。这些方法在此处列出,并附有各自的描述:
| 方法 | 描述 |
|---|---|
animate() |
它执行一组 CSS 属性的自定义动画。 |
clearQueue() |
它从队列中移除尚未运行的所有项。 |
delay() |
它设置一个计时器来延迟队列中后续项的执行。 |
dequeue() |
它执行匹配元素队列中的下一个函数。 |
fadeIn() |
它通过使匹配的元素逐渐变为不透明来显示它们。 |
fadeOut() |
它通过使匹配的元素逐渐变为透明来隐藏它们。 |
fadeTo() |
它调整匹配的元素的不透明度。 |
fadeToggle() |
它通过动画其不透明度来显示或隐藏匹配的元素。 |
finish() |
它停止当前正在运行的动画,移除所有排队的动画,并完成所有匹配元素的动画。 |
hide() |
它隐藏匹配的元素。 |
queue() |
它显示要在匹配的元素上执行的函数队列。 |
show() |
它显示匹配的元素。 |
slideDown() |
它以滑动动画显示匹配的元素。 |
slideToggle() |
它以滑动动画显示或隐藏匹配的元素。 |
slideUp() |
它以滑动动画隐藏匹配的元素。 |
stop() |
它停止匹配的元素上当前正在运行的动画。 |
toggle() |
它显示或隐藏匹配的元素。 |
需要注意的是,有两个属性可以更改全局 jQuery 对象。它们如下所示:
| 属性 | 描述 |
|---|---|
jQuery.fx.interval |
它是动画触发的速率(以毫秒为单位)。 |
jQuery.fx.off |
它全局禁用所有动画。 |
总的来说,它为我们提供了一个强大而稳健的环境,可以轻松添加几乎任何类型的动画。
动画也是插件的热门主题,有许多可用的插件,可以让我们即时使用各种不同类型的动画,只需进行最小的配置。我们将在本书的后面看到几个插件。
创建项目文件夹
所以,这就是我们将在整本书中引用和使用的模板文件。让我们也花点时间看看示例文件使用的文件夹结构。创建一个项目文件夹并将其命名为jquery-animation或类似的名称。在其中创建三个新文件夹并将它们命名为css、img和js。
我们创建的 HTML 页面将放在jquery-animation文件夹中的子文件夹旁边。我们创建的所有 CSS 文件将放在css文件夹中,我们在示例中使用的所有图像将放在img文件夹中。jQuery 库和我们使用或创建的任何其他脚本文件将放在js文件夹中。这也是您在下载和解压缩包含所有示例的附带代码存档时将找到的目录结构。
模板文件
在本书的整个课程中,我们将创建的每个示例文件都依赖于一组公共元素。与其在书中的每个代码部分和示例中反复显示相同的元素,不如在这里仅查看它们一次:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" href="css/.css">
</head>
<body>
<script src="img/jquery.js"></script>
<script>
$(function(){
});
</script>
</body>
</html>
小贴士
下载示例代码
您可以从您在 packtpub.com 购买的所有 Packt 图书的帐户中下载示例代码文件。如果您在其他地方购买了本书,您可以访问 packtpub.com/support 并注册,文件将直接通过电子邮件发送给您。
把这个文件保存到刚刚创建的jquery-animation文件夹中,并把它命名为template.html。这是我们将用于每个示例的基础文件,所以当我们开始逐个示例地工作时,我说将以下标记添加到 <body> 的模板文件中时,意思是将其直接插入到我们刚刚在前面的代码中创建的模板文件的第一个 <script> 标记之前的 <body> 标记之间。每当我们向模板文件中添加任何 JavaScript 时,它都将添加到第二个 <script> 标记中的匿名函数内。
让我们来看看模板文件包含了什么。我们从 HTML5 doctype 声明开始,因为我们将在示例中使用大量的 HTML5 元素。我们还将<html>元素的lang属性设置为en,以及<meta>标签的charset属性设置为utf-8,虽然这两者都不是严格必需的,但仍然是最佳实践。
接下来是一个空的<title>元素,我们可以在其中添加每个示例的名称,以及一个不完整的<link>元素的href,准备好添加每个示例将使用的样式表的名称。
由于Internet Explorer 9(IE9)之前的版本不支持任何 HTML5 元素,我们需要使用 Remy Sharp 的html5shiv脚本来正确使用 HTML5。我们可以链接到此文件的在线版本以便使用条件注释,该注释针对所有低于版本 9 的 IE 版本。如果您计划在没有互联网连接的情况下在 IE 中尝试示例,请随时下载html5.js并将其存储在本地。
为了充分利用本书中的示例,最好将浏览器升级至撰写本文时(Firefox 24, Chrome 30, Safari 6, and Opera 17)的最新稳定发布版本,尽管这些版本可能会迅速改变。
注
值得注意的是,jQuery 2.0 不支持oldIE,也就是 IE8 及以下版本。因此,我们不会为这些版本的 IE 提供任何浏览器兼容性修复。
如果您的项目需要与 IE8 或更早的浏览器兼容,您需要使用jQuery 1.10或更低版本。此外,如果您的项目使用 HTML5 元素并需要与 IE8 或更低版本兼容,则需要使用html5shiv (code.google.com/p/html5shiv)。
IE9 确实支持大量的 HTML5 和 CSS3,因此通常只有在 IE8 保持全球最常用浏览器的首位时才需要使用html5shiv文件。撰写本文时,根据 NetMarketShare 的数据,全球范围内 IE8 的市场份额为 21%(netmarketshare.com)。IE10 占据第二位,占有 19%,Chrome 29、FireFox 23 和 IE9 紧随其后。页面的<body>标签为空,除了一些<script>标签。我们显然会在每个示例中使用 jQuery,所以第一个标签链接到 jQuery。撰写本文时,当前版本的 jQuery 是 2.0(但与浏览器版本一样,这很可能会迅速改变)。
在本书中,我们将使用 jQuery 的本地版本,这样我们就不必依赖互联网连接或担心互联网速度。然而,在大多数情况下,在生产环境中,建议链接到 jQuery 的 4 个 CDN(内容分发网络)之一。这些可以在下面找到:
| CDN 主办 | URL |
|---|---|
| jQuery | code.jquery.com |
developers.google.com/speed/libraries/devguide?csw=1#jquery |
|
| Microsoft | asp.net/ajaxlibrary/cdn.ashx#jQuery_Releases_on_the_CDN_0 |
| CDNJS | cdnjs.com/libraries/jquery |
在第二个 <script> 标签中,我们有一个空函数,我们编写的所有示例 JavaScript 代码将放入其中。我们将 jQuery 对象传递到匿名函数中,并将其命名为 $ 字符。虽然这并非绝对必要(除非我们创建 jQuery 插件的示例),但这是一个很好的习惯。
一个基本的动画示例
让我们看一个基本的例子,这种动画可以帮助我们的访问者放心,显示一些事情正在发生。如果用户执行了一个动作,但结果并没有立即显示,提供反馈给用户,告诉他们的动作正在执行的过程中是一种有用的动画使用。
在下一个截图中,我们可以看到加载指示器位于“启动动作”按钮的正下方。它包含三个单独的加载条,顺序点亮,以显示有事发生。每个条形稍有不同的样式。
时机成熟 - 创建一个动画加载器
在这个示例中,我们将创建一个简单的动画加载指示器,当特定进程被启动时我们可以启动它,并在进程完成后停止它。
-
打开刚刚查看的模板文件,并将以下
<button>元素添加到<body>中(这应该放在<script>元素之前):<button id="go">启动动作</button> -
接下来,在页面底部第二个空函数中,添加以下代码:
var loader = $("<div></div>", { id: "loader" }).css("display", "none"); var bar = $("<span></span>").css("opacity", 0.2); var loadingInterval = null; for (var x = 0; x < 3; x++) { bar.clone().addClass("bar-" + x).appendTo(loader); } loader.insertAfter("#go"); function runLoader() { var firstBar = loader.children(":first"), secondBar = loader.children().eq(1), thirdBar = loader.children(":last"); firstBar.fadeTo("fast", 1, function () { firstBar.fadeTo("fast", 0.2, function () { secondBar.fadeTo("fast", 1, function () { secondBar.fadeTo("fast", 0.2, function () { thirdBar.fadeTo("fast", 1, function () { thirdBar.fadeTo("fast", 0.2); }); }); }); }); }); }; $("#go").click(function () { if (!$("#loader").is(":visible") ) { loader.show(); loadingInterval = setInterval(function () { runLoader(); }, 1200); } else { loader.hide(); clearInterval(loadingInterval); } }); -
将文件保存为
loading.html,放在主项目文件夹(jquery-animation)中。最后,我们需要在示例中添加一些基本样式。在文本编辑器中创建一个新文件,将以下代码添加到其中:#loader { margin:10px 0 0 36px; } #loader span { display:block; width:6px; float:left; margin-right:6px; border:1px solid #336633; position:relative; background-color:#ccffcc; } #loader .bar-0 { height:15px; bottom:-20px; } #loader .bar-1 { height:25px; bottom:-10px; } #loader .bar-2 { height:35px; margin-right:0; } -
将此文件保存在
css文件夹中为loading.css,并更新 HTML 文件以调用此样式表。 -
此时,当我们点击按钮后,你的代码应该看起来像以下截图一样:
![行动时间-创建动画加载器]()
刚才发生了什么?
在页面上硬编码的 <button> 用于显示和隐藏加载动画。这纯粹是为了这个例子。在实际的实现中,我们会在加载操作开始时显示加载动画,例如当新内容被添加到页面上时,并且在操作完成后再次隐藏它。
在外部函数内部我们做的第一件事是设置一些变量。我们创建了一个新的 <div> 元素作为加载器的容器,使用对象字面量作为匿名函数的第二个参数,给它赋予一个 id 为 loader。然后我们用 jQuery 的 css() 方法将其样式设置为 display:none,这样它就不会立即可见。
注意
对象字面量是一组由逗号分隔并用大括号括起来的成对值。
我们还创建了一个新的 <span> 元素,它将被用作创建三个单独的加载条的模板。我们使用 css() 方法将其不透明度设置为 0.2(20% 不透明)。jQuery 为我们标准化了这个样式,使其在 Internet Explorer 中正确工作。最后一个变量,loadingInterval,将用于存储一个间隔的 id,以便我们在需要时清除间隔。最初我们将其设置为 null,因为间隔还没有设置。
注意
间隔是一个数字值(以毫秒为单位),用于暂停或延迟一个操作。
一旦我们的变量被定义和初始化,我们就执行了一个简短的 for 循环,只有三次迭代。在这个循环中,我们克隆了我们创建的 span 元素,给它一个类名(以便每个条可以分别样式化),然后将它附加到容器中。一旦三个加载条被添加到容器中,我们就在 <button> 元素之后插入了加载器。
接下来,我们定义了一个名为 runLoader 的函数。这是将会被间隔重复调用的函数。该函数在按钮被点击之前不会运行。在这个函数内部,我们缓存了每个单独条形图的选择器,然后运行了一系列嵌套函数。
我们首先使用 fadeTo() jQuery 动画方法将第一个加载条的不透明度增加到完全不透明。此方法将一个字符串作为其第一个参数(以毫秒为单位表示动画的速度,或使用字符串 "fast" 或 "slow"),将元素应该淡出到的不透明度作为其第二个参数(值范围从 0 到 1,包括小数,如 0.50),并将回调函数作为第三个参数。回调函数在动画结束后立即执行。
在回调函数中,我们将第一个加载条的不透明度淡化为原始的0.2。我们为这个方法调用提供了另一个回调函数,在这个回调函数中,我们将第二个加载条的不透明度动画到完全不透明,然后再动画到原始的不透明度。同样的过程也用于第三个加载条。
最后,我们使用 jQuery 的click()方法添加两个函数,这两个函数将在每次点击按钮时交替执行。我们将使用if语句来检查我们的#loader元素是否在页面上可见,使用.is(":visible")并添加一个感叹号(!),以便如果#loader元素不可见,则返回 true。如果它不可见,我们将显示加载器,然后设置一个重复调用runLoader()函数的间隔。如果元素已经可见,我们隐藏加载器并清除间隔。
动手试试英雄 —— 扩展加载动画
我提到过,在进行请求并等待响应时,我们可以使用加载动画。试试在使用 jQuery 的 AJAX 方法时使用它,在发出请求之前显示加载器,一旦响应被处理,再次隐藏它。jQuery 网站上的 JSONP 示例(api.jquery.com/jQuery.getJSON),用来获取猫的图片,是一个很好的测试案例。根据您的连接速度,加载器可能不会显示很长时间。
快速测验 —— 使用 jQuery 进行基本动画
Q1. 考虑我们之前讨论的关于何时使用动画和何时不使用动画的问题,什么时候使用这个动画是合适的?
-
当浏览器进行密集的操作时
-
当有一个从服务器请求某些内容并且请求返回服务器的延迟,但浏览器需要处理的内容很少时
-
作为 Flash 动画的替代方案
-
当不支持动画 GIF 图像时
Q2. jQuery 的fadeTo()方法使用了哪些参数?
-
表示结束不透明度的整数
-
包含动画的配置选项的对象
-
第一个参数表示动画的速度或持续时间,目标元素的最终不透明度,可选的回调函数在动画结束时执行。
-
不需要参数
总结
在这个介绍性的章节中,我们简要介绍了 Web 动画的历史,包括它是如何开始的,早期的 HTML 元素和浏览器支持,Flash 的兴起,以及它在不太遥远的未来的发展方向。
我们还看到了动画如何在用户界面中用来增强用户体验。我们了解了一些关于何时应该使用动画和何时不应该使用动画的指导方针,并看了一些在实现动画时应该考虑的事项。
我们用一个加载动画的基本示例结束了本章。在这个例子中,我们使用了fadeTo() jQuery 方法来改变页面元素的不透明度,以及一个简单的间隔来播放动画。我们没有详细讨论这个方法,但我们看到了一个它的使用示例。在下一章中,我们将更详细地讨论这个方法,该章涵盖了 jQuery 提供的所有淡入淡出动画。
在下一章中,我们将专注于图片动画。我们将创建一个基本的图片旋转器,然后扩展该脚本的功能,以构建更多功能。我们将得到一个非常轻量级的图片旋转器,可以在未来的开发项目中使用。
第二章:图像动画
在本章中,我们将使用 jQuery 动画函数创建一个基本的图像轮换器(幻灯片)。我们还将扩展我们脚本的功能,以便在用户悬停在轮换器上时暂停动画。然后,我们将在脚本中添加上一页和下一页链接,以允许用户以自己的节奏滚动图像。最后,我们将添加分页链接,以便我们的用户可以翻页查看图像轮换器中的图像。
图像动画
在您学习 jQuery 的过程中,您会发现有时需要某种形式的内容或图像旋转。图像轮换器比直接在页面上显示图像更具视觉吸引力。它们还可以导致更紧凑和高效的设计,允许预加载内容或图像,并且还可以使我们能够控制用户何时以及何时看到。
注意
图像轮换器通常被称为幻灯片、滑块、滚动器或走马灯,根据其不同的功能。
在本章中,我们将讨论以下动画方法:
-
fadeIn() -
fadeOut() -
fadeToggle()
渐变动画
fadeIn() 和 fadeOut() 方法是通过 jQuery 实现的最简单的动画效果。它们只是简单地调整选定元素的不透明度,以显示或隐藏元素,并且可以在不需要额外配置的情况下使用。fadeToggle() 方法几乎同样简单,但确实提供了一些基本逻辑来检查选定元素的当前状态。
使用 display:none 隐藏的元素在 fadeIn() 动画开始时将尽可能设置为其正确的显示类型(对于块级元素为 display:block,对于内联元素为 display:inline)。重要的是要注意这一点,因为您的 CSS 样式可能会影响您要淡入的元素的外观。尽可能使用元素的自然显示类型,因此隐藏的 <li> 元素将设置为 display:list-item,隐藏的 <td> 元素将设置为 display:table-cell。
被设置为 display:block(或被设置为另一种显示类型但仍然在页面上可见)的元素将在 fadeOut() 动画结束时设置为 display:none。使用 fadeToggle() 方法时,元素将在其可见和不可见状态之间切换。
使用 fadeIn() 方法显示的元素必须最初使用 display:none 隐藏,而使用 visibility:hidden 等方式隐藏的元素在动画结束时将保持隐藏,因为淡入淡出方法专门修改 opacity 和 display 属性,而不是 visibility 属性。
在它们最简单的形式中,这些方法可以在不使用任何额外配置的情况下使用。我们可以简单地在任何一组选定的元素上调用这些方法,而不使用任何参数:
-
$(elements).fadeIn(); -
$(elements).fadeOut(); -
$(elements).fadeToggle();
当未提供参数时,动画将具有默认持续时间 400 毫秒和默认缓动swing。我们很快会讨论动画缓动。
用参数配置动画
带有参数的淡化方法可以采用以下形式(方括号表示可选参数):
$(elements).fadeIn([duration] [,easing] [,callback]);
$(elements).fadeOut([duration] [,easing] [,callback]);
$(elements).fadeToggle([duration] [,easing] [,callback]);
我们可以使用duration参数来控制动画的持续时间,指定整数毫秒或字符串"slow"和"fast"。这些字符串是 600 毫秒和 200 毫秒的快捷方式,分别。如果未指定,则默认给出的持续时间为 400。
我们还可以将duration参数设置为0,这将有效地禁用动画。我们不太可能需要这样做,因为根本不使用动画会更高效,但了解这一点是有用的。我应该指出,淡出仍将发生;只是会在0毫秒的持续时间内发生。这样做基本上与使用.hide()相同。
缓动参数可以从其默认值swing更改为linear,这会使动画在整个动画过程中以相同的速度进行。默认值swing会使动画开始缓慢,稍微加速,然后在动画结束时放慢速度。
提示
duration参数与动画运行的时间长度有关,而不是动画的速度。因此,较高的值将意味着较慢、较长的动画,而不是更快、更短的动画。使用插件可以大大增加缓动类型的数量。我们将在本书后面看到 jQuery UI 添加的额外缓动类型。
我们可以提供一个回调函数(可以是函数引用,也可以是匿名函数,后者更常见)。此回调函数将在选择集中的每个元素的动画结束后执行,因此如果有多个元素正在进行动画,可能会触发多次。
注意
回调函数是作为参数传递给另一个函数内部的函数。
下面的回调代码示例在动画完成后触发警报(回调部分加粗):
$(".selector").fadeOut("slow", function() { alert("callback triggered!"); });
为了可读性,您经常会看到前面的行像以下代码块一样格式化:
$(".selector").fadeOut("slow", function() {
alert("callback triggered!");
});
行动时间——设置标记和样式
首先,我们需要创建示例中将要使用的元素以及设置它们的视觉外观的样式。
-
使用我们在第一章中创建的模板文件创建一个新的 HTML 文档,在
<body>标签之间添加以下底层标记,用于我们的图像幻灯片演示:`<div class="container">` `<div id="slider">` `<img src="img/200?image=1">` `<img src="img/200?image=2">` `<img src="img/200?image=3">` `<img src="img/200?image=4">` `<img src="img/200?image=5">` `<img src="img/200?image=6">` `<img src="img/200?image=7">` `</div>` `</div>` -
将页面保存在
jquery-animation目录下,文件名为image-rotator.html。 -
我们还需要为这个示例添加样式表。在我们刚刚创建的 HTML 文件中,将
image-rotator添加到我们的占位符样式表链接中。 -
接下来,我们应该创建我们刚刚链接的样式表。在一个新文件中,添加以下代码:
`.container {` `position:relative;` `width:200px;` height:200px; } `#slider img {` `position:absolute;` `display:none;` `border-radius:3px;` `}` -
将此文件保存为
image-rotator.css,保存在我们项目文件夹中的css文件夹中。
发生了什么?
对于这个例子,我们将在我们的图像旋转器中使用七个图像。这可以根据我们的需求轻松更改,只需简单地将其他图像添加到<div id="slider">中即可。
我们将我们的#slider元素包裹在一个类名为container的<div>元素中,这样我们可以设置我们的图像旋转器的尺寸,以防我们的所有图像的宽度和高度不同。另外,我们将position:relative设置为#slider div,这样被设置为position:absolute的旋转器图像就不会从页面流中移除。
注意
当一个元素被设置为position:absolute时,该元素不再保持其所在的空间,这使得其他元素可以根据周围元素上使用的其他 CSS 在其后面或其前面。这与浮动元素的情况相似,当浮动元素被移出页面流时。
需要注意的是,在某些情况下,如果一个元素(或一组元素)被设置为position:absolute,而没有父元素被设置为position:relative,那么position:absolute元素可能会脱离其父元素,导致父元素崩溃。
这些图像被设置为position:absolute,因为它们需要在彼此之后堆叠,这样我们的图像旋转器元素在图像淡入淡出时不会跳动。这是必要的,因为所有的图像将占据页面上的同一相对位置。然而,我们只想要显示一张图像。使用display:none将关闭所有图像的可见性。这是必要的,这样我们就不必担心图像的堆叠顺序。我们希望我们的图像呈现良好,所以我们在图像上添加了一个小的border-radius来软化角落。
注意
堆叠顺序 指的是元素在页面上堆叠的顺序。如果一个元素在另一个元素之前加载,那么它将在后面的元素之前。可以通过在 CSS 中使用 z-index 和为元素添加 position 来修改堆叠顺序。
突然测验 —— 使用 fadeIn()
Q1. 作为 fadeIn() 方法的第一个参数,可以传递哪些字符串?
-
字符串
"short"或"long",它们指的是动画的持续时间。 -
字符串
"low"或"high",它们指的是元素淡出到的不透明度。 -
字符串
"slow"或"fast",它们指的是动画的持续时间。 -
一个十六进制字符串,指定了元素的
background-color。
Q2. 还可以传递什么到这个方法里?
-
一个字符串,指定了用于动画的缓动函数,以及在动画结束时执行的回调函数。
-
包含额外配置选项的对象。
-
一个包含额外配置选项的数组。
-
在动画开始时执行的回调函数,以及在动画结束时执行的回调函数。
编写图片轮播脚本
接下来,我们将通过添加 jQuery 代码为我们的图像旋转器添加最后的修饰。
行动时间 —— 编写图像旋转器
现在让我们添加脚本的代码,这些代码将为我们的图像添加动画效果。在 <body> 标签下面的匿名函数中添加以下代码:
var image = $("#slider img");
var numSlides = image.length;
var activeSlide = 0;
var speed = 2000;
var fade = 1000;
var timer = setInterval(rotate, speed);
image.eq(activeSlide).show();
function rotate() {
activeSlide++;
if (activeSlide == numSlides) {
activeSlide = 0;
}
image.not(activeSlide).fadeOut(fade);
image.eq(activeSlide).fadeIn(fade);
}
刚刚发生了什么?
我们做的第一件事是缓存对位于 #slider 元素内的所有 <img> 元素的引用。我们将多次引用它,因此只从 文档对象模型 (DOM) 中选择一次更有效率。出于性能考虑,通常最好尽量减少执行的 DOM 操作数量。
使用 length() 来计算图片数量。这会计算父元素 (#slider) 内的子元素 (<img>) 的数量。在我们的例子中,我们使用了七张图片。通过使用 length() 函数来计算 <img> 元素的数量,我们可以在不更改 jQuery 代码的情况下轻松地添加或移除图片来实现图片轮播。
我们将 activeSlide 变量设置为 0,以便从我们集合中的第一张图片开始。一般情况下,这是您不想更改的内容,除非您想要从特定的图片开始。只要我们的图片集合中至少有这个数量的图片,这个数字就可以更改为您喜欢的任何数字。
activeSlide变量表示我们刚刚选择的元素组内的位置。 length()函数返回元素的数量,从0开始。 在我们的示例中,image.length()将返回6,因此activeSlide可以为0至6,因为有七个<img>元素。 我们将activeSlide初始化为0,因此我们从序列中的第一个图像开始。 如果我们想要从不同的图像开始,初始化activeSlide为该组内的位置,记住,第一个位置是0而不是1。
要设置rotate()函数每次执行之间的时间,我们将speed变量设置为2000毫秒(2 秒)。 对于我们的示例来说,2 秒是一个很好的速度,但根据您旋转的图像而定,可能需要设置更长的持续时间。 如果您的图像上有您希望用户阅读的文本,应基于您认为用户舒适阅读所有文本需要多长时间来设置旋转速度。 如果您的图像中有高度细节,将速度设置为您认为可以充分欣赏所有细节的时间。 如果您有一个需要“呼吁行动”的可点击元素,那么这个时间将需要根据用户消化信息并采取您希望他们采取的行动所需要的时间来考虑。
我们的fade变量设置为1000(1 秒),因为这是一个不错的淡出图像的速度。 这可以根据您的需求进行更改,您会发现没有标准时间或速度。 您需要调整这些时间,以便为您的用户在网站上提供最佳体验。
setInterval()(原生 JavaScript 方法)函数在脚本中创建一个计时器,在此期间执行被调用的函数。 在我们的示例中,setInterval()将执行rotate()函数,但会等待直到经过speed变量指定的时间量再次调用它。 由于speed设置为2000,所以rotate()函数将每 2 秒执行一次。
提示
带有参数的,setInterval 事件可以采用以下形式:
setInterval(function, duration);
然后,我们告诉脚本使用show()来显示活动图像。 由于我们最初将activeSlide变量设置为0,所以我们设置的图像组中的第一个图像将首先显示。 这是必要的,因为如果您回忆一下,在我们的 CSS 中,我们使用display:none关闭了旋转器中所有图像的可见性。 如果更改了activeSlide变量的初始值,则在脚本启动时将显示该图像。
接下来,我们转向脚本的主要部分。对于我们的素食主义读者,无论你在饮食中吃什么蛋白质等效物,豆浆?豆腐?总之,rotate() 函数是我们在代码中普遍进行大量工作的地方。rotate() 函数上面的代码主要是设置我们的图像旋转器要使用的设置。在我们庞大的rotate()函数中,我们有一个变量(activeSlide),每次调用它时我们都会递增一次。这是为了在函数循环时将我们的活动图像设置为我们组中的下一个图像。
if语句用于在脚本到达所选组中最后一个<img>元素时重置activeSlide编号为0。
最后,我们有我们代码中最重要的两行(有人说的)。我们使用fadeOut()动画函数对所有非活动图像执行动画。然后,我们对等于activeSlide图像的图像使用fadeIn()。您会注意到我们的fadeOut()和fadeIn()动画中的 fade 变量。这决定了动画执行的速度。除了一些其他 jQuery 动画函数之外,"slow"和"fast"也可以使用—分别为 600 和 200 毫秒。
这是我们使用之前的代码创建的截图。您会注意到第一张图像在我们的下一张图像淡入时减弱。这种效果被称为交叉淡入。

课堂测验 – length() 和毫秒
Q1. length() 指的是什么?
-
变量的字符计数。
-
对象中的元素数量。
-
对象的宽度。
-
动画运行的时间量。
Q2. 1 秒中有多少毫秒?
-
10
-
100
-
1000
-
10000
行动时间 – 扩展悬停时暂停功能
当你的图像具有许多细节、用户需要阅读的文本或你希望他们采取的特定行动时,悬停暂停是必要的。即使你不需要这些东西中的任何一种,添加这个功能仍然是一个好主意,因为它允许用户在希望时好好查看图像。
以下截图说明了当用户在图片上悬停时图像旋转停止的情况:

为了检测我们何时悬停在图像旋转器上和离开,以便我们可以暂停图像旋转器,我们需要将以下代码添加到image.eq(activeSlide).show();下面的行:
$("#slider").hover(function() {
clearInterval(timer);
}, function() {
timer = setInterval(rotate, speed);
});
刚刚发生了什么?
我们添加了一个悬停事件,以便告知我们的脚本当我们悬停在#slider元素上以及当我们离开该元素时。我们在timer变量上使用clearInterval()(原生 JavaScript 方法)停止我们旋转器的计时器,有效暂停动画。
注意
更多关于悬停事件的信息可以在这里找到:api.jquery.com/hover/
需要注意的是,stop()和clearQueue()是停止动画或函数运行的其他方法。但是,在这个示例中,我们不想使用它们,因为它们会立即停止我们的动画。这意味着它会在动画进行到一半时暂停动画,并且会部分淡化显示当前活动和下一个活动的图像。或者,我们可以让间隔保持运行,并在rotate()函数中使用标志来确定是执行fadeIn()还是fadeOut()方法。
下一行告诉脚本我们不再悬停在其上,并且要恢复图像的动画。然后,使用setInterval(本机 JavaScript 方法)将计时器重置回最初设置的值。
执行操作的时机 - 扩展上一个和下一个链接功能
为了让用户更好地控制旋转图像的速度,我们将按照以下步骤添加上一个和下一个链接:
-
我们需要添加用于上一个和下一个链接的锚标签。为此,请在最后两个
</div>标签之间添加以下代码:<a id="prev">prev</a> <a id="next">next</a> -
我们的下一个和上一个链接需要一些基本的样式,所以让我们在我们的
image-rotator.css文件的底部添加以下 CSS 行:#prev, #next { position:absolute; bottom:10px; padding:5px 10px; color:#000; background:#FFF; border-radius:3px; text-decoration:none; opacity:0.7; } #prev:hover, #next:hover { opacity:1; cursor:pointer; } #prev {left:10px;} #next {right:10px;} -
为了处理下一个和上一个链接上的点击事件,我们需要在
rotate()函数的上面添加以下代码:$("#prev").click(function(event) { activeSlide--; rotate(); event.preventDefault(); }); $("#next").click(function(event) { activeSlide++; rotate(); event.preventDefault(); }); -
在
image.not(activeSlide).fadeOut(fade);上面添加以下代码行:if (activeSlide < 0) { activeSlide = numSlides - 1; } -
通过以下代码替换
rotate()函数来更新它:if (!pause == true) { activeSlide++; } -
查找
hover()函数并用以下代码替换它(新代码已经突出显示):$("#slider, #prev, #next").hover(function() { clearInterval(timer); pause = true; }, function() { timer = setInterval(rotate, speed); pause = false; });
以下截图显示,在点击下一个链接后,我们的图像旋转器移动到下一个图像:

刚刚发生了什么?
在第三步中,我们为上一个和下一个链接添加了两个单击函数。在上一个函数中,我们将活动图像编号减一,在下一个函数中,我们将其加一。然后我们需要再次调用 rotate 函数,以便我们的旧图像淡出,新图像淡入。我们使用 preventDefault()(本地 JavaScript 方法)使得上一个和下一个链接不会向我们地址栏中的 URL 添加一个井号(#)。这样可以防止上一个和下一个链接像传统锚点标签一样工作。
第四步允许我们在我们的图像集中向后移动。这个 if 语句类似于我们已经在 rotate() 函数中使用的 if 语句,用于在活动变量等于我们旋转器中的图像数时重置它。
我们需要更改 rotate() 函数,以便仅在我们的图像旋转器未悬停在其上时才递增 active 图像变量。为此,我们用一个 if 语句替换了递增 activeSlide 变量的行。使用此 if 语句,我们告诉脚本仅在用户未悬停在图像旋转器上时才允许 activeSlide 变量递增。
我们需要在暂停悬停功能中添加下一个和上一个链接,这样当您悬停在上面时,图像旋转也会暂停。这可以通过在 #slider 后面添加逗号,并添加我们的 #next 和 #previous ID 选择器来实现。我们将 pause 变量设置为基于我们是否触发了 hover 事件的布尔值 true 或 false。这是告诉 rotate 函数仅在我们没有悬停在其上时才递增 activeSlide 变量所需的。为了告诉我们的脚本我们正在悬停在其上,我们将变量 pause 设置为值 true。然后一旦我们的光标离开图像旋转器,我们就将其设置为 false。
快速测验 – preventDefault() 和 setInterval()
Q1. preventDefault() 用于什么?
-
防止脚本在函数中默认变量。
-
防止事件的默认操作被使用时。
-
在其所在的函数中关闭所有 JavaScript 错误。
-
关闭返回空值的变量的 JavaScript 错误。
Q2. setInterval() 方法需要使用的两个参数是什么?
-
speed和time -
function和duration -
duration和speed -
speed和function
行动时间 – 扩展分页功能
为了让我们的用户更多地控制我们的图像旋转器,我们将添加所谓的分页。分页允许您直接移动到我们旋转器中的特定图像,而不必点击下一个和上一个链接,直到找到所需的图像。如果我们的图像旋转器中有大量图像,此功能非常有用。为了添加分页,我们执行以下步骤:
让我们首先将以下代码添加到 image-rotator.css 中:
#pagination {
position:absolute;
top:10px;
width:100%;
text-align:center;
}
#pagination a {
padding:2px 5px;
color:#000;
background:#FFF;
border-radius:3px;
text-decoration:none;
opacity:0.7;
}
#pagination a:hover {
opacity:1;
cursor:pointer;
}
-
在
image-rotator.html中,在var pause;下方直接添加以下行:var paging = ""; -
在我们的 HTML 中,在
<a id="next" href="#">next</a>下面添加以下代码:<div id="pagination"></div> -
在
image.eq(activeSlide).show();下面放置以下代码:for (var page = 0; page < numSlides; page++) { paging += "<a rel=\"" + (page + 1) + "\">" + (page + 1) + "</a>\n"; } $("#pagination").html(paging); -
找到下面的
hover事件,并用以下代码替换(新代码已突出显示):$("#slider, #prev, #next, #pagination").hover(function() { clearInterval(timer); pause = true; }, function() { timer = setInterval(rotate, speed); pause = false; }); -
在我们的
rotate()函数上方直接添加以下代码:$("#pagination a").click(function(event) { event.preventDefault(); activeSlide = $(this).attr("rel") - 1; rotate(); });
以下截图说明了我们添加的分页功能,以及在点击相应链接后显示的第四张图像:

刚刚发生了什么?
我们做的第一件事是声明并设置新的分页变量。如果没有这个变量,我们的代码将会出现严重的 JavaScript 错误。
使用 for 循环,我们定义了我们的 page 变量,告诉它继续循环直到 page 小于我们集合中的图像数量,然后使用 ++ 递增这个新定义的变量。下一行是到目前为止我们脚本中最复杂的代码,所以请跟着我走!一个变量后面跟着 += 告诉变量使用已经存储的内容,并继续添加到末尾。这种将值或字符串串在一起的方法称为 串联。
接下来我们需要构建分页链接的 HTML 结构。我们正在构建一系列七个 <a> 标签,每个标签对应我们组中的一个图像。为了在链接上打印图像编号,我们将使用 (page + 1)。我们使用 + 1 是因为 JavaScript 以所谓的 零索引 或 基于零的编号 对事物进行编号,这意味着在对组或列表中的项目进行编号时,它不是从 1 开始,而是从 0 开始。直到现在这没有成为问题(因为我们没有打印出值),但现在我们需要告诉我们的脚本从 1 开始,以便正确显示分页链接。for 循环的最后一行用 html() 替换 #pagination 的内容,并用 paging 变量内部存储的值替换它。
注
html() 方法用于获取或设置所选元素的 HTML 内容。
再次,我们需要扩展我们的鼠标悬停暂停功能,以便在悬停在新的 #pagination 元素上时知道要暂停。如果不这样做,当您悬停在 #pagination div 上时,图像会继续旋转。
我们添加了另一个点击函数($("#pagination a").click)来处理我们的分页链接。您会注意到我们之前使用的preventDefault()以确保在点击分页链接时不会向页面 URL 中添加井号(#)。下一行将activeSlide变量设置为分页锚点标签中rel的值,然后减去一。这样做是因为我们将其设置为增加一,以抵消我们在第三步中看到的零索引问题。
最后,我们添加了包含所有分页链接的 <div> 元素。
尝试进一步扩展图片轮播器
在此示例中,我们使用 fadeIn() 和 fadeOut() 来轮换我们的图像。尝试扩展示例,使脚本能够检测应该进行动画处理的子元素。
扩展图片轮播的其他想法:
-
让脚本动态设置子元素的尺寸,使脚本能够按比例缩放以适应内容。
-
构建能够同时显示多个元素的功能
-
为分页栏中的当前活动链接提供不同的外观,以便我们的用户知道轮播器当前显示的是哪张图像。
-
添加额外的过渡效果(例如,滑动)。
小测验 - 更改变量和零索引
Q1. 在变量后使用 ++ 是什么意思?
-
合并两个变量的值。
-
告诉脚本只允许向变量添加,而不允许减去。
-
将变量增加一个。
-
将变量增加两个。
Q2. 零索引是什么意思?
-
JavaScript 是从零开始计数的。
-
如果未明确定义,变量的默认值为零。
-
将元素的索引设置为零的方法。
-
在使用后将变量的值设置为零的方法。
摘要
在本章中,我们看了一些 jQuery 最基本的动画方法。淡入淡出方法是 jQuery 中最简单的动画方法,只会动画选定元素的不透明度。
show()、hide() 和 toggle() 方法也可用于执行动画,但会改变元素的尺寸以及不透明度。所有这些方法都简单易用,几乎不需要额外的配置即可运行。
在下一章中,我们将学习如何操纵元素的背景属性以创建背景动画。
第三章:背景动画
在上一章中,我们使用fadeIn()和fadeOut()方法来对图像元素进行动画处理。在本章中,我们将使用animate()效果来对背景颜色进行动画处理,并学习如何对元素内部的背景图像的位置进行动画处理。在第七章,Custom Animation中,我们将更深入地了解animate()方法所能做的一切。
背景颜色动画
对元素的背景颜色进行动画处理是吸引用户眼球到我们想让他们看到的对象的绝佳方法。对元素的背景颜色进行动画处理的另一个用途是显示发生了某些变化。如果对象的状态发生了变化(添加、移动、删除等),或者需要关注以解决问题,通常会以这种方式使用动画。我们将在接下来的两章中了解到其中的一些内容。
注意
由于 jQuery 2.0 不支持背景颜色动画,我们将使用 jQuery UI 来为我们提供所需功能以创建此效果。
我们将在第六章中详细介绍 jQuery UI 给予我们的能力,使用 jQuery UI 扩展动画。
介绍动画方法
animate()方法是 jQuery 在动画领域提供的最有用的方法之一。借助它,我们能够做一些事情,比如将元素移到页面上的其他位置,或者改变并动画处理颜色、背景、文本、字体、框模型、位置、显示、列表、表格、生成内容等属性。
行动时间 - 对 body 背景颜色进行动画处理
按照下面的步骤,我们将从创建一个示例开始,该示例更改body的背景颜色。
-
首先创建一个名为
background-color.html的新文件(使用我们的模板)并将其保存在jquery-animation文件夹中。 -
接下来,我们需要通过在 jQuery 库下面直接添加这行来包含 jQuery UI 库:
<script src="img/jquery-ui.min.js"></script>注意
jQuery UI 的自定义或稳定版本可从
jqueryui.com下载,或者您可以使用下面三个内容传送网络(CDN)之一链接到库。要快速访问库,转到jqueryui.com,滚动到最底部,找到快速访问部分。在这里使用 jQuery UI 库 JS 文件将完全符合我们本章示例的需求。媒体模板:
code.jquery.com谷歌:
developers.google.com/speed/libraries/devguide#jquery-ui微软:
asp.net/ajaxlibrary/cdn.ashx#jQuery_Releases_on_the_CDN_0CDNJS:
cdnjs.com/libraries/jquery -
然后,我们将以下 jQuery 代码添加到匿名函数中:
var speed = 1500; `$( "body" ).animate({ backgroundColor: "#D68A85" }, speed);` $( "body" ).animate({ backgroundColor: "#E7912D" }, speed); `$( "body" ).animate({ backgroundColor: "#CECC33" }, speed);` `$( "body" ).animate({ backgroundColor: "#6FCD94" }, speed);` `$( "body" ).animate({ backgroundColor: "#3AB6F1" }, speed); `$( "body" ).animate({ backgroundColor: "#8684D8" }, speed);` `$( "body" ).animate({ backgroundColor: "#DD67AE" }, speed);`
刚才发生了什么?
首先,我们向页面添加了 jQuery UI 库。这是必需的,因为当前版本的 jQuery 不支持动画显示背景颜色。接下来,我们添加了将动画显示背景的代码。然后,我们将speed变量设置为1500(毫秒),以便我们可以控制动画的持续时间。最后,使用animate()方法,我们设置了 body 元素的背景颜色,并将持续时间设置为我们上面命名为speed的变量。我们多次复制了相同的行,只改变了背景颜色的十六进制值。
以下截图是整个 body 背景颜色动画经过的颜色示意图:

链接在一起的 jQuery 方法
需要注意的是,jQuery 方法(在这种情况下为animate())可以链接在一起。如果我们将animate()方法链接在一起,我们之前提到的代码将如下所示:
`$("body")`
.animate({ backgroundColor: "#D68A85" }, speed) //红色
.animate({ backgroundColor: "#E7912D" }, speed) //橙色
.animate({ backgroundColor: "#CECC33" }, speed) //黄色
.animate({ backgroundColor: "#6FCD94" }, speed) //绿色
.animate({ backgroundColor: "#3AB6F1" }, speed) //蓝色
.animate({ backgroundColor: "#8684D8" }, speed) //紫色
.animate({ backgroundColor: "#DD67AE" }, speed); //粉色
这里是链接方法的另一个示例:
$(selector).animate(properties).animate(properties).animate(properties);
尝试一下吧,英雄- 用循环扩展我们的脚本
在这个示例中,我们使用了animate()方法,并借助 jQuery UI 的帮助,我们能够动画显示页面的整个背景颜色。试着扩展脚本以使用循环,这样一旦脚本到达函数的末尾,颜色就会持续动画而不会停止。
突发小测验 - 用 animate() 方法进行链接
Q1. 哪个代码能够正确地使用链接从红色渐变到蓝色?
-
`$("body")` .animate({ background: "red" }, "fast") .animate({ background: "blue" }, "fast"); -
`$("body")` .animate({ background-color: "red" }, "slow") .animate({ background-color: "blue" }, "slow"); -
`$("body")` .animate({ backgroundColor: "red" }) .animate({ backgroundColor: "blue" }); -
`$("body")` .animate({ backgroundColor, "red" }, "slow") .animate({ backgroundColor, "blue" }, "slow");
视差的深度 illision
在计算机图形学的背景下,特别是在视频游戏中,术语视差指的是使用多个背景层,以稍微不同的速度滚动,以创建深度 illision 的技术。尽管在现代游戏中不如以前那样广泛应用,因为有了更丰富的 3D 图形引擎,但视差仍然经常在便携式游戏设备上看到,并且越来越多地出现在 Web 上。
使用纯 CSS 可以实现视差效果,正如在 Silverback 站点上演示得很好一样(请查看silverbackapp.com获取效果,以及blog.teamtreehouse.com/how-to-recreate-silverbacks-parallax-effect获取有关如何实现的详细信息)。当窗口水平调整大小时,视差的这种应用只有在窗口调整大小时才会显现出来。虽然这在窗口调整大小时是一个很棒的效果,但如果我们希望效果更加突出,这并不能帮助我们。
行动时间 - 创建舞台并添加样式
该底层页面仅需要四个元素(对于此简单示例),这些元素位于页面的<body>中。
-
将以下结构的元素添加到模板文件的新副本中,在第一个
<script>标签之间:<div id="背景"></div> <div id="中景"></div> <div id="前景"></div> <div id="地面"></div> -
将此页面保存为
parallax-horizontal.html,放在我们的jquery-animation文件夹中。 -
此示例中的 CSS 和底层 HTML 一样简单。将以下代码添加到文本编辑器中的新文件中:
div { width:100%; height:1000px; position:absolute; left:0; top:0; } #背景 { background:url(../images/background.png) repeat-x 0 0; } #中景 { background:url(../images/midground.png) repeat-x 0 0; } #前景 { background:url(../images/foreground.png) repeat-x 0 0; } #舞台 { background:url(../images/ground.png) repeat-x 0 100%; } -
将此文件保存为
parallax-horizontal.css,放在css目录中,并更新我们刚刚创建的 HTML 文件,以链接到此文件。 -
此时,页面应如下截图所示:

前景区域是地面,前景层是较暗的灌木丛,中景是较浅的灌木丛,背景是天空和云。
刚才发生了什么?
您还会在此书附带的代码下载的 images 文件夹中找到此示例的图像。我们为每个要成为视差效果一部分的元素准备了单独的图像,在此示例中有三个,一个用于背景,一个用于中景,一个用于前景。
底层 HTML 也非常简单。我们只需要为背景的每一层添加一个单独的<div>。在 CSS 中,每个图像层都被绝对定位,这样它们就能重叠在一起。现在,让我们让视差的层移动起来吧!
时间行动 - 动画背景位置
现在轮到 <script> 本身了。在 HTML 文件底部,像往常一样,在空匿名函数中添加以下代码:
var bg = $("#background");
var mg = $("#midground");
var fg = $("#foreground");
$(document).keydown(function(e) {
if (e.which === 39) { //右箭头键
bg.animate({ backgroundPosition: "-=1px" }, 0, "linear" );
mg.animate({ backgroundPosition: "-=10px" }, 0, "linear" );
fg.animate({ backgroundPosition: "-=20px" }, 0, "linear" );
}
});
如果我们现在在浏览器中运行这个页面,我们应该会发现当我们按住右箭头键时,不同的背景图像切片以相对较慢的速度移动,前景几乎匆匆而过,而背景则悠闲地移动。
刚才发生了什么?
在脚本中,我们首先缓存了将要使用的选择器,这样我们就不必在每次background-position变化时都创建一个新的 jQuery 对象并从 DOM 中选择元素,而这将非常频繁。然后,我们在 document 对象上设置了一个keydown事件监听器。在作为事件处理程序的匿名函数中,我们检查事件对象的which属性提供的键码是否等于39,这是右箭头键返回的键码。
然后我们在backgroundPosition上调用animate(),并为每一层提供-=1px、-=10px和-=20px的相对值,以逐渐加快速度,从而产生视差效果。这些动画同时进行,持续时间设置为零(0)毫秒,并且采用linear缓动。这是我们的keydown处理程序需要做的最后一件事情。
试试看 - 扩展视差
在这个示例中,背景只从右向左进行动画。扩展示例,使左向右和右向左的运动都可用。需要帮助开始吗?您需要为左箭头键创建另一个函数,并递增backgroundPostion值,而不是像我们在示例中所做的那样递减。
自动化的背景动画
在这个示例中,我们将使背景图像在页面上自动上移,而无需我们的用户特别交互。
时间行动 - 创建自动化的背景动画
我们将创建一个示例,现在将自动地对背景图像进行动画。
-
使用我们的模板创建一个名为
background-auto.html的新文件,并将其保存在我们的jquery-animation目录中。 -
由于我们的示例只有一行 CSS,我们不打算创建样式表。我们将其放在我们刚刚创建的文件(
background-auto.html)下的<title>标签下:<style> body {background:url(images/background.jpg) top center fixed;} </style> -
接下来,我们将删除样式表
<link>,因为我们不会在此示例中使用它。这将是我们刚刚添加的代码之后的一行。 -
最后,将以下代码添加到我们等待的匿名函数中:
var yPos = 0; var timer = setInterval(start, 50); function start() { yPos = yPos - 5; $('body').css({ backgroundPosition: '50% ' + yPos + 'px' }); }
以下是我们刚刚创建的屏幕截图。您会注意到,在查看示例时,背景图像从底部向上方向动画。

刚刚发生了什么?
我们做的第一件事是将我们的变量 yPos 声明为整数。做到这一点,你可能知道,会吓跑任何在 Internet Explorer 和类似的非现代浏览器版本中出现的可怕的 JavaScript 错误。
接下来,我们使用 setInterval() 声明了我们的 timer 变量。在上一章中,我们学习了这个方法的参数是 function 和 duration。我们的函数名是 start,所以我们将 function 参数设置为那个。我们还将我们的 duration 设置为 50(毫秒),因为这是我们的函数在再次执行之前等待的合适时间框架。
然后,我们创建了一个可以由我们的计时器调用的函数,名为 start。我们每次函数执行时,都将 yPos 的当前值减去五。我们函数的最后一行是做所有繁重工作的地方。这一行每次我们的脚本中的函数来到这一行时,都会垂直地将 <body> 背景图像的位置向上移动五个像素。
尝试吧,英雄 - 在引擎盖下玩耍
尝试更改 timer 持续时间和 yPos 偏移值,看看这些值如何影响我们的背景动画的速度和帧率。另一个挑战是尝试使背景水平动画,而不是垂直动画,就像我们为此示例所做的那样。
让我们使它变成斜线!
现在,我们不再垂直地使背景图像动画,而是斜向地进行动画。抓住你们的编程帽子!
行动时间 - 对背景进行斜向动画
现在我们要让我们的动画斜向移动。
-
让我们使用与之前相同的文件(
background-auto.html),并使用以下代码替换我们匿名函数中的代码(新代码已突出显示):var xPos = 0; var yPos = 0; var timer = setInterval(start, 50); function start() { xPos = xPos - 5; yPos = yPos - 5; $('body').css({ backgroundPosition: xPos + 'px ' + yPos + 'px' }); } -
将此文件保存为
background-auto-diagonal.html,并在您的网络浏览器中查看。预览动画应该是这样的:
![行动时间 - 对背景进行对角线动画]()
刚刚发生了什么?
使用相同的代码,我们对其进行了一些升级,以便能够动画化背景位置的 X 坐标以及 Y 坐标。添加了变量xPos来控制左右水平位置,并且还将其添加到backgroundPostion行中。
试试看看
在我们之前提到的示例中,我们使背景图像向西北方向动画化。尝试使背景动画向东北、东南和西南移动。还尝试使用不同的xPos和yPos偏移值,以查看它如何影响背景图像的动画方向。
页面元素的视差背景
我们的下一个示例将向您展示如何根据窗口滚动的交互来动画化元素的背景位置。根据您的浏览器中平滑滚动的外观以及您鼠标上的平滑滚轮的外观,可能很难看到此动画效果。如果您看不到平滑的滚动效果,只需抓住浏览器上的滚动条并缓慢上下移动,即可更清楚地看到效果。您会注意到,背景位置的移动速度比页面上的元素慢。
行动时间 - 设置标记和样式
要开始,我们需要向新文档添加必要的 HTML 和 CSS。
-
使用与之前相同的模板创建一个新的 HTML 页面,并将以下代码插入
<body>中:<div class="row row1"> <img src="img/image1.png"> </div> <div class="row row2"> <img src="img/image2.png"> </div> <div class="row row3"> <img src="img/image3.png"> </div> -
将页面保存在
jquery-animation目录中,命名为parallax-vertical.html。 -
接下来,我们应该创建刚刚链接的样式表。在一个新文件中,添加以下代码:
html, body { margin:0; padding:0; } img { display:block; width:1000px; margin:0 auto; padding-top:200px; } .row { height:700px; } .row1 { background:url(images/background1.jpg) repeat-x top center fixed;} .row2 { background:url(images/background2.jpg) repeat-x top center fixed;} .row3 { background:url(images/background3.jpg) repeat-x top center fixed;} -
将此文件保存为
parallax-vertical.css,放在project文件夹内的css文件夹中。
刚刚发生了什么?
首先,我们添加了示例的 HTML 结构。这包括三行,每行只包含一个图像。CSS 也很简单。我们首先删除了html和body元素周围的所有空格。然后,我们设置了图像的宽度和位置。然后,我们设置了行的高度,以便我们有一些空间来看到效果。在实际应用中,这通常会由元素的内容来调整。最后,我们在每一行上设置了一个背景图像,以便我们在示例中看到一些变化。
行动时间 - 编写我们的视差脚本
现在,让我们添加代码,使我们的背景在页面向下滚动时动画。
-
将以下代码添加到我们的匿名函数中,以便我们能够启动并运行此脚本:
$(window).scroll(function() { var yPos = -($(window).scrollTop() / 2); $(".row").css({ backgroundPosition: "50% " + yPos + "px" }); });
这里有一个截图示例,展示了我们的脚本在浏览器中预览时的功能:

刚刚发生了什么?
我们在这里使用窗口滚动函数,因为我们希望每次用户使用鼠标滚轮或浏览器滚动条滚动窗口时都触发我们的代码。
我们的变量 yPos 设置为负值,因为我们希望背景动画与正在滚动的页面元素朝同一个方向移动。 使用 scrollTop() 给我们当前 window 的垂直滚动条位置。 然后我们将该数字除以二。
我们使用 css() 方法来设置我们的背景位置。 值 50% 是用于 x 轴的,即浏览器的水平轴。 这告诉我们的背景图像在垂直方向上居中。 y 轴(或此处的 yPos)设置为我们上面的变量 yPos,然后附加 px 以告诉脚本这个数字是以像素为单位的。 yPos 控制图像的水平放置,因此水平居中背景图像。
自定义速度和方向效果
尝试更改 yPos 被除的数字的值,然后尝试将负数更改为正数。 更改这些值会影响我们的背景位置滚动的速度和方向。
知识问答 - scroll() 和 scrollTop() 方法
Q1. scroll() 方法是做什么的?
-
滚动到集合中的下一个同级元素
-
允许您平滑地滚动到页面上的元素或数字值(以像素为单位)
-
允许在每次选择的元素滚动时运行代码
-
当设置为
false时,启用页面上的禁用滚动
Q2. scrollTop() 方法是做什么的?
-
跳回到页面顶部
-
输出所选元素的当前滚动位置
-
当与
click()方法一起使用时,可以让您滚动到元素的顶部 -
将选定的元素动画成像一张纸一样卷起来
摘要
在本章中,我们看了几个示例,它们在元素上动画背景图像。 我们学到的一些事情包括:
-
animate()方法及其一些伟大的应用 -
使用 jQuery UI 为我们的脚本提供颜色动画支持
-
在元素之间淡入淡出背景颜色
-
链接 jQuery 方法在一起
-
视差动画,其中背景层以不同的速度和方向动画,以创造深度的幻觉
-
创建自动背景图像动画以及如何使它们在不同方向上动画
-
scroll()和scrollTop()方法
在下一章中,我们将看看导航动画以及如何为这个常见的网站功能注入一些生机。我们将创建一个单页面滚动脚本,根据点击的链接跳转到页面中的各个部分。此外,我们还将研究如何更改元素的背景颜色,以吸引用户关注网站的特定区域。
第四章:导航动画
在本章中,我们将讨论一些用于导航的动画方法。导航允许用户在我们的网站中的不同页面之间移动。在这个常见的网站功能中添加一些动画将为我们的 Web 项目增添一些情趣。辣味是好的!
下面是本章我们将学习的内容:
-
当我们的鼠标指针进入和离开元素时,向元素添加和删除 CSS 类
-
使用
animate()方法更改悬停元素的样式,同时指定持续时间 -
学习如何平滑地滚动窗口到页面元素
-
我们将制作一个示例,当单击链接时平滑滚动并更改页面背景颜色。花哨!
创建简单的导航动画
我们将从简单地在鼠标悬停在锚标签(<a>)上时更改背景颜色开始。这是导航动画的最简单形式,所以这是一个很好的开始。我们将通过向元素添加类来更改背景颜色。这将轻松地允许我们根据需要构建更多样式到类中。
注意
在本章中我们将再次使用 jQuery UI 来弥补 jQuery 2.0 中对颜色动画的支持不足。请参考上一章关于从哪里下载 jQuery UI 库。
配置 addClass()和 removeClass()
addClass()和removeClass()的语法可能如下所示(方括号表示可选参数):
$(selector).addClass( className [,duration] [,easing] [,complete] );
$(selector).removeClass( className [,duration] [,easing] [,complete] );
注意
重要的是要注意,duration不是addClass()或removeClass()的 jQuery 选项。这个选项是由 jQuery UI 添加的,并且被称为方法重写。
行动时间 - 设置我们的导航
让我们通过执行以下步骤创建我们的导航结构和基本动画:
-
我们将从根据第一章的模板文件创建一个新文档开始,将其命名为
navigation-animation1.html并保存在我们的jquery-animation目录中。 -
接下来,我们需要在我们的 jQuery 库之后添加 jQuery UI 库,方法是添加这一行:
<script src="img/jquery-ui.min.js"></script> -
然后,我们将把以下 HTML 代码添加到我们新创建的文档中的
<body>标签下:<nav> <a href="#">链接 1</a> <a href="#">链接 2</a> <a href="#">链接 3</a> <a href="#">链接 4</a> <a href="#">链接 5</a> <a href="#">链接 6</a> <a href="#">链接 7</a> </nav> -
将以下代码保存到名为
navigation-animation1.css的文件中,并将其链接到我们的 HTML 文档:nav a { display:block; float:left; padding:5px 10px; background:#DDD; } nav a.hover {background:#F0F;} -
将此代码添加到我们的空匿名函数中,以便我们的脚本能够运行:
$("nav a").hover(function(){ $(this).addClass("hover", 300); }, function(){ $(this).removeClass("hover", 300); });
发生了什么?
我们使用 hover() 处理程序告诉我们的导航链接当鼠标光标进入和离开元素时该做什么。我们还将持续时间设置为 300(毫秒),以便 hover() 方法动画略有延迟,并为我们提供所需的动画效果。
以下屏幕截图是说明动画应该如何工作的示例,通过从第一个链接移动光标到最后一个链接:

动手试一试 - 扩展我们的悬停样式
尝试一下,看看通过向我们的 hover 类添加其他样式可以实现什么其他效果。首先,尝试更改元素的 height 和 position。
使用 stop() 方法
上一个示例是一种简单的方式,允许轻松更新样式。您会注意到,如果您非常快地将鼠标悬停在所有导航链接上,来回多次并停止,动画会继续播放直到每个动画都播放完毕。这通常不是一种非常理想的效果,因此我们需要添加 stop() 方法来在下一个动画开始之前停止上一个动画。由于 addClass() 和 removeClass() 无法在动画队列中停止,因此我们需要调整我们的代码一点。为此,我们将使用 animate() 方法来允许我们停止动画。
行动时间 - 添加 stop() 方法
在下一个动画开始之前停止我们的动画,我们需要稍微修改我们的代码。在 animate() 效果之前添加 stop() 就是我们需要做的。
使用与之前相同的文件(navigation-animation1.html),我们将在我们的匿名函数中更新代码,用以下代码替换(新代码已突出显示):
$("nav a").hover(function(){
$(this).stop().animate({ backgroundColor:"#F0F" }, 300);
}, function(){
$(this).stop().animate({ backgroundColor:"#DDD" }, 300);
});
刚才发生了什么?
现在,如果我们迅速将鼠标指针移动到导航链接上(来回移动光标),您会注意到上一个动画在下一个动画开始之前会停止。这比以前的动画更优雅。就像香辣一样,我们也喜欢优雅。
使用 scrollTop() 动画窗口
在上一章中,我们学习了如何使用 scrollTop() 来使我们 <body> 元素的背景图像在页面上以不同的方向和速度动画。在下一个示例中,我们将使用 scrollTop() 来通过平滑滚动到页面上的一个元素来动画窗口。
平滑滚动 动画方法可以用于向我们的用户视觉地指示窗口位置已根据他们在页面上采取的操作而更改,通常是在鼠标单击元素后。这种动画方法通常被称为一页式,正如我们将要构建的一样。
行动时间 - 编写我们的平滑滚动动画
在接下来的步骤中,我们将创建我们的平滑滚动、单页动画,它将动画到页面内容的不同部分:
-
首先,让我们从使用我们的模板
smooth-scrolling.html创建一个新文件开始,然后将其保存在我们的jquery-animation文件夹中。 -
其次,我们将再次添加我们的 jQuery UI 库,方法是直接在我们的 jQuery 库下面插入以下代码(新代码已经被突出显示):
<script src="img/jquery.js"></script> <script src="img/jquery-ui.min.js"></script> -
接下来,我们需要将以下 CSS 代码添加到一个名为
smooth-scrolling.css的新文件中,并在smooth-scrolling.html中链接它:body, html { margin:0; padding:0; } body {background:#CCC;} nav { width:100%; position:fixed; top:0; padding:10px 0; text-align:center; outline:1px dotted #FFF; background:#EEE; background-color:rgba(255, 255, 255, 0.9); } nav a { color:#222; margin:0 10px; text-decoration:none; } content {margin-top:50px;} content div { height:400px; margin:10px; padding:10px; outline:1px solid #FFF; background:#EEE; background-color:rgba(255, 255, 255, 0.8); } -
然后,我们将以下 HTML 代码添加到
<body>标签下面:<nav> <a href="#link1">链接 1</a> <a href="#link2">链接 2</a> <a href="#link3">链接 3</a> <a href="#link4">链接 4</a> <a href="#link5">链接 5</a> <a href="#link6">链接 6</a> <a href="#link7">链接 7</a> </nav> <div class="content"> <div id="link1">链接 1</div> <div id="link2">链接 2</div> <div id="link3">链接 3</div> <div id="link4">链接 4</div> <div id="link5">链接 5</div> <div id="link6">链接 6</div> <div id="link7">链接 7</div> </div> -
最后,将以下内容添加到我们的匿名函数中:
$("a[href^='#']").click(function(e){ var pos = $(this.hash).offset().top - 50; $("body, html").stop().animate({ scrollTop:pos }, 1000); e.preventDefault(); });
刚才发生了什么?
我们使用了 click() 处理程序与一个看起来复杂的选择器。我们使用的选择器意味着:选择所有 href 属性以井号 (#) 开头的锚标签 (<a>)。
对于这个示例,我们的选择器将是 <body> 标签,我们正在使用 animate() 方法来处理我们的繁重工作。再次使用 stop() 方法,以便在下一个动画开始之前停止前一个动画。我们设置一个名为 pos 的新变量,用来保存点击链接(<a>)距页面顶部的位置,使用 offset().top。此外,我们从 pos 变量中减去 50 作为偏移量,因为我们希望 content 元素的顶部落在导航栏的下方。我们将动画的持续时间设置为 1000 毫秒,因为我们希望动画从页面上的当前位置跳转到下一个位置需要 1 秒钟的时间。
平滑滚动和页面背景颜色
现在,让我们将上面学到的两种动画方法合并到一起。此示例将使用平滑滚动方法跳转到我们的链接元素,并同时更改页面背景颜色。
以下截图展示了在我们的导航栏中点击链接后对应链接的停止点:

行动时间-创建超级动画
将我们之前的两个示例合并在一起,我们需要创建一个新文件,并将来自两个示例的 CSS 和 jQuery 代码混合在一起。当然,我们需要进行一些调整,以使它们能够一起工作。
-
使用文件模板创建名为
navigation-animation2.html的新文档,并将其保存在我们的jquery-animation文件夹下。 -
然后,将以下 CSS 代码放入一个名为
navigation-animation2.css的新文件中,并在我们刚创建的 HTML 文档中链接它:body, html { margin:0; padding:0; } body {background:#F00;} nav { width:100%; position:fixed; top:0; padding:10px 0; text-align:center; outline:1px solid #FFF; background:#EEE; background-color:rgba(255, 255, 255, 0.5); } nav a { color:#222; margin:0 10px; text-decoration:none; } content {margin-top:50px;} content div { height:400px; margin:10px; padding:10px; outline:1px solid #FFF; background:#EEE; background-color:rgba(255, 255, 255, 0.8); } -
最后,我们需要将以下代码放入我们的匿名函数中:
$("a[href^='#']").click(function(e){ e.preventDefault(); var link = $(this).index() + 1; var background = ""; if (link == 1) { background = "#F00" //红色 } else if (link == 2) { background = "#FF5000" //橙色 } else if (link == 3) { background = "#FF0" //黄色 } else if (link == 4) { background = "#0F0" //绿色 } else if (link == 5) { background = "#0FF" //浅蓝色 } else if (link == 6) { background = "#00F" //深蓝色 } else if (link == 7) { background = "#F0F" //紫红色 } var pos = $(this.hash).offset().top - 50; $("body, html").stop().animate({ scrollTop:pos, backgroundColor:background }, 1000); });
发生了什么?
我们做的第一件事是添加一个新的 link 变量。这将保存我们的用户点击的链接的索引值。我们将索引值递增了 1,因为 index() 方法是从零开始的,而且今天已经很长时间了,所以我们不想从零开始计数。
background 变量被声明以抵御那些肮脏的 JavaScript 错误怪物,一如既往。我们创建了一个 if 语句来处理背景颜色的十六进制值。background 变量被设置为点击的链接的颜色(我们定义的)。
我们这个魔术技巧的选择器将再次是<body>标签,因为我们既要滚动到页面上的另一个位置,又要改变页面的背景颜色。这与之前的代码相同,唯一不同的是,这次我们添加了backgroundColor,并且根据上面的 if 语句设置了值(背景)。
挑战英雄 – 进一步扩展脚本
尝试为我们合并的动画示例添加一些功能。以下是一些启发你的想法:
-
动态地将内容
<div>元素的高度改变为窗口的高度(不要忘记添加窗口调整大小函数) -
使用窗口滚动功能改变背景颜色,这样当你手动滚动页面时,颜色就会改变,而不仅仅是通过点击链接实现
-
当内容
<div>元素通过点击链接或手动滚动页面进入视图时,进行淡入 -
自动滚动内容,无需点击链接
突击测验 – 符号^ 与 stop() 方法
Q1. 我们在<a>选择器中使用符号^代表什么意思?
-
它表示“等于”
-
它表示“包含”
-
它表示“以…开始”
-
它表示“以…结束”
Q2. stop()方法的作用是什么?
-
它会停止所选元素的动画队列
-
它会阻止页面加载
-
它会停止页面上的所有动画
-
它会阻止动画运行,直到页面重新加载
总结
在这一章中,我们学会了如何根据鼠标交互来改变元素的样式,使用addClass()和removeClass(),以及如何通过 jQuery UI 的方法覆盖来控制添加和移除这些类的速度(持续时间)。
接着,我们学会了如何平滑地将窗口滚动到页面上指定的元素。之后,我们将两个示例合并成了一个示例,这个示例可以平滑地滚动窗口并让页面背景颜色淡入。同时在这一章中,我们找到了一只小狗。什么?你没有找到小狗?你一定是错过了某个分号。
现在我们已经为我们的导航元素添加了一些活力,接下来,我们将在下一章中学习如何为我们的表单输入添加一些生命。下一章中我们将学到一些内容,包括表单验证动画,视觉上改变表单以提醒用户提交时出现问题,并且如何在需要用户修正输入时摇动表单。
第五章:表单和输入动画
在本章中,我们将使用 jQuery 来查看表单动画的示例和概念。我们可以使用几乎任何类型的动画来动画表单输入(由于怪癖和兼容性)。然而,请记住,许多动画都没有意义或会使我们的用户感到困惑,因此我们将避开它们。这些动画类型是对眼睛太“刺耳”的。例如,如果我们改变表单输入的尺寸,我们的用户可能不知道这意味着什么。对于我们的第一个示例,我们将主要坚持颜色变化。在本章后面,我们将使用 jQuery UI 添加更多的动画支持,以真正使我们的表单动起来!
在本章的示例中,我们将创建 HTML 表单,以便我们可以获得表单动画的视觉表示。对于这些示例,我们不会添加表单操作,因为我们不需要它们正确提交。
在本章中,我们将涵盖以下主题:
-
当我们的用户将鼠标光标移动到表单字段上时,动画表单
-
当我们的用户点击其中一个输入字段时,更改输入字段的背景颜色
-
根据表单验证动画化表单的外观
这些是我们在本章中将要使用的事件处理程序,以使我们的表单元素移动起来:
-
hover() -
focus() -
blur()
使用简单的表单动画
表单动画可以用于许多原因,最明显的是当我们的用户与我们的表单进行交互时。更具体地说,当他或她的光标进入或离开我们的表单元素(文本框)时,可以使用表单。动画也非常适合指示表单验证错误。这些动画通常会在表单输入上产生轻微的颜色变化,以使我们的用户在填写表单时获得更好的体验,并使流程更容易跟踪。
注意
在本章中,我们将再次使用 jQuery UI 来弥补 jQuery 2.0 中对颜色动画的支持不足。请参考 第三章,背景动画,以获取 jQuery UI 库的下载位置。
行动时间 - 创建表单
我们将使用以下步骤创建一个 HTML 表单。创建表单后,我们将添加表单验证动画。
-
从 第一章,入门 中使用我们的模板创建一个新文档,称为
form-animation.html,并将其保存在我们的jquery-animation文件夹中。 -
然后,我们将以下代码放置在我们的
<body>标签内:<form id="form1"> <input type="text" placeholder="名字"> <input type="text" placeholder="姓"> <input type="text" placeholder="电子邮件地址"> <input type="text" placeholder="电话号码"> <input type="submit" value="提交"> </form> -
然后,我们需要添加 jQuery UI 库,跟在我们的 jQuery 库后面,通过添加以下代码。(有关获取 jQuery UI 库的详细信息,请参见前面提到的信息框):
<script src="img/jquery-ui.min.js"></script> -
在我们的匿名函数中,添加以下代码:
$("input").hover( function() { $(this).addClass("hover", 500); }, function() { $(this).removeClass("hover", 500); } ); $("input").focus(function() { $(this).addClass("focus", 500); }); $("input").blur(function() { $(this).removeClass("focus", 500); }); -
创建一个名为
form-animation.css的新文件,将其保存在我们的jquery-animation文件夹下,并添加以下代码:form { float:left; margin:5px; } input { display:block; width:200px; padding:10px; border-radius:3px; background:#F5F5F5; border:1px solid #D5D5D5; } input[type=submit] { width:auto; border:0; color:#FFF; text-transform:uppercase; } input:focus {outline:0;} #form1 input[type=submit] { background:#FF6B6B; border:1px solid #FF3A3A; }
注意
属性选择器(input[type=submit])在旧版本的 Internet Explorer 中有一些小问题。在使用之前,请确保您的选择器受支持(或进行多重填充)。
这里有一个 HTML5 跨浏览器填充的很好的列表:github.com/Modernizr/Modernizr/wiki/HTML5-Cross-browser-Polyfills。
到此为止,我们的表单应该看起来像下面的截图:

刚刚发生了什么?
我们添加的第一部分代码是这一章中我们将用于代码示例的 HTML 骨架结构。我们在表单上使用了一个 ID 进行一般的定位。一旦我们添加另一个表单,我们将在本章后面更改这个 ID。显然,这些表单没有动作,因为我们不需要它们提交以查看我们的动画。
其次,我们添加了 jQuery UI 库,以支持颜色动画,并为addClass()和removeClass()添加时长选项,以及在前几章中添加颜色动画。
我们接下来添加的 jQuery 代码是我们在表单上需要用来通过向输入元素添加和删除 CSS 类来实现样式动画的处理程序。hover()处理程序有mouseenter和mouseleave事件。这有助于保持我们的代码在同一个函数中。JavaScript 中onFocus和onBlur的 jQuery 等效物分别是focus()和blur()。与hover()不同,这些处理程序必须分开。
行动时间-将我们的动画样式添加到表单
我们已经添加了所有我们需要的表单代码;现在,让我们添加一些样式到我们的表单,使动画样式生效。
-
在我们创建的名为
form-animation.css的样式表的底部附近添加此代码,它位于input:focus和#form1 input[type=submit]的样式之间:#form1 input.hover {border:1px solid #FF7F7F;}#form1 input.focus { background:#FFD8D8; border:1px solid #FF7F7F; }
刚刚发生了什么?
现在,让我们预览一下带有新添加样式的表单,并进行测试。您会注意到当您悬停在每个文本输入上时,红色边框会缓慢淡入,并且当我们将鼠标光标移出文本输入时,红色边框会缓慢淡出。点击文本输入现在会使背景颜色渐变为红色。当我们点击离开焦点输入时,红色背景颜色会慢慢恢复到其原始颜色。
提示
这些动画也可以使用 CSS3 过渡实现。
我们添加的 CSS 样式的顺序非常重要。第一个状态是 hover 状态,因此这些样式排在最前面。我们希望焦点类在动画之间的过渡期间覆盖 hover 状态,因此它在 hover 样式下面。
最后,提交按钮的样式放在最后一行,因为我们不希望在悬停在其上时样式发生变化。如果您对样式排序方法以及为什么重要不熟悉,请阅读 CSS 特异性。交换我们刚刚添加的样式的顺序将说明为什么顺序很重要。
表单验证动画
验证用户表单提交是确保我们从用户那里获得正确信息的好方法。为了增强表单的可用性,我们将介绍一些处理表单验证的动画方法。我们将从基本的表单验证开始,并从那里构建。
行动时间 - 基本对话框表单验证
我们将通过使用警报来创建表单验证,告诉用户表单提交出了什么问题。
-
首先,我们需要将以下代码放在我们先前添加的代码之后的匿名函数中:
$("#form1 input[type=submit]").click(function(e) { e.preventDefault(); var msg_error = ""; $("#form1 input[type=text]").each(function() { if ($(this).val() == "") { msg_error += $(this).attr("placeholder") + "不能为空。\n"; } }); if (msg_error) { alert(msg_error); } else { alert("表单提交成功!"); } });
发生了什么?
我们在提交按钮上使用了 click() 处理程序来触发表单验证检查。为了本示例的缘故,我们使用了 preventDefault(),这样当我们点击提交按钮时,URL 散列不会更改。确保在将此代码发布到互联网的野外时删除此行。
each() 方法用于定位表单中的所有输入项。使用 if 语句将其缩小到所有值为空的输入项(通过使用 $(this),因为我们已经在 each() 方法中)。每当我们的 if 语句返回 true 时,我们将添加到变量 msg_error。我们使用之前设置的空输入的占位符值作为错误消息的第一部分。我们添加了 can't be left blank 作为验证错误消息的其余部分,并以新行(\n)结束,以使所有错误消息不在对话框窗口的同一行上。
最后,我们需要确保是否有错误消息要显示,所以我们检查我们的msg_error变量返回true。如果是,我们将使用alert()内的变量msg_error引发对话窗口。否则,我们将使用alert()告知用户表单已成功提交。
尝试一下英雄 - 扩展我们的表单验证
在这个例子中,我们使用了非常基本的表单验证,以确保我们的输入字段都没有留空。尝试扩展脚本以检查不同类型的值。其中一些验证类型的例子是:
-
仅包含数字
-
仅包含字母
-
包含一定数量的字符
-
仅包含字母并在一定范围内(例如:1-100)
-
日期在未来
行动时间 - 动画展示表单验证错误
现在,我们不再给用户一个包含所有验证错误的对话框,而是会直观地指出需要修复的表单字段错误。
-
我们将通过在我们的页面上添加一个新的表单来开始。在我们第一个
<form>之后添加以下代码(新代码已突出显示):<form id="form2"> <input type="text" placeholder="名"> <input type="text" placeholder="姓"> <input type="text" placeholder="电子邮箱地址"> <input type="text" placeholder="电话号码"> <input type="submit" value="提交"> </form> -
接下来,我们将通过将以下代码放置在样式表的底部来添加新的表单样式:
#form2 input.hover {border:1px solid #7FA1D1;} #form2 input.focus { background:#E6F0FF; border:1px solid #7FA1D1; } #form2 input[type=submit] { background:#8FACD7; border:1px solid #638CC7; } #form2 input.error {border:1px dashed #F00;} -
然后,让我们加入检查表单的代码:
$("#form2 input[type=submit]").click(function(e) { e.preventDefault(); $("#form2 input[type=text]").each(function() { if ($(this).val() == "") { $(this).addClass("error"); } else { $(this).removeClass("error"); } }); });
刚发生了什么?
我们使用与上一个示例相同的代码,但不再触发具有所有输入验证错误的对话框,而是为每个需要在表单中通过之前需要注意的文本输入更改了虚线红色边框。我们使用了我们的老朋友addClass()和removeClass()来通过添加或删除类"error"来为具有错误的输入添加样式。
尝试一下英雄 - 把所有东西放在一起
现在,尝试将我们刚学到的所有表单验证动画类型组合在一起。还要尝试更多地扩展样式。以下是扩展样式和动画的一些想法:
-
在表单字段的侧面显示一个错误消息,说明验证错误是什么。
-
在表单字段内显示背景图像,表示提交存在问题。
-
自动将光标焦点设置到第一个具有验证错误的表单字段。
-
如果您的表单有标签(而不是占位符),请更改相应标签的颜色。
-
使具有验证错误的表单字段脉动,以持续表示错误(使用边框颜色和/或背景颜色)。
尝试自己玩一些 jQuery UI 动画效果,并想出一些时髦的动画。我们将在第六章中了解更多关于 jQuery UI 的优秀功能,但这里有一个 jQuery UI 动画让您开个头:
$("form").effect("bounce", { direction:"left", easing:"linear" }, 500);
快速测验 - 表单动画和 jQuery UI
Q1. 何时可以使用表单动画?
-
当我们想引导用户注意表单的特定区域时
-
当用户与我们的表单交互时
-
以增强用户对我们表单的体验
-
以上所有
Q2. 为什么我们在本章的示例中添加了 jQuery UI?
-
给予我们使用非 jQuery 原生动画效果的能力
-
要向
addClass()和removeClass()添加duration选项,并给我们以动画颜色的能力 -
允许我们使用
scrollTop()的平滑滚动选项 -
为了让
preventDefault()在我们的示例中正常工作
摘要
在本章中,我们学习了如何根据与表单交互的情况来直观改变表单的外观。为了更进一步,您可以使用其他 CSS 样式创建自定义动画,通过 jQuery 改变元素或使用 jQuery UI 效果。只需记住 - 不要使动画太突然或突然。表单动画通常用于帮助用户填写表单的流程。确保逐步走过每个表单步骤和动画,就像您是第一次填写该表单的用户一样。
在下一章中,我们将研究使用 jQuery UI 在 jQuery 库之上添加的出色动画效果。jQuery UI 很棒,因为它不仅添加了新的动画效果,还为原生 jQuery 方法添加了额外选项。
第六章:使用 jQuery UI 进行扩展动画
jQuery UI 是 jQuery 的官方用户界面库,添加了一套交互式小部件,如标签页和手风琴,一系列交互助手,如拖放,以及一套全面的效果,扩展了 jQuery 原生提供的效果。
在本章中,我们将看到 jQuery UI 添加的额外效果。我们将涵盖的主题包括:
-
获取和设置 jQuery UI
-
jQuery UI 添加的新效果
-
使用
effect()方法 -
扩展
show()、hide()和toggle()方法 -
在 jQuery UI 中使用缓动
-
为一个元素的颜色添加动画
-
动画类转换
jQuery UI 添加了几种新的动画方法,以及修改了几种 jQuery 方法。本章将要讨论的方法包括:
-
animate() -
addClass() -
effect() -
hide() -
switchClass() -
show() -
toggle()
获取和设置 jQuery UI
获取和设置 jQuery UI 非常简单。有一个在线工具可以为我们构建一个自定义下载包,其中只包含我们需要的 jQuery UI 部分。由于 jQuery UI 的模块化性质,尽量减少我们在任何给定的 Web 项目中使用的代码负载是有意义的,因此只包含我们打算使用的代码模块的能力有助于我们尽量减少我们的代码可能对访问者产生的任何影响。
jQuery UI 下载构建器可以在 jqueryui.com/download 找到。该页面分为两个部分,库的组件列在顶部,主题细节列在底部。下载构建器具有一定的智能,当我们选择所需的组件时,它将自动选择任何依赖项。

在前一个截图中显示的下载构建器页面为我们提供了运行库组件的任何子集所需的一切。
在本章中我们将只使用效果,所以当我们下载包时,应该只选择页面底部效果小节中找到的组件。我们不需要包括主题,甚至不需要包括库核心。这些效果可以完全独立于库的其余部分使用;我们所需要的只是效果核心文件和我们需要的各个效果。确保它们都被选择,并下载包。
下载构建器允许您仅下载您需要的部分,因为 jQuery UI 基本上是一个附加组件。分解每个部分,允许您仅选择您需要的部分,这在定制时极大地减少了插件的文件大小。
这个包将为我们提供一切我们需要使用我们选择的组件,包括最新稳定版本的 jQuery 的副本,所以在使用 jQuery UI 时,jQuery 本身不需要单独下载。
所选组件的所有 JavaScript 将由下载构建器合并并压缩为单个文件,并且任何功能性 CSS 或主题文件将被合并为单个样式表。我们不需要任何主题文件来处理效果,但确保从下载构建器提供的归档中的 .js 文件进入我们的 js 文件夹。
创建一个新的模板文件
在本章的其余部分中,示例将会很简短,主要是基于图像的示例,依次说明每个效果,因此使用稍微不同的模板文件是有道理的。通过在 jQuery 结束标签 </body> 前直接添加对 jQuery UI 源文件的引用来创建一个新的模板文件。
jQuery UI 新增的效果
在撰写本文时,jQuery UI 提供了 14 个新的预定义动画效果供我们在页面中使用;下面列出了这些效果以及它们的简要用法:
| 动画 | 描述 |
|---|---|
blind |
通过像窗帘一样向下或向上滚动,显示或隐藏目标元素。 |
bounce |
目标元素水平或垂直地反弹指定次数。 |
clip |
通过将相对的边缘向元素中心移动或向外扩展到其完整宽度或高度,显示或隐藏目标元素。 |
drop |
元素看起来落在或离开页面,以便显示或隐藏它。 |
explode |
爆炸效果使目标元素分成指定数量的碎片然后淡出,或者在几个碎片中淡入视图然后汇合形成完整元素。 |
fold |
元素看起来折叠关闭或打开。 |
highlight |
目标元素的 background-color 属性被设置(默认为黄色,尽管这是可配置的),然后在短时间后淡出。 |
puff |
目标元素稍微增大然后淡出。 |
pulsate |
调整目标元素的不透明度指定次数,使元素似乎闪烁不断。 |
scale |
目标元素的尺寸被调整以增加或减小其大小。 |
shake |
目标元素被震动指定次数。这种效果类似于反弹效果,其关键区别在于每次动画迭代时摇动的距离保持不变。 |
size |
调整目标元素的尺寸以增加或减小其大小。此效果与 scale 几乎相同。 |
slide |
目标元素被制造成水平或垂直地滑入或滑出视图。 |
transfer |
指定元素的轮廓被转移到页面上的另一个元素。 |
使用效果 API
jQuery UI 引入了 effect() 方法,该方法可用于触发上表中列出的任何效果。effect() 方法的使用模式如下:
$(selector).effect( effect [,options] [,duration] [,complete] );
我们想要使用的特效名称总是effect()方法的第一个参数。它以字符串格式提供。
每个特效都有自定义配置选项,可以设置以控制特效的显示方式。这些选项是设置在配置对象中的,作为effect()方法的第二个参数,跟在特效名称后面。
我们还可以作为参数为特效提供持续时间。就像标准的 jQuery 动画一样,可以提供一个整数,表示特效的持续时间(毫秒为单位),或者是字符串slow或fast。
如果不需要配置,则持续时间可以作为第二个参数传递给effect()方法。如果没有提供持续时间,将使用默认持续时间400毫秒。
可选地,回调函数可以作为最后一个参数。提供的函数将在特效结束时针对每个选定元素执行一次。
让我们看看effect()方法可以如何使用一些示例。
弹跳特效
bounce特效类似于easeOutBounce的缓动函数,但比后者更易控制。根据需求,可以使用特效 API 或显示/隐藏逻辑。
语法
$(selector).effect( "bounce" [,configuration] [,duration]);
配置选项
弹跳特效有以下配置选项:
| 选项 | 默认 | 用法 |
|---|---|---|
direction |
"up" |
弹跳的方向。其他可能的选项是字符串 down |
distance |
20 |
弹跳的初始距离(连续弹跳会减少距离)以像素为单位 |
mode |
"effect" |
是否正常运行特效,或使用显示/隐藏逻辑,其他可接受的值可能是字符串 show、hide 或 toggle |
times |
5 |
弹跳的次数 |
动手实践 – 使用弹跳特效
在这个例子中,我们将看到如何结合 jQuery UI 特效来创建一个穿过页面的弹跳球:
-
在模板文件的
<body>中使用以下简单元素:<div id="travel"> <div id="ball"></div> </div> -
我们只需要一个简单的容器
<div>和一个内部的<div>。在<body>末尾的空函数中,加入以下脚本:$("#ball").click(function() { $("#travel").animate({ left: "+=300px" }, 1500).find("div").effect( "bounce", { times: 4, 距离:100}, 1500); }); -
将文件保存为
bounce.html。我们还需要一些简单的样式。将以下 CSS 添加到一个新文件中:#travel { position:absolute; top:100px; } #ball { width:150px; height:150px; cursor:pointer; 背景:url(../img/ball.jpg) no-repeat 0 0; } -
将其保存为
bounce.css,放置在css文件夹中。当我们运行页面并点击球时,我们应该发现它会沿着页面弹跳,逐渐停止:![行动时间 - 使用弹跳效果]()
前一组成将显示球横穿页面,从左到右弹跳上下。
刚刚发生了什么?
当点击球时,我们首先使用 jQuery 的 animate() 方法,通过 300 像素动画化容器 #travel 的 left 样式属性,在 1.5 秒的持续时间内。我们减慢此动画以改善整体动画的外观,但这并不是严格要求的。然后,我们向下导航到内部 <div> 元素,并使用 effect() 方法,指定 bounce 效果。
我们需要同时使用 #travel 和 #ball 元素,因为如果我们在同一元素上使用 animate() 和 effect() 方法,弹跳效果将进入元素的动画队列,两个动画将按顺序执行,而不是同时运行。
高亮效果
highlight 效果是吸引访问者注意力的简单而有效的方法,用于突出显示页面中已添加的新项目,在今天许多领先的基于网络的界面中用于此目的。
语法
$(selector).effect( "highlight" [,configuration] [,duration] );
配置选项
对于 highlight 效果,只有两个配置选项;它们如下所列:
| 选项 | 默认值 | 用法 |
|---|---|---|
color |
"#ffff99" |
设置正在高亮显示的元素的 background-color 属性 |
mode |
"show" |
设置使用 effect() 方法时效果是隐藏还是显示的,其他可能的值包括 hide、toggle 或 effect |
行动时间 - 高亮元素
在这个示例中,我们将创建一个简单的待办事项列表,其中包含一系列可以勾选的默认项目。我们还可以允许将新项目添加到列表中,并在添加时将 highlight 效果应用于新项目。
-
将以下 HTML 代码添加到模板文件的
<body>元素中:<div id="todo"> <h2>待办事项列表</h2> <ul> <li><label><input type="checkbox">项目 1</label></li> <li><label><input type="checkbox">项目 2</label></li> <li><label><input type="checkbox">项目 3</label></li> </ul> <input type="text" id="new"> <button id="add">添加</button> </div> -
使用以下代码为我们的待办事项列表添加行为:
$("#add").click(function() { var newItem = $("#new"), text = newItem.val(); if (text) { var li = $("<li>"), label = $("<label>").html("<input type=\"checkbox\">" +text).appendTo(li); li.appendTo("#todo ul").effect("highlight", 2000); newItem.val(""); } // 阻止表单提交 return false; }); -
将此页面保存为
highlight.html。我们还需要一些 CSS 来完成此示例。在文本编辑器中新建一个文件,添加以下代码:#todo { width:208px; font:normal 13px sans-serif; } #todo ul { padding:0; margin-bottom:30px; } #todo li { list-style-type:none; } #todo label { display:block; border-bottom:1px dotted #000; } li input { position:relative; top:2px; } input { margin-right:10px; } -
将此页面保存为
highlight.css。 -
在浏览器中运行页面时,我们可以添加一个新项,并且它将在添加到列表时短暂地突出显示:
![行动时间 – 突出显示元素]()
在上一张截图中,我们看到了新添加的项目在从列表中淡出之前的淡出效果。
刚才发生了什么?
我们在列表底部的<button>元素上添加了一个点击处理程序,它驱动了其他行为的功能。当点击<button>元素时,我们缓存了<input>字段的选择器,并获取了输入到其中的文本。
如果保存文本的变量不为空,那么我们就创建新的<label>和<input>元素。我们将文本添加到<label>元素中,然后将新项目附加到列表中。最后,我们应用highlight效果,并清空<input>字段。
脉动效果
pulsate效果让元素上下视图出现指定次数,以便目标元素看起来在脉动。像我们迄今为止看到的大多数效果一样,它很容易使用,并且需要很少或不需要配置。
语法
$(selector).effect( "pulsate", [,configuration] [,duration] );
配置选项
pulsate效果也只有两个可配置的选项;这些在下表中显示:
| 选项 | 默认 | 用法 |
|---|---|---|
mode |
"show" |
设置目标元素在与effect()方法一起使用时是显示还是隐藏,其他可能的值包括hide、toggle和effect |
times |
5 |
设置目标元素脉动的次数 |
行动时间 – 使元素脉动
在这个例子中,我们将展示一个简单的时间表,可以通过点击链接删除行。如果点击了链接,相应的行在删除之前会被脉动。
-
在模板文件中使用以下标记:
<table> <tr> <th>工作编号</th> <th>开始时间</th> <th>结束时间</th> <th colspan="2">总时间</th> </tr> <tr> <td>05432</td> <td>8:00</td> <td>8:43</td> <td>43 分钟</td> <td> <a class="delete" href="#" title="删除此项"> 删除 </a> </td> </tr> <tr> <td>05684</td> <td>8:43</td> <td>10:21</td> <td>1 小时 38 分钟</td> <td> <a class="delete" href="#" title="删除此项"> 删除 </a> </td> </tr> <tr> <td>05684</td> <td>10:21</td> <td>13:30</td> <td>3 小时 9 分钟</td> <td> <a class="delete" href="#" title="删除此项"> 删除 </a> </td> </tr> </table> -
添加应用效果的代码到页面底部的闭包中:
$(".delete").click(function(e) { e.preventDefault(); var row = $(this).closest("tr"); row.closest("tr").children().css("backgroundColor","red").effect("pulsate", function() { row.remove(); }); }); -
将此文件保存为
pulsate.html。这个例子只需要一些样式。这些应该放在一个新文件中:table { border-spacing:0; font:normal 13px sans-serif; } th, td { text-align:left; padding-right:20px; } -
将此文件保存在
css文件夹中,命名为pulsate.css。 -
单击任何行中的删除链接将应用
pulsate效果,然后删除该表行:![行动时间——使一个元素跳动]()
前一个截图显示了一个pulsate动画,当它淡出时。
刚才发生了什么?
当点击删除链接时,我们的处理函数首先设置链接所在的<tr>元素的background-color属性。这对于效果并非必需,但确实有助于让效果栩栩如生。
然后,我们使用effect()方法将pulsate效果应用于行中的所有<td>元素。我们需要将效果应用于<td>元素而不是<tr>元素,以便在 IE 中效果按预期工作。
当效果结束时,我们的内联回调函数将被执行,该函数会移除<tr>元素。显然,<tr>元素只能被移除一次,一旦被移除,随后的尝试将会静默失败。
shake效果
shake效果使应用它的元素来回抖动指定次数。
语法
$(selector).effect( "shake", [,configuration] [,duration] );
配置选项
shake效果公开了三个配置选项,允许我们自定义其行为。
配置选项列在下表中:
| 选项 | 默认值 | 用法 |
|---|---|---|
direction |
"left" |
设置元素移动的方向 |
distance |
20 |
设置元素抖动时的像素数 |
times |
3 |
设置元素抖动的次数 |
行动时间——抖动一个元素
开源 CMS WordPress 在后端管理区域的登录表单中输入错误的登录详细信息时使用了shake效果。在这个例子中,我们可以看到使用shake效果实现此行为是多么简单。
-
将以下标记添加到模板文件中,作为登录表单的基础:
<form> <h2>登录</h2> <label>用户名:<input id="name" type="text"></label> <label>密码:<input id="pass" type="text"></label> <input type="submit" id="submit" value="登录"> </form> -
现在将以下代码添加到模板文件底部的空闭包中:
$("#submit").click(function(e) { e.preventDefault(); $("input").each(function(i, val) { if (!$(this).val()) { $(this).css("border", "1px solid red").effect("shake", { distance: 5 }, 100); } }); }); -
将此文件保存为
shake.html。我们还需要一个基本的样式表来示例。将以下 CSS 添加到一个新文件中:form { width:145px; padding:20px; margin:auto; border:1px solid #000; font:normal 13px sans-serif; } h2 { font-size:14px; margin-top:0; } input { display:block; margin-bottom:10px; border:1px solid #000; } -
将此文件保存为
shake.css。 -
如果我们在浏览器中运行页面,并且点击登录输入按钮,而不填写任何一个
<input>字段,两个字段都将边框设置为红色,并且会从一边摇摆到另一边:![行动时间 - 摇摆一个元素]()
在前面的屏幕截图中,我们看到当输入框为空时并且点击登录按钮时,输入框会被摇晃。
刚才发生了什么?
当点击登录按钮时,我们只需检查每个<input>是否有值,如果没有,我们就会应用红色边框,然后调用effect()方法,指定shake作为效果。我们使用配置对象来减少元素移动的距离,以及指定相对较短的持续时间。
尺寸效果
size效果用于调整元素的大小,使其根据其配置而增大或缩小。与大多数其他效果不同,size效果必须进行配置才能成功使用。
size效果也是唯一一个具有基本核心文件以及另一个效果作为依赖关系的效果之一。大多数组件仅依赖于核心文件。由于我们从 jQuery UI 下载构建器下载了整个效果套件,因此无需担心包含额外效果。当我们在本章开头下载时,它已经在下载构建器创建的单个文件中。
语法
$(selector).effect( "size", [,configuration] [,duration] );
配置选项
size效果给了我们四个可配置的选项,如下所示:
| 选项 | 默认值 | 用法 |
|---|---|---|
from |
none |
设置动画开始时目标元素的大小。此选项接受一个对象,该对象具有height和width键,用于设置目标元素的起始大小。此选项不是强制性的 |
to |
none |
设置动画结束时目标元素的大小。此选项接受一个对象,该对象具有height和width键,用于设置目标元素的结束大小。此选项必须提供 |
origin |
['middle','center'] |
设置隐藏动画的消失点,或者与显示逻辑一起使用时增长的点 |
scale |
"both" |
此选项设置元素的整个box(包括边框和填充的 CSS 值)是否被缩放,仅content,或者默认的both |
行动时间 - 调整元素大小
增大和缩小元素的一个常见用途是鱼眼菜单,在鼠标指针悬停在它们上方时,元素会增大,当鼠标指针移出它们时,它们会缩小。这个效果也被苹果的 OSX 的码头上的图标使用。
使用size效果,我们可以只用几行代码实现自己的基本鱼眼菜单。
-
将以下标记添加到模板文件的
<body>中:<div id="dock"> <a href="#" class="icon" id="finder"> <img src="img/finder.png"></a> <a href="#" class="icon" id="mail"> <img src="img/mail.png"></a> <a href="#" class="icon" id="safari"> <img src="img/safari.png"></a> <a href="#" class="icon" id="firefox"> <img src="img/firefox_small.png"></a> <a href="#" class="icon" id="itunes"> <img src="img/itunes.png"></a> </div> -
将以下 JavaScript 添加到
<body>元素底部的第三个<script>元素中:$(".icon", "#dock").hover(function() { $(this).stop().animate({ top: -31 }).find("img").stop().effect("size", { scale: "box", to: { width: 64, height: 64 } }); }, function() { $(this).stop().animate({ top: -15 }).find("img").stop().effect("size", { scale: "box", to: { width: 48, height: 48 } }); }); -
将此文件保存为
size.html。我们还需要一些样式。在新文件中添加以下代码:#dock { width:380px; height:90px; position:fixed; bottom:0; background:url(../img/dock.png) no-repeat 0 0; } .icon { position:absolute; top:-15px; left:44px; } .icon img { border:none; } #mail { left:108px; } #safari { left:170px; } #firefox { left:229px; } #itunes { left:289px; } -
将此文件保存为
size.css,保存在css文件夹中。 -
当我们在浏览器中运行文件时,应该看到菜单中的各个项目在鼠标指针移动到它们上方时变大和缩小:
![动作时间 - 调整大小元素]()
在上一个截图中,我们看到鼠标指针悬停在菜单项上时的菜单。
刚才发生了什么?
我们使用 jQuery 的hover()方法将mouseenter和mouseleave事件处理程序附加到码头内的每个项目上,该方法接受两个函数,第一个函数在mouseenter事件上执行,第二个函数在mouseleave事件上执行。
在第一个函数中,我们使用stop()方法来管理队列,然后通过更改其topCSS 值来动画元素的位置。在这里使用stop()可以防止屏幕上元素位置的不美观的突变。
然后,我们进入链接内的图像并在此元素上调用stop()方法,然后再应用size效果。我们为配置对象中的width和height键提供整数值,由于这些值大于图像的尺寸,因此图像将增大。
注意
注意,当我们在图像上使用stop()方法时,它是为了防止鼠标指针重复在链接上移动时效果堆积。第二个函数实际上是第一个函数的反向,它只是将元素调整回其原始位置和大小。
传输效果
transfer 效果只是将一个元素的轮廓转移到另一个元素。与我们刚才看到的 size 效果一样,如果未经过配置,transfer 效果将无法生效。
语法
$(selector).effect( "transfer", [,configuration] [,duration] );
配置选项
transfer 效果仅有两个配置选项,尽管只需要设置其中一个即可使效果生效。配置选项列在以下表格中:
| 选项 | 默认值 | 用途 |
|---|---|---|
className |
none |
如果设置了此选项的值,则在效果运行时将其添加到传输元素中 |
to |
none |
指定传输元素发送到的目标元素的 jQuery 选择器 |
执行操作-将一个元素的轮廓转移到另一个元素上
在此示例中,我们将重新创建 OSX 中流行的应用程序安装对话框,并使用 transfer 效果来帮助展示访问者将图标拖动到何处(实际上图标不可拖动;我们只是在观察 transfer 效果)。
-
在模板文件的
<body>元素中添加以下元素以创建install对话框:<div id="install"> <div id="firefox"></div> <div id="apps"></div> </div> <p>要安装应用程序,请将其图标拖动到应用程序文件夹 文件夹图标。</p> <button id="show">显示</button> -
将以下脚本添加到模板文件底部的空函数中:
$("#show").click(function() { $("#firefox").effect("transfer", { to: "#apps", 类名: "ui-effect-transfer" }, 1000); }); -
将页面保存为
transfer.html。 对于样式表,请将以下代码添加到新文件中:body { font:normal 14px sans-serif; } #install { width:417px; height:339px; position:relative; background:url(../img/install.jpg) no-repeat 0 0; } #firefox { width:124px; height:121px; position:absolute; left:34px; top:132px; background:url(../img/firefox.png) no-repeat 0 0; } #apps { width:54px; height:52px; position:absolute; right:58px; top:172px; background:url(../img/apps.png) no-repeat 0 0; } .ui-effect-transfer { border:2px solid #7bee76; } -
将此文件保存为
transfer.css放在css文件夹中。 -
当点击
<button>元素时,Firefox 图标的轮廓会转移到应用程序文件夹图标上,以引导访问者:![执行操作-将一个元素的轮廓转移到另一个元素上]()
传输元素在从起始元素移动到目标元素时调整大小。在前一张截图中,动画完成了大约 50%。
刚才发生了什么?
在底层 HTML 中,我们有一个容器<div>元素,它被赋予了应用程序安装对话框的背景图像。在其中,我们有一个给定 Firefox 图标背景的<div>元素,以及一个给定应用程序文件夹图标的第二个<div>元素。这两个内部<div>元素都具有用于样式目的和轻松使用 jQuery 选择的id属性。
在脚本中,我们为<button>元素添加了一个单击处理程序函数,每次单击<button>元素时都会应用效果。处理程序函数在#firefox元素上调用传输效果,该效果将图标设置为起始元素。
在配置对象中,我们将to选项设置为apps元素的选择器,并将className选项设置为ui-effect-transfer字符串。此字符串将作为类名应用于元素,并在传输元素可见时用于添加绿色边框。
每次单击<button>元素时,传输元素都会显示,并且会从起始元素(Firefox 图标)动画到结束元素(应用程序文件夹图标)。
小测验-使用效果 API
Q1. jQuery UI 给我们带来了多少种新效果?
-
2
-
18
-
9
-
14
Q2. 我们希望使用的效果如何指定?
-
通过调用函数来调用效果,例如,
bounce() -
效果的名称以字符串格式作为第一个参数传递给
effect()方法,例如,effect("bounce") -
效果的名称作为传递给
animate()方法的对象中的effect键的值提供,例如,animate({ effect: "bounce" }) -
效果的名称作为字符串传递给事件助手,例如,
click("bounce")
使用带有显示和隐藏逻辑的效果
一些 jQuery UI 效果也可以与 jQuery 的show(),hide()和toggle()方法结合使用,当需要显示或隐藏逻辑时。实际上,一些效果更适合这种执行方法。
盲效果
blind效果是一个完美的例子,通常最好与显示/隐藏逻辑一起使用,而不是标准的效果 API。虽然blind效果可以与标准效果 API 一起使用,但会发生的情况是效果会按照其默认模式运行,但然后元素将被放回到其原始状态。对于所有具有mode配置选项的效果都是如此。
语法
$(selector).hide|show|toggle|effect( "blind", [,configuration][,duration] );
配置选项
blind效果具有以下配置选项:
| 选项 | 默认值 | 用法 |
|---|---|---|
direction |
"vertical" |
设置沿着目标元素显示或隐藏的轴 |
mode |
"hide" |
设置是否在与effect()方法一起使用时显示或隐藏元素。其他可能的值包括show,toggle和effect |
行动时间-使用盲效果
我之前提到这个效果让人联想到窗帘升起或下降的情景,所以让我们的下一个例子以此为基础:
-
在模板文件的
<body>元素中添加以下代码:<div id="window"> <div id="blind"></div> </div> -
使用以下脚本实现效果:
$("#window").click(function() { $("#blind").toggle("blind"); }); -
将此文件保存为
blind.html。此示例的样式表如下:#window { width:464px; height:429px; position:relative; cursor:pointer; background:url(../img/window.jpg) no-repeat 0 0; } #blind { display:none; width:332px; height:245px; position:absolute; left:64px; top:113px; background:url(../img/blind.png) no-repeat 0 100%; } -
将此保存为
blind.css,放在css文件夹中。 -
当我们在浏览器中运行页面时,每次点击窗口时,百叶窗应该交替卷下和卷起:
![行动时间 - 使用百叶窗效果]()
之前的屏幕截图显示了完全打开的百叶窗。
刚刚发生了什么?
我们在外部容器上设置了一个点击处理程序,该处理程序调用内部元素的 toggle() 方法。在 CSS 中,我们最初将内部元素设置为隐藏,因此第一次单击容器元素时,内部元素将显示。
剪裁效果
clip 效果会导致调用它的元素垂直或水平缩小直到消失。
语法
$(selector).hide|show|toggle|effect( "clip", [,configuration][,duration] );
配置选项
使用 clip 效果时,我们可以利用的配置选项允许我们控制动画进行的方向以及元素是显示还是隐藏的:
| 选项 | 默认 | 用法 |
|---|---|---|
direction |
"vertical" |
设置元素动画的轴线方向 |
mode |
"hide" |
配置元素是隐藏还是显示。其他可能的值是 show、toggle 和 effect |
行动时间 - 剪裁元素进入和退出
此效果被称为类似于旧电视机关闭时图片的情景,所以让我们在例子中加以体现。
-
将以下元素添加到模板文件的
<body>元素中:<div id="tv"> <div id="bg"></div> <div id="static"></div> </div> -
然后,在页面底部使用以下简单的脚本:
$("#tv").click(function() { $("#static").effect("clip"); }); -
将此文件保存为
clip.html。此示例的样式表如下:#tv { width:300px; height:269px; position:relative; cursor:pointer; background:url(../img/tv.png) no-repeat 0 0; } #bg { width:220px; height:180px; position:absolute; left:42px; top:30px; z-index:-2; background-color:#000; } #static { width:216px; height:178px; position:absolute; left:44px; top:31px; z-index:-1; background:url(../img/static.gif) no-repeat 0 0; } -
将此文件保存在
css文件夹中为clip.css。 -
当页面运行时,我们应该能够单击电视的任何位置并看到效果运行:
![操作时间 - 剪切元素进入和退出]()
前一个截图显示了静态元素被剪辑的情况。
刚刚发生了什么?
底层页面有一组元素,其中外部容器被样式化为电视,并有两个内部元素,其中一个是简单的背景,位于静态元素的后面。两个内部容器都使用 CSS z-index 位于外部容器的后面。
当电视的任何部分被点击时,静态元素会自动应用效果,而无需任何额外的配置,因为效果的默认模式是 hide,所以在效果结束时元素会自动隐藏。要看到效果的反向,我们可以默认隐藏静态元素并将模式设置为 show,或者我们可以将 mode 设置为 toggle 并让静态元素交替显示和隐藏。
下拉效果
drop 效果用于在滑动打开元素时显示元素,或在滑动关闭元素时隐藏元素。该效果同时作用于所应用元素的 position 和 opacity。
语法
$(selector).hide|show|toggle|effect( "drop", [,configuration][,duration] );
配置选项
drop 效果允许我们控制元素下降的方向,以及是否显示或隐藏:
| 选项 | 默认 | 用法 |
|---|---|---|
direction |
"left" |
设置元素进入或离开页面的方向。另一个选项是字符串 right |
mode |
"hide" |
设置使用 effect() 方法时元素是显示还是隐藏的。其他可能的值包括 show、toggle 和 effect |
操作时间 - 使用效果
社交网络网站 Twitter 推出了一种新颖的效果,通过在页面顶部显示一个下拉消息来向访问者报告动作。我们可以很容易地使用 drop 效果复制这种行为。
-
将以下标记添加到我们模板页面的
<body>元素中:<div id="confirmation"> <p>您的请求已完成!</p> </div> -
现在,在页面底部添加以下代码:
$("#confirmation").effect("drop", { mode: "show", direction: "up" }, function() { var timer = function() { $("#confirmation").effect("drop", { mode: "hide", direction: "up"}); } setTimeout(timer, 3000); }); -
将页面保存为
drop.html。对于这个示例,我们只需要一些基本样式。创建以下非常基本的样式表:body { background-color:#3cf; } #confirmation { display:none; width:100%; height:60px; position:absolute; top:0; left:0; z-index:999; background-color:#fff; text-align:center; font:normal 18px sans-serif; } #confirmation p { margin:0; position:relative; top:18px; } -
将 CSS 保存为
drop.css。 -
当页面加载时,消息应该在短暂的间隔后显示,然后逐渐消失:
![动作时间 - 使用效果]()
上一张截图显示了消息慢慢被隐藏。当计时器间隔过去后,它会看起来同时向上滑动和淡出。
刚刚发生了什么?
消息本身的底层标记非常简单;我们只需要一个容器和实际的消息。在我们的例子中,消息被硬编码到页面中,但根据报告的动作,我们可以很容易地动态设置它。
CSS 同样简单,为页面提供了一个背景颜色,以更好地突出消息,并为容器和消息本身提供了一些基本样式。最重要的规则(在这个实现中)是容器最初是隐藏的。
我们的脚本在页面加载后立即显示消息,但通常情况下,它会在完成某些系统操作后触发。我们使用effect()方法来启动效果,并使用配置对象作为effect()方法的第二个参数来配置mode为show,direction为up(由于它的绝对定位,元素仍然会向下掉落)。
在传递给effect方法的回调函数内部,我们创建了一个存储在timer变量中的内联函数。在这个函数内部,我们只是隐藏确认消息,使用effect()方法并再次将mode配置选项设置为hide,将direction选项设置为up。
在这个函数定义之后,我们使用 JavaScript 的setTimeout函数在 3 秒后执行timer函数。我们使用一个闭包来调用我们的timer函数,以保持当前的最佳实践。
爆炸效果
explode效果通过将选定的元素炸成指定数量的碎片然后淡出,提供了一个很好的视觉效果。这个效果可以与效果 API 一起使用,也可以与show、hide或toggle逻辑一起使用。
语法
$(selector).hide|show|toggle|effect( "explode", [,configuration][,duration] );
配置选项
当使用explode效果时,我们可以控制元素爆炸成多少个碎片,以及元素是显示还是隐藏:
| 选项 | 默认值 | 用法 |
|---|---|---|
mode |
"hide" |
设置在使用effect()方法时元素是显示还是隐藏。其他值是show、effect和toggle |
pieces |
9 |
设置元素爆炸成的碎片数量 |
动作时间 - 爆炸元素
在这个例子中,我们将使一个图片爆炸。
-
只需将以下简单的图片添加到模板文件的
<body>元素中:<img src="img/grenade.jpg" alt="手榴弹"> -
在模板文件底部的空函数中添加以下同样简单的代码:
$("img").click(function() { $(this).effect("explode"); }); -
将此页面保存为
explode.html。 -
这个示例非常简单,我们甚至不需要样式表。一旦我们点击手榴弹,它就会爆炸成默认数量的碎片:
![行动时间-爆炸一个元素]()
爆炸的元素随着元素的个体移开而逐渐消失。
刚刚发生了什么?
在示例中,我们所需要做的就是直接将点击处理程序附加到图像上,该处理程序使用effect()方法应用explode效果。在这种情况下,不需要配置,因为效果的默认mode是hide。
注意
请注意,我们还可以通过将mode选项设置为show或使用show()逻辑来以相反的方式运行此效果。在这种情况下,我们将看到目标元素由一系列淡入并飞到一起的碎片构成——一个逆向的爆炸。
折叠效果
fold效果模拟了沿一条轴折叠一些东西,然后沿另一条轴折叠一半。当然,元素在三维意义上并没有折叠;首先,元素的一侧移动了指定的量,然后另一侧被移进来,元素消失了。
默认情况下,该效果使用hide模式,因此在动画结束时会自动隐藏。被折叠的元素不会被缩放;它被剪切,因此在效果运行时图像和文本不会挤在一起。
语法
$(selector).hide|show|toggle|effect( "fold", [,configuration][,duration] );
配置选项
fold效果暴露了三个可配置选项,如下表所示:
| 选项 | 默认 | 用法 |
|---|---|---|
horizFirst |
false |
设置元素首先是否沿水平轴剪切 |
mode |
"hide" |
设置在使用effect()方法时元素是显示还是隐藏。其他值可能包括show,effect或toggle |
size |
15 |
这设置了以像素为单位的第一个折叠的距离,可以是整数,也可以是指定值的字符串,例如百分比 |
行动时间-将元素折叠起来
在此示例中,我们将折叠效果应用于一张简单的纸张图片。
-
我们只需要一张图片;将以下代码添加到模板文件的
<body>元素中:<img src="img/paper.jpg" alt="一张纸"> -
接下来,像以前的例子一样,在页面底部的空函数中添加以下简单脚本:
$("img").click(function() { $(this).effect("fold", { size: "50%" }, 1000); }); -
将此文件保存为
fold.html。 -
这是另一个我们不需要样式表的例子。当点击图像时,它应该折叠起来并消失:
![行动时间-将元素折叠起来]()
在上一屏幕截图中,我们可以看到图像首先开始,然后是效果隐藏了图像的下半部分,最后,图像的上半部分正在被隐藏。请注意,目标元素被剪裁而不是重新调整大小。
刚刚发生了什么?
我们只是在<img>元素上设置了一个点击处理程序,将应用fold效果。我们将size选项指定为50%,以便每个轴上的折叠量是相等的,并通过指定比默认持续时间长的1000毫秒稍微减慢效果。
puff 效果
puff效果通过指定的数量使应用效果的元素扩展,并淡去到消失,或者淡入然后略微缩小,具体取决于如何使用它。
语法
$(selector).hide|show|toggle|effect( "puff", [,configuration][,duration] );
配置选项
puff效果使我们能够控制元素增加的大小,以及它是显示还是隐藏:
| 选项 | 默认 | 用法 |
|---|---|---|
mode |
"hide" |
设置与effect()方法一起使用时元素是显示还是隐藏。其他可能的值包括show,effect和toggle |
百分比 |
150 |
设置元素以百分比进行缩放的大小 |
时间已到 – 让元素在一瞬间消失
在这个例子中,当单击确定或取消按钮时,我们将在浏览器窗口的中心显示一个对话框,并对其应用puff效果。
-
在我们的模板文件的
<body>元素中,添加以下对话框元素:<div id="confirm"> <img src="img/help.png" alt="帮助图标"> <p>您确定要这样做吗?</p> <button>确定</button><button>取消</button> </div> -
在空函数中添加附带的脚本如下:
$("#confirm").css({ left: $(window).width() / 2 - $("#confirm").width() / 2, 上:$(window).height() / 2 - $("#confirm").height() / 2 }); $("#confirm, button").click(function() { $("#confirm").effect("puff"); }); -
将此页面另存为
puff.html。在文本编辑器中的新文件中为对话框框添加以下样式:#confirm { 显示:块; 宽度:400px; 高度:120px; 绝对定位; 边框:1px 实心#ccc; 背景:#EEE; 字体:普通 13 像素无细体; } #confirm img { margin:20px 20px 0 20px; 浮动到左侧; } #confirm p { margin:40px 0 0 0; } #confirm button { 宽度:68px; 边距:20px 10px 0 0; 右浮动; } -
在
css目录中将此新文件保存为puff.css。 -
在浏览器中运行页面时,我们应该发现对话框最初位于窗口的中心,并且单击
<button>元素之一会使用puff效果关闭它:![行动时间 – 让元素在一瞬间消失]()
上一个截图显示了对话框扩展时正在消失。
刚刚发生了什么?
我们脚本的第一部分将对话框在窗口中垂直和水平居中。需要注意的一点是,我们不能使用margin:auto来居中对话框,因为在应用效果时它会丢失这些边距。
脚本的第二部分只是为每个<button>元素添加点击处理程序,在单击它们时应用puff效果。
滑动效果
slide效果与drop效果非常相似。唯一的区别是,使用slide效果时,目标元素的不透明度根本不会调整。它也与 jQuery 本身提供的滑动效果系列非常相似,尽管使用 jQuery UI 的slide效果,我们不限于垂直轴,我们也可以水平滑动。
语法
$(selector).hide|show|toggle|effect( "slide", [,configuration][,duration] );
配置选项
slide效果有三个配置选项,让我们可以指定滑动的方向和距离,以及是否显示或隐藏:
| 选项 | 默认 | 用法 |
|---|---|---|
direction |
"left" |
设置动画进行的方向 |
distance |
目标元素的宽度,包括填充 | 设置目标元素滑动的距离 |
mode |
"show" |
与effect()方法一起使用时设置元素显示或隐藏。其他可接受的值包括hide,effect和toggle |
行动时间 - 将元素滑动进出视图
当访客悬停在图像上时显示标题是一种交互式和有趣的方式,可以显示有关图像的其他信息,而不会使设计看起来凌乱。使用slide效果,我们可以轻松地动画显示和隐藏标题,这就是我们在这个示例中要做的。
-
将以下代码添加到模板文件的
<body>元素中:<div id="image"> <img src="img/mantis.jpg" alt="Praying Mantis"> <div>Praying Mantis: Mantis religiosa</div> </div> -
然后,在页面底部的空函数中,添加以下短脚本:
$("#image").hover(function() { $(this).find("div").stop(true, true).show("slide"); }, function() { $(this).find("div").stop(true, true).hide("slide"); }); -
将此文件保存为
slide.html。接下来,创建以下样式表:#image { position:relative; float:left; } #image img { margin-bottom:-5px; } #image div { display:none; width:100%; padding:10px 0; position:absolute; left:0; bottom:0; top:auto!important; text-align:center; font-style:italic; background-color:#000; color:#fff; } -
将此文件保存为
slide.css。 -
当我们查看页面时,应该发现当我们将鼠标移到图像上时立即显示标题,然后在我们将鼠标移出图像时将其移除:
![行动时间 - 将元素滑动进出视图]()
在前面的屏幕截图中,我们看到标题从容器的左边缘滑出。
刚刚发生了什么?
图像和标题都放在一个容器中,以便可以精确定位标题。我们使用 jQuery 的hover()方法,它允许我们为mouseover和mouseout事件附加事件处理程序,通过滑动显示标题,或者通过滑动隐藏标题。
在这个简单的例子中,我们不需要任何额外的配置,但是我们需要有效地管理队列,以防止鼠标重复在图像上移动导致动画的积累,我们使用stop()方法来处理这个问题。
缩放效果
scale效果与我们之前看过的size效果非常相似,正如我们看到的那样,几个效果实际上都需要这个效果作为依赖项。这个效果与size效果的主要区别在于,使用scale时,我们只能指定目标元素应该缩放到的百分比,而不能提供精确的像素大小。
语法
$(选择器).hide|show|toggle|effect( "scale", [,配置][,持续时间] );
配置选项
scale效果具有比 jQuery UI 添加的任何其他效果更多的配置选项。
配置选项列在以下表中:
| 选项 | 默认 | 用法 |
|---|---|---|
direction |
"both" |
设置元素沿着哪个轴缩放。其他选项包括vertical和horizontal |
from |
无 | 设置元素的起始尺寸 |
origin |
['middle', 'center'] |
设置元素的消失点(如果元素被隐藏),或者从哪里增长(如果元素正在显示) |
percent |
0 |
设置元素增长或缩小的百分比 |
scale |
"both" |
此选项设置元素的整个box(包括边框和填充的 CSS 值)是否缩放,只是content,或者像默认值both一样 |
行动时间——缩放元素
在图片较多的网站上,通常会显示一组缩略图,这些缩略图链接到点击图片后显示的全尺寸图片,可以是内联的模态弹出,也可以是单独的窗口。在本例中,我们将创建一个缩略图,当点击后会缩放为全尺寸版本。
-
将以下几个元素添加到模板文件的
<body>元素中:<div id="container"> <img src="img/europa.jpg" alt="Europa"> </div> -
我们需要的脚本有点长,但还是相当简单的。在页面末尾的空函数中,添加以下代码:
$("img").click(function() { var img = $(this); if(!img.hasClass("full")) { img.addClass("full").effect("scale", { 百分比: 400, 缩放: "盒子", origin: ['top','left'] }); } else { img.removeClass("full").effect("scale", { 百分比: 25, 缩放: "盒子", origin: ['top','left'] }); } }); -
将页面保存为
scale.html。在此示例的样式表中,我们需要以下代码:#container { 定位:相对; float:left; 光标:指针; } #container img { 宽度:150px; 高度:150px; } -
将此文件保存为
scale.css。 -
当我们运行页面时,应该发现点击图像会使其放大到其初始大小的 400%:
![执行步骤 - 缩放元素]()
前面的屏幕截图展示了效果的实际操作。再次点击图像将使图像缩放回初始状态。
刚才发生了什么?
在页面上,我们的图像存储在一个简单的<div>容器中。使用 CSS 将图像从其原始大小缩小,因此当我们放大图像时,实际上将其恢复到全尺寸,因此看起来不会有块状或模糊。
在脚本中,我们首先在图像上设置一个点击处理程序,然后缓存对其的引用,以便我们不必不断创建指向此的 jQuery 对象。如果图像没有full类名,我们知道图像尚未被放大,因此我们添加full类并使用percent选项将其放大 400%。
图像缩放后,我们创建一个新的锚元素,该元素将附加到容器元素并用作关闭按钮。我们设置链接的内部文本和href属性,然后为其分配一个点击处理程序。在此处理程序中,我们阻止浏览器跟随链接,然后再次缓存选择器,这次选择器指向锚点。
然后我们将图像缩小到其大小的四分之一,将其恢复到原始尺寸。完成此操作后,我们将删除关闭链接。
突发测试 - 使用显示/隐藏逻辑
Q1. 支持的参数如何传递给效果?
-
作为第二个参数的字符串格式,例如,
show("blind", "vertical") -
作为直接传递给
animate()方法的配置对象中的值,例如,animate({ effect: "blind", configuration: { direction: "vertical" }) -
作为第二个参数传递的配置对象中的值,例如,
show("blind", { direction: "vertical" }) -
通过设置
effect.config全局属性,例如,$.effect.config = { direction: "vertical" })
Q2. 还可以传递什么给该方法?
-
代表持续时间的整数或字符串,以及回调函数或函数引用
-
无
-
一个布尔值,控制动画是否应该无限重复
-
一个布尔值,指示是否应该排队或并行执行进一步的效果
亲自动手 - 尝试使用效果 API
我强烈建议您尝试在本节中查看的效果,以查看哪些效果与effect()方法配合使用效果良好,哪些效果最适合使用显示/隐藏逻辑,并且您可以清楚地看到在使用效果不佳时发生了什么。这应该可以提高您快速决定每种方法在何时何地适用的能力。
缓动函数
缓动可以与 jQuery UI 的所有效果一起使用,除了explode之外,尽管在一些效果中看起来可能有些奇怪,比如bounce或pulsate。如果 jQuery UI 存在,则还可以使用缓动与标准 jQuery 一起使用。
每种淡入淡出方法都可以通过将参数传递给正在使用的动画方法来设置缓动类型。滑动动画也是相同的,也可以接受缓动类型作为参数。让我们花一点时间了解一下缓动究竟是什么,以及如何将其与 jQuery 动画一起使用。
缓动是一种技术,在动画运行时改变动画的速度和/或方向。缓动可以使动画以缓慢的速度开始并逐渐加速,以快速开始并逐渐减速,并产生一系列其他效果。
jQuery 内置了两种缓动模式:linear和swing,其中swing是所有类型动画的默认模式。有时,使用linear缓动可以使连续动画运行更顺畅,但swing和linear之间的区别微妙至极。
注意
可以通过访问以下网址查看所有缓动类型的动画演示:
jQuery UI 插件的缓动类型列在以下表格中:
easeInQuad |
easeOutQuad |
easeInOutQuad |
|---|---|---|
easeInCubic |
easeOutCubic |
easeInOutCubic |
easeInQuart |
easeOutQuart |
easeInOutQuart |
easeInQuint |
easeOutQuint |
easeInOutQuint |
easeInExpo |
easeOutExpo |
easeInOutExpo |
easeInSine |
easeOutSine |
easeInOutSine |
easeInCirc |
easeOutCirc |
easeInOutCirc |
easeInElastic |
easeOutElastic |
easeInOutElastic |
easeInBack |
easeOutBack |
easeInOutBack |
easeInBounce |
easeOutBounce |
easeInOutBounce |
行动时间-将缓动添加到效果
要使用缓动,我们只需将缓动函数名称作为配置选项包含即可。例如,要为我们之前查看过的blind.html示例添加缓动,我们可以更改 JavaScript,使其如下所示:
$("#window").click(function() {
$("#blind").toggle("blind", { easing: "easeOutBounce" });
});
刚刚发生了什么?
我们使用配置选项easing,将缓动函数的名称作为字符串提供为选项的值。通过引用它们的名称,可以使用任何缓动函数。
使用对象字面量添加缓动
我们还可以改变我们传递给预定义动画方法的参数的格式,以便使用缓动。在 jQuery 的 1.4.3 版本之前,使用缓动与动画方法(fadeIn()、slideDown()等)的默认方法就是这样。
不是提供字符串或数字参数(或回调函数),我们可以提供一个对象文字,其中每个键都是持续时间、缓动类型,并且可选地是完成时要调用的回调。然后使用如下方式:
$(elements).toggle("blind", {
duration: [duration],
easing: [easing],
complete: [callback]
});
试一试吧——使用缓动
在一些早期示例中尝试一些其他缓动方法。在本书的剩余部分中,我们将在适当的地方使用缓动,但除了简要的解释之外,这些不会受到太多关注。
小测验——使用缓动
Q1. 总共有多少种缓动类型?
-
20
-
32
-
17
-
48
Q2. 我们在使用缓动的备用格式时可以传递什么给effect()方法?
-
一个带有可选键的对象,指定持续时间、缓动类型和完成时要调用的函数
-
一个指定缓动类型的字符串
-
一个数组,其中第一个项目是持续时间,第二个是缓动类型,第三个是完成时要调用的函数
-
一个指定缓动持续时间的整数
在不同颜色之间进行动画
除了完整的缓动函数范围外,effects核心文件还为我们提供了在不同颜色之间吸引人且平滑地进行动画的能力。可以动画化几个 CSS 属性,包括color、background-color、border-color和outlinecolor。
jQuery UI 通过扩展 jQuery 的animate()方法来实现颜色动画,因此实现它的语法与使用animate()相同。对于任何其他目的,我们只需要目标其中一个以上的 CSS 属性,并提供有效的颜色值(十六进制、RGB/RGBa、HSL 等)。让我们看一个基本示例。
行动时间——在颜色之间进行动画
在本示例中,我们将使用颜色动画来显示表单字段已被留空。
-
在模板文件的
<body>中使用以下元素:<input><button id="search">搜索</button> -
要在单击
<button>时调用颜色更改,我们可以在文档底部附近的空函数中使用以下 JavaScript:$("#search").click(function (e) { e.preventDefault(); var input = $(this).prev(); if (input.val() == "") { input.animate({ backgroundColor: "#f78080", borderColor: "#a72b2e"}, 1200); }; }); -
将此页面保存为
color-animations.html。对于这个示例,我们实际上只需要一些样式。我们可能可以在页面的<head>元素中的<style>块中定义它们。我们只需使用以下 CSS:input { width:200px; border:2px solid #27659f; } -
当我们运行页面时,如果在文本字段为空时单击了
<button>元素,我们会看到文本字段的颜色会更改。
刚才发生了什么?
在此示例中,虽然 CSS 非常小,但是必需的,因为当颜色动画时,<input>字段将失去现代浏览器提供的任何吸引人的样式。设置我们正在动画化的 CSS 属性有助于防止这种丑陋的切换。
在脚本中,我们只需缓存一个指向<input>字段的选择器,然后测试字段是否为空。如果是,我们调用animate()方法,指定我们想要动画化的目标元素的方面。
类过渡
除了扩展 jQuery 的animate()方法以提供颜色动画外,jQuery UI 还扩展了一些 jQuery 的元素操作方法。以下方法被扩展以提供类过渡:
-
addClass() -
removeClass() -
toggleClass()
jQuery UI 还公开了一种用于在两个类之间过渡的新方法:switchClass()方法,它接受当前类和新类,以及持续时间、缓动和回调参数。
行动时间 - 在类之间过渡
我们可以重新调整以前的示例,以便使用一些类过渡方法。
-
将类名
default添加到<input>元素中,然后更改 JavaScript,使其如下所示:$("#search").click(function(e) { e.preventDefault(); var input = $(this).prev(); if (input.val() == "") { input.switchClass("default", "error", 1200); } else if (input.val() && input.hasClass("error")) { input.removeClass("error", 1200); } }); -
将新页面保存为
class-animation.html。我们还需要对样式表进行一些更改。创建一个新的样式表,并添加以下规则(或在页面的<head>元素中更改样式):input { width:200px; } input, .default { border:2px solid #27659f; } .error { border:2px solid #a72b2e; 背景颜色:#f78080; } -
将新文件保存为
class-animation.css。 -
在浏览器中运行页面,然后再次点击没有输入任何文本字段的
<button>元素。<input>字段应该过渡到error类,并且与上一个示例中的样子相同。然而,这一次,在<input>字段中输入一些文本,然后再次点击<button>元素。错误应该转换回默认值。
刚刚发生了什么?
这一次,如果<input>字段没有值,我们只需调用switchClass()方法,指定默认类的当前类,error的新类和1.2秒的持续时间。请注意,您必须为示例正确运行提供当前类和新类。
在条件的下一个分支中,我们检查<input>字段是否具有值和error类名。如果是,我们调用removeClass()方法,仅指定要删除的类和持续时间。需要持续时间以触发过渡效果。
在 CSS 中,我们使用default类名提供默认样式,通常适用于所有<input>字段。我们需要这样做,因为否则元素在正在移除error类时会丢失其样式,导致其恢复为标准的未样式化的<input>字段。
注意
性能:在使用 jQuery 时,最好是改变元素的类名而不是直接操作元素的style属性。因此,自然而然地会认为使用switchClass()比使用animate()更有效率。
然而,情况并非如此,Firebug 的分析工具会显示出来。在前面的例子中,如果将条件语句的第二个分支移除,并对页面、color-animation.html和class-animation.html进行分析,结果会显示color-animation.html胜出,优势约为 20 毫秒。
小测验 - 缓动、颜色和类动画
Q1. 如何指定缓动函数?
-
以字符串格式作为
effect()方法的第三个参数,例如,effect("blind", {}, "easeOutBounce") -
作为回调函数中的布尔值,例如,
effect("blind", function() { easeOutBounce = true }) -
不能使用缓动
-
以字符串格式作为缓动配置选项的值,例如,
effect("blind", { easing: "easeOutBounce" })
Q2. 哪种方法被扩展以产生颜色动画?
-
effect()方法 -
show()方法 -
animate()方法 -
switchClass()方法
总结
在本章中,我们研究了 jQuery UI 库添加的完整范围的效果。我们看到了它们如何与effect()方法或必要时与show()、hide()和toggle()方法一起使用。我们了解了每个效果所接受的配置参数以及在默认情况下使用时的默认值。
我们还介绍了 jQuery UI 如何扩展animation()、addClass()和removeClass()方法,以及添加的switchClass()方法,以便实现在颜色和类之间进行动画处理的能力。
从本章中要记住的要点包括:
-
jQuery UI 与 jQuery 结合可以使用 jQuery UI 下载构建器下载,该构建器会构建一个自定义包,如果需要,还会附带一个主题供您下载。
-
jQuery UI 向我们的动画工具包中添加了共计 14 个新的预定义效果。这些效果易于使用,但高度可配置。
-
effect()方法是指定效果、其配置选项、持续时间和回调函数的基本手段。 -
一些效果与
show()、hide()或toggle()方法配合使用效果更佳,并且与 API 的这一方面一样容易使用。 -
缓动函数直接内置到 jQuery UI 中,可以通过将它们指定为
easing配置选项的值来使用。 -
jQuery UI 还通过扩展 jQuery 的一些方法并添加新的
switchClass()方法,为我们提供了过渡元素颜色或类名的能力。
在下一章中,我们将切换回 jQuery,并研究自定义动画,包括自定义过渡、自制幻灯片、元素尺寸动画以及如何创建 jQuery 动画插件。
第七章:自定义动画
迄今为止我们已经看过的预定义效果在执行它们的任务时非常出色,但它们只能满足非常具体的需求,有时在需要更复杂的动画时可能会不够用。
在这些情况下,我们可以使用 jQuery 的animate()方法,它允许我们轻松定义自定义动画,可以像任务所需的那样复杂和专业化。这是我们将在本章中探讨的内容。
本章我们将涵盖的主题包括:
-
使用
animate()方法创建自定义动画 -
向方法传递参数
-
动画化元素的尺寸
-
动画化元素的位置
-
创建 jQuery 动画插件
-
使用我们创建的 jQuery 插件
动画方法
jQuery 的所有自定义动画都由animate()方法驱动。尽管该方法可以动画化几乎任何具有数值的样式属性,但该方法使用简单,只需几个参数。该方法可以如下使用:
$(elements).animate( properties [,duration] [,easing] [,complete] );
第一个参数应采用对象的形式,其中对象的每个属性都是我们想要动画的样式,与我们使用 jQuery 的css()方法非常相似。
正如我之前提到的,这可以是任何接受纯数值参数的 CSS 样式(颜色除外,尽管使用 jQuery UI 库,我们也可以动画化颜色。有关 jQuery UI 更多信息,请参见第六章,使用 jQuery UI 进行扩展动画)。jQuery 无法本机地动画化背景位置,但手动动画化此属性非常容易;有关此技术的更多信息,请参见第三章,背景动画。
持续时间、缓动和回调参数的格式与本书中早期的淡入淡出方法使用的格式相同(第二章,图像动画),并且使用方式完全相同。
逐属性缓动
自 jQuery 版本 1.4 起,您可以在单个animate()调用中设置逐属性缓动函数。因此,例如,如果我们正在动画元素的宽度和高度参数,我们可以对宽度动画使用线性缓动,对高度动画使用摆动缓动。这适用于 jQuery 内置的标准缓动函数,或我们在上一章中讨论的任何缓动函数(第六章,使用 jQuery UI 进行扩展动画)。
为了在每个属性的基础上为animate()方法提供缓动类型,我们需要提供一个数组作为我们正在动画化的属性的值。可以使用以下语法完成此操作:
$(elements).animate({
属性:[值,缓动类型]
});
一个 animate() 的替代语法
与单独使用持续时间、缓动和回调参数不同,我们可以将以下配置选项的配置对象传递给 animate() 方法,而不是单独使用这些参数:
-
duration -
easing -
complete -
step -
queue -
specialEasing
前三个选项(duration、easing 和 complete)与以标准方式将它们传递到方法中时的参数相同。然而,最后三个选项(step、queue 和 specialEasing)是有趣的,因为我们没有其他任何方式可以访问它们。
-
step选项允许我们指定一个在动画的每一步上执行的回调函数。 -
queue选项接受一个布尔值,控制动画是立即执行还是放入所选元素的队列中。 -
specialEasing选项允许我们为正在进行动画处理的每个单独样式属性指定一个缓动函数,使用以下替代语法使我们能够基于每个属性进行缓动。第二种用法的模式如下:$(elements).animate(properties [,configuration]);
像大多数(但不是全部)jQuery 方法一样,animate() 方法返回一个 jQuery 对象,以便可以将其他方法链接到它。像其他效果方法一样,对同一元素多次调用 animate() 将导致为该元素创建一个动画队列。如果我们想同时动画两个不同的样式属性,我们可以将所有所需属性都传递给 animate() 方法的第一个参数所传递的对象中。
动画一个元素的位置
animate() 方法能够动画处理对具有数值的任何 CSS 样式属性所做的更改,但颜色和背景位置除外。在此示例中,我们将使用 animate() 方法创建一个内容查看器,通过滑动它们的方式将不同的内容面板显示在视图中。
这种类型的小部件通常用于作品集或展示网站,是一种吸引人的方式来显示大量内容,而不会使单个页面混乱不堪。在此示例中,我们将会动画显示元素的位置。
行动时间 - 创建一个动画内容查看器
我们将重新开始添加底层标记和样式:
-
应该使用我们的模板文件将内容查看器的底层标记添加如下:
<div id="slider"> <div id="viewer"> <img id="image1" src="img/amstrad.jpg" alt="Amstrad CPC 472"> <img id="image2" src="img/atari.jpg" alt="Atari TT030"> <img id="image3" src="img/commodore16.jpg" alt="Commodore 64"> <img id="image4" src="img/commodore128.jpg" alt="Commodore 128"> <img id="image5" src="img/spectrum.jpg" alt="Sinclair ZX Spectrum +2"> </div> <ul id="ui"> <li class="hidden" id="prev"><a href="" title="Previous">«</a></li> <li><a href="#image1" title="Image 1" class="active">图像 1</a></li> <li><a href="#image2" title="Image 2">图像 2</a></li> <li><a href="#image3" title="Image 3">图像 3</a></li> <li><a href="#image4" title="Image 4">图像 4</a></li> <li><a href="#image5" title="Image 5">图像 5</a></li> <li class="hidden" id="next"><a href="" title="下一页">»</a></li> </ul> </div> -
将文件保存为
animate-position.html。 -
接下来,我们应该创建基本的 CSS。我指的是我们应该添加的 CSS,这些 CSS 对于内容查看器的正常运行至关重要,而不是给小部件添加主题或皮肤的样式。在创建插件时,将样式分离出来是一个很好的做法,这样小部件就与 jQuery UI 的 ThemeRoller 主题化机制兼容。
-
在文本编辑器中的新文件中,添加以下代码:
#slider { width:500px; position:relative; } #viewer { width:400px; height:300px; margin:auto; position:relative; overflow:hidden; } #slider ul { width:295px; margin:0 auto; padding:0; list-style-type:none; } #slider ul:after { content:"."; visibility:hidden; display:block; height:0; clear:both; } #slider li { margin-right:10px; float:left; } #prev, #next { position:absolute; top:175px; } #prev { left:20px; } #next { right:10px; } .hidden { display:none; } #slide { width:2000px; height:300px; position:absolute; top:0; left:0; } #slide img { float:left; } #title { margin:0; text-align:center; } -
将此文件保存在
css文件夹中,文件名为animate-position.css,并不要忘记从我们页面的<head>标签中链接到新样式表。现在在浏览器中运行页面,然后再我们进入脚本之前,看一下小部件在没有附带脚本的情况下的行为。您会发现,任何图像都可以通过单击其相应的链接来查看,仅使用 CSS 即可,在任何浏览器中都可以使用。前进和后退箭头会被我们的 CSS 隐藏,因为这些箭头在关闭 JS 时根本不起作用,并且当不显示图像标题时,但是小部件的核心功能仍然完全可访问。这被称为渐进增强,被许多人认为是 Web 开发的最佳实践。
刚刚发生了什么?
这个示例中的基本 HTML 构造非常简单。我们有一个用于内容查看器的外部容器,然后在其中,我们有一个用于内容面板(在此示例中是简单的图像)的容器,以及一个导航结构,允许查看不同面板。
我们在 CSS 文件中为一些元素添加了样式规则,这些元素并没有硬编码到基本标记中,但将在需要时根据需要创建。以这种方式做可以确保即使访问者禁用了 JavaScript,内容查看器仍然可用。
一个重要的要点是,我们创建并围绕图片包装的 #slide 包装元素具有等于单个图片的 height 参数和等于所有图片宽度之和的 width 参数。另一方面,#viewer 元素具有等于单个图片的 width 和 height 参数,因此一次只能看到一张图片。
当 JavaScript 被禁用时,图片将看起来像是堆叠在一起,但一旦创建了 #slide 包装元素,图片就会被设置为浮动以水平堆叠。
在这个示例中,我们将使用缓动效果;因此,请确保在 <body> 标记末尾的 jQuery 引用后直接链接到 jQuery UI:
<script src="img/jquery-ui.js"></script>
行动时间 – 初始化变量并准备小部件
首先,我们需要准备底层的标记并存储一些元素选择器。在我们新创建的 HTML 文件中的匿名函数之间添加以下代码:
$("#viewer").wrapInner("<div id=\"slide\"></div>");
var container = $("#slider"),
prev = container.find("#prev"),
prevChild = prev.find("a"),
next = container.find("#next").removeClass("hidden"),
nextChild = next.find("a"),
slide = container.find("#slide"),
key = "image1",
details = {
image1: {
position: 0, title: slide.children().eq(0).attr("alt")
},
image2: {
position: -400, title: slide.children().eq(1).attr("alt")
},
image3: {
position: -800, title: slide.children().eq(2).attr("alt")
},
image4: {
position: -1200, title: slide.children().eq(3).attr("alt")
},
image5: {
position: -1600, title: slide.children().eq(4).attr("alt")
}
};
$("<h2>", {
id: "title",
text: details[key].title
}).prependTo("#slider");
刚刚发生了什么?
首先,我们将所有图片放在一个新的容器 #viewer 中。我们将使用此容器来动画显示面板的移动。我们给这个新容器一个 id 属性,这样我们就可以在需要时轻松地从文档对象模型(DOM)中选择它。
这是我们稍后将要动画显示的元素。
接下来,我们缓存一些经常需要操作的元素的选择器。我们创建一个指向外部 #slider 容器的单个 jQuery 对象,然后使用 jQuery 的 find() 方法选择我们要缓存的所有元素,如上一页和下一页箭头。
还初始化了一个 key 变量,它将用于跟踪当前显示的面板。最后,我们创建了一个 details 对象,其中包含内容查看器中每个图像的信息。我们可以存储 slide 容器必须以像素为单位进行动画显示任何给定面板的 left 位置,并且我们还可以存储每个内容面板的标题。
每个面板的标题是从每个图像的alt属性中读取的,但如果我们使用其他元素,我们可以选择title属性,或者使用 jQuery 的 data 方法来设置和检索内容的标题。
<h2>元素用于标题是通过 JS 创建并插入到内容查看器中的,因为我们没有办法在不使用 JavaScript 的情况下更改它。因此,当访问者禁用 JS 时,标题是无用的,并且最好根本不显示。
在代码的第一部分中,我们做的最后一件事是从下一个按钮中移除hidden类名,以便显示它。
前一个链接(我指的是让访问者移动到序列中上一个图像的链接)最初不显示,因为第一个内容面板始终是页面加载时可见的面板,因此没有上一个面板可移动到。
行动时间 - 定义一个动画后的回调
接下来,我们需要一个函数,每次动画结束时都可以执行。在我们之前添加的代码下面添加以下代码:
function postAnim(dir) {
var keyMatch = parseInt(key.match(/\d+$/));
(parseInt(slide.css("left")) < 0) ? prev.show() : prev.hide();
(parseInt(slide.css("left")) === -1600) ? next.hide() : next.show();
if (dir) {
var titleKey = (dir === "back") ? keyMatch - 1 : keyMatch + 1;
key = "image" + titleKey;
}
container.find("#title").text(details[key].title);
container.find(".active").removeClass("active");
container.find("a[href=#" + key + "]").addClass("active");
};
刚刚发生了什么?
在代码的第二部分中,我们定义了一个函数,该函数在动画结束后调用。这用于进行一些可能需要重复执行的各种事务处理;因此,将它们捆绑到单个函数中比在事件处理程序中单独定义它们更有效。这是postAnim()函数,它可能接受一个参数,该参数指示滑块移动的方向。
这个函数中我们要做的第一件事是使用 JavaScript 的match()函数与正则表达式/\d+$/来从保存在key变量中的字符串中解析面板编号,我们在代码的第一部分中初始化了key变量,它始终指向当前可见的面板。
我们的postAnim()函数可能在使用数字链接选择面板时调用,也可能在使用上一个/下一个链接时调用。但是,当使用上一个/下一个链接时,我们需要key变量来知道当前显示的是哪个面板,以便移动到下一个或上一个面板。
然后我们检查第一个面板是否当前正在显示,方法是检查#slide元素的left CSS 样式属性。如果#slide元素为0,我们知道第一个面板是可见的,所以隐藏上一个链接。如果left属性小于0,我们显示上一个链接。我们进行类似的测试来检查最后一个面板是否可见,如果是,则隐藏下一个链接。只有当前隐藏的上一个和下一个链接才会显示。
然后我们检查是否已向函数提供了dir(方向)参数。如果有,我们必须通过阅读我们之前创建的keyMatch变量来确定当前显示的面板是哪个,然后根据dir参数是back还是forward来减去1或加上1。
结果保存回key变量,然后用于更新<h2>标题元素。当前面板的标题文本从我们的details对象中使用key变量获取。最后,我们将active类名添加到与可见面板对应的数字链接中。
虽然不是必要的,但在我们添加小部件皮肤时会用到。我们使用属性选择器选择正确的链接,该选择器与当前链接的href属性匹配。请注意,在此函数中我们不会创建任何新的 jQuery 对象;我们使用我们缓存的container对象和find()方法来获取我们需要的元素。
行动时间 - 为 UI 元素添加事件处理程序
现在滑块已经创建好了,我们可以添加驱动功能的事件处理程序了。将以下代码插入我们刚刚添加的postAnim函数下方:
$("#ui li a").not(prevChild).not(nextChild).click(function(e){
e.preventDefault();
key = $(this).attr("href").split("#")[1];
slide.animate({
left: details[key].position
}, "slow", "easeOutBack", postAnim);
});
nextChild.add(prevChild).click(function(e){
e.preventDefault();
var arrow = $(this).parent();
if (!slide.is(":animated")) {
slide.animate({
left: (arrow.attr("id") === "prev") ? "+=400" : "-=400"
}, "slow", "easeOutBack", function(){
(arrow.attr("id") === "prev") ? postAnim("back") : postAnim("forward")
});
}
});
刚刚发生了什么?
第一个处理程序绑定到用于显示不同面板的主链接上,使用 jQuery 的not()方法排除了上一个和下一个链接。我们首先使用preventDefault()方法停止浏览器跟随链接。
然后,我们从链接的href属性中提取面板名称来更新key变量。我们使用 JavaScript 的split()方法仅获取面板id而不是#符号。
最后,我们通过将其left CSS 样式属性设置为从details对象中提取的值来对滑动元素进行动画处理。我们使用key变量来访问position属性的值。
作为动画的一部分,我们将持续时间配置为 slow,将缓动配置为 easeOutBack,并将我们的 postAnim 函数指定为动画结束时要执行的回调函数。
最后,我们需要为用于导航到下一个或上一个图片的上一个/下一个链接添加点击处理程序。这两个链接可以共享一个单击处理程序。我们可以使用之前缓存的选择器 nextChild 和 prevChild,以及 jQuery 的 add() 方法来选择这两个链接,将它们都添加到一个 jQuery 对象中,以便将处理程序函数附加到这两个链接上。
我们再次使用 preventDefault() 阻止浏览器跟随链接。然后,我们使用 arrow 变量缓存对已点击链接的父级的引用,以便我们稍后可以轻松地引用它。这是因为在 animate() 方法的回调函数中,$(this) 关键字的作用域将是 #slide 元素,而不是被点击的链接。
然后,我们检查 #slide 元素是否正在进行动画处理,使用 :animated 过滤器进行检查。此检查很重要,因为它防止了查看器在重复点击其中一个链接时出现错误。
如果尚未进行动画处理,我们执行动画处理并将幻灯片元素向后或向前移动 400 像素(单个内容面板的 width 参数)。我们可以通过查看 arrow 变量引用的元素的 id 属性来检查点击了哪个箭头。
我们在动画方法中指定与之前相同的持续时间和缓动值,但是我们不是将 postAnim 函数的引用作为回调参数传递,而是传递一个匿名函数。在这个匿名函数中,我们确定点击了哪个链接,然后使用适当的参数调用 postAnim 函数。记住,这是必要的,以获取 details 对象的正确键,因为上一个链接和下一个链接都没有指向图片的 href 属性。
此时在浏览器中尝试页面,你会发现点击任何链接,包括上一个和下一个链接,都可以查看图片。这是小部件在此阶段应该出现的样子:

前一个屏幕截图显示了小部件处于未经过皮肤处理的状态,只有为其功能所需的 JavaScript。
为小部件添加皮肤
曾经有人说过,“杀鸡焉用牛刀”,这适用于小部件,也适用于猫。最后,让我们给小部件添加一些自定义样式,看看如何轻松地使小部件既有吸引力,又具有功能性。这些样式可以轻松更改,以重新设计小部件,赋予它完全不同的外观。
行动时间——添加新皮肤
在 animate-position.css 文件的底部,添加以下代码:
a { outline:0 none; }
#slider {
border:1px solid #999;
-moz-border-radius:8px;
-webkit-border-radius:8px;
border-radius:8px;
background-color:#ededed;
-moz-box-shadow:0 2px 7px #aaa;
-webkit-box-shadow:0 2px 7px #aaa;
box-shadow:0 2px 7px #aaa;
}
#title, #slider ul {
margin-top:10px;
margin-bottom:12px;
}
#title {
font:normal 22px "Nimbus Sans L", "Helvetica Neue",
"Franklin Gothic Medium", Sans-serif;
color:#444;
}
#viewer {
border:1px solid #999;
background-color:#fff;
}
#slider ul { width:120px; }
#slider ul li a {
display:block;
width:10px;
height:10px;
text-indent:-5000px;
text-decoration:none;
border:2px solid #666;
-moz-border-radius:17px;
-webkit-border-radius:17px;
border-radius:17px;
background-color:#fff;
text-align:center;
}
#slider #prev, #slider #next {
margin:0;
text-align:center;
}
#slider #prev { left:10px; }
#slider #prev a, #slider #next a {
display:block;
height:28px;
width:28px;
line-height:22px;
text-indent:0;
border:1px solid #666;
-moz-border-radius:17px;
-webkit-border-radius:17px;
border-radius:17px;
background-color:#fff;
}
#prev a, #next a {
font:bold 40px "Trebuchet MS", sans-serif;
color:#666;
}
#slider ul li a.active { background-color:#F93; }
刚刚发生了什么?
使用此代码,我们可以在不干扰任何控制其工作的内容的情况下为部件的所有视觉方面添加样式。我们为它添加了一些漂亮的圆角,并向部件添加了一个阴影,将数字链接变成了可点击的小图标,并为上一个和下一个链接设置了样式。颜色和字体也在此部分设置,因为它们显然也高度依赖于主题。
这些样式为部件添加了基本的中性主题,如下面的屏幕截图所示:

我们用来创建主题的样式是纯粹任意的,仅用于示例目的。它们可以根据需要在任何给定的实现中更改,以适应页面上的其他元素或站点的整体主题。
快速测验 - 创建一个动画内容查看器
Q1. animate() 方法可能传递哪些参数?
-
数组,其中数组项是要进行动画处理的元素、持续时间、缓动以及回调函数
-
第一个参数是一个包含要进行动画处理的样式属性的对象,可选地跟随着持续时间、缓动类型和回调函数
-
一个对象,其中每个属性都指向要进行动画的样式属性、持续时间、缓动以及回调函数
-
必须返回要进行动画处理的样式属性、持续时间、缓动以及回调函数的函数
Q2. animate() 方法返回什么?
-
包含已进行动画处理的样式属性的数组
-
已进行动画处理的元素的数组
-
用于链接目的的 jQuery 对象
-
一个布尔值,指示动画是否成功完成
有一个尝试的英雄 - 使图像查看器更具可伸缩性
在我们的动画内容查看器中,有固定数量的图片和硬编码的导航结构来访问它们。扩展内容查看器,使其能够使用不确定数量的图片。要做到这一点,您需要完成以下任务:
-
在运行时确定内容查看器中的图像数量,并根据图像数量设置
#slide包装元素的width参数 -
根据图像数量动态构建导航链接
-
动态创建
details对象,根据图像数量设置正确的left属性来显示每个图像
动画元素大小
就像在本章开头提到的那样,几乎任何包含纯数值的样式属性都可以使用animate()方法进行动画处理。
我们先看了如何通过操纵left样式属性来动画元素的位置,现在让我们继续看看如何通过操纵height和width样式属性来动画元素的大小。
在这个例子中,我们将创建图像包装器,可以通过操纵元素的大小来显示页面上任何图像的大尺寸版本。
时间行动 - 创建基础页面和基本样式
首先,我们将创建示例运行的基础页面。
-
将以下 HTML 代码添加到我们模板文件的
<body>标签中:<article> <h1>文章标题</h1> <p><img id="image1-thumb" class="expander" alt="An ASCIIZebra" src="img/ascii.gif" width="150" height="100">Lorem ipsum dolor...</p> <p><img id="image2-thumb" class="expander" alt="An ASCII Zebra" src="img/ascii2.gif" width="100" height="100">Lorem ipsum dolor...</p> </article> -
将示例页面保存为
animate-size.html。在这个示例中,我们将保持样式轻巧;在您的文本编辑器中的新文件中,添加以下代码:article { display:block; width:800px; margin:auto; z-index:0; font:normal 18px "Nimbus Sans L", "Helvetica Neue", "Franklin Gothic Medium", sans-serif; } article p { margin:0 0 20px; width:800px; font:15px Verdana, sans-serif; line-height:20px; } article p #image2-thumb { float:right; margin:6px 0 0 30px; } img.expander { margin:6px 30px 1px 0; float:left; } .expander-wrapper { position:absolute; z-index:999; } .expander-wrapper img { cursor:pointer; margin:0; position:absolute; } .expander-wrapper .expanded { z-index:9999; } -
将此文件保存为
animate-size.css放在css文件夹中。
刚才发生了什么?
HTML 可以是任何简单的博客文章,由一些文本和几张图片组成。要注意的是,每个图片都被赋予了一个id属性,以便可以轻松引用,并且每个图片实际上都是图片的全尺寸版本,通过width和height属性进行缩放。
所使用的样式纯粹是为了布置示例;实际上,使示例工作的代码很少。expander-wrapper 样式是为了正确定位叠加的图片而需要的,除此之外,样式完全是任意的。
我们把第二张图片向右浮动。再次强调,这并不是绝对必要的;仅仅是为了让示例更有趣一点。
行动时间 - 定义图片的完整大小和小尺寸
首先,我们需要指定每张图片的完整大小和小尺寸。将下面的代码放入我们刚刚创建的 HTML 文件内的匿名函数中:
var dims = {
image1: {
small: { width: 150, height: 100 },
big: { width: 600, height: 400 }
},
image2: {
小图:{ width: 100, height: 100 },
big: { width: 400, height: 400 }
}
},
webkit = ($("body").css("-webkit-appearance") !== "" && $("body").css("-webkit-appearance") !== undefined) ? true : false;
刚刚发生了什么?
我们创建了一个包含与每张图片文件名匹配的属性的对象。每个属性中包含另一个嵌套对象,其中包含 small 和 big 属性以及相关整数作为值。这是一种方便的存储结构化信息的方式,可以很容易地在脚本的不同点访问。
我们还创建了一个名为 webkit 的变量。在基于 WebKit 的浏览器中,向右浮动的图片的处理存在轻微错误。这个变量将保存一个布尔值,指示是否使用了 WebKit。
执行了一个测试,尝试读取 -webkit-appearance CSS 属性。在 WebKit 浏览器中,测试将返回 none,因为该属性未设置,但其他浏览器将返回空字符串或值 undefined。
行动时间 - 创建叠加图片
接下来,我们应该在页面上创建每张图片的一个几乎完全相同的副本,以用作叠加层。将以下代码添加到我们刚刚添加到 HTML 文件中的代码下方:
$(".expander").each(function(i) {
var expander = $(this),
coords = expander.offset(),
复制 = $("<img>", {
id: expander.attr("id").split("-")[0],
src:expander.attr("src"),
宽度:expander.width(),
高度:expander.height()
});
刚刚发生了什么?
在这个 <script> 标签的一部分,我们选择页面上的每张图片,并使用 jQuery 的 each() 方法对它们进行处理。我们设置了一些变量,缓存了对当前图片的引用,并使用 jQuery 的 offset() 方法将其相对于文档的坐标存储在页面上。
然后,我们为页面上的每张现有图片创建一个新的图片,为其增加一个 id 属性,与它重叠的图片配对,原始图片的 src 变量以及原始图片的 width 和 height 参数。当设置新图片的 id 属性时,我们使用 JavaScript 的 split() 函数去掉字符串中标有 thumb 的部分。
注意事项
请注意,上述代码不代表完整的完全功能代码片段。each()方法传递给的外部函数尚未关闭,因为我们需要在这些变量之后添加一些额外的代码。
行动时间-创建覆盖包装器
现在我们需要为每个覆盖图像创建包装器(请注意,此代码仍在each()方法内,因此将为具有expanded类名的每个图像执行此代码)。直接在我们刚刚添加的each函数的最后一行下面添加以下代码:
$("<div></div>", {
"class": "expander-wrapper",
css: {
top: coords.top,
left: (webkit === true && expander.css("float") === "right") ? (coords.left + expander.width()) : coords.left,direction: (expander.css("float") === "right") ? "rtl" : "ltr"
},
html: copy,
width: expander.width(),
height: expander.height(),
click: function() {
var img = $(this).find("img"),
id = img.attr("id");
if (!img.hasClass("expanded")) {
img.addClass("expanded").animate({
width: dims[id].big.width,
height: dims[id].big.height
}, {
queue: false
});
} else {
img.animate({
width: dims[id].small.width,
height: dims[id].small.height
}, {
queue: false,
complete: function() {
$(this).removeClass("expanded");
}
});
}
}
}).appendTo("body");
刚刚发生了什么?
在此代码部分中,我们为新图像创建包装器元素。我们给它一个新的类名,以便可以正确定位。
提示
引用类属性
我们需要在class属性名称周围使用引号,因为它是 JavaScript 中的保留字,如果不这样做可能会引发脚本错误。
我们使用css属性和从offset()方法中获取的坐标来设置包装器元素的位置。
设置包装器元素的left位置时,我们需要检查我们的webkit变量,以查看是否正在使用 Safari 或 Chrome。如果此变量设置为true,并且图像被浮动到右侧,我们将根据原始图像的width参数以及cords.left值定位覆盖层。如果webkit变量为false,或者原始图像浮动到left,我们只需将包装器的left位置设置为存储在coords.left中的值。
我们还需要设置任何浮动到右侧的图像的direction属性。我们检查float样式属性,并设置direction为rtl如果图像浮动到右侧,或者如果没有,则设置为ltr。这是使用 JavaScript,三元条件完成的。
这个检查是为了在图像浮动right时,使包装器从右向左扩展。如果我们没有设置这个,包装器将从左向右打开,这可能导致全尺寸图像溢出视口,或者内容容器出现滚动条。
通过将对其的引用传递到 jQuery 的html()方法中,我们将新图像添加到包装器中,并将包装器的width参数设置为原始(和新)图像的width参数。这对于正确定位覆盖在任何向右浮动的图像上是必要的。
接下来,我们向包装器添加一个点击处理程序。在作为click()方法值传递的匿名函数内部,我们首先缓存了在包装器中被点击的图像的引用,并为方便起见获取了图像的id属性。请记住,覆盖图像的id属性将与其覆盖的原始图像相同,减去文本字符串-thumb。
然后,我们检查图像是否具有类名expanded。如果没有,我们添加类名,然后使用animate()方法的第二种格式将图像动画变为其全尺寸。我们将两个对象作为参数传递给该方法;第一个包含我们希望动画的 CSS 属性,在本例中是图像的width和height参数。
获取要增加图像的正确width和height参数是使用被点击的图像的id属性作为键从dims对象中检索的。在传递给animate()方法的第二个对象中,我们将queue属性设置为false。这与直接在animate()方法之前使用stop()方法具有相同的效果,并确保在重复点击叠加包装器时不会发生任何不好的事情。
如果图像已经具有类名expanded,我们将图像动画变回其小尺寸。同样,我们使用animate()方法的两个对象格式,将false作为queue属性的值,并在传递给complete属性的匿名回调函数中删除类名expanded。创建包装器后,我们将其附加到页面的<body>标签。
在这一点上,我们编写的代码将按预期工作 - 单击图像将导致扩展版本动画变为其全尺寸。但是,如果页面被调整大小,叠加将不再覆盖其图像。
行动时间 - 维护叠加位置
由于叠加位置是绝对定位的,我们需要防止它们在窗口调整大小时错位:
$(window).resize(function() {
$("div.expander-wrapper").each(function(i) {
var newCoords = $("#image" + (i + 1) + "-thumb").offset();
$(this).css({
top: newCoords.top,
left: newCoords.left
});
});
});
刚刚发生了什么?
我们所需要做的就是确保叠加图像在页面调整大小时直接位于原始图像的顶部,我们可以通过将调整事件的处理程序绑定到window对象来实现。在处理程序函数中,我们只需获取底层图像的新坐标,并相应地设置包装器的top和left属性。请注意,我们不会对叠加层的重新定位进行动画处理。
保存文件并在浏览器中预览。我们应该发现,我们可以点击任一图像,它都会展开显示图像的全尺寸版本,第一个图像展开到右侧,第二个图像展开到左侧:

在前一个截图中,我们看到第一个图像展开到了它的全尺寸。
突击测验——创建展开图像
Q1. 在这个例子中,我们使用了一个不同的格式来传递给 animate() 方法的参数。这些参数采用了什么样的格式?
-
两个数组,第一个数组包含要动画的元素的选择器,第二个数组包含持续时间、缓动、
specialEasing字符串和回调函数 -
包含要动画的样式属性、持续时间、缓动和
specialEasing字符串,以及step和complete回调函数的单个对象 -
必须返回要动画的样式属性、持续时间和缓动字符串以及回调函数的函数
-
两个对象,第一个对象包含要动画的样式属性,第二个对象包含持续时间、缓动和
specialEasing字符串,一个布尔值指示是否排队重复调用animate(),以及步进和完成的回调函数
Q2. 动画的回调函数中的关键字 this 被限定在哪个范围?
-
被动画的元素
-
当前窗口
-
被动画的元素的容器
-
事件对象
挑战英雄——消除硬编码的 dims 对象
在前一个例子中,我们在脚本顶部硬编码了一个图像,用于告诉 animate() 方法应该将图像动画到什么大小。虽然这在例子中是可以的,但作为长期解决方案,它并不是一个很好的扩展方式,因为我们必须记住每次使用脚本时都要设置它(或者确保我们的图像始终是固定大小的)。
问题在于我们没有办法从单个图像中编程方式获取全尺寸和缩略图尺寸。好消息是,任何可以存储在 JavaScript 对象中的数据也可以作为 JSON 对象传递到网络上供消费。扩展此示例,使页面加载时将页面上图像的 src 属性传递到服务器,服务器返回包含小图像和大图像尺寸的 JSON 对象。在这里,图像处理库,如 PHP 的 GD 或 ImageMagick,ImageResizer,或者 .NET 中的 System.Drawing.Image 类型,将是你的朋友。
创建一个 jQuery 动画插件
插件是将功能打包成易于部署和共享的代码模块的绝佳方式。jQuery 提供了 fn.extend() 方法来实现这一目的,使得创建强大而有效的插件变得轻而易举,这些插件可以轻松分发和使用。
创建 jQuery 插件时应遵循一些准则。具体如下:
-
新方法应像其他 jQuery 方法一样调用,例如,
$(elements).newMethod(),应该附加到fn对象,而新函数,例如,$.myFunction(),应该附加到jQuery对象 -
当插件被压缩时,新方法和函数应始终以分号(
;)结尾以保持功能性。 -
在方法内部,
this关键字始终指向当前元素的选择,并且方法应始终返回this以保留链式调用 -
除非使用带有别名
$对象的匿名函数,否则始终将新方法和函数附加到jQuery对象,而不是$别名
在本节中,我们将创建一个插件,用于在显示一系列图像时创建高级转换效果。完成的小部件在某些方面类似于我们之前创建的图像查看器,但不会对图像本身进行动画处理。相反,它将在显示它们之间应用转换效果。
行动时间 - 创建一个测试页面并添加一些样式
再次,我们将首先创建示例页面和基本样式,然后最后再添加脚本。
-
此示例的底层 HTML 非常简洁。在模板文件的
<body>标记中,我们只需要以下元素:<div id="frame"> <img class="visible" src="img/F-35_Lightning.jpg" alt="F-35 Lightning"> <img src="img/A-12_Blackbird.jpg" alt="A-12 Blackbird"> <img src="img/B-2_Spirit.jpg" alt="B-2 Spirit"> <img src="img/SR-71_Blackbird.jpg" alt="SR-71 Blackbird"> <img src="img/F-117_Nighthawk.jpg" alt="F-117 Nighthawk"> </div> -
将此页面保存为
advanced-transitions.html。 -
像标记一样,我们为插件依赖的 CSS 也应尽可能简洁。幸运的是,我们的小型元素集合所需的 CSS 不多。
-
将以下代码添加到文本编辑器中的新文件中:
#frame { 位置:相对; 宽度:520 像素; 高度:400 像素; 层级:0; } #frame img { 位置:绝对; 顶部:0; 左:0; 层级:1; } #frame img.visible { 层级:2; } #frame a { 显示:块; 宽度:50%; 高度:100%; 位置:绝对; 顶部:0; 层级:10; 颜色:透明; 背景图片:url(transparent.gif); 滤镜:alpha(opacity = 0); 文本对齐:居中; 文本装饰:无; 字体:90 像素 "Palatino Linotype","Book Antiqua", 帕拉蒂诺,衬线; 行高:400%; } #frame a:hover { 颜色:#fff; 文本阴影:0 0 5 像素 #000; 滤镜:alpha(opacity=100); 滤镜:阴影(颜色=#000,方向=0); } #frame a:focus { 轮廓:无; } #prev { 左:0; } #next { 右:0; } #overlay { 宽度:100%; 高度:100%; 位置:绝对; 左:0; 顶部:0; 层级:3; } #overlay div { 位置:绝对; } -
将其保存在
css文件夹中,命名为advanced-transitions.css。
刚发生了什么?
我们在基础页面上唯一拥有的就是我们希望在容器内进行转换的图像。最好尽可能简化插件的标记要求,以便其他人可以轻松使用,并且不会对他们想要使用的元素或结构施加不必要的限制。
图像在容器内通过 CSS 绝对定位,使它们彼此叠加,并且我们在第一个元素上设置了visible类,以确保其中一个图像位于堆栈的顶部。
大多数样式都用于上一个和下一个锚点,我们将使用插件创建这些锚点。这些被设置为每个锚点将占据容器的一半,并且被定位为并排显示。我们设置这些链接的z-index属性,使它们显示在所有图像的上方。font-size属性被大幅提高,过多的line-height意味着我们不需要使用padding来使文本居中。
在大多数浏览器中,我们只需将锚点的color属性设置为transparent,即可隐藏它们。然后,我们在hover状态下将color属性设置为白色。然而,在 IE 中,这种方法效果不佳,因此我们最初将链接设置为透明,并使用 Microsoft 的opacity filter,然后在hover状态下将其设置为完全不透明,其目的相同。
注意
另一个针对 IE 的修复
IE 也给我们带来了另一个问题:由于链接的绝对定位,其可点击区域仅会延伸到其中的文本高度。我们可以通过设置背景图像的引用来克服这一问题。
最好的部分是即使图像不存在也可以使修复工作(因此您在书籍的附带代码包中找不到对应的transparent.gif文件)。该修复对于正常浏览器没有不利影响。
创建插件
现在,让我们创建插件本身。与我们看过的大多数其他示例代码不同,我们插件的代码将放入自己的单独文件中。
行动时间 – 添加许可证和定义可配置选项
在新文件中,创建插件的以下外部结构,并将其保存在名为jquery.tranzify.js的js文件夹中:
/*
插件名称 jQuery 插件版本 1.0
版权所有(c)日期版权所有者
许可证
*/
;(function($) {
$.tranzify = {
defaults: {
transitionWidth: 40,
transitionHeight: "100%",
containerID: "overlay",
transitionType: "venetian",
prevID: "prev",
nextID: "next",
visibleClass: "visible"
}
};
})(jQuery);
刚刚发生了什么?
所有插件都应包含插件名称、版本号、版权所有者(通常为代码的作者)以及发布的许可证或许可证链接的条款信息。
插件被封装在一个匿名函数中,以便其变量受到在其部署的页面上可能正在使用的其他代码的保护。它还在其前面放置了一个分号,以确保在潜在的缩小之后它仍然保持为一个离散的代码块,并且以防它与比我们自己不那么严谨的其他代码一起使用。
我们还将$字符别名为安全地在我们的函数中使用,以确保它不会被页面上运行的任何其他库劫持,并保留 jQuery 的noConflict()方法的功能。
将插件尽可能地可配置是一个好习惯,以便最终用户可以根据自己的需求进行调整。为了方便起见,我们应该为任何可配置选项提供一组默认值。在决定将什么内容设为可配置时,一个好的经验法则是将除了纯逻辑之外的所有内容都硬编码到插件中。因此,ID、类名之类的东西应该可配置。
我们为插件设置的默认值存储在一个对象中,该对象本身作为传递给函数的jQuery对象的属性存储。添加到jQuery对象的属性称为tranzify,这是我们插件的名称,并将用于存储我们创建的属性、函数和方法,以便我们所有的代码都在一个单一的命名空间中。
我们的默认属性包含在一个名为defaults的单独对象中,该对象位于tranzify对象内部。我们设置了过渡元素的width和height参数,创建的容器的id属性,默认过渡效果,上一个和下一个链接的id属性,以及我们给当前显示的图像的类名。
正如我提到的,如果可能的话最好不要将任何id值或类名硬编码到插件中。实施插件的人可能已经在页面上有一个id属性为overlay的元素,因此我们应该给他们更改的选项。
行动时间 - 将我们的插件方法添加到 jQuery 命名空间
接下来,我们可以添加代码,将我们的插件插入到 jQuery 命名空间中,以便像其他 jQuery 方法一样调用它。在我们刚刚添加的代码的最后一行之上直接添加以下代码:
$.fn.extend({
tranzify: function(userConfig) {
var config = (userConfig) ? $.extend({}, $.tranzify.defaults, userConfig) : $.tranzify.defaults;
config.selector = "#" + this.attr("id");
config.multi = parseInt(this.width()) / config.transitionWidth;
$.tranzify.createUI(config);
return this;
}
});
刚刚发生了什么?
jQuery 专门提供了fn.extend()方法来添加可以链接到jQuery()函数的新方法,这是大多数插件创建的方式。我们将一个函数定义为传递给extend()方法的对象的唯一属性的值。我们还指定该方法可能会接受一个参数,这个参数可能是由使用插件的人传递给方法的配置对象,以改变我们设置的默认属性。
我们的方法首先要做的是检查是否有配置对象传入方法中。如果有,我们使用extend()方法(不过这里不是fn.extend())来将用户的配置对象与我们自己的defaults对象合并。
通过合并这两个对象创建的结果对象,存储在变量config中,以方便我们的函数访问。在userConfig对象中找到的任何属性将覆盖存储在defaults对象中的属性。在defaults对象中找到但在userConfig对象中找不到的属性将被保留。如果未传递userConfig对象到方法中,我们简单地将defaults对象赋值给config变量。
接下来,我们建立了一个id选择器,用来匹配被调用的方法的元素,并将其作为额外的属性添加到config对象中,这样在整个插件中使用起来更加方便。我们不能将这个作为默认属性存储,因为它很可能在插件使用的每个页面上都是不同的,而且我们也不能期望插件的用户每次使用插件时都要在配置对象中定义这个。
我们需要创建的过渡元素的数量将取决于图像的大小和过渡元素的宽度(定义为可配置属性),因此我们根据图像的宽度计算出一个快速乘数,然后配置过渡宽度以便稍后使用。
接着,我们调用将创建前/后链接的函数(我们将很快定义它),并传递函数,config对象,以便它可以读取用户配置的任何属性。
最后,我们返回 jQuery 对象(它会自动分配给插件方法内的this关键字的值)。这是为了保留链接,以便用户在调用我们的插件后可以调用其他 jQuery 方法。
行动时间–创建 UI
接下来,我们需要创建在图像上方叠加的前一个和后一个链接,让访问者可以浏览图像。在刚刚添加的$.fn.extend()部分下面,添加以下代码块:
$.tranzify.createUI = function(config) {
var imgLength = $(config.selector).find("img").length,
prevA = $("<a></a>", {
id: config.prevID,
href: "#",
html: "«",
click: function(e) {
e.preventDefault();
$(config.selector).find("a").css("display", "none");
$.tranzify.createOverlay(config);
var currImg = $("." + config.visibleClass, $(config.selector));
if(currImg.prev().filter("img").length > 0) {
currImg.removeClass(config.visibleClass).prev().addClass(config.visibleClass);
} else {
currImg.removeClass(config.visibleClass);
$(config.selector).find("img").eq(imgLength - 1).addClass(config.visibleClass);
}
$.tranzify.runTransition(config);
}
}).appendTo(config.selector),
nextA = $("<a></a>", {
id: config.nextID,
href: "#",
html: "»",
click: function(e) {
e.preventDefault();
$(config.selector).find("a").css("display", "none");
$.tranzify.createOverlay(config);
var currImg = $("." + config.visibleClass, $(config.selector));
if(currImg.next().filter("img").length > 0) {
currImg.removeClass(config.visibleClass).next().addClass(config.visibleClass);
} else {
currImg.removeClass(config.visibleClass);
$(config.selector).find("img").eq(0).addClass(config.visibleClass);
}
$.tranzify.runTransition(config);
}
}).appendTo(config.selector);
};
刚刚发生了什么?
这是到目前为止我们的最大函数,处理创建前后链接以及在创建时使用 jQuery 语法定义它们的点击处理程序。我们要做的第一件事是获得容器中的图像数量,因为我们添加的点击处理程序需要知道这一点。
我们为上一个链接创建了锚点,并在作为第二个参数传递的对象中定义了id属性(使用来自config对象的值)、一个虚拟的href、一个 HTML 实体作为其innerHTML以及一个点击处理程序。
在点击处理程序中,我们使用preventDefault()方法阻止浏览器跟随链接,然后隐藏上一个和下一个链接,以保护小部件免受多次点击的影响,因为这会破坏过渡效果。
接下来,我们调用我们的createOverlay()函数,传递config对象,以创建叠加容器和过渡元素。我们还使用存储在config对象中的类名缓存对当前选择的图像的引用。
然后我们测试是否有另一个图像元素位于可见图像之前。如果有,我们从当前具有该类的元素中删除该类,并将其给予前一个图像,以将其移到堆栈顶部。如果在当前图像之前没有更多图像,则从当前图像中删除visible类,并移至容器中的最后一个图像以显示该图像。
一旦我们定义了所需的一切,我们就可以将新的锚点附加到指定的容器中。我们还在当前函数内创建了下一个链接,给它一个非常相似的一组属性和一个点击处理程序。在这个点击处理程序中唯一不同的是,我们测试当前图像后面是否有图像,并且如果没有图像,则移动到容器中的第一个图像。
行动时间 - 创建过渡覆盖
我们的下一个函数将处理创建叠加层和过渡元素:
$.tranzify.createOverlay = function(config) {
var posLeftMarker = 0,
bgHorizMarker = 0
overlay = $("<div></div>", {
id: config.containerID
});
for (var x = 0; x < config.multi; x++) {
$("<div></div>", {
width: config.transitionWidth,
height: config.transitionHeight,
css: {
backgroundImage: "url(" + $("." + config.visibleClass, $(config.selector)).attr("src") + ")",
backgroundPosition: bgHorizMarker + "px 0",
left: posLeftMarker,
top: 0
}
}).appendTo(overlay);
bgHorizMarker -=config.transitionWidth;
posLeftMarker +=config.transitionWidth;
}
overlay.insertBefore("#" + config.prevID);
};
刚刚发生了什么?
我们之前的函数处理了创建遮罩容器和将提供过渡动画的过渡元素。插件将需要分别设置每个过渡元素的 position 和 background-position 属性,以便水平堆叠元素。我们将需要一些计数器变量来实现这一点,因此我们在函数开始时对它们进行初始化。
然后,我们创建了遮罩容器 <div>,并且只给它设置了一个 id 属性,以便我们在运行过渡时可以轻松选择它。
接下来,我们创建过渡元素。为此,我们使用标准的 JavaScript for 循环,根据脚本中之前设置的乘数执行若干次。在循环的每次迭代中,我们创建一个新的 <div>,根据存储在配置对象中的属性设置其 width 和 height 参数。
我们使用 css() 方法将遮罩的 backgroundImage 属性设置为当前可见图像,并根据当前的 bgHorizMarker 计数器变量的值设置 backgroundPosition 属性。我们还设置 left 属性以正确地根据 posLeftMarker 变量定位新元素,并将 top 属性设置为 0 以确保正确的定位。
创建完成后,我们将新元素附加到容器并增加计数器变量。一旦循环退出,并且我们已经创建并附加了所有过渡元素到容器中,我们就可以将容器附加到页面上调用该方法的元素上。
行动时间 - 定义过渡
最终的函数将执行实际的过渡:
$.tranzify.runTransition = function(config) {
var transOverlay = $("#" + config.containerID),
transEls = transOverlay.children(),
len = transEls.length - 1;
switch(config.transitionType) {
case "venetian":
transEls.each(function(i) {
transEls.eq(i).animate({
width: 0
}, "slow", function() {
if (i === len) {
transOverlay.remove();
$(config.selector).find("a").css("display", "block");
}
});
});
break;
case "strip":
var counter = 0;
function strip() {
transEls.eq(counter).animate({
height: 0
}, 150, function() {
if (counter === len) {
transOverlay.remove();
$(config.selector).find("a").css("display", "block");
} else {
counter++;
strip();
}
});
}
strip();
}
};
刚刚发生了什么?
我们的最后一个函数处理实际运行的过渡。 在这个例子中,只有两种不同类型的过渡,但我们可以很容易地扩展它以添加更多的过渡效果。
这个函数还需要一些变量,所以我们在函数的开头设置这些变量以供以后使用。 我们缓存对覆盖容器的引用,因为我们将多次引用它。 我们还存储了过渡元素的集合和过渡元素的数量。 我们从子项的数量中减去1,因为这个数字将与 jQuery 的eq()方法一起使用,该方法是基于零的。
为了确定我们要运行哪个过渡,我们使用 JavaScript 的switch语句并检查config.transitionType属性的值。 第一个过渡是一种威尼斯百叶窗效果。 要运行此过渡,我们只需使用 jQuery 的each()方法将每个元素的width参数动画化为0。 我们指定为此方法的参数的函数自动接收当前元素的索引,我们使用i来访问它。
对于每个动画的回调函数,我们检查i是否等于过渡元素的length,如果是,则移除覆盖层并再次显示上一个和下一个链接。
第二个过渡一次一个条带地移除旧图像。 为此,我们使用一个简单的counter变量和一个标准的 JavaScript 函数。 这次我们不能使用each()方法,否则所有的过渡元素将同时下滑,但我们希望每个元素都自己下滑。
在函数内部,我们将当前过渡元素的高度动画化为0,并设置一个相当低的持续时间,以便它发生得相当快。 如果动画太慢,它会破坏效果。 在回调函数中,我们检查我们的counter变量是否等于过渡元素的数量,如果是,则移除覆盖层并再次显示链接。 如果此时counter变量尚未达到最后一个元素,则递增counter变量并再次调用该函数。
将此文件保存为jquery.tranzify.js,并将其放在js文件夹中。 这是 jQuery 插件的标准命名约定,应遵循。
使用插件
要使用该插件,我们只需像调用任何其他 jQuery 方法一样调用它,在我们的 ready 函数或匿名函数内部,如下所示:
<script>
$(function() {
$("#frame").tranzify();
});
</script>
在这种形式下,将使用默认属性。 如果我们想要更改其中一个属性,我们只需提供一个配置对象,例如:
$("#frame").tranzify({
transitionType: "strip"
});
默认动画应该运行如下:

在上一张截图中,我们看到过渡元素同时缩小到0 width,产生了一种威尼斯百叶窗被打开以显示新图像的效果。
使用这个插件很简单;只需要记住一点。所有图像的大小都应该相同,并且每个图像的width参数都应该能够被transitionWidth属性完整地除尽。由于我们已经将transitionWidth属性公开为可配置属性,我们应该能够使用任何大小的图像,并相应地进行设置。
供参考,第二个过渡效果是这样运行的,旧图像的条纹滑开以显示新图像:

在前面的截图中,我们可以看到第二种过渡类型的效果,旧图像被剥去以显示新图像。
爆笑测验 - 创建插件
Q1.插件方法和函数有什么区别?
-
在概念上和实践上,它们是一样的,没有区别
-
方法可以接受参数,而函数不行
-
方法执行更快
-
方法附加到
fn对象上,并像现有的 jQuery 方法一样使用,而函数直接附加到 jQuery 对象上,并像任何普通函数一样调用
Q2.每个新方法必须返回什么?
-
包含所选元素的
id属性的字符串 -
包含所选元素的
id属性的数组 -
this对象指向当前选择的元素 -
什么都不应该被返回
有一个尝试英雄 - 扩展插件
我们的插件目前只包含了两种过渡效果(百叶窗和条纹)。扩展插件以包括自己设计的更多过渡效果。插件目前创建了一些与每个图像高度相同的过渡元素。
通过将现有的for循环包裹在另一个for循环中,并添加一些新的计数变量来控制top位置和垂直background-position,可以比较容易地以棋盘风格添加正方形过渡元素,这样就可以实现更复杂和更有吸引力的过渡效果。做到这一点。
总结
在本章中,我们看了一些animate()方法的常见用法,这是我们在 jQuery 中创建自定义动画的手段,当内置效果不能满足我们的要求时。这个方法强大、易于使用,并使复杂的动画变得轻而易举。
当简单的滑动或淡出不能满足我们的要求时,我们可以退而使用animate()方法来制作我们自己的高质量自定义动画。我们学到了关于animate()方法的以下要点:
-
animate()方法可用于动画化任何数字类型的 CSS 属性(除了颜色,需要使用 jQuery UI)。 -
传递到方法中的参数可以采用两种格式之一。第一种允许我们传递一个包含要执行动画的 CSS 属性的对象,以及单独的持续时间、缓动和回调参数。第二种格式允许我们传递两个对象,第一个对象允许我们像以前一样指定要执行动画的 CSS 属性,而第二个对象允许我们指定附加选项,比如持续时间、缓动和回调。第二种选项让我们可以访问一些在第一种格式中无法访问的特殊参数,比如
specialEasing和step回调。 -
所有在第一个对象中指定的 CSS 属性将同时执行。
-
如何实现涉及元素位置或其尺寸的动画。
我们还研究了如何通过插件形式扩展 jQuery 库的全新功能和方法。插件是将代码封装起来以便轻松部署和共享的绝佳方式。
现在我们已经看过了所有 jQuery 的动画方法,在下一章中,我们将看看其他流行的动画,比如添加鼠标和键盘事件以及动画化帖子链接。
第八章:其他热门动画
此章将遵循与上一章类似的格式,并由一系列示例式的示例组成,展示动画在实际操作中的实现。我们不会约束自己—一切皆有可能!
我们将在本章中查看以下示例:
-
接近动画,其中动画是对鼠标指针接近目标元素或页面区域的反应
-
一个动画的页眉元素
-
文本滚动的跑马灯组件
理解近性动画
常按近性动画,这通常由鼠标指针相对于页面上一个元素或一系列元素的位置驱动,是一种令人敬畏的效果。虽然并非适用于所有网站和所有环境,但在特定情况下使用时,它可以增加真正的魅力。
这种效果通常并不非常实用,并且基本上关闭了非鼠标用户的大门,但它可以作为额外的奖励(通常称为渐进增强)实施给能够利用它的访客,同时提供其他更可访问的交互形式。
在本示例中,我们将创建一个图像滚动器,当鼠标指针进入其容器时将触发。图像滚动的速度将由鼠标指针与容器中心的距离决定。移动指针将相应地减慢或加快动画速度。
行动时间—创建和样式化页面
在本示例的这一部分中,我们将创建动画将在其上运行的基础页面,并添加样式。
-
首先,我们将创建默认页面,并添加示例的 CSS。将以下元素添加到模板文件的
<body>元素中:<div id="proximity"> <img src="img/proximity1.jpg" alt="CH-47 Chinook"> <img src="img/proximity2.jpg" alt="Mi-24W"> <img src="img/proximity3.jpg" alt="Mil Mi-24A"> <img src="img/proximity4.jpg" alt="AH-1J Cobra"> <img src="img/proximity5.jpg" alt="Mi-24P"> <img src="img/proximity6.jpg" alt="AH-1Z Viper"> <img src="img/proximity7.jpg" alt="AH-1W Cobra"> <img src="img/proximity8.jpg" alt="UH-1Y Huey"> <img src="img/proximity9.jpg" alt="AH-64 Apache"> <img src="img/proximity10.jpg" alt="AH-1W Super Cobra"> <img src="img/proximity11.jpg" alt="MI-28 Havoc"> <img src="img/proximity12.jpg" alt="AH-1W Super Cobra"> <img src="img/proximity13.jpg" alt="AH-1W Cobra"> <img src="img/proximity14.jpg" alt="Mi-24 HIND E"> <img src="img/proximity15.jpg" alt="AH-1W Super Cobra"> <img src="img/proximity16.jpg" alt="UH-1N Huey"> <img src="img/proximity17.jpg" alt="AH-64D Apache"> <img src="img/proximity18.jpg" alt="UH-1N Huey"> <img src="img/proximity19.jpg" alt=" Lempira Bell 412"> <img src="img/proximity20.jpg" alt="UH-60L Blackhawk"> </div> -
将此文件另存为
proximity.html。接下来,我们将添加一些 CSS。在新文件中,添加以下代码:/* 基础类(已禁用脚本) */ #proximity { width:960px; margin:auto; border:1px solid #000; -moz-border-radius:8px; -webkit-border-radius:8px; border-radius:8px; } #proximity img { border:1px solid #000; } /* scripting enabled classes */ #proximity.slider { width:550px; height:250px; position:relative; overflow:hidden; } .slider #scroller { position:absolute; left:0; top:0; } .slider #scroller img: display:block; width:150px; height:150px; margin:50px 0 0 50px; float:left; color:#fff; background-color:#000; } .slider #scroller img:first-child { margin-left:0; } #message { width:100%; height:30px; padding-top:10px; margin:0; -moz-border-radius:0 0 8px 8px; -webkit-border-bottom-radius:8px; -webkit-border-bottom-right-radius:8px; border-radius:0 0 8px 8px; position:absolute; bottom:0; left:0; background-color:#000; color:#fff; text-align:center; font:18px "Nimbus Sans L", "Helvetica Neue", "Franklin Gothic Medium", Sans-serif; } -
将其保存在
css文件夹中,命名为proximity.css,并不要忘记从 HTML 页面的<head>中链接到它。
刚才发生了什么?
保持 HTML 尽可能简单和轻便,我们只需将要显示的图像添加到一个容器元素中。我们需要的任何额外元素都可以以渐进增强的方式动态添加。
CSS 文件中有两个部分。第一部分是基本样式的集合,如果页面由禁用 JavaScript 的访问者加载,则使用这些样式。这确保所有图像都是可见的,因此可访问 - 没有隐藏或其他遮挡。
第二部分改变了容器元素的外观,并为动态添加的元素或类添加了样式,以改变滑块的外观,前提是启用了 JavaScript。
我们设置容器的height和width,以便任何时候只有三个图像可见,并将其overflow样式属性设置为hidden,以便所有其他图像都被隐藏,准备滚动到视图中。
我们还为具有id为scroller的元素添加了定位。此元素尚不存在,将由稍后查看的脚本添加。此元素还需要一个width,但我们可以根据容器中的图像数量动态分配。
我们还改变了图像本身的样式,将它们设置为块级元素,并将它们向左浮动,以便它们在一行中水平堆叠,而不会换行到两行,因为这样会破坏滚动条的功能。浮动图像并设置容器的width,允许它们按水平方向堆叠。我们将添加一个告诉访客如何使用滚动条的消息,因此我们还包括了一些用于此目的的样式。
以下截图显示了页面在禁用脚本时的外观:

在前面的图像中,我们可以看到所有图像都是可见的。这不太好看,但非常易于访问,并且在客户端禁用脚本时不会隐藏内容。
开始行动-为滑动功能准备页面
当脚本启用时,我们可以增强页面以添加近距离滑块所需的附加元素。将以下代码添加到 HTML 页面底部的空函数中:
var prox = $("#proximity"),
scroller = $("<div></div>", {
id: "scroller"
}),
pointerText = "使用指针滚动,移动到"+
"边缘滚动更快!",
keyboardMessage = "使用箭头键滚动图像!",
message = $("<p></p>", {
id: "message",
text: keyboardMessage
});
prox.addClass("slider").wrapInner(scroller).append(message);
var middle = prox.width() / 2;
scroller = $("#scroller");
scroller.width(function() {
var total = 0;
scroller.children().each(function(i, val) {
var el = $(this);
total = total + (el.outerWidth() + parseInt(el.css("marginLeft")));
});
return total;
}).css("left", "-" + (scroller.width() / 2 - middle) + "px");
刚刚发生了什么?
首先,我们缓存了近距离容器的选择器,在这段代码中我们会使用几次,在脚本稍后的地方还会使用几次。接下来,我们创建一个新的 <div> 元素,并给它一个 id 属性,这样我们在需要时可以轻松地再次选择它。我们还使用这个 id 进行样式处理。
接下来,我们为了方便起见,将一些文本字符串存储在变量中。这些将用作在不同点显示给访问者的消息。我们还创建一个新的段落元素作为消息文本的容器,为元素设置一个 ID(再次是为了选择的目的),并使用 jQuery 的text()方法设置其innerText为其中一个文本字符串。然后,我们在传递给元素创建 jQuery 方法格式的第二个参数的对象上使用text属性,它会自动映射到text()方法。
接下来,我们向外部近距离容器添加一个类名。请记住,这个类名用于区分脚本启用和禁用,以便我们可以添加所需的样式。我们还将近距离容器的内容(20 个 <img> 标签)包装在我们新创建的滚动条元素中,并将消息附加到近距离容器。
接下来,我们设置一个变量,它等于近距离容器的 width 除以二。这给了我们元素的水平中心,这将是我们需要在一些计算中使用的,以定位滚动条元素,并计算鼠标指针相对于近距离容器的位置。
我们可以很容易地设置middle变量需要包含的数字,而不是以这种方式计算它。接近容器的width(启用脚本)在我们的 CSS 文件中设置,并且与此特定示例高度任意。但是,如果我们直接在变量中设置数字而不是通过程序计算它,那么如果更改了其width,脚本将中断。尽量避免将“魔术”数字硬编码到脚本中是最好的。
此时,我们还需要缓存对滚动条元素的引用,因为它已附加到页面上。我们不能使用我们在脚本开始时创建的scroller变量的内容,因此我们通过再次从页面中选择该元素来用新引用覆盖它。
现在,我们需要设置scroller元素的width,以便它足够宽以容纳单行中的所有图像。为此,我们将一个函数传递给 jQuery 的width()方法,该函数返回要设置的width。
该函数通过迭代每个图像并将其width和水平margin相加到total变量中来计算此数字。这意味着可以使用不确定数量的图像而无需更改脚本,并且可以使用具有不同宽度和间距的图像。
设置了scroller元素的width后,我们需要将其定位,以使滚动条的中心位于接近容器的中心。这样,当页面加载时,访问者可以将其向左或向右移动,这取决于他们移动鼠标指针的位置或按下哪个箭头键。
如果此时在浏览器中加载页面,我们应该发现页面上元素的外观已更改。

在前一个屏幕截图中,我们可以看到接近容器已调整大小,并且scroller元素居中于其中。我们还可以看到接近容器底部的默认消息。
行动时间 - 动画滚动条
代码的下一部分涉及基于鼠标指针相对于外部接近容器的位置实际动画化scroller元素。在}).css("left", "-" + (scroller.width()行下面添加以下代码:
function goAnim(e) {
var offset = prox.offset(),
resetOffset = e.pageX - offset.left - middle,
normalizedDuration = (resetOffset > 0) ? resetOffset : -resetOffset,
duration = (middle - normalizedDuration) * 50;
scroller.stop().animate({
left: (resetOffset < 0) ? 0 : "-" + (parseInt(scroller.width()) - parseInt(prox.width()))
}, duration, "linear");
}
刚刚发生了什么?
在 goAnim() 函数内部,我们首先获取接近容器的 offset 值,以便了解其相对于文档的位置。然后我们计算鼠标指针相对于接近容器中心的位置。这意味着在数值上,当鼠标指针位于中心时,指针偏移量将为 0。
如果鼠标指针位于接近容器的左半部分,resetOffset 变量中的数字将为负数。这将导致我们在函数后面的计算出现错误,因此我们需要检查 resetOffset 变量是否大于 0,如果不是,我们使用其负值来取反。
最终,我们希望随着指针移向接近容器的任一端,滚动条的速度增加,并且当指针移向中心时减速。换句话说,动画的速度需要与指针距离接近容器中心的距离成反比。
此时我们遇到的问题是,表示指针距离接近容器中心的数字随着指针移向边缘而增大,因此如果将此数字用作动画的持续时间,动画将减速而不是加速。
为了取反存储在 normalizedDuration 变量中的值,我们将其从表示接近容器中心的值中减去,然后将得到的数字乘以 50。持续时间参数以毫秒为单位,因此如果我们不使用乘数(50 是通过反复试验得出的),动画将发生得太快。
现在我们可以启动动画了。我们使用 JavaScript 三元运算符来测试 resetOffset 数字是否小于 0,如果是的话,我们知道要让滚动条向右滑动,只需将滚动条元素的 left 样式属性设置为 0。
如果变量大于 0,我们必须将滚动条元素向负方向移动(向左)以显示右侧隐藏的图像。为了使滚动条 <div> 元素的右边缘与接近容器的右边缘对齐,我们将动画的终点设置为滚动条 <div> 元素的 width 减去接近容器的 width。
行动时间 - 添加鼠标事件
现在,我们需要添加触发动画的鼠标事件。以下代码将添加在我们之前添加的两行代码下面:
}, duration, "linear");
}
在上述代码的下面添加以下代码行:
prox.mouseenter(function(e) {
message.text(pointerText).delay(1000).fadeOut("slow");
goAnim(e);
prox.mousemove(function(ev) {
goAnim(ev);
});
});
prox.mouseleave(function() {
scroller.stop();
prox.unbind("mousemove");
});
刚刚发生了什么?
首先,我们设置一个 mouseeenter 事件处理程序,以便我们可以检测指针最初进入接近容器的时候。当这种情况发生时,我们更改消息文本,以便显示指针该如何操作,然后在一秒的延迟后缓慢淡出消息。
我们然后调用我们的 goAnim() 函数来开始动画。此时,我们设置了一个 mousemove 事件,以便在接近容器内移动指针时增加或减少动画的速度。每次指针移动时,我们再次调用 goAnim() 函数。每次调用此函数时,我们都会传入事件对象。
我们还在接近容器上设置了一个 mouseleave 事件处理程序,以便我们可以检测指针何时完全离开此元素。当发生这种情况时,我们会停止当前正在运行的动画并解绑 mousemove 事件处理程序。
此时,我们应该有一个完全可用的接近滑块。稍早,我们讨论了接近效果仅对鼠标用户有用,因此让我们向脚本中添加一个键盘事件处理程序,以便键盘用户也可以导航滚动条。
行动时间 - 添加键盘事件
现在,我们将启用键盘驱动的动画。我们将专门为键盘上的左右箭头键添加触发器。
在我们刚刚在前一节中添加的 prox.mouseleave 函数下方添加以下代码:
$(document).keydown(function(e) {
//37 = 左箭头 | 39 = 右箭头
if (e.keyCode === 37 || e.keyCode === 39) {
message.fadeOut("slow");
if (!scroller.is(":animated")) {
scroller.stop().animate({
left: (e.keyCode === 37) ? 0 : -(scroller.width() - prox.width())
}, 6000, "linear");
}
}
}).keyup(function() {
scroller.stop();
});
刚刚发生了什么?
我们将 keydown 事件处理程序附加到 document 对象上,以便访问者不必以某种方式聚焦接近容器。在匿名函数内部,我们首先检查左箭头键或右箭头键是否被按下。
按键码 37 指的是左箭头键,而代码 39 指的是右箭头键。jQuery 规范化了 keyCode 属性,以便所有浏览器都可以访问,该属性将包含按下的任何键的代码,但我们只想对按下的这两个键中的任何一个做出反应。
当按下这两个键中的任何一个时,我们首先淡出消息,然后检查滚动条是否已经在使用 jQuery 的 is() 方法与 :animated 过滤器结合使用。
只要 scroller 元素尚未被动画化(在条件开始处使用 ! 符号表示),我们就会对其进行动画处理。我们再次使用 JavaScript 三元条件来检查 keyCode 属性,以便根据按下的键移动滚动条的方向。
最后,我们添加了一个keyup事件处理程序,一旦释放键就停止滚动动画。这提高了动画的互动性,因为它允许访问者在希望时直观地停止滚动器。
尝试一下英雄 – 扩展接近动画
扩展我们示例的明显方法是在垂直轴上触发动画。我们可以有一个图像网格而不是单行,并且还可以向上和向下以及向左和向右移动网格。
扩展示例的一件事情是添加额外的键盘功能。例如,检查额外的键,如 home 和 end 键,这些键可以相应地导航到scroller元素的开头或结尾。
突击测验 – 实施接近动画
Q1. 我们在上一个示例中通过添加键盘可导航性提供了额外的功能;为什么?
-
为了好玩
-
为了看起来好
-
为了提供另一种内容被非使用鼠标的用户探索的方式
-
当使用鼠标事件时,必须绑定键盘事件
Q2. 为什么我们应该避免在脚本中硬编码'魔法'数字?
-
为了使我们的代码更易读
-
这样我们的脚本就不那么依赖于它们所操作的内容了
-
编写硬编码的整数需要更长时间来处理
-
因为 jQuery 更喜欢使用字符串
动画页面标题
另一种非常时尚的技术是在主页加载时在页面的页眉中运行动画。有时动画在站点的每一页上持续运行;在主页上只运行一次。
这种技术是使您的网站脱颖而出的一种简单有效的方式,它们不需要复杂或非常明显的动画;一个简短、微妙的动画就足以增加惊叹号!的因素。
本书的前面部分中,我们研究了在与一个预先编写的文件一起使用cssHooks的情况,该文件利用了 cssHooks,它扩展了 jQuery 的css()方法,以允许对元素的background-position样式属性进行动画处理。在这个例子中,我们将看看如何在不使用插件的情况下手动实现这一点。
设计良好的插件可以是一种有效且简便的解决方案,但有时插件添加的功能远远超出我们实际需要的范围,因此会增加页面的脚本开销。重复造轮子并不经常是必要或明智的,但有时编写一个只做我们需要的事情的自定义脚本是有益的。
行动时间 – 创建一个动画页眉
此示例的基础页面将相对简单,只有一个放置在<body>标签中的空的<header>元素,我们将手动对其background-position进行动画处理:
-
示例页面的页眉将只包括一个空的
<header>元素,放置在<body>标签内部:<header> </header> -
将此保存为
animated-header.html。CSS 更简单,只有一个选择器和几条规则:header { display:block; width:960px; height:200px; margin:auto; background:url(../img/header.jpg) repeat 0 0; } -
将此保存为
animated-header.css。我们需要从我们刚创建的页面的<head>链接到该文件。 -
脚本本身也非常简单。将以下代码添加到
<body>元素末尾的函数中:var header = $("header"); header.css("backgroundPosition", "0 0"); var bgscroll = function() { var current = parseInt(header.css( "backgroundPosition").split(" ")[1]), newBgPos = "0 " + (current - 1) + "px"; header.css("backgroundPosition", newBgPos); }; setInterval(function() { bgscroll() }, 75); -
当我们在浏览器中运行该文件时,应该会发现用于
<header>的背景图片会缓慢滚动。
刚刚发生了什么?
在脚本中,我们在主函数之外缓存 header 选择器以提高效率,这样我们不会在每次函数执行时都创建新的 jQuery 对象。虽然 <header> 元素在函数之外以变量形式缓存,但变量仍然可以被函数访问。
在函数中,我们首先获取 header 元素当前的垂直 background-position,使用 JavaScript 的 split() 函数提取我们需要的字符串部分。我们还使用 parseInt 将字符串转换为整数。
接着我们递减整数一次。这意味着背景图片会向上滚动。这并不重要。当然,图片也可以向下滚动,我个人只是偏好向上移动的动作。最后,我们使用 jQuery 的 css() 方法设置新的 background-position。
在函数定义之后,我们使用 JavaScript 的 setInterval() 方法每 75 毫秒调用一次函数。这相对来说很快,但非常顺滑,如果速度更快,动画会开始有点卡顿。然而,不同的背景图片可能不需要以如此快的速度运行。
尝试一下英雄 - 扩展动态页眉
由于示例太简单,可以进行许多延伸。根据所使用的背景图片,可以扩展为沿水平轴移动,甚至可能同时移动,也许朝西北方向对角线移动。
使用 marquee 效果实现文本动画
<marquee> 元素的使用在许多年前就已经消失了,但是最近几年,由于在知名网站上的应用,如新闻网站标题的滚动字幕和旧版 Twitter 首页上的动态热门话题,使用 JavaScript 创建的类似效果重新出现。
这是一种有效和吸引人的方式,可以向访问者呈现潜在相关的内容,而不会占用太多内容空间。当然,并不适用于所有网站,但适度使用,并尽可能不引人注意,可以产生很好的效果。
行动时间 - 创建和设计基础页面
在这个示例中,我们可以看到多么容易地抓取一系列文本字符串并以平滑滚动的走马灯样式显示它们。我们将使用 jQuery 内置的 AJAX 功能从我的博客的最新帖子中抓取一个 JSON 文件。让我们开始吧。
-
在模板文件的
<body>元素中添加以下标记:<div id="outer"> <header> <hgroup> <h1>网站标题</h1> <h2>网站描述</h2> </hgroup> <nav>主站导航在这里</nav> </header> <article> <h1>一篇博客文章标题</h1> <p>帖子内容</p> </article> <aside> <div> <h2>广告</h2> <p>可能有一堆广告在这里,占用旁白的合理部分垂直空间</p> </div> <div> <h2>热门文章</h2> <p>这里有一些链接到其他帖子的链接,这些帖子可能与当前帖子相关,也可能不相关,但基于评论数量,它们被认为是热门的</p> </div> <div> <h2>相关帖子</h2> <p>这里有一些链接到其他帖子的链接,这些链接与当前帖子肯定相关,基于帖子标签</p> </div> <div> <h2>Twitter 动态流</h2> <p>也许这里有一个显示最近推文或其他内容的 Twitter 动态流。现在旁白可能已经相当长了。</p> </div> </aside> </div> -
将新页面保存为
marquee.html。 -
此时,我们还可以添加一些基本的 CSS 来以一种可接受的通用方式布局示例。在您的文本编辑器中的新文件中,添加以下代码:
#outer { width:960px; margin:auto; color:#3c3c3c; font:normal 17px "Palatino Linotype", "Book Antiqua", Palatino, serif; } header { display:block; padding:0 20px 0; margin-bottom:40px; border:3px solid #d3d1d1; background-color:#e5e5e5; } hgroup { float:left; } h1, h2 { margin-bottom:10px; } nav { display:block; width:100%; height:40px; clear:both; text-align:right; } article { width:700px; height:900px; border:3px solid #d3d1d1; background-color:#e5e5e5; float:left; } article h1, article p { margin:20px; } p, nav{ font:normal 17px "Nimbus Sans L", "Helvetica Neue", "Franklin Gothic Medium", Sans-serif; } p { margin-top:0; } [旁白](https://example.org/aside) { width:220px; height:900px; border:3px solid #d3d1d1; background-color:#e5e5e5; float:right; } aside div { padding:0 20px 20px; } -
将此文件保存为
marquee.css在css目录中。从我们刚刚创建的页面的<head>元素链接到这个样式表。
刚刚发生了什么?
底层 HTML 表示一个典型的博客。我们添加了一系列元素有两个原因,主要是为了在这里插入走马灯,但也是为了我们能够看到为什么这种方法是必要的。
最新帖子在网站顶部滚动,确保此内容立即被看到,并且它是动画的事实也有助于吸引访问者的注意。
迄今为止所使用的 CSS 纯粹是为了以准确而略微美学的方式布局示例元素,为我们提供通用布局和轻微的外观设计。稍后我们将在示例中添加更多 CSS,用于我们动态创建的走马灯。此时,页面应该如下所示:

记住,前一个屏幕截图中的所有元素都是为了插入跑马灯而存在的。它们不是特别必需的,并且仅用于此示例。
行动时间 - 检索和处理帖子列表
现在,我们已经准备好检索最新帖子列表并处理它们,使它们准备好作为跑马灯中的项目显示。为了从另一个域通过互联网访问这些数据,我们需要使用 JSONP,它代表 JSON with Padding,并涉及动态创建和注入 <script> 元素到页面中,尽管实际上是 jQuery 为我们处理了这个方面。
注意
更多关于 JSONP 的信息可以在这些精彩文章中找到:remysharp.com/2007/10/08/what-is-jsonp 和 jquery4u.com/json/jsonp-examples
-
jQuery 提供了对 JSONP 的原生支持,并允许我们绕过浏览器的同源安全策略。为了以正确的格式输出 JSON,我正在使用 WordPress 驱动的博客上的 JSON API (
wordpress.org/plugins/json-api) 插件,该插件以以下格式输出 JSON:{ "status": "ok", "count": 1, "count_total": 1, "pages": 1, "posts": [ { "id": 1, 等等... }, { "id": 2, 等等... } ] } -
在前一个代码块中显示的
posts数组中还有更多的属性,以及外部对象中的其他数组和属性,但是前面的代码片段应该给您一个关于我们将要处理的数据结构的概念。 -
将以下代码添加到我们 HTML 页面的匿名函数中:
$.getJSON("http://adamculpepper.net/blog?json=1&count=10&callback=?", function(data) { var marquee = $("<div></div>", { id: "marquee" }), h2 = $("<h2></h2>", { text: "最近的帖子:" }), fadeLeft = $("<div></div>", { id: "fadeLeft" }), fadeRight = $("<div></div>", { id: "fadeRight" }); for(var x = 0, y = data.count; x < y; x++) { $("<a></a>", { href: data.posts[x].url, title: data.posts[x].title, html: data.posts[x].title }).appendTo(marquee); } marquee.wrapInner("<div></div>").prepend(h2).append(fadeLeft).append(fadeRight).insertAfter("header").slideDown("slow"); $("#marquee").find("div").eq(0).width(function() { var width = 0; $(this).children().each(function() { var el = $(this); width += el.width() + parseInt(el.css("marginRight")); }); return width; }); marquee.trigger("marquee-ready"); }); -
我们还可以添加一些更多的 CSS 样式,这次是为新创建的元素。在
marquee.css的底部添加以下代码:#marquee { display:none; height:58px; margin:-20px 0 20px; border:3px solid #d3d1d1; position:relative; overflow:hidden; background-color:#e5e5e5; } #marquee h2 { margin:0; position:absolute; top:10px; left:20px; } #marquee a { display:block; margin-right:20px; float:left; font:normal 15px "Nimbus Sans L", "Helvetica Neue", "Franklin Gothic Medium", Sans-serif; } #marquee div: margin:20px 0 0 210px; overflow:hidden; } #marquee div:after { content:""; display:block; height:0; visibility:hidden; clear:both; } #fadeLeft, #fadeRight { width:48px; height:21px; margin:0; position:absolute; top:17px; left:210px; background:url(../img/fadeLeft.png) no-repeat; } #fadeRight { left:906px; background:url(../img/fadeRight.png) no-repeat; } -
当我们现在运行页面时,我们应该看到新的滚动条元素及其链接被插入到页面中。
![执行动作的时间 - 检索和处理帖子列表]()
之前的截图显示了新滚动条部分中的元素,包括标题、链接本身和仅用于美观的淡出元素。
刚刚发生了什么?
我们所有的 JavaScript 都包含在 jQuery 的getJSON()方法中,该方法使用 jQuery 的 AJAX 功能向指定为方法第一个参数的 URL 发出请求。第二个参数是一个匿名函数,如果请求成功,则执行该函数。返回的 JSON 数据会自动传递给此函数。
在函数内部,我们首先创建一些组成我们滚动条的元素,包括外部容器、标题和两个纯粹用于在链接行的开头和结尾添加左右淡出效果的<div>元素。所有这些元素都存储在变量中,以便在需要时轻松访问。
接下来,我们处理传递给函数的 JSON 对象。请记住,该对象包含一系列属性,其中一些属性的值是数组,比如posts数组,它包含每个返回的帖子作为其数组项中的对象。
我们使用for循环遍历返回的posts数组中的每个对象。此对象包含一个名为count的属性,其中以整数形式存储了返回的帖子数,因此我们可以使用这个来告诉for循环执行多少次,这比计算posts数组中的对象稍微容易一些。
对于返回的每个帖子,我们创建一个新的<a>元素,将其href设置为当前对象的url属性,将元素的title和text设置为当前对象的title属性,然后将新的<a>元素附加到我们一分钟前创建的marquee元素中。
一旦我们为每个帖子创建并附加了一个链接,我们就会将滚动条元素(链接)的内容包裹在一个新的<div>元素中,将<h2>元素前置到滚动条的开头,并将淡出的<div>元素追加到marquee元素的末尾。然后我们将滚动条附加到页面,然后使用slideDown()方法将其滑入视图中。
在这一点上,我们需要在我们刚刚包裹链接的容器的<div>元素上设置一个width。这样,链接就可以排成一行。我们需要考虑每个链接的width值,加上它的任何margin(我们在 CSS 中设置的)。
我们使用一个函数作为 jQuery 的width()方法的值,来迭代每个链接,并将其width和margin添加到一个运行总数中。直到滚动字幕被添加到页面上,我们才能执行此操作,因为在此时每个元素实际上才具有我们可以检索的width或margin。
我们在getJSON()方法的回调函数中最后要做的一件事是,使用trigger() jQuery 方法触发一个自定义事件。自定义事件称为marquee-ready,用于告诉我们的脚本marquee已被添加到页面中。我们将很快使用这个自定义事件来对帖子链接进行动画处理。
我们还在样式表中添加了一些新的 CSS。其中一些代码是为了给我们的marquee元素提供与页面其余部分相同的浅色皮肤。但其中的其他部分,比如浮动链接,并将 marquee 的overflow属性设置为hidden,是为了使链接排成一行,并且大多数链接都是隐藏的,准备好滚动到视图中。我们还将淡入的图片添加到marquee元素内的最后两个<div>元素中。
行动时间 - 动画化帖子链接
我们现在准备开始在 marquee 中滚动帖子链接。我们可以使用我们的自定义事件来完成这个任务。
-
在
getJSON()方法之后,向页面添加以下代码:$("body").on("marquee-ready", "#marquee", function() { var marquee = $(this), postLink = marquee.find("a").eq(0); width = postLink.width() + parseInt(postLink.css("marginRight")), time = 15 * width; postLink.animate({ marginLeft: "-=" + width }, time, "linear", function() { $(this).css({ marginLeft: 0 }).appendTo(marquee.find("div").eq(0)); marquee.trigger("marquee-ready"); }); }); -
我们的示例现在已经完成。当我们此时运行页面时,帖子应该会从左向右滚动。
刚发生了什么?
我们使用 jQuery 的on()方法将事件处理程序绑定到我们的自定义marquee-ready事件上。我们需要使用on()事件来实现这一点,因为当此部分代码被执行时,JSON 响应不太可能返回,因此marquee元素甚至都不存在。将事件处理程序附加到页面的<body>元素是准备页面准备好marquee元素时的一种简单方法。
在匿名事件处理函数内部,我们首先使用this对象(作用域限于我们的marquee元素)缓存了对 marquee 元素的引用。然后,我们选择滚动字幕中的第一个链接,并确定其包括margin在内的总width。
我们还计算了动画的有效速度。jQuery 动画使用持续时间来确定动画运行的速度,但这给我们带来的问题是,标题较长的帖子将移动得更快,因为它们在相同时间内需要动画的距离更长。
为了解决这个问题,我们计算出一个持续时间,以传递给动画方法,该持续时间基于任意速度15乘以当前<a>元素的宽度。这确保了每篇文章无论有多长,都以相同的速度滚动。
一旦我们获得了总width和duration,我们就可以在marquee中运行动画,使用我们的width和time变量来配置动画。我们通过设置第一个链接的负margin来动画帖子链接,这将所有其他链接一起拉动。
一旦动画完成,我们从链接中删除margin-left,将其重新附加到marquee元素中的<div>的末尾,并再次触发marquee-ready事件以重复此过程。这一过程反复发生,创建了持续的动画,将我们带到了这个示例的结尾。
尝试一下 - 扩展跑马灯滚动器
对我们的用户肯定有益处的一个功能是,如果鼠标指针悬停在帖子标题上时,帖子标题停止动画。当鼠标指针再次移开标题时,动画可以重新启动。尝试自己添加此功能。这一点一点也不难,只需添加mouseenter和mouseleave事件处理程序即可。
您需要计算任何给定链接已经在跑马灯的可见区域之外的部分有多少,以确保动画以与停止时相同的速度重新启动,但这应该与我们在本例中计算持续时间的方式非常相似。看看你能做到什么。
快速测验 - 创建跑马灯滚动器
Q1. 为什么我们创建了一个动态持续时间变量(时间),而不是使用 jQuery 的预定义持续时间之一?
-
因为使用整数更快,即使必须计算该整数,也比使用其中一个持续时间字符串更快
-
因为这更有趣
-
确保链接在被动画后附加到正确的元素上
-
为了确保所有链接无论有多长都以相同的速度进行动画
摘要
在本章中,我们的第二个重点是基于实例而不是理论的章节,我们看了一些在网络上越来越常见的动画。具体来说,我们看了以下类型的动画:
-
一个基于接近距离的图像滚动器,其中图像根据鼠标指针的移动方向和速度滚动
-
背景位置动画,在此我们只需几行代码手动创建了一个连续的页眉动画
-
一个文本跑马灯,其中一系列的头条新闻从实时互联网源中抓取,并显示在滚动的跑马灯式横幅中。
在下一章中,我们将开始研究一些纯 CSS 动画,这些动画是由 CSS3 引入的,以及如何使用 jQuery 来增强它们,并通常使与它们一起工作更容易。
第九章:CSS3 动画
CSS3 为 Web 开发带来了许多令人印象深刻的新样式,即使规范还远未完成,它的许多方面已经在最新的浏览器中使用。纯 CSS 动画甚至可能在某个时点被纳入规范中。在写作时,几乎所有现代浏览器都完全支持这一点。然而,通过一点点 jQuery 的帮助,我们可以创建自己的 CSS3 动画,这些动画在各种常见浏览器中都可以有不同程度的成功。
在本章中,我们将涵盖以下主题:
-
可用的不同 CSS3 变换
-
对元素旋转进行动画处理
-
使用 CSS3 变换矩阵
-
使用 jQuery 对元素的倾斜进行动画处理
注意
关于 CSS3 2D 变换的更多信息,请参阅 W3C 工作草案规范www.w3.org/TR/css3-transforms/。
CSS3 2D 变换
CSS3 定义了一个名为transform的样式属性,允许我们在二维空间沿着 x 和 y 轴转换目标元素。一系列的变换函数可以作为transform属性的值,决定了变换应该如何应用。下面定义了以下 2D 变换函数:
| 函数 | 例子用法 | 变换描述 |
|---|---|---|
matrix |
matrix(a, b, c, d, tx, ty) |
它根据提供的参数的组合旋转,缩放,倾斜或平移元素。 |
rotate |
rotate(x) |
它围绕变换原点将元素旋转指定角度。默认情况下,原点应该是元素的中心。 |
scale |
scale(x, y) |
它沿着 x 和 y 轴方向按指定的单位数进行缩放元素。如果没有给出 y,就假定它与 x 相同。 |
scaleX |
scale(x) |
它沿 x 轴按指定的单位数进行缩放元素。 |
scaleY |
scale(y) |
它沿 y 轴按指定的单位数进行缩放元素。 |
skew |
skew(x, y) |
它沿着 x 和 y 轴以指定的角度倾斜元素。如果没有提供 y,则假定为 0。 |
skewX |
skew(x) |
它沿 x 轴沿指定角度倾斜元素。 |
skewY |
skew(y) |
它沿 y 轴沿指定角度倾斜元素。 |
translate |
translate(x, y) |
它将元素沿 x 和 y 轴重新定位指定像素。如果没有提供 y,假设为 0。 |
translateX |
translate(x) |
它将元素沿 x 轴重新定位指定像素。 |
translateY |
translate(y) |
它将元素沿 y 轴重新定位指定像素。 |
理解矩阵
所有单独的转换函数(rotate(),skew()等)可以看作是执行特定矩阵转换的快捷方式。实际上,大多数浏览器在提供转换函数时,甚至在幕后都会应用矩阵。
matrix函数接受六个参数,并且前面表中提到的每种转换都可以通过为这些参数提供不同组合的值来执行。有时我们可以使用matrix函数同时应用多个变换。让我们看些快速的示例来说明如何使用矩阵。
平移
对元素进行平移会使其从原始位置移动。正值将元素移动到页面的右侧或下方(取决于轴),而负值将元素移动到页面的左侧或上方。例如,可以使用以下变换矩阵使元素在 x 轴沿右移动 100 像素,y 轴沿下移动 100 像素:
transform: matrix(1, 0, 0, 1, 100, 100);
这个matrix函数等同于使用转换函数translate(100px, 100px),将导致目标元素看起来如下截图:

正如我们在前一个截图中看到的,尽管我们没有使用 CSS 来定位该元素,但该元素已从其原始位置(屏幕左上角)移动,我们可以在 DOM Inspector 中看到这一情况。
此示例中矩阵的第五个参数对应 x 轴,第六个参数对应 y 轴。不要过于担心前四个参数,因为我们很快就会更详细地讨论这些内容。
注意
需要特别注意的是,一些浏览器(如 Firefox)期望这些值带有指定的单位(正如前一个截图中),而其他浏览器(如 Opera)或基于 WebKit 渲染引擎的浏览器则希望这些值不带单位。
元素不需要定位才能进行平移,并且转换不会影响文档的流或周围其他元素。相邻元素将根据元素的原始位置而不是其平移后的新位置进行定位。转换后,元素的内容也将被一起平移。
缩放
也许你会想为什么在我们第一个矩阵代码片段中,我们提供了值 1 作为第一个和第四个参数,但是第二和第三个参数的值却为 0,而不提供所有的零值。
这是因为参数(第一和第四)对应于scale转换函数,因此为保留变换后的元素原始大小,scale参数被设置为 1。要使元素的大小加倍(而不移动其位置),我们可以使用以下变换矩阵:
transform: matrix(2, 0, 0, 2, 0, 0);
此片段相当于使用 transform: scale(2, 2),并会导致目标元素显示如下:

在前面的截图中,我们可以看到该元素现在是其原始大小的两倍。
前面的代码会沿着 x 和 y 轴对目标元素进行对称缩放。这些值在所有支持的浏览器中都是无单位的,且不能指定为值 0。可以提供整数或浮点数,并且如果需要可以进行非对称缩放。
缩放的一个有趣效果是,提供负值会导致元素被反转,而不是像我们直观地推测的那样收缩。因此,如果我们在前面的代码片段中提供 -2 和 -2 作为第一个和第四个值,那么该元素将在垂直和水平方向上都反射,并且大小将增加两倍。甚至可以为这种类型的转换提供正负值的组合。
反射的元素会显示如下:

该元素沿其 x 和 y 轴反转,就像在镜子中倒置查看一样。如果,例如,我们正在实现纯 CSS 反射,这可能非常有用。
倾斜
与矩阵中第二个和第三个参数对应的两个零值可以用作倾斜值,其中 x 轴使用第二个参数,y 轴使用第三个参数。我们可以使用以下矩阵变换函数对元素进行倾斜(而不修改其比例或位置):
transform: matrix(1, 1, 0, 1, 0, 0);
以下截图显示了一个倾斜的元素:

前面的截图显示了一个沿 x 轴倾斜的元素。与其他矩阵函数一样,对这些参数的正值会导致沿右侧或向下的方向转换,负值会导致沿左侧或向上的方向转换。
在前面的片段中,只有 x 轴被倾斜。倾斜的一个后果是元素增大了。转换后元素的边界框尺寸从 200 px(元素的原始大小)增加到了 400 px。
尽管尺寸增加,但文档的流程不受变换的影响,而且与其他变换一样,变换元素内的任何内容也会被转换。
注意
在不同的浏览器中,变换对元素中包含的任何文本的影响各不相同,在一些浏览器中,文本在变换后仍然清晰可读,而在其他浏览器中则会降级。
旋转
要使用矩阵旋转元素,我们需要使用三角函数正弦和余弦来计算前四个参数的值。第一个和第四个参数分别为旋转角度的余弦函数,而第二个和第三个参数分别为旋转的正弦函数和负正弦函数。
注意
正弦和余弦函数是相对较高级的数学构造,用于表示三角形的边和角之间的不同关系。
虽然精确理解它们的本质对于使用它们并不是必要的(JavaScript 有内置函数可以自动计算它们),但深入理解它们的本质和用途在特定处理旋转时会有所帮助。
关于基本介绍,请参阅维基百科对三角函数的文章 en.wikipedia.org/wiki/Trigonometric_functions。
例如,要将一个元素旋转 37 度,我们将使用以下变换:
transform: matrix(0.7986355, 0.6018150, -0.6018150, 0.7986355, 0, 0);
我们旋转后的元素应该是这样的:

如我们所见,旋转后元素的边缘超出了视口。应谨慎正确地定位将要旋转的元素,以确保在必要时有足够的空间显示元素的全部内容。
可以通过科学计算器轻松计算旋转角度的正弦和余弦函数,或者当然也可以通过 JavaScript 自行进行编程。
处理变换
使用诸如rotate()或skew()之类的快捷变换函数比使用矩阵更容易更方便。然而,这种易用性是有代价的——我们只能在单个元素上一次使用其中一个。如果我们试图在 CSS 语句中使用多个,只会应用最后一个定义的。
如果我们需要将多个不同的变换应用于一个元素,我们可以使用矩阵函数,具体取决于我们需要应用哪些变换。例如,我们可以倾斜一个元素,同时使用以下方法进行平移和缩放:
transform: matrix(2, -1, 0, 2, 300px, 0);
在这个例子中,元素沿着 x 轴倾斜,大小加倍,并向右移动 300 px。我们无法在上一个代码片段中同时对目标元素进行旋转。
即使我们提供两个矩阵函数,一个用于倾斜、缩放和平移,另一个用于旋转,只有旋转会被应用。但是,我们可以使用单个矩阵函数同时旋转和平移,或旋转和缩放一个元素。
使用 jQuery 和变换
我们可以使用 jQuery 的css()方法在设置器模式下在所选元素上设置 CSS3 变换,并且我们可以在获取器模式下检索在元素上设置的任何变换函数。我们只需要确保使用正确的供应商前缀,例如-moz-transform用于 Firefox,或-webkit-transform用于 WebKit/Blink-based 浏览器。Opera 也有自己的供应商前缀(对于较旧的版本),新版本的 IE 也有。
需要注意的一件事是,虽然我们可以在选定的元素上设置特定的变换函数,比如 rotate(),但我们只能以其矩阵格式获取 style 属性的值。看一下下面的代码:
$("#get").css("-moz-transform", "rotate(30deg)");
$("#get").text($("#get").css("-moz-transform"));
这将导致以下结果:

在上一张屏幕截图中,我们看到我们在第一行代码中应用的 rotate() 变换函数在第二行代码中作为矩阵函数返回。
提示
cssHooks
重要的是要注意,使用 cssHooks 可以帮助测试你的代码在各种供应商前缀下的浏览器兼容性。有关 cssHooks 的更多信息可以在这里找到:api.jquery.com/jQuery.cssHooks/。
此外,你可以在这里找到一个很棒的 jQuery 插件,用于 cssHooks:github.com/brandonaaron/jquery-cssHooks。它包含了一些 CSS3 2D 变换的行为。虽然从学习的角度来看,手动创建这些效果是有益的,正如我们在本章的其余部分中所做的那样,但请记住在将来使用这个文件来节省你的时间和精力。
CSS3 3D 变换
我们迄今为止看到的所有变换函数都是二维的,仅在 x 和 y 轴上操作。还提出了在 x、y 和 z 轴上操作的三维变换。
所有的变换函数都存在三维等效版本,通常只需要一个额外的参数,对应于每个维度的向量和角度。例如,可以使用以下代码添加 3D 旋转:
transform: rotate3d(0, 1, 0, 30deg);
与 2D 变换一样,有一个包罗万象的矩阵函数,可以让我们实现任何其他变换,并允许我们将其中一些变换组合到单个元素上。
如果像我一样,你觉得 2D 变换矩阵,具有六个参数,复杂且可能有点难以理解,那等你开始使用总共有 16 个参数的 3D 矩阵时,你就会觉得更复杂了!
目前,3D 变换在基于 WebKit 的浏览器和 Firefox 中得到支持(对于 IE10 则有部分支持),所以我们不会对这些进行进一步的详细讨论。
注意
关于 CSS3 3D 变换的浏览器支持可以在这里找到:caniuse.com/transforms3d。
使用 jQuery 和 CSS3 进行动画旋转
在这个例子中,我们将设置一个动画,使用 rotate() 变换函数旋转图像。由于这个在大多数常见浏览器中都受支持,所以实际上实现起来非常容易,并且可以成为增强所使用页面的外观和行为的一个很好的效果。
行动时间 - 动画化元素的旋转
在这个示例中,我们只会旋转一个简单的图像,因此这是我们需要在页面的<body>元素中的唯一可见元素。
-
将以下
<img>标签添加到模板文件的新副本中:<img src="img/color-wheel.png" id="colorWheel">在这一点上,我们甚至不需要任何样式,因为我们需要设置的所有内容都可以在接下来要添加的 JavaScript 中完成。
-
在 HTML 页面底部的匿名函数中,添加以下代码:
var img = $("#colorWheel"), offset = img.offset(), origWidth = img.width(), origHeight = img.height(), rotateStrings = [ "rotate(", 0, "deg)" ], getVendor = function() { var prefix = null, vendorStrings = { pure: "transform", moz: "-moz-transform", webkit: "-webkit-transform", op: "-o-transform" }; for (props in vendorStrings) { if(img.css(vendorStrings[props]) === "none") { prefix = vendorStrings[props]; } } if (prefix === null) { prefix = "filter"; img.css({ position: "absolute", filter: "progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand');" }); } return prefix; }, vendor = getVendor(); function doRotate() { rotateStrings[1]++; if (vendor === "filter") { var rad = rotateStrings[1] * (Math.PI * 2 / 360), cos = Math.cos(rad), sin = Math.sin(rad), driftX = (img.width() - origWidth) / 2, driftY = (img.height() - origHeight) / 2, el = img.get(0); img.css({ left: offset.left - driftX, top: offset.top - driftY }); el.filters.item("DXImageTransform.Microsoft.Matrix").M11 = cos; el.filters.item("DXImageTransform.Microsoft.Matrix").M12 = -sin; el.filters.item("DXImageTransform.Microsoft.Matrix").M21 = sin; el.filters.item("DXImageTransform.Microsoft.Matrix").M22 = cos; } else { img.css(vendor, rotateStrings.join("")); } } setInterval(function() { doRotate() }, 100); -
将页面保存为
rotate.html。如果我们现在在浏览器中运行页面,我们应该看到颜色轮围绕其中心缓慢旋转。
刚刚发生了什么?
我们做的第一件事是缓存图像的选择器,因为我们将在代码中多次引用它。请注意,这是我们在整个脚本中创建的唯一 jQuery 对象,正如我们在本书中之前讨论过的,这对于提高性能非常有帮助。
我们在这一点上还设置了一些其他变量,包括图像的偏移(它在页面上的absolute位置),其原始宽度和高度,以及一个包含我们将以字符串和整数格式设置的 CSS 属性的不同部分的数组。
我们还将内联函数(getVendor())设置为变量的值,我们可以使用它来确定要使用的供应商前缀。这个函数首先设置一些变量,这些变量将用于存储确定的供应商前缀和一个包含我们要测试的所有不同前缀的对象文字。我们还包括原生的 transform 属性。虽然目前还没有任何浏览器支持这一点,但总有一天可能会支持,所以这有助于未来保护我们的代码。
doRotate()函数使用for...in循环迭代对象文字中的每个属性。在循环内部,我们尝试使用每个供应商前缀读取transform属性的值。一个有趣的事实是,每个浏览器都会报告其支持的前缀的值为none,对于它不支持的前缀,会报告一个假值,如false、null或undefined。我们可以使用这个来可靠地确定正在使用哪个浏览器,因此需要使用哪个供应商前缀。然后将正在使用的浏览器的正确供应商前缀保存到vendor变量中,以备返回。
注意
如果这些测试都没有识别出供应商前缀,那么可能正在使用的浏览器是较旧版本的 Internet Explorer。再次注意,jQuery 2.0 不支持 IE8 及以下版本。
如果此时vendor变量仍设置为 null,则将变量设置为filter。为了在 IE 中以编程方式处理filter属性的值,必须已将filter应用于元素,因此我们还在代码的此部分使用 jQuery 的css()方法为元素设置了一个过滤器,以备以后在代码中操纵。我们还将sizing mode设置为auto expand,以防止在应用旋转时裁剪元素。
函数结束时,返回包含当前使用的浏览器的供应商前缀的字符串的prefix变量。在函数之后,我们设置一个名为vendor的变量,该变量将包含函数返回的值,以便轻松引用。
接下来,我们定义一个常规函数doRotate(),该函数将用于执行实际的旋转。在此函数中,我们首先将rotateStrings数组的第二个属性递增 1。
然后,我们检查vendor变量是否等于filter。如果是,则我们知道正在使用的浏览器是 IE,可以继续确定专有的filter所需的值。IE 允许以两种不同的方式实现旋转。我们可以使用BasicImage过滤器属性来旋转图像,尽管这样只能设置四个旋转值之一:0、1、2或3,这分别对应于 0、90、180 或 270 度。在本示例中,这根本不够灵活。
所以,我们使用Matrix过滤器,它让我们对旋转的程度有更多控制。这与 CSS3 矩阵变换非常相似,有六个参数值,这些值组合起来生成不同的变换(在这种情况下是旋转)。
我们在本示例中使用的参数是M11、M12、M21和M22,它们大致映射到 CSS3 版本的前四个值,但在 Microsoft 的供应商前缀版本中,第二个值和第三个值被颠倒了。
这些属性的值必须使用 JavaScript 的三角函数Math.cos和Math.sin来计算。我们设置一些变量来计算这些值。第一个变量rad将旋转的度数转换为弧度,因为这是Matrix过滤器所需的单位。弧度通过将当前旋转度数(存储为rotateStrings数组中的第二项)乘以 PI 乘以 2,然后除以 360 来计算。
在 IE 中旋转元素时出现的一个不幸问题是,旋转的元素在旋转过程中在页面上漂移。这是由于元素的边界框随着元素旋转而增大导致的。旋转确实发生在元素的中心周围,但是因为 IE 认为元素已经增大,所以旋转后的元素中心在每次旋转时都会发生位移。
我们设置的drifX和driftY变量允许我们确定元素移动了多远,以便我们可以进行修正。通过比较元素在旋转之前的原始宽度和高度与旋转后的新宽度和高度,可以计算出移位。
我们还使用 jQuery 的get()方法将 jQuery 对象中的原始img元素存储为 DOM 节点,并将参数设置为0,这将返回实际的 DOM 节点而不是 jQuery 对象。filter必须应用于正确的 DOM 元素。
一旦我们设置了变量,我们就可以使用 jQuery 的css()方法来纠正由于上一次旋转引起的漂移,然后将计算得到的三角函数值插入Matrix过滤器中。
最后,如果vendor变量不等于filter,我们可以简单地将相关的供应商前缀设置为我们的rotateStrings数组中的项目。我们通过调用 JavaScript 的join()方法来执行此操作。这比使用连接来创建所需的 CSS 属性的字符串要高效得多,因为此函数将被重复执行,我们确实需要确保它尽可能高效。
我们代码中的最后一件事是通过设置每隔 100 毫秒调用我们的doRotate()函数的间隔来开始旋转动画。我们使用匿名函数作为setInterval()函数的第一个参数,这样可以避免将要执行的函数附加到window对象上。
IE 的问题
除了 IE 让我们比其他任何浏览器都要努力地设置元素的旋转之外,它还给我们带来了另一个问题:它完全破坏了我们正在旋转的 PNG 的 alpha 层。突然间,我们漂亮的反锯齿圆边变得锯齿状且不美观(在 IE 中查看此示例以查看问题)。
- IE 下的动画也稍显卡顿,同时,使用带有 alpha 层的 PNG 可能会成为 IE 的一个硬伤。如果真是这样,我们可以通过
filter属性是否返回来轻松地在 IE 中禁用动画,只需在我们的getVendor()函数中什么都不做即可。但是,我们仍然可以做一些事情来抵消 IE 中的问题。
例如,我们可以简单地使用一个没有透明度的 PNG,这将在 IE 中保留圆形的边框(在本例中)。或者,我们可以将另一张图像覆盖在我们正在旋转的图像的顶部,以隐藏锯齿状的边缘。
突击测验——实现 CSS3 旋转
Q1. 在这个例子中,我们使用了一个数组,结合 JavaScript 的join()方法生成了字符串。为什么?
-
因为这样更有趣。
-
因为它能让我们的代码看起来更好。
-
因为在性能上,它比字符串连接快得多。
-
否则该元素无法正确旋转。
Q2. 为了使动画在 Internet Explorer 中正确运行,我们不断地调整了旋转元素的top和left样式属性以维持其位置。为什么 IE 中会出现偏移?
-
因为旋转元素的边界框在整个动画过程中会发生变化。由于旋转元素位于其边界框的中心位置,因此随着框的增大和减小而其位置也会发生变化。
-
因为 PNG 的 alpha 层被移除了。
-
因为使用了矩阵滤镜属性。
-
由于 IE 的 CSS3 旋转属性存在错误。
尝试吧——扩展 CSS3 旋转
旋转效果可以应用在许多地方,无论是动画还是静态。但是在动画效果中,比如在本例中,作为较大元素组合的一部分的背景效果非常出色。例如,作为半透明 logo 的背景,它创造了令人惊叹的效果。
在页面中尝试将这种效果融入,并将其用作另一幅图像的背景。此外,您还会第一手看到这在 IE 中如何改善效果的外观。
动态偏移
就像rotate()函数一样,我们可以使用skew()变换来创建吸引人的特效动画。在这个例子中,为了在所有浏览器中应用多个变换到一个元素上,我们将使用matrix()函数,而不仅仅是 IE。
本例中的上下文将是以 cover-flow 风格的小部件,通过对图像的倾斜进行动画处理,依次显示图像。用户可以使用链接在图像之间前后循环滚动:

前一个截图显示了完成的小部件将会如何显示。
实战时间——创建基础标记和基本样式
首先,我们将看一下我们在示例程序中将使用的 HTML,然后我们将看一下在被扭曲之前为元素添加的初始样式。
-
将以下代码添加到模板文件的
<body>元素中:<div id="viewer"> <div id="flow"> <img src="img/atreyu.jpg"> <img src="img/beatles.jpg"> <img src="img/blink.jpg"> <img src="img/cold.jpg"> <img src="img/disturbed.jpg"> <img src="img/floyd.jpg"> <img src="img/korn.jpg"> <img src="img/prodigy.jpg"> <img src="img/the-birthday-massacre.jpg"> <img src="img/xx.jpg"> </div> <ul> <li id="left"><a href="#" title="向左移动">左</a></li> <li id="right"><a href="#" title="向右移动">右</a></li> </ul> </div> -
将页面保存为
skew.html。接下来,在一个新文件中添加以下代码:#viewer { width:700px; height:220px; padding:100px 0 30px; margin:auto; border:1px solid #000; position:relative; } #flow:after { content:""; display:block; height:0; clear:both; visibility:hidden; } #flow img { display:block; margin-left:-165px; position:relative; top:-15px; left:245px; float:left; background-color:#fff; } #viewer li { list-style-type:none; position:absolute; bottom:10px; } #left { left:20px; } #right { right:20px; } -
将此文件保存在
css目录中,命名为skew.css。
刚发生了什么?
对于此示例,我们使用了一组简单的元素。我们使用了一个外部容器,主要用于定位,以便我们可以在视口中心放置小部件并在其中定位其他元素。
<img> 元素是我们将应用倾斜动画的元素,因此将它们隔离在自己的容器中,以便以后在脚本中更轻松地选择它们。我们还有一个包含两个链接的列表元素。这些将用于触发动画。
CSS 与 HTML 一样简洁。我们只需根据示例要求定位容器、图像和控件即可。所有有趣的 CSS3 效果都将使用脚本进行设置和操作。您应该注意,此示例没有渐进增强,因为这会使其与已经相当大的示例偏离太远,如我们稍后将在添加 JavaScript 时看到的那样。
该行动开始——初始化小部件
我们需要做的第一件事是设置好准备进行倾斜操作的图像。我们还可以添加一个函数,该函数将返回我们在上一个示例中使用的 transform 样式属性的正确供应商特定前缀。在 HTML 页面底部的空函数中,添加以下代码:
var viewer = $("#viewer"),
flow = viewer.find("#flow"),
order = flow.children().length,
oneRad = 1 * (Math.PI / 180),
matrix = ["matrix(", 1, ",", 0, ",", 0, ",", 1, ",","0px,", "0px)"],
msMatrix = "progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand')",
getVendor = function() {
var prefix = null,
vendorStrings = {
pure: "transform",
moz: "-moz-transform",
webkit: "-webkit-transform",
op: "-o-transform"
};
for (props in vendorStrings) {
if(flow.css(vendorStrings[props]) === "none") {
prefix = vendorStrings[props];
}
}
if (prefix === null) {
prefix = "filter";
}
返回前缀;
},
vendor = getVendor(),
property = (vendor !== "filter") ? matrix.join("") : msMatrix;
flow.children().eq(0).addClass("flat").css(vendor, property).css("zIndex", order + 1);
flow.children().not(":first").each(function(i) {
el = flow.children().eq(i + 1);
matrix[1] = 0.7;
matrix[3] = -30 * oneRad;
matrix[5] = -10 * oneRad;
matrix[7] = 0.7;
matrix[9] = (vendor === "-moz-transform") ? "90px," : "90,";
matrix[10] = (vendor === "-moz-transform") ? "-30px)" : "-30)";
if (vendor !== "filter") {
el.addClass("skew-right").css(vendor, matrix.join("")).css("zIndex", order);
} else {
el.addClass("skew-right").css(vendor, msMatrix).css({
zIndex: order,
top: -30,
left: 270,
width: 140,
height: 140,
marginLeft: -100
});
el.get(0).filters.item("DXImageTransform.Microsoft.Matrix").M11 = 1;
el.get(0).filters.item("DXImageTransform.Microsoft.Matrix").M12 = matrix[5];
el.get(0).filters.item("DXImageTransform.Microsoft.Matrix").M21 = matrix[3];
el.get(0).filters.item("DXImageTransform.Microsoft.Matrix").M22 = 1;
}
order--;
});
matrix[3] = 0;
matrix[5] = 0;
刚刚发生了什么?
在脚本的第一部分,我们初始化了变量。如果你曾经想知道为什么我们总是在函数顶部初始化变量,原因是因为一个称为 Hoisting 的现象。这是指函数中初始化的变量被“提升”到函数顶部,并且可以包含我们没有预期到的结果。
注意
您可以在以下网址了解更多关于 JavaScript Hoisting 的信息:thecomputersarewinning.com/post/a-dangerous-example-of-javascript-hoisting/。
我们创建的第一个变量是我们小部件外部容器的缓存选择器。这是我们在整个示例中创建的唯一一个 jQuery 对象。我们将添加的一些代码在某些地方非常耗费资源,因此为了性能原因,保持我们创建的 jQuery 对象数量尽可能少是至关重要的。
接下来我们使用原始的 jQuery 对象和 find() jQuery 方法来缓存 flow 元素的选择器(图像元素的直接父元素,将被扭曲),因为我们需要多次访问或操作此元素。
然后,我们使用包含 flow 元素子元素的 jQuery 对象的 length 属性存储小部件中图像元素的数量。我们还存储了将一度转换为一弧度的结果,以便我们可以在整个脚本中轻松地从一种单位转换到另一种单位,而无需重复执行相同的计算。CSS3 变换 matrix 和 IE 的 matrix 滤镜都可以接受弧度作为单位,因此这使它们成为方便的工作单位。
然后,我们创建了我们的矩阵数组和 Microsoft 的matrix属性作为字符串。该数组包括所有单独的属性作为数组项,包括必需的逗号作为字符串。我们在数组中包含逗号的原因是,我们可以稍后调用数组上的join() JavaScript 函数,而不指定分隔符,也不必担心它会错误地插入不必要的逗号。
接下来,我们添加了在上一个示例中使用的getVendor()函数。这是一种方便的方式,可以确保在应用倾斜样式时使用正确的前缀。我们不会详细介绍此函数,因为我们在本章的早些时候已经看过它了(在动手操作——动画化元素旋转部分)。再次强调,我们在定义函数后立即调用该函数,并将结果存储在变量中以供以后使用。
我们创建的最后一个变量将保存一个字符串,其中包含 CSS3 矩阵函数和所有所需参数,或者它将包含 IE 的matrix属性在其最基本形式下只定义了sizingMethod参数。如果您还记得前面的示例,IE 只能在初始设置后才能操作矩阵属性。
在此时,我们可以继续准备第一张图片。我们使用 jQuery 的eq()方法选择第一张图片,将0作为我们感兴趣的元素的索引传递进去。我们在第一张图片上设置一个类名为flat,这样我们就可以很容易地后续选择它,并且还将其z-index设置得比其他图片高,以便完全可见。接下来,我们使用 jQuery 的each()方法循环遍历剩余的图片。
我们传递给方法的匿名函数接受参数i,这是当前迭代的索引。这反过来将允许我们在循环的每次迭代中依次选择每个元素。函数中我们做的第一件事是使用索引作为eq()方法的参数缓存对当前<img>元素的引用。我们将索引值加 1 以避免选择第一张图片。
在下一段代码中,我们设置了matrix数组中的一些项目。我们将比例参数(数组中的第 1 和第 7 项)设置为 0.7,以便略微缩小倾斜的图像,将倾斜参数(数组中的第 3 和第 5 项)设置为相应的-30 和-10 度的弧度。这将略微向上和向右倾斜图像。
我们还设置了平移参数(数组中的第 9 和第 10 项),以便正确定位倾斜的元素,使它们水平堆叠。如果使用的浏览器是 Firefox,我们必须在平移属性的值中使用px,但是对于其他浏览器,值应该是无单位的。我们使用三元条件来检查vendor变量(这将包含当前浏览器的供应商前缀)并相应地设置值。
设置完数组项目后,我们检查当前浏览器是否不是 IE,并且只要不是,就将倾斜应用于当前元素。我们还使用设置为图像数量的长度的order变量设置当前元素的z-index。这样做可以使当前元素成为顶部图像。
如果正在使用的浏览器是 IE,我们将应用微软matrix并在图像上设置一些不同的 CSS。在 IE 中,translate 参数不起作用,因此我们使用 jQuery 来定位图像。在 IE 中将元素倾斜也会导致元素增大,因此我们必须大幅减少它们的尺寸,这也是我们用 jQuery 做的。
在循环的每次迭代中,我们将该变量的值减一。因此,每个元素的z-index会随着我们处理每个图像而逐渐降低。
设置所需的 CSS 样式后,我们通过操作专有的微软matrix滤镜来倾斜元素。请记住,这些属性只能在实际的 DOM 元素上操作,而不能在 jQuery 对象上操作,因此我们使用 jQuery 的get()方法通过索引为 0 检索原始元素。
each()循环完成后,我们重新设置了matrix数组中的第三和第五个参数。这是因为我们将多次使用该数组,因此每次都应使用参数的默认值。
行动时间 - 动画元素的倾斜
现在我们将添加一个函数,用于将元素向左倾斜。该函数必须应用于两个元素:于平坦或非倾斜元素,以及在其之前的一个元素(在本例中为右侧)。用于从右向左动画倾斜的函数如下,并应放置在matrix[5] = 0;行下方:
function skewRTL() {
var flat = flow.find(".flat").css("zIndex", order + 1),
preFlat = flat.next(),
flatMatrix = matrix.slice(0),
preMatrix = matrix.slice(0),
flatDims = 200,
preDims = 170,
倾斜 = function() {
如果(preFlat.length) {
if (flatMatrix[3] <= 30 * oneRad && flatMatrix[5] <= 10 * oneRad) {
var flatTranslateX = parseInt(flatMatrix[9].split("p")[0], 10),
flatTranslateY = parseInt(flatMatrix[10].split("p")[0], 10),
preTranslateX = parseInt(preMatrix[9].split("p")[0], 10),
preTranslateY = parseInt(preMatrix[10].split("p")[0], 10);
flatMatrix[1] = flatMatrix[1] - 0.001;
flatMatrix[3] = flatMatrix[3] + oneRad;
flatMatrix[5] = flatMatrix[5] + (oneRad / 3);
flatMatrix[7] = flatMatrix[7] - 0.001;
preMatrix[1] = preMatrix[1] + 0.01;
preMatrix[3] = preMatrix[3] + oneRad;
preMatrix[5] = preMatrix[5] + (oneRad / 3);
preMatrix[7] = preMatrix[7] + 0.01;
flatMatrix[9] = (vendor === "-moz-transform") ? flatTranslateX - 6 + "px," : flatTranslateX - 6 + ",";
preMatrix[9] = (vendor === "-moz-transform") ? preTranslateX - 3 + "px," : preTranslateX - 3 + ",";
preMatrix[10] = (vendor === "-moz-transform") ? preTranslateY + 1 + "px)" : preTranslateY + 1 + ")";
if (vendor !== "filter") {
flat.css(vendor, flatMatrix.join(""));
preFlat.css(vendor, preMatrix.join(""));
} else {
flat.get(0).filters.item("DXImageTransform.Microsoft.Matrix").M12 = flatMatrix[5];
flat.get(0).filters.item("DXImageTransform.Microsoft.Matrix").M21 = flatMatrix[3];
preFlat.get(0).filters.item("DXImageTransform.Microsoft.Matrix").M12 = preMatrix[5];
preFlat.get(0).filters.item("DXImageTransform.Microsoft.Matrix").M21 = preMatrix[3];
flatDims = flatDims - 2;
preDims = preDims + 0.5;
flat.css({
width: flatDims,
height: flatDims
});
preFlat.css({
width: preDims,
height: preDims
});
}
} else {
clearInterval(flatInterval);
if (vendor !== "filter") {
preMatrix[3] = 0;
preMatrix[5] = 0;
preFlat.css(vendor, preMatrix.join(""));
} else {
flat.css({
top: -30,
left: 260
});
}
flat.prev().css("zIndex", "");
flat.removeClass("flat").css("zIndex", "");
preFlat.addClass("flat");
}
} else {
clearInterval(flatInterval);
flat.css("zIndex", order + 1);
}
};
preMatrix[3] = -30 * oneRad;
preMatrix[5] = -10 * oneRad;
if(!flatInterval) {
var flatInterval = setInterval(function() { skew() }, 1);
}
};
刚刚发生了什么?
我们在函数中做的第一件事是设置函数使用的变量。我们缓存了具有flat类的当前元素的引用,并将该元素的z-index设置为比任何其他图像高一级,以确保它始终位于其他图像的顶部。
我们还缓存了flat图像后面的下一个图像的引用。在这个函数中,这将是未变形图像右侧的图像。然后,我们复制了原始的matrix数组的两个副本,一个是flat元素的副本,一个是preFlat元素的副本。要复制一个数组,我们只需使用 JavaScript 的slice()方法并指定索引为零。
我们创建的下两个变量是flat和preFlat图像的初始尺寸。这些变量只在 IE 中使用,但由于变量提升,我们需要在这里定义它们,而不是在函数的后面的 IE 特定代码块中定义它们。
接下来,我们定义了一个名为skew()的内联函数,我们将重复调用该函数以产生实际的动画。在这个函数中,我们首先检查flat元素后面是否有元素,方法是通过检查preFlat对象是否有长度。如果长度等于零(即长度为零),我们简单地清除可能存在的任何间隔,并确保flat元素位于 z-index 堆栈的顶部。然而,如果preFlat对象确实有长度,则我们检查当前的skewX属性是否小于或等于 30 度的弧度等价值,并且skewY属性是否小于或等于 10 度的弧度等价值(我们可以通过将 30 或 10 分别乘以我们存储的 1 弧度的数字来计算得到)。当前的flat图像的倾斜属性当前存储在flatMatrix数组的第三个和第五个项目中。
如果两个条件都为真,则可以继续执行动画。动画的一部分涉及翻译flat和preFlat图像,以便随着倾斜,图像也随之移动(我们还会调整它们的大小,但稍后再谈)。
为了正确地翻译图像,我们需要获取它们当前的翻译,我们首先定义四个新变量,并用来自两个矩阵数组的当前翻译值填充它们。这些数字需要是数字的,所以我们使用 JavaScript 的parseInt和split()函数来拆分字符串并将数字转换为整数。
接下来,我们需要用新值更新我们的两个矩阵数组。从右到左的函数将逐步更新flatMatrix和preMatrix数组中的值,然后将数组应用到元素上。因此,动画将由对每个变换参数的快速更新组成。
flat图像在翻译时也需要被倾斜,所以我们分别将skewX和skewY参数增加一个弧度和三分之一的一个弧度。记住,为了使元素向左和向上倾斜,倾斜参数应为正值,所以我们在每次函数通过时增加flatMatrix数组的项 3 和 5 的值。
flat图像比倾斜的图像开始时要大,所以每次函数运行时我们需要稍微减少数组项 1 和 7。skew()函数将被调用 30 次;所以为了减小flat图像的比例,使其最终达到正确的大小,我们在每次函数通过时将比例参数减小0.001。
我们想要的值是 x 轴上的 30 度倾斜和 y 轴上的 10 度倾斜。10 是 30 的三分之一,这就是为什么我们通过一个弧度除以三来增加skewY参数。
我之前提到在 Firefox 中,翻译参数需要单位,如px,但其他浏览器的这些值是无单位的。我们使用 JavaScript 的三元条件运算符来检查vendor字符串,如果它等于 Firefox 的供应商前缀(-moz-transform),我们将px添加到值中。平坦图像只需要在 x 轴上翻译,并且需要向左移动 6 个像素,所以我们更新数组项 9 的值,该值比其当前值小 6。
我们还必须更新preFlat图像,使其从向右倾斜变为平坦。我们还必须增加preFlat图像的大小,因为它们起初较小。同样,我们更新了preMatrix中的相关数组项,以便在skew()函数的 30 次迭代过程中它们最终达到正确的值。preFlat图像还需要被平移,但这次沿着x和y轴。
接下来我们再次检查 vendor string,只要不是filter(IE),我们通过连接数组将变换应用到flat和preFlat图像。如果是 IE,我们需要做更多的工作来应用变换。
我们在flat和preFlat图像上应用了每个相关的Matrix属性,M12和M21。我们再次使用了 jQuery 的get()方法,并使用0索引获取实际的 DOM 元素。我们还使用我们之前初始化的flatDims和preDims变量,缩小了flat图像的大小,同时增加了preFlat图像的大小,然后我们使用 jQuery 的css()方法应用了新的尺寸。
IE 的Matrix属性在sizingMethod设置为auto expand时会忽略缩放参数,但必须设置该属性以防止图像被裁剪。这就是为什么我们要返回到 jQuery 的css()方法。
令人意外的是,我们在 IE 中能够设置分数像素大小,这很幸运,因为它允许我们按正确的顺序设置图像的大小,使它们在动画结束时以正确的尺寸结束。
然后我们来到skewRTL()函数的最后一部分,在我们的flatMatrix数组中的第三和第五项大于 30 和 10 时执行此代码块。
首先我们清除间隔,以便扭曲不再进行动画。然后我们再次检查 vendor string,只要不是filter,我们将flat元素的扭曲重置为0(在 x 轴和 y 轴上都是)。
这是因为由于某种原因,preFlat图像并没有完全回到零。我认为这是因为 JavaScript 的Math函数不允许数字有足够的小数位数来完全精确。然而,图像只是稍微偏离,因此在动画结束时对0的突然切换并不会被注意到。
不幸的是,在 IE 中似乎不可能同时进行元素的平移和扭曲。IE 会应用新的扭曲,但直到扭曲动画结束时才应用新的位置,因此元素会以两个单独的步骤进行扭曲然后移动。这看起来不太好看,因此在扭曲动画完成后,我们简单地重新定位flat元素而不进行动画。
在修正了扭曲或位置后,我们将flat元素的z-index删除(现在它已经向左扭曲),并将其 class name 从flat删除,并给preFlat元素添加 class name flat。
此时,flat图像已经被扭曲到左侧,调整了大小和平移,preFlat图像已经扭曲回零,调整了大小和平移。flat和preFlat图像同时进行了变换,这就是为什么函数会如此庞大。
在skewRTL()函数的最后,该函数在skew()函数之后定义,skew()函数将由setInterval()函数重复调用,我们初始化了preMatrix数组中的第三个和第五个值,以便数组包含元素的初始状态的正确倾斜。当我们通过复制初始化小部件时使用的原始matrix数组创建数组时,这些项目都将设置为0。
在对需要倾斜的两个图像调用setInterval()函数之前,我们首先检查是否已存在间隔。这样可以防止访问者重复点击链接导致小部件崩溃。如果访问者连续点击几次链接,该元素将会倾斜多次,但是小部件将继续正常工作,页面不会抛出错误。
行动时间 - 将元素从左边倾斜到右边
我们现在可以添加一个函数,用于将元素从左倾斜到平的状态,再从平到右倾斜。这个函数与我们刚刚看到的函数非常相似。代码中的变化已在以下代码中突出显示:
function skewLTR() {
var flat = flow.find(".flat"),
preFlat = flat.prev(),
flatMatrix = matrix.slice(0),
preMatrix = matrix.slice(0),
flatDims = 200,
preDims = 170,
倾斜 = function() {
if (preFlat.length) {
if (flatMatrix[3] >= -30 * oneRad && flatMatrix[5] >=-10 * oneRad) {
var preTranslateX = parseInt(preMatrix[9].split("p")[0], 10),
preTranslateY = parseInt(preMatrix[10].split("p")[0], 10);
flatMatrix[1] = flatMatrix[1] - 0.001;
flatMatrix[3] = flatMatrix[3] - oneRad;
flatMatrix[5] = flatMatrix[5] - (oneRad / 3);
flatMatrix[7] = flatMatrix[7] - 0.001;
preMatrix[1] = preMatrix[1] + 0.01;
preMatrix[3] = preMatrix[3] - oneRad;
preMatrix[5] = preMatrix[5] - (oneRad / 3);
preMatrix[7] = preMatrix[7] + 0.01;
preMatrix[9] = (vendor === "-moz-transform") ? preTranslateX + 3 + "px," : preTranslateX + 3 + ",";
preMatrix[10] = (vendor === "-moz-transform") ? preTranslateY + 1 + "px)" : preTranslateY + 1 + ")";
if (vendor !== "filter") {
flat.css(vendor, flatMatrix.join(""));
preFlat.css(vendor, preMatrix.join(""));
} else {
flat.get(0).filters.item("DXImageTransform.Microsoft.Matrix").M12 = flatMatrix[5];
flat.get(0).filters.item("DXImageTransform.Microsoft.Matrix").M21 = flatMatrix[3];
preFlat.get(0).filters.item("DXImageTransform.Microsoft.Matrix").M12 = preMatrix[5];
preFlat.get(0).filters.item("DXImageTransform.Microsoft.Matrix").M21 = preMatrix[3];
flatDims = flatDims - 1.5;
preDims = preDims + 1.5;
flat.css({
width: flatDims,
height: flatDims
});
preFlat.css({
width: preDims,
height: preDims
});
}
} else {
clearInterval(flatInterval);
clearInterval(preInterval);
if (vendor !== "filter") {
preMatrix[3] = 0;
preMatrix[5] = 0;
preFlat.css(vendor, preMatrix.join(""));
}
flat.removeClass("flat").css("zIndex", parseInt(flat.next().css("zIndex")) + 1);
preFlat.addClass("flat").css("zIndex", order + 1);
}
} else {
clearInterval(flatInterval);
clearInterval(preInterval);
flat.css("zIndex", order + 1);
}
};
order = flow.children().length;
preMatrix[3] = 30 * oneRad;
preMatrix[5] = 10 * oneRad;
preMatrix[9] = (vendor === "-moz-transform") ? "-90px," : "-90,";
preMatrix[10] = (vendor === "-moz-transform") ? "-30px," :"-30,";
if(!flatInterval) {
var flatInterval = setInterval(function() { skew() }, 1),
preInterval = setInterval(function() { skew() }, 1);
}
};
刚刚发生了什么?
我们不会完全覆盖整个函数,因为它与我们之前讨论的非常相似,但让我们花点时间看看这个函数有什么不同。首先,我们不是选择flat元素右侧的下一个图像,而是使用 jQuery 的prev()方法选择它左侧的图像,而不是next()。
当更新我们的flat和preFlat元素的斜率时,我们将元素斜向相反方向。要将元素斜向右移动,我们需要使用负数,因此我们不是从0到30或从-30到0,而是反向操作,从30到0或0到-30,因此我们减去 1 度的弧度值而不是加上它。
我们还将向右平移而不是向左,因此我们不是每次从图像左侧删除 3 个像素以向左移动图像,而是添加 3 个像素以将其向右移动。我们还为 IE 使用的维度变量提供不同的值。
这次当我们设置以前是平的元素的z-index时,我们将1添加到下一个元素(向右)的z-index中,以确保它比此元素更高。但是,我们不能使用之前的长度变量(order),否则它将与flat元素具有相同的z-index,但会出现在其上面,因为它在 DOM 中在元素之后。
最后一个区别是,当我们初始化数组中的第三个和第五个项目时,我们指定了向左而不是向右的当前斜率,因此这些项目设置为相当于 30 和 10 度的弧度,而不是-30 和-10。
行动时间——连接控件
剩下的事情就是在小部件底部的左右链接上添加事件处理程序,以便查看不同的图像。在两个斜率函数之后,添加以下代码:
viewer.find("#left a").click(function(e) {
e.preventDefault();
skewRTL();
});
viewer.find("#right a").click(function(e) {
e.preventDefault();
skewLTR();
});
刚刚发生了什么?
我们所做的一切就是为每个链接添加一个点击处理程序,该处理程序使用preventDefault阻止链接被跟随,然后调用相关的斜率函数。现在,该示例应在所有常见浏览器中完全工作,尽管 IE 一般处理效果较差,动画速度较慢,斜率不太准确,抖动且难以控制。
需要注意的一点是,当使用 jQuery 源文件的完整版本和压缩版本时,存在差异,这会导致旧版本的 IE 在使用压缩版本时抛出错误,但在使用未压缩版本时不会抛出错误。
有一个尝试的英雄 – 扩展矩阵动画
建议构建这个例子,以便它包含渐进增强。开发一个替代的、可访问的布局,在禁用脚本时工作,并将小部件转换为本示例中使用的格式。
你还可以为 IE 开发一个更合适的备用方案,在这个例子中使用一个更简单的图像查看器,也许是在书中早些时候看过的那些之一。
快速测验 – 使用矩阵
Q1. CSS3 矩阵变换函数在哪种情况下有用?
-
当我们想要使用弧度而不是度数时。
-
当我们需要动画化一个变换函数时。
-
当我们想要将多个变换函数应用到一个元素时。
-
编写 Internet Explorer 代码时。
Q2. 在变换函数 matrix(a, b, c, d, e, f) 中,哪些参数指的是元素的平移?
-
a和b -
a和d -
b和c -
e和f
总结
在本章中,我们详细讨论了 CSS3 变换样式属性,涵盖了一些不同的变换函数,包括:
-
matrix -
rotate -
scale -
scaleX -
scaleY -
skew -
skewX -
skewY -
translate -
translateX -
translateY
我们在本章中学到了很多关于 CSS3 的 matrix 属性,以及如何与 jQuery 结合使用它。具体来说,我们学到了以下内容:
我们首先看到了这些函数接受的不同值,以及它们对应用于的元素产生的效果。我们还看到,为了对这些样式进行动画处理,我们可以使用简单的本地 JavaScript 定时器或超时来连续调整函数参数,或者以快速的顺序应用它们。我们了解到,在大多数情况下,这些变换函数只能单独应用到元素上,我们还看到,只有最后一个定义的变换函数才会应用到元素上。然而,矩阵函数允许我们将多个函数应用到单个元素上。
我们学到了我们不能旋转和倾斜单个元素,但是我们可以旋转、缩放和平移一个元素,或者如果愿意,倾斜、缩放和平移它。CSS3 变换的浏览器支持非常好,在大多数浏览器之间只有非常小的差异。
尽管我们不能在 jQuery 的 animate() 方法中使用转换函数,但我们可以轻松地手动创建自己的动画,并且可以将它们与其他方法一起使用,比如 css() 方法。不要忘记使用 cssHooks(参见前面的提示)来实现这种功能。
在本书的下一章,也是最后一章中,我们将介绍一个新的 HTML5 元素,它允许我们对页面上的区域进行像素级的控制—<canvas> 元素—以及它如何用于创建交互式动画。
第十章:画布动画
在上一章中,我们看了最新的 CSS3 功能之一,transform属性,它使我们能够创建动画旋转、扭曲、缩放和转换。在本章中,我们将看一下 HTML5 的新添加——<canvas>元素。
最好的方法是将<canvas>元素视为艺术家绘画的画布。我们可以使用 JavaScript API 方法绘制简单的线条或复杂的形状,并且还支持图像和文本。目前画布是二维的,但将来可能会扩展到包括 3D 支持。
<canvas>元素首先由苹果提出和使用,已被大多数现代浏览器实现,并被认为是 HTML5 规范中最稳定的元素之一。
我见过的关于<canvas>元素的最好描述是:“一个画布是你页面上的一个矩形,你可以用 JavaScript 来绘制任何你想要的东西”,来自diveintohtml5.info,我觉得这描述得很好。
本章将讨论的主题包括:
-
HTMLCanvasElement 接口
-
绘制到
<canvas>元素 -
动画化
<canvas>元素 -
使用
<canvas>与 jQuery -
创建基于
<canvas>的游戏
学习 HTMLCanvasElement 接口
HTMLCanvasElement 接口公开了允许我们定义和控制在画布上绘制的形状的方法和属性。HTMLCanvasElement 接口可以根据方法的作用被分解为不同的部分。
使用 canvas 元素
<canvas>元素本身有一些可以调用的方法,包括:
| 方法 | 用法 |
|---|---|
getContext(a) |
返回一个对象(一个精确的CanvasRenderingContext2D对象),然后可以调用 API 的其他方法来操纵<canvas>元素。参数指定要检索的上下文类型。目前仅支持二维上下文。 |
toDataURL() |
返回代表<canvas>元素上图像的数据 URL。可选参数包括数据 URL 表示的图像类型(默认为 image/PNG),以及特定于类型的任何参数,例如图像/JPG 数据 URL 的质量。 |
<canvas> 元素可以被视为类似于没有 src 属性的 <img> 元素。允许的属性包括元素的 width 和 height 参数,以及 id 和 class 属性,等等。<canvas> 元素没有特殊的属性,尽管它可以包含其他元素。当浏览器无法显示 <canvas> 元素时,它可以将元素的内容显示为备用内容。我们只能访问 <canvas> 元素的 width 和 height 参数。设置其中任何一个属性会导致 <canvas> 元素将其内容重置为空,这在我们想要清除它时会很有用。
理解上下文方法
有两个方法与由 getContext() 方法返回的上下文对象直接相关。它们是:
| 方法 | 用法 |
|---|---|
save() |
保存画布的当前状态;只保存转换,不保存形状或路径。 |
restore() |
恢复保存的状态。 |
我们还可以设置一些适用于 <canvas> 元素上所有形状的全局属性。这些属性包括:
| 属性 | 用法 |
|---|---|
globalAlpha |
设置形状的 alpha 透明度。取值范围为 0.0 到 1.0 的小数。 |
globalCompositeOperation |
设置形状如何叠放在彼此之上。可以用来创建遮罩和清除形状的区域。 |
本地形状
<canvas> 元素仅定义了一个本地形状:矩形。这里需要注意的一点是,<canvas> 元素没有内部的 DOM 树——在 <canvas> 元素上绘制的形状或路径不会被创建为 <canvas> 元素的子元素,并且不能使用标准的 DOM 操作方法来访问它们。它们不是单独的对象,它们只是像素。与矩形相关的脚本 API 方法包括:
| 方法 | 用法 |
|---|---|
clearRect(a, b, c, d) |
从画布的某个区域移除所有形状和路径。参数 a 和 b 指定开始清除的坐标,参数 c 和 d 指定要清除的区域的宽度和高度。 |
fillRect(a, b, c, d) |
绘制矩形。参数 a 和 b 指定开始绘制的坐标,参数 c 和 d 指定其边的宽度和高度。 |
strokeRect(a, b, c, d) |
绘制矩形的轮廓。参数 a 和 b 表示形状的起始坐标,参数 c 和 d 表示其边的宽度和高度。 |
我们可以使用以下属性设置描边(轮廓)或填充的颜色,以及阴影:
| 属性 | 用法 |
|---|---|
fillStyle |
设置填充的颜色。可以设置为 CSS 颜色或渐变对象。 |
shadowBlur |
设置阴影的模糊程度。 |
shadowColor |
设置阴影的颜色。可以设置为 CSS 颜色或梯度对象。 |
shadowOffsetX |
设置阴影沿 x 轴的相对位置。 |
shadowOffsetY |
设置阴影沿 y 轴的相对位置。 |
strokeStyle |
设置描边的颜色。可以设置为 CSS 颜色或梯度对象。 |
这些属性也可以设置在路径和文本上。它们不仅限于本地形状。
使用路径进行绘制
除了矩形之外的任何形状都必须使用路径来绘制。这为我们提供了绘制自定义和复杂形状的灵活方式。创建路径的一些方法包括:
| 方法 | 用法 |
|---|---|
arc(a, b, c, d, e, f) |
绘制圆形子路径。参数 a 和 b 是子路径的起始坐标,c 是半径,d 是以弧度表示的起始角度,e 是以弧度表示的结束角度。最后一个参数 f 接受一个布尔值,表示是否逆时针绘制子路径。 |
arcTo(a, b, c, d, e) |
绘制到指定点的圆形子路径。参数 a 和 b 是起始坐标,c 和 d 是结束坐标。参数 e 是半径。 |
beginPath() |
开始新路径。 |
bezierCurveTo(a, b, c, d, e, f) |
沿贝塞尔曲线绘制子路径,贝塞尔曲线具有两个控制点。参数 a、b、c 和 d 表示两个控制点的坐标,参数 e 和 f 表示子路径的结束坐标。 |
closePath() |
通过从当前位置到当前路径列表中第一个子路径的起始位置绘制一条线来关闭路径。 |
fill() |
给当前路径创建的形状上色。 |
lineTo(a, b) |
从当前位置创建到指定坐标的新子路径。 |
moveTo(a, b) |
移动到由参数指定的坐标,而不绘制新的子路径。 |
quadraticCurveTo(a, b, c, d) |
沿二次曲线绘制子路径,二次曲线具有一个控制点。参数 a 和 b 表示控制点的坐标,而参数 c 和 d 表示子路径的结束坐标。 |
stroke() |
给当前路径列表的轮廓上色。 |
路径具有可以设置的几个属性,包括样式、线条或端点以及路径如何连接:
| 属性 | 用法 |
|---|---|
lineCap |
可以设置为 butt(默认)、round 或 square。 |
lineJoin |
可以设置为 miter(默认)、round 或 bevel。 |
lineWidth |
指定路径的宽度的小数。 |
miterLimit |
确定两个路径连接的内点与连接斜接前的外点之间的长度。 |
绘制图像和图案
画布允许我们以与为其他元素分配背景图像的方式将图像绘制到画布上。我们还可以基于图像或渐变绘制图案。这类方法包括:
| 方法 | 用法 |
|---|---|
drawImage(a, b, c) |
在<canvas>元素上绘制图像。参数a是要绘制的图像,参数b和c是放置图像左上点的坐标。请注意,该方法存在其他变体,允许以不同的参数组合放置、缩放和切片图像。 |
createPattern(a, b) |
在<canvas>元素上绘制重复的图案。参数a是要用作图案的图像,b是重复的类型。 |
createLinearGradient(a, b, c, d) |
在两个点之间创建线性渐变。参数a和b是渐变的起始坐标,c和d是结束坐标。 |
createRadialGradient(a, b, c, d, e, f) |
在两个圆之间创建径向渐变。参数a和b是起始坐标,c是第一个圆的半径。参数d和e是第二个圆的起始坐标,f是它的半径。 |
addColorStop(a, b) |
为渐变添加颜色。第一个参数是介于 0.0 和 1.0 之间的十进制数,表示要添加颜色的渐变内的相对位置。第二个参数是要使用的颜色。 |
drawImage()和createPattern()方法非常相似;它们都用于在<canvas>元素上绘制图像。不同之处在于图案是重复的。渐变方法返回一个渐变对象,然后可以将其用作形状的填充或描边样式。
文本字符串
可以将文本字符串写入画布,但我们无法对其进行太多的样式设置,因为文本没有关联的盒模型;这意味着没有填充、边距或边框。但是,我们可以使用其他属性设置字体和对齐方式,以及填充颜色或描边颜色。这些方法包括:
| 方法 | 用法 |
|---|---|
fillText(a, b, c) |
在<canvas>元素上创建实心文本字符串。第一个参数a是要写入的文本,参数b和c是文本的起始坐标。 |
measureText(a) |
测量指定的文本字符串,并返回一个具有宽度属性的度量对象。 |
stroketext(a, b, c) |
在<canvas>元素上创建轮廓文本字符串。第一个参数是要写入的文本,参数b和c是文本的起始坐标。 |
我们可以设置文本的属性包括:
| 属性 | 用法 |
|---|---|
font |
设置文本的大小和字体系列。 |
textAlign |
设置文本的对齐方式。可以是start(默认值)、end、left、right或center。 |
textBaseline |
设置文本的基线。可以是alphabetic(默认值)、top、hanging、middle、ideographic或bottom。 |
应用变换方法
<canvas>元素可以应用与上一章中看到的相同的变换,可以使用以下方法应用:
| 方法 | 用法 |
|---|---|
rotate(a) |
将形状旋转指定的弧度数。 |
scale(a, b) |
按指定量沿每个轴缩放形状,其中a为 x 轴,b为 y 轴。 |
translate(a, b) |
沿每个轴按指定量平移形状,其中a为 x 轴,b为 y 轴。 |
transform(a, b, c, d, e, f) |
transform()方法等效于矩阵变换函数形式,可以用相同的方式来缩放、平移和/或倾斜形状。 |
setTransform(a, b, c, d, e, f) |
将当前变换重置为标识矩阵,然后使用相同的参数调用transform()方法。这本质上是撤消当前变换,然后一次性设置指定的变换。 |
像素处理
<canvas>元素甚至允许我们直接处理画布中的像素,并且可以将形状检索为imageData对象,或者通过在像素级别操作<canvas>元素来直接创建形状。我们有以下用于操作像素的方法:
| 方法 | 用法 |
|---|---|
createImageData(a, b) |
使用提供的参数作为宽度和高度属性创建一个新的空白imageData对象。此方法还可以传递给另一个imageData对象,这将导致该方法返回一个与原始对象相同宽度和高度的(空白的)imageData对象。 |
getImageData(a, b, c, d) |
返回一个包含<canvas>元素指定区域的像素数据的imageData对象。参数a和b是区域的起始坐标,参数c和d是宽度和高度。 |
putImageData(a, b, c) |
将像素数据绘制到<canvas>元素。第一个参数是要使用的imageData对象,第二个和第三个是结果形状的起始坐标。 |
所有imageData对象,无论是我们从<canvas>元素获取的对象,还是我们使用createImageDate()方法创建的对象,都有几个属性可供我们使用,包括:
| 属性 | 用法 |
|---|---|
data |
此属性是CanvasPixelArray,当我们从<canvas>元素获取imageData对象时为只读。我们还可以使用它来设置我们创建的imageData对象中的像素数据。数组每个像素包含四个项:像素的r、g和b值以及 alpha 值。 |
height |
图像的高度由imageData对象表示。此属性为只读。 |
width |
图像的宽度由imageData对象表示。此属性为只读。 |
绘制到画布上
以编程方式绘制到 <canvas> 元素在理论上非常简单。方法和属性易于使用,在支持的浏览器之间相当一致。直接像素操作是掌握的 API 中最棘手的部分,但除此之外没有什么真正复杂的。
我们发现的一件事是我们的代码很快就会堆积起来。一旦我们绘制多个复杂形状,并设置各种属性,即使对于相对简单的绘图,我们的代码很容易就会达到几百行甚至更多。这在动画 <canvas> 元素的内容时尤为明显。
动作时间 - 绘制到画布上
让我们看一个快速绘制非动画形状的示例。我们甚至不需要 jQuery。
-
将
<canvas>元素添加到我们模板文件的<body>标签中:<canvas id="c" width="500" height="300"> <p>您的浏览器不支持画布元素!</p> </canvas> -
接下来,我们可以添加 JavaScript 来绘制
<canvas>元素。我们将绘制一个联合国旗。在模板文件底部的<script>元素中的函数中添加以下代码,并将其替换为以下代码:var canvas = document.getElementById("c"), context = canvas.getContext("2d"); context.fillStyle = "#039"; context.fillRect(50, 50, 400, 200); context.beginPath(); context.strokeStyle = "#fff"; context.lineWidth = 50; context.moveTo(250, 50); context.lineTo(250, 250); context.moveTo(50, 150); context.lineTo(450, 150); context.moveTo(50, 50); context.lineTo(450, 250); context.moveTo(50, 250); context.lineTo(450, 50); context.stroke(); context.closePath(); context.strokeStyle = "#C00"; context.lineWidth = 30; context.beginPath(); context.moveTo(250, 50); context.lineTo(250, 250); context.moveTo(50, 150); context.lineTo(450, 150); context.stroke(); context.closePath(); context.lineWidth = 1; context.fillStyle = "#C00"; context.beginPath(); context.moveTo(50, 50); context.lineTo(195, 125); context.lineTo(165, 125); context.lineTo(50, 66); context.fill(); context.closePath(); context.beginPath(); context.moveTo(450, 50); context.lineTo(305, 125); context.lineTo(275, 125); context.lineTo(422, 50); context.lineTo(450, 50); context.fill(); context.closePath(); context.beginPath(); context.moveTo(450, 250); context.lineTo(310, 175); context.lineTo(335, 175); context.lineTo(450, 235); context.lineTo(450, 250); context.fill(); context.closePath(); context.beginPath(); context.moveTo(50, 250); context.lineTo(200, 175); context.lineTo(225, 175); context.lineTo(80, 250); context.lineTo(50, 250); context.fill(); context.closePath(); -
将文件保存为
canvas.html。 -
如果我们现在在任何现代浏览器中运行页面,应该会看到类似以下截图的内容:
![动作时间 - 绘制到画布上]()
-
在前面的屏幕截图中,我们可以看到组成英国国旗的简单几何形状的简单排列(注意国旗并非完全按比例缩放)。像这样的图像使用
<canvas>元素很容易生成,但是即使简单的形状也可能需要大量的代码。
刚才发生了什么?
我们首先使用 JavaScript 的getElementById()方法获取<canvas>元素,然后使用getContext()方法从<canvas>元素获取二维上下文对象。现在我们可以通过上下文对象与<canvas>元素交互了。
我们使用fillStyle属性设置一些上下文的颜色,然后使用fillRect()方法绘制一个实心矩形。指定的参数是矩形的起始 x 和 y 位置,以及宽度和高度。
填充的矩形采用我们刚刚设置的填充样式,即深蓝色,并将形成国旗的背景。现在我们需要在蓝色背景上创建一个白色的水平和对角十字。我们可以通过在国旗中间绘制两条粗线条,一条垂直,一条水平来实现这一点。我们将使用路径进行此操作,因此我们使用beginPath()方法开始一个新路径。
接下来,我们使用strokeStyle属性将描边颜色设置为白色,并使用lineWidth属性设置路径的宽度。要绘制路径,我们必须告诉<canvas>元素(或者实际上是上下文对象)从哪里开始路径,我们使用moveTo()方法,将要移动到的坐标作为参数指定(矩形的顶部中间)。
要生成路径,我们然后使用lineTo()方法并指定路径的结束坐标(矩形的底部中间)。这给我们了垂直线。要生成水平路径,我们重复相同的过程,移动到矩形的左中部并绘制到右中部。
使用moveTo()方法指定的坐标始终相对于画布本身,其中0, 0代表画布的左上角。即使对于lineTo()方法也是如此,尽管所绘制的线条始于上一次调用moveTo()指定的点。
接下来,我们需要在背景矩形和垂直十字上绘制一个对角的白色十字,我们将使用相同的方法绘制路径,使用moveTo()和lineTo()方法的组合。
到目前为止,我们添加的所有路径都是同一路径的一部分——它们是子路径,并且此时它们实际上是不可见的。为了使它们可见,我们需要填充或描边它们,因此我们使用stroke()方法描边它们,然后使用closePath()方法关闭路径。
对于国旗的下一部分,我们需要在白色十字上绘制一个略细的红色十字。我们将使用另一个路径来实现这一点。我们设置新的颜色样式和宽度,并再次在矩形中心绘制新的路径,垂直和水平方向。
要完成国旗,我们需要添加四个形状来制作红色十字架的对角部分。对于这些部分,我们不能使用直线路径,因为它们不相交,而且它们的位置略有不同。这意味着我们必须手动绘制它们作为自定义形状并对其进行填充。
这四个形状实际上构成了大部分代码,但我们基本上在做与以前非常相似的事情。每个形状都是通过绘制子路径并填充它们来制作的。我们为每种形状使用新路径以保留线条的抗锯齿效果。如果我们为所有四个形状使用一个大路径,形状的边缘会变得锯齿状。
快速测验——绘制到画布上
Q1. fillRect()方法需要什么参数?
-
矩形的 x 和 y 位置
-
矩形的宽度和高度
-
矩形的 x 和 y 位置,宽度和高度以及颜色
-
矩形的 x 和 y 位置,以及宽度和高度
Q2. 使路径可见需要哪个方法?
-
strokeStyle和lineWidth -
moveTo()和lineTo() -
stroke()或fill() -
closePath()
尝试英雄——创建你的国旗
如果你不是来自英国,可以尝试在画布上绘制你自己国家的国旗。我们可以使用标准 JavaScript for 循环创建重复形状的组合,所以尽可能充分利用它,以使你的国旗所需的代码尽可能少。如果你来自英国,可以尝试重新创建一个喜爱的标志或图标。
如果你的国旗(或 logo)的某一部分非常复杂,记住我们可以将图像绘制到<canvas>元素以外,还可以使用线条和形状,所以可以随意使用<canvas>元素的绘制方法绘制国旗的基本部分,然后对于复杂的部分使用图像。
画布动画
到目前为止,我们所看到的<canvas>方法很容易使用,它们无非就是有点重复。在<canvas>元素上对对象进行动画处理才是开始变得有趣的地方。动画比简单绘制在<canvas>上要复杂,而且因为我们除了试错之外没有真正的调试方法,解决 bug 可能很快变得棘手和耗时。
在我们的国旗例子中,使用<canvas>元素其实没有太大的好处。我们完全可以通过在页面上简单包含国旗图像来达到完全相同的效果,并且代码和处理量要少得多。但是,从<canvas>元素的动画开始才是它真正好处的地方。这是我们可以做比简单图像更多的东西的地方。动画<canvas>元素带来额外的复杂性完全是值得的。
行动时间——在画布上创建一个动画
在这个例子中,我们将像以前一样绘制相同的国旗,只是这一次我们将以动画不同的形状。此示例中使用的基本 HTML 与以前的示例完全相同。改变的只是在<body>元素末尾的<script>元素的内容。
-
为了制作本示例的工作文件,只需删除
canvas-explorer.html底部<script>元素中的所有内容,然后将文件另存为canvas-animated.html。 -
首先,我们将把蓝色矩形从
<canvas>的侧面移动到<canvas>元素的中心。将以下代码添加到页面底部现在空的<script>元素中:(function() { var canvas = document.getElementById("c"), init = function(context) { var width = 0, pos = 0, rectMotion = function() { if (width < 400) { width = width + 2; context.fillStyle = "#039"; context.fillRect(0, 50, width, 200); } else if (pos < 50) { pos = pos + 2; canvas.width = 500; context.fillStyle = "#039"; context.fillRect(pos, 50, 400, 200); } else { clearInterval(rectInt); whiteLines(context); } }, rectInt = setInterval(function() { rectMotion() }, 1); }; if (window.ActiveXObject) { window.onload = function() { var context = canvas.getContext("2d"); init(context); } } else { var context = canvas.getContext("2d"); init(context); } })();
刚才发生了什么?
在本章的之前示例中,我们的所有变量都是全局的,这在实际编码中通常是不良的实践。在这个示例中,我们的代码位于匿名函数的范围内,因此变量只能在该函数内部访问,因此不被视为全局变量。
在 init() 函数中,我们声明 width 和 pos 变量,然后定义另一个内联函数 rectMotion(),该函数将被间隔地重复调用。在 <canvas> 元素的边界之外绘制的任何形状都不存在,因此我们无法在视图之外绘制一个矩形,然后将其动画化为视图内。相反,我们逐渐建立矩形,从左边缘开始逐渐增加矩形的宽度,直到它的宽度正确为止。
这是使用 if 语句的第一个分支完成的,当 width 变量小于 400 时将执行该分支。为了加快动画速度,我们实际上每次增加两个像素的矩形宽度(尽管动画速度在不同的浏览器之间也有很大的差异),通过增加 width 变量,然后将变量用作 fillRect() 方法的 width 参数。
一旦 width 变量达到 400,我们就切换到使用 pos 变量。在这部分条件中,我们将 pos 变量增加两个(矩形将看起来每次移动两个像素,再次为了速度),通过设置其宽度重置 <canvas> 元素,并设置 fillStyle 属性。然后,我们绘制新的矩形,使用 pos 变量作为 x 轴位置的参数。
看起来好像矩形正在向右移动,但实际上并非如此。我们实际上是销毁了矩形,然后绘制了一个完全新的矩形,它比原来的位置右移了两个像素。
矩形位于正确的位置后,我们清除间隔,然后调用下一个函数(很快我们将添加这个),传递上下文对象。在rectMotion()函数之后,我们添加一个包含调用函数以动画矩形的 ID 的最终变量。完成动画后,我们使用此变量清除间隔。
如果此时在浏览器中运行页面,蓝色矩形似乎从左侧移入<canvas>元素,然后停在中间。接下来,我们需要在蓝色矩形上方动画显示水平和对角线白色交叉线。
实施行动-动画显示白十字架
在动画的这一部分,我们将在矩形的中间和中心画一条白线,然后将对角线交叉点从中心扩展到角落。以下代码应该添加到目前为止的代码中的canvas和init变量之间:
whiteLines = function(context) {
context.fillStyle = "#fff";
context.strokeStyle = "#fff";
context.lineWidth = 50;
var width = 0,
height = 0,
pos = {
ne: { x: 250, y: 150 },
se: { x: 250, y: 150 },
nw: { x: 250, y: 150 },
sw: { x: 250, y: 150 }
},
growDiagonal = function() {
if (pos.ne.x >= 50) {
context.beginPath();
context.moveTo(pos.ne.x, pos.ne.y);
context.lineTo(pos.ne.x - 4, pos.ne.y - 2);
context.moveTo(pos.se.x, pos.se.y);
context.lineTo(pos.se.x - 4, pos.se.y + 2);
context.moveTo(pos.nw.x, pos.nw.y);
context.lineTo(pos.nw.x + 4, pos.nw.y + 2);
context.moveTo(pos.sw.x, pos.sw.y);
context.lineTo(pos.sw.x + 4, pos.sw.y - 2);
context.stroke();
context.closePath();
pos.ne.x = pos.ne.x - 2;
pos.ne.y = pos.ne.y - 1;
pos.se.x = pos.se.x - 2;
pos.se.y = pos.se.y + 1;
pos.nw.x = pos.nw.x + 2;
pos.nw.y = pos.nw.y + 1;
pos.sw.x = pos.sw.x + 2;
pos.sw.y = pos.sw.y - 1;
} else {
clearInterval(crossInt);
redCross(context);
}
},
growVertical = function() {
if (height < 200 || width < 400) {
if (height < 200) {
height = height + 2;
context.fillRect(225, 50, 50, height);
}
if (width < 400) {
width = width + 4;
context.fillRect(50, 125, width, 50);
}
} else {
clearInterval(rectInt);
crossInt = setInterval(function() { growDiagonal() }, 1);
}
},
rectInt = setInterval(function() { growVertical() }, 1);
},
发生了什么?
本质上,我们有另一个内联函数,其中包含另一个函数,该函数以另一个间隔重复调用。由于我们这次画白色十字架,因此我们需要设置一些样式属性(在该函数中我们将画线和矩形,因此设置fillStyle和strokeStyle)以及lineWidth属性。
我们初始化width和height控制变量,这些变量将控制间隔运行的次数,还将起始位置垂直和对角线交叉点存储在一个名为pos的对象中。
接下来,我们定义两个内联函数,一个用于创建垂直十字架,另一个用于创建对角十字架。首先使用一个间隔调用growVertical()函数,我们只需在背景的中心从上到下绘制一个白色矩形,然后使用width和height变量重复间隔,直到矩形达到正确尺寸。一旦矩形的尺寸正确,间隔就会清除,然后使用另一个间隔调用growDiagonal()函数。
在这个函数中,我们需要绘制四条线,每条线都从垂直交叉点的中间开始。我们使用pos对象中的不同属性来实现这一点。每次执行函数时,我们移动到对象中每条线指定的 x 和 y 位置,然后绘制到相关的角落。然后我们更新对象中的属性,以准备函数的下一次迭代。
每个属性都需要以不同的量进行更新,例如,从中心移动到矩形的左上角的线需要沿着 x 和 y 轴负向移动,而移动到右上角的线需要沿着 x 轴正向移动,但沿着 y 轴负向移动。我们在函数的每次迭代中使用新路径来保留线条的反锯齿效果。
一旦线条绘制完成,我们清除间隔并调用下一个函数。现在我们来定义这个函数。它应该放在canvas变量之后,但直接放在我们刚刚添加的whiteLines()函数之前。
动手行动-为红色十字架添加动画
现在我们只需要绘制垂直红色十字架和四个自定义红色形状。在<script>元素顶部附近的rectInt变量声明和我们在上一节中定义的whiteLines函数之间添加以下代码:
redCross = function(context) {
context.fillStyle = "#C00";
context.strokeStyle = "#C00";
context.lineWidth = 30;
var width = 0,
height = 0,
pos = {
up : { x: 250, y: 150 },
down : { x: 250, y: 150 },
left: { x: 250, y: 150 },
right: { x: 250, y: 150 }
},
addStripes = function() {
context.lineWidth = 1;
function makeStripe(props) {
context.beginPath();
context.moveTo(props.startX, props.startY);
context.lineTo(props.line1X, props.line1Y);
context.lineTo(props.line2X, props.line2Y);
context.lineTo(props.line3X, props.line3Y);
context.fill();
context.closePath();
}
setTimeout(function() { makeStripe({
startX: 50, startY: 50,
line1X: 195, line1Y: 125,
line2X: 165, line2Y: 125,
line3X: 50, line3Y: 66
})}, 1);
setTimeout(function() { makeStripe({
startX: 450, startY: 50,
line1X: 305, line1Y: 125,
line2X: 275, line2Y: 125,
line3X: 422, line3Y: 50
})}, 50);
setTimeout(function() { makeStripe({
startX: 450, startY: 250,
line1X: 310, line1Y: 175,
line2X: 335, line2Y: 175,
line3X: 450, line3Y: 235
})}, 100);
setTimeout(function() { makeStripe({
startX: 50, startY: 250,
line1X: 200, line1Y: 175,
line2X: 225, line2Y: 175,
line3X: 80, line3Y: 250
})}, 150);
},
growVertical = function() {
if (height < 100 || width < 200) {
如果 (height < 100) {
context.beginPath();
context.moveTo(pos.up.x, pos.up.y);
context.lineTo(pos.up.x, pos.up.y - 2);
context.moveTo(pos.down.x, pos.down.y);
context.lineTo(pos.down.x, pos.down.y + 2);
context.stroke();
context.closePath();
height = height + 2;
pos.up.y = pos.up.y - 2;
pos.down.y = pos.down.y + 2;
}
if (width < 200) {
context.beginPath();
context.moveTo(pos.left.x, pos.left.y);
context.lineTo(pos.left.x - 2, pos.left.y);
context.moveTo(pos.right.x, pos.right.y);
context.lineTo(pos.right.x + 2, pos.right.y);
context.stroke();
context.closePath();
width = width + 2
pos.left.x = pos.left.x - 2;
pos.right.x = pos.right.x + 2;
}
} else {
clearInterval(crossInt);
addStripes();
}
},
crossInt = setInterval( function() { growVertical() }, 1);
},
刚刚发生了什么?
再次,我们有一个外部内联函数(称为redCross())包含设置颜色和线样式的一些属性,以及将用于绘制红十字和四个自定义形状的一些嵌套函数。与前一个函数一样,我们声明了width和height控制变量,以及一个名为pos的对象,其中包含组成十字形的线的起始位置。首先用growVertical()函数绘制十字。
这个函数与上一节代码中的函数非常相似。我们从矩形中间开始绘制四条线,向上和向下中心,以及右边和左边中心辐射。
四个自定义形状使用一个主函数来绘制,该函数接受一个配置对象,指定开始点(传递给moveTo()方法)和组成每个子路径的点(传递给lineTo()方法)。然后,我们使用setTimeout JavaScript 函数来依次创建每个形状,使用传递给主函数的对象来指定在画布上绘制每个形状的相关点。
这是我们需要的所有代码;因此,当我们运行页面时,我们应该看到绘制旗帜动画。代码在所有浏览器中都可以运行,每个浏览器的性能各不相同。动画<canvas>元素主要是关于条件if语句,间隔和超时。正如我们所见,代码本身非常简单。我们只需要相当多的代码来产生甚至简单的动画。
快速测验 – 动画化画布
Q1. 为什么我们将每次调用setInterval()存储在一个变量中?
-
出于性能原因
-
为了在适当时清除间隔
-
由于使用匿名函数作为函数的第一个参数创建的闭包
-
以便我们可以将参数传递给间隔调用的函数
Q2. 在第一个函数中,我们绘制了蓝色矩形,在每次间隔调用rectMotion()函数时,我们设置了<canvas>元素的宽度。为什么?
-
确保
<canvas>元素足够大,以容纳矩形随着其增长而增长 -
为了纠正 Internet Explorer 中的一个错误
-
重置
<canvas>元素的状态,确保动画中每个点上只有一个矩形 -
作为设置
fillStyle属性的要求
试试吧——创建画布动画
返回您绘制的家乡国家的国旗(或您选择的标志或图像)的静态版本,并将其转换为动画,并呈现出不同的国旗部分。
创建一个画布游戏
最好的动画是那些交互性强、能够吸引用户的动画,这正是游戏可以被视为一种连续的、用户驱动的动画的方式。当它用于创建游戏时,<canvas>元素的强大功能最能得到展示,正如我们将在本节中看到的那样。
我们将创建一个非常基本的街机经典游戏Space Invaders的克隆版本,其中包括一系列慢慢向屏幕下方移动的外星飞船,以及底部由用户控制可以射击进来的外星飞船:

行动时间——创建初始页面
我们将用于此示例的初始页面与上一个示例中使用的类似:
-
在文本编辑器中创建一个新页面,其中包含以下标记:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>一个画布和 jQuery 游戏</title> <link rel="stylesheet" href="css/canvas-game.css"> </head> <body> <canvas tabindex="1" id="c" width="900" height="675"> <p>您的浏览器不支持 canvas 元素!</p> </canvas> <script src="img/jquery.js"></script> <script> $(function() { }); </script> </body> </html> -
将文件保存为
canvas-game.html。我们还需要一个非常基本的样式表来制作我们的游戏。我们所做的全部就是对<canvas>元素本身进行样式设置。创建一个包含以下样式规则的新样式表:画布 { border:1px solid #000; margin:auto; display:block; outline:none; 背景:url(../img/bg.gif) no-repeat; } -
将此文件保存在
css目录中,命名为canvas-game.css。
刚刚发生了什么?
页面上的主要元素当然是<canvas>元素。与以前示例中使用的元素唯一的区别在于我们在其上设置了tabindex属性,以便它可以接收键盘事件,这对于检测和响应用户输入是必要的。在此示例中,我们还使用了 jQuery,并使用了标准的匿名函数和我们在整本书中一直使用的$别名构造。
我们使用的样式简单地将<canvas>元素定位在页面中央,给它加上边框,并移除一些浏览器中焦点元素周围出现的虚线轮廓。我们还在元素上设置了背景图像。
应用于<canvas>元素的背景图像有助于为我们的游戏设置场景,并且使用 CSS 在<canvas>元素上设置背景图像比在其中绘制图像要容易得多。
采取行动的时间 - 创建初始脚本
游戏的脚本相当长,因此我们将以不同的部分来查看它,从脚本的初始结构开始。以下代码应该放入页面底部的匿名函数中:
var canvas = document.getElementById("c"),
context = canvas.getContext("2d"),
motionInt = null,
dirCounter = 0,
alienSpeed = 1000,
aliens = [],
alienMotion = function(dir) {
},
addAliens = function() {
},
ship = new Image(),
shipPos = [430, 600];
ship.src = "img/ship.png";
ship.onload = function() {
context.drawImage(ship, shipPos[0], shipPos[1]);
addAliens();
};
刚刚发生了什么?
本质上,我们在这里所做的一切就是定义一系列变量和一个onload事件处理程序。首先定义canvas和context变量,就像以前的示例一样,以便访问和操作画布。
我们还设置了一个名为motionInt的变量,稍后将用于保存setInterval()函数的 ID,一个名为dirCounter的变量,用于确定外星人移动的方向,一个alienSpeed变量来设置外星人移动的速度,并且一个空的aliens数组,我们将用它来跟踪页面上的每个外星人。
接着,我们定义了两个内联函数,一个用于移动外星人,一个用于将外星人添加到页面。目前它们都是空的,但接下来我们将填充它们。我们还创建了一个新的图像,它将是用户控制的太空船,并且一个shipPosition数组,将用于跟踪船在页面上的位置。
定义了所有变量后,我们将新创建的图像对象的src设置为代表太空船的图像。然后,我们将一个onload事件处理程序附加到船对象,该处理程序将在图像加载完成后执行。在此函数中,我们使用存储在imagePosition数组中的值在画布上绘制船。然后,我们调用addAliens()函数,该函数将在画布上添加外星人。接下来,我们可以添加代码到addAliens()函数。
采取行动的时间 - 将外星人添加到页面
用以下代码替换前一代码块中的addAliens()内联函数:
addAliens = function() {
var alienPos = [13, 0],
alien = new Image();
alien.src = "img/alien.gif";
alien.onload = function () {
for (var x = 0; x < 15; x++) {
for (var y = 0; y < 3; y++) {
context.drawImage(alien, alienPos[0], alienPos[1]);
var data = {
img:alien,posX:alienPos[0],posY:alienPos[1]
};
aliens.push(data);
if (alienPos[1] < 100) {
alienPos[1] = alienPos[1] + 50;
} else {
alienPos[0] = alienPos[0] + 50;
alienPos[1] = 0;
}
};
}
};
motionInt = setInterval(function () { alienMotion("right"); }, alienSpeed);
},
刚刚发生了什么?
我们首先定义一个新数组,用于逐步设置每艘外星人飞船的位置,同时外星人最初被绘制到画布上。我们为将用于所有外星人飞船的图像定义一个新的Image对象,并设置其src属性。然后我们为新外星人图像设置一个onload处理程序,以便在图像加载完成后对图像进行操作。
我们想要创建三行各含 15 个外星人,所以在onload处理程序中,我们从两个嵌套的for循环开始,其中外部循环运行 15 次,在每次循环时,内部for循环执行三次。在嵌套循环内部,我们首先使用存储在alienPos数组中的值将新外星人绘制到画布上。然后,我们创建一个新的data对象,该对象存储了图像对象的引用以及图像在画布上的 x 和 y 位置。然后将新的data对象推入我们之前在脚本开始处定义的aliens数组中。
我们接着更新alienPos数组中的值。如果数组中的第二项(索引为1的项)小于100,我们就给该数组项的值加上50。数组中的第二项对应画布上 y 轴的位置。这样我们就得到了一个包含三个外星人的单列。请注意,我们将第一列外星人的 x 位置从0开始改为从13开始,这样画布的边缘和第一列外星人之间就有了间隙。
如果第二个数组项大于100,我们将50添加到数组中的第一个项,该项对应画布上的 x 轴,并将第二个数组项重置为零。这样我们就得到了三个外星人的 15 列。
一旦所有外星人都已绘制到画布上,我们就设置一个间隔,根据alienSpeed变量中包含的毫秒数,重复执行接下来的函数alienMotion(),该变量在脚本开始时最初设置为1000。间隔 ID 存储在我们在脚本开始时还创建的motionInt变量中。我们可以接着向alienMotion()函数中添加代码。
行动时间 - 移动外星人
我们接下来的代码块将赋予外星人运动,使它们先向右沿着画布前进,然后下移一行,然后向左移动,依此类推。用以下代码替换我们之前定义的alienMotion()函数:
alienMotion = function (dir) {
var alienLength = aliens.length;
if (dirCounter < 4) {
for (var x = 0; x < alienLength; x++) {
context.clearRect(aliens[x].posX, aliens[x].posY, aliens[x].img.width, aliens[x].img.height);
}
for (var y = 0; y < alienLength; y++) {
aliens[y].posX = (dir === "right") ? aliens[y].posX + 35 : aliens[y].posX - 35;
context.drawImage(aliens[y].img, aliens[y].posX, aliens[y].posY);
}
dirCounter++;
} else {
clearInterval(motionInt);
dirCounter = 0;
for (var z = 0; z < alienLength; z++) {
context.clearRect(aliens[z].posX, aliens[z].posY, aliens[z].img.width, aliens[z].img.height);
}
if (aliens[alienLength - 1].posY > 530) {
canvas.width = 900;
context.fillStyle = "#fff";
context.textAlign = "center";
context.font = "bold 36px Tahoma";
context.fillText("GAME OVER!", 450, 350);
$(canvas).blur().unbind("keydown");
} else {
for (var a = 0; a < alienLength; a++) {
aliens[a].posY = aliens[a].posY + 29;
context.drawImage(aliens[a].img, aliens[a].posX, aliens[a].posY);
}
motionInt = (dir === "right") ? setInterval(function () { alienMotion("left"); }, alienSpeed) : setInterval(function () { alienMotion("right"); }, alienSpeed);
}
}
},
刚发生了什么?
我们要做的第一件事是在一个本地变量中存储外星人数组的长度。在这个函数中我们会使用几个for循环,因此最好仅在开始时检索这个值,并将for循环的计数变量与该变量进行比较,而不是在各种循环的每次迭代中检查长度。
接着,我们使用一个if语句来检查dirCounter变量是否小于4。请记住,这是脚本开头设置的变量之一。如果变量小于4,我们首先使用一个for循环来遍历aliens数组中的每个项目,并使用clearRect()函数从画布中移除外星人。
然后我们使用第二个for循环再次遍历aliens数组,这次更新每个外星人的 x 位置,根据数组中当前项目中存储的当前 x 位置是增加还是减少 35。
添加或移除 35 取决于传递给函数的参数。第一次调用alienMotion()函数,它将接收参数right,所以外星人最初会向右移动到画布。然后在新位置绘制每个外星人。一旦for循环完成,所有外星人都以新位置绘制,我们更新dirCounter变量。
如果dirCounter变量等于4,外星人已经水平穿过画布所需的距离,这时我们需要将外星人从画布横向移动改为向下移动。在这个条件分支中,我们清除控制横向移动的时间间隔,然后将dirCounter变量重置为0。然后通过清除每个外星人覆盖的矩形来从画布中删除外星人。
在将外星人下移一行之前,我们首先检查数组中最后一个外星人的 y 位置是否大于530,因为这是外星人应该达到的距离的最大值。如果大于这个数字,至少有一个外星人已经达到了画布底部,对于玩家来说游戏已经结束。
在这种情况下,我们清除整个画布,删除太空船和任何存活的外星人,并在画布中央打印文本游戏结束!。我们还使用 jQuery 解除控制太空船的键盘事件(我们很快会添加这些绑定)。
如果外星人还没有到达画布底部,我们会使用另一个for循环遍历数组中的每个外星人,并将它们的 y 位置向下移动一行,然后在新位置上绘制每个外星人。
然后,我们设置一个新的间隔,向alienMotion()函数传递相反方向的字符串,这与先前使用的方法相反。这些四个步骤的循环,向右,向下一步,向左四步,依此类推,将持续到外星人到达画布底部并且游戏结束。接下来,我们需要添加处理程序,使玩家能够控制太空飞船。
行动时间-添加控制飞船的处理程序
以下代码块应该替换太空船图像对象的onload事件处理程序:
ship.onload = function () {
context.drawImage(ship, shipPos[0], shipPos[1]);
addAliens();
$(canvas).focus().bind("keydown", function (e) {
if (e.which === 37 || e.which === 39) {
context.clearRect(shipPos[0], shipPos[1], ship.width, ship.height);
if (e.which === 37 && shipPos[0] > 4) {
shipPos[0] = shipPos[0] - 4;
} else if (e.which === 39 && shipPos[0] < 896 - ship.width) {
shipPos[0] = shipPos[0] + 4;
}
context.drawImage(ship, shipPos[0], shipPos[1]);
} else if (e.which === 32) {
context.fillStyle = "#fff";
var bulletPos = shipPos[0] + 20,
newBulletPos = [bulletPos, 596],
alienLength = aliens.length,
fire = function () {
if (newBulletPos[1] > 0) {
context.clearRect(newBulletPos[0], newBulletPos[1], 3, 6);
newBulletPos[1] = newBulletPos[1] - 2;
context.fillRect(newBulletPos[0], newBulletPos[1], 3, 6);
for (var x = 0; x < alienLength; x++) {
if (newBulletPos[1] === aliens[x].posY || newBulletPos[1] === aliens[x].posY + aliens[x].img.height) {
if (newBulletPos[0] > aliens[x].posX && newBulletPos[0] - aliens[x].posX < aliens[x].img.width + 13) {
context.clearRect(aliens[x].posX, aliens[x].posY, aliens[x].img.width, aliens[x].img.height);
aliens.splice(x, 1);
clearInterval(bulletInt);
context.clearRect(newBulletPos[0], newBulletPos[1], 3, 6);
if (!aliens.length) {
clearInterval(motionInt);
dirCounter = 0;
alienSpeed = alienSpeed - 100;
addAliens();
}
}
}
}
} else {
context.clearRect(newBulletPos[0], newBulletPos[1], 3, 6);
clearInterval(bulletInt);
}
},
bulletInt = setInterval(function () { fire(); }, 1);
}
});
};
刚刚发生了什么?
我们使用 jQuery 为<canvas>元素附加事件处理程序,监听keydown事件。尽管我们不需要 jQuery 用于在附加事件时进行跨浏览器规范化支持 IE,但其仍使事件处理过程更加简单。
在检测到 keydown 事件时执行的函数内部,我们检查左箭头或右箭头键的存在,这些键在事件对象的 which 属性中分别为 37 和 39,或者检查空格键,其代码为 32。
如果检测到代码 37 或 39,则我们使用嵌套的 if 语句来区分这两个键。我们还检查飞船是否已经到达了画布的左边缘或右边缘。
然后我们使用 clearRect() 函数删除飞船,并在左侧或右侧绘制新的飞船,具体取决于按下的键是左箭头还是右箭头。这样一来,飞船就在画布底部左右移动。
外层条件语句的第二个分支处理按下空格键的情况,这将导致子弹离开飞船直线向画布顶部移动。子弹将是白色的,因此我们将画布的 fillStyle 属性设置为 #fff。
我们还在此处声明了一些局部变量,包括 bulletPos,它是子弹的当前位置加上飞船宽度的一半,并且声明一个数组来保存子弹的 x 和 y 坐标。该数组的值设置为子弹的 x 位置,以及飞船鼻子正上方的位置。我们还将外星人数组的长度存储为局部变量,以便再次在 for 循环中使用。
我们定义了一个内联函数和我们的变量,名为 fire()。该函数与间隔一起使用以创建子弹的运动。在此函数中,我们检查子弹是否未到达画布顶部,并且只要它未到达,也就是说,如果其 y 位置大于 0,我们就使用 clearRect() 函数移除子弹,然后更新 bulletPos 数组中的值,并使用数组中的更新值在新位置绘制子弹。
更新子弹位置后,我们需要检查子弹在新位置是否与外星人发生碰撞,因此我们使用 for 循环迭代 aliens 数组中的每个外星人。
在每次迭代中,我们首先检查子弹是否在外星人的 y 轴范围内,即其位置是否小于外星人的底部边缘,但大于其顶部边缘。外星人的位置是根据其左上角确定的,因此要判断子弹是否已经通过了其底部边缘,我们只需将外星人的高度添加到其 y 位置即可。
如果子弹在 y 轴上与外星人重叠,则我们检查其在 x 轴上是否与外星人所占据的空间重叠。如果是,则使用 clearRect() 函数从画布上移除外星人,并从数组中删除外星人,以确保其不再出现。
然后我们再次使用clearRect()函数从画布中移除子弹,并清除bulletInt间隔。如果没有更多的外星人了,我们会清除产生外星人运动的间隔,重置dirCounter变量,将alienSpeed变量减小100,然后调用addAliens()函数在画布顶部重新绘制外星人。
这实际上是玩家升级到下一级的方式,每次重新绘制外星人时,它们移动得更快,从而创建了游戏的基本进展。现在我们来到了代码的结尾。如果我们现在在兼容标准的浏览器中运行游戏,比如 Firefox 或 Chrome,我们应该发现我们有一个完全可玩的游戏,完全使用 JavaScript 和<canvas>元素实现。
爆炸小测验——创建基于画布的游戏
Q1. 在这个例子中,与玩家的太空飞船相关的许多特性都被放置在一个onload事件处理程序中。为什么?
-
因为在图像完全加载之前,我们无法与图像进行交互
-
为了让代码在 Internet Explorer 中正确工作
-
因为图像加载完成后,代码运行速度更快
-
帮助使我们的代码更模块化
Q2. 当写下GAME OVER!消息时,为什么我们将画布的textAlign属性设置为居中?
-
设置对齐是将文本写入画布的先决条件
-
因为相比于计算文本的宽度并在 x 轴上设置其位置以将文本定位在画布中心,这种方法更简单。
-
为了使文本具有反锯齿效果
-
因为与使用填充相比更有效率
试试看吧——扩展太空入侵克隆
我们的游戏是原始太空入侵的一个简化版本。原始的街机游戏还有许多其他功能,包括外星人向玩家的飞船开火、可以躲藏在后面的基地,以及在游戏中随机出现并击中时会掉落奖励的一次性特殊外星人。
当然,游戏需要的一件事情是计分机制;否则就没有动力玩了。实现一个计分系统,跟踪玩家在整个游戏中的得分,并将最高分保存到玩家的机器上。这可以很容易地通过 jQuery 和 cookie 插件,或者使用 LocalStorage 来实现。
由于这是本书的最后一个例子,我还要敦促你实现一些其他缺失的功能,比如让外星人具有还击能力,并添加玩家在困境中可以躲藏在其下的基地或护盾。
总结
在本章中,我们研究了 HTML5 的<canvas>元素,并了解了它如何用于创建简单静态图像、基本动画,甚至复杂的交互式游戏。它提供了丰富的 API,允许我们以编程方式与之交互,并完全控制页面上的一个区域的像素级别。
在本章中,我们涵盖了HTMLCanvasElement接口,绘制到<canvas>元素,创建<canvas>元素上的动画,我们还使用<canvas>元素创建了一个交互式游戏。与上一章的 CSS3 示例一样,jQuery 没有专门用于<canvas>的方法或属性,尽管有一些插件结合了<canvas>的强大功能和 jQuery 的易用性,并且有几个项目扩展了 jQuery 的animate()方法,使其能够在画布上绘制的对象上工作。有关更多信息,请参阅 Steven Wittens 的博客 acko.net/blog/abusing-jquery-animate-for-fun-and-profit-and-bacon。
我们现在已经到达了本书的结尾。我希望在这 10 章中,我已经为您提供了一个扎实的基础,以便使用 jQuery 制作动画,这是一个坚实的起点,让您的基于网络的用户界面生动起来。
附录 A. 弹出测验答案
第一章,入门
弹出测验 – 使用 jQuery 进行基本动画
| Q1 | 2 |
|---|---|
| Q2 | 3 |
第二章,图像动画
弹出测验 – 使用 fadeIn()
| Q1 | 3 |
|---|---|
| Q2 | 1 |
弹出测验 – length() 和毫秒
| Q1 | 2 |
|---|---|
| Q2 | 3 |
弹出测验 – preventDefault() 和 setInterval()
| Q1 | 2 |
|---|---|
| Q2 | 2 |
弹出测验 – 修改变量和零索引
| Q1 | 4 |
|---|---|
| Q2 | 1 |
第三章,背景动画
弹出测验 – 用 animate() 方法串联
| Q1 | 3 |
|---|
弹出测验 – scroll() 和 scrollTop() 方法
| Q1 | 2 |
|---|---|
| Q2 | 2 |
第四章,导航动画
弹出测验 – ^ 符号和 stop() 方法
| Q1 | 3 |
|---|---|
| Q2 | 1 |
第五章,表单和输入动画
弹出测验 – 表单动画和 jQuery UI
| Q1 | 4 |
|---|---|
| Q2 | 2 |
第六章,使用 jQuery UI 进行扩展动画
弹出测验 – 使用 API 的效果
| Q1 | 4 |
|---|---|
| Q2 | 2 |
弹出测验 – 使用显示/隐藏逻辑
| Q1 | 3 |
|---|---|
| Q2 | 1 |
弹出测验 – 使用缓动
| Q1 | 2 |
|---|---|
| Q2 | 1 |
弹出测验 – 缓动,颜色和类动画
| Q1 | 4 |
|---|---|
| Q2 | 3 |
第七章,自定义动画
弹出测验 – 创建一个动画内容查看器
| Q1 | 2 |
|---|---|
| Q2 | 3 |
弹出测验 – 创建扩展图片
| Q1 | 4 |
|---|---|
| Q2 | 1 |
弹出测验 – 创建一个插件
| Q1 | 4 |
|---|---|
| Q2 | 3 |
第八章,其他流行动画
弹出测验 – 实现接近动画
| Q1 | 3 |
|---|---|
| Q2 | 2 |
弹出测验 – 创建一个跑马灯滚动器
| Q1 | 4 |
|---|
第九章,CSS3 动画
弹出测验 – 实现 CSS3 旋转
| Q1 | 3 |
|---|---|
| Q2 | 1 |
弹出测验 – 使用矩阵
| Q1 | 3 |
|---|---|
| Q2 | 4 |
第十章,画布动画
弹出测验 – 在画布上绘图
| Q1 | 4 |
|---|---|
| Q2 | 3 |
弹出测验 – 画布动画
| Q1 | 2 |
|---|---|
| Q2 | 3 |
弹出测验 – 创建基于画布的游戏
| Q1 | 1 |
|---|---|
| Q2 | 2 |




















浙公网安备 33010602011771号