面向-Flash-开发者的-HTML5-全-

面向 Flash 开发者的 HTML5(全)

原文:zh.annas-archive.org/md5/EE4F7F02D625483135EC01062083BBEA

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

《HTML5 for Flash Developers》专门为准备立即投入 HTML5 开发的 Flash 开发人员编写。我们将首先分析组成 HTML5 的每个元素,然后开始学习如何通过比较它们的特性与典型的 Flash 开发来利用它们。

本书涵盖的内容

第一章《为什么学习 HTML5?》开始回答了为什么学习如何在 HTML5 中开发可以成为一项非常重要的技能。我们将继续全面概述组成 HTML5 的所有不同技术以及它们的利用方式。

第二章《为战斗做准备》涵盖了为网络准备图像、音频和视频等资产的重要过程。本章还涵盖了 JavaScript 开发的许多重要方面,以及它们与 ActionScript 3 的不同之处。

第三章《可伸缩性、限制和效果》旨在告诉您 HTML5 开发人员在今天为网络开发时经常遇到的许多限制。我们还将首次详细了解最新的层叠样式表 CSS3 的添加。最后,我们将通过查看 HTML5 Web Workers、Web Sockets、Canvas 元素和最终的 WebGL 来概述 HTML 开发的一些最有趣的新添加。

第四章《使用 HTML5 构建强大的应用程序》继续深入研究 JavaScript 开发,旨在以面向对象的方式构建代码。还将进行与 ActionScript 3 类结构、用法和语法的比较,以及一些库和框架的概述,以帮助构建强大的 HTML5 应用程序。

第五章《一次编码,到处发布》探讨了使开发人员能够轻松地针对多个平台进行单个应用程序构建的应用程序和代码库,最大限度地减少开发时间并最大化公众使用。我们将花费大部分时间深入研究 CreateJS 框架及其所有包。最后,我们将介绍 CSS3 媒体查询如何允许在各种屏幕上进行有针对性的元素样式设置。

第六章《HTML5 框架和库》继续深入挖掘在开发下一个 HTML5 应用程序时可用的各种令人惊叹的框架和库。我们首先查看了当今最受欢迎的库之一,即 jQuery。除了 jQuery JavaScript 库,我们还将看看 jQuery Mobile 项目如何将简单的 HTML 文档转换为移动友好的用户界面。最后,我们将查看其他开源项目,如 Google V8、Node.js 和 Three.js。

第七章《选择开发方式》探讨了许多适用于 HTML5 开发人员的流行代码编辑平台。我们将概述大多数开发人员在为网络开发时从编码环境中需要的绝对必需品。我们还将花一些时间了解 Adobe Edge Animate 平台,该平台为创建 HTML5 动画提供了类似 Flash 的用户界面。

第八章《导出到 HTML5》继续查看更多允许您在平台上编写 HTML5 应用程序并直接编译为 HTML5 的软件。我们将概述许多流行的导出到 HTML5 的平台,如 Jangaroo、Haxe 和 Google 的 Dart。

第九章,避免障碍,试图展示和讨论许多开发人员在使用 HTML5 的许多新添加时所面临的典型问题。在本章中,我们将开发一个简单的 2D 横向滚动游戏,并检查常见问题发生的位置。

第十章,发布准备,通过讨论通常在将 HTML5 应用程序发布到互联网之前执行的许多常见任务,结束了本书。我们将讨论适当的浏览器测试方法以及利用“夜间”网络浏览器版本。我们将讨论许多方法,通过外部应用程序和浏览器插件对 HTML5 内容进行基准测试,以便您检查运行时问题。最后,我们将讨论通过使用 Grunt 等应用程序自动化 Web 开发人员反复执行的流程的方法。

您需要为本书准备什么

为了完全理解本书,需要以下软件:

访问互联网以下载最新版本的开源库和框架。

本书的受众

本书专门针对有 Adobe Flash 网页应用程序和游戏开发经验,准备将 HTML5 开发添加到其技能组合中的开发人员。

约定

在本书中,您会发现一些文本样式,用于区分不同类型的信息。以下是一些这些样式的示例,以及它们的含义解释。

文本中的代码词如下所示:"我们可以通过使用include指令来包含其他上下文。"

代码块设置如下:

  this.setX = function(x) { _xVal = x; }
  this.setY = function(y) { _yVal = y; }
  this.currentX = function() { return _xVal; }
  this.currentY = function() { return _yVal; }
  this.currentWidth = function() { return _widthVal; }
  this.currentHeight = function() { return _heightVal; }

当我们希望引起您对代码块的特别关注时,相关的行或项目将以粗体显示:

  this.setX = function(x) { _xVal = x; }
  this.setY = function(y) { _yVal = y; }
 this.currentX = function() { return _xVal; }
 this.currentY = function() { return _yVal; }
 this.currentWidth = function() { return _widthVal; }
 this.currentHeight = function() { return _heightVal; }

新术语重要单词以粗体显示。例如,屏幕上看到的单词,菜单或对话框中的单词等,会在文本中以这种方式出现:"单击下一步按钮将您移至下一个屏幕"。

注意

警告或重要说明会以这样的方式出现在框中。

提示

提示和技巧会以这种方式出现。

第一章:为什么选择 HTML5?

在开始之前,重要的是您了解 HTML5 是什么,它是如何工作的,以及它与您作为 Flash 开发人员已经了解的内容有何关联。本书在比较 HTML5 的功能与 Flash 开发人员通常在 Flash 中创建应用程序时习惯使用的功能时,使用 Adobe Flash(CS6)作为参考。因此,要正确地跟随本书中的示例,需要对 Adobe Flash IDE 和 ActionScript 3 有扎实的了解。

在本章中,您将学到:

  • HTML5 实际上是什么,其中包括哪些技术

  • 将 HTML5 开发添加到您的技能集中的原因

  • 避免从 Flash 开发转换中涉及的初始问题

  • 现代浏览器与 HTML5 功能的兼容性

理解 HTML5

作为 Flash 开发人员,您可能经常甚至每天都使用 HTML 作为在 Web 上发布编译后的 Adobe Flash SWF 文件的平台。虽然安装了 Flash Player 的 Web 浏览器可以查看独立的 SWF 文件,但将 Flash 内容嵌入 HTML 文档是正常的做法。由于 HTML 的简单语法和可用性,许多甚至不是开发人员的人已经学会了传统的 HTML 开发技术。不幸的是,早期的 HTML 规范在许多在线显示内容的领域存在不足。创建丰富的交互体验、共享多媒体资产或创建基于 Web 的用户界面以更典型的软件方式运行等常见任务变得非常繁琐,甚至不可能。最近,许多 Web 上的这些功能已经使用 HTML5 重新制作,使用户可以以相同的方式访问内容,但不需要使用 Adobe Flash Player 等技术。

构建可以全球访问的高质量基于 Web 的内容是成功产品或服务的关键。Facebook、Google 和 Twitter 都在使用 HTML5 来改善其应用程序的用户体验,并向用户提供内容,而无需第三方插件。越来越多的企业通过利用 HTML5 开发堆栈在桌面和移动 Web 浏览器以及两个平台上的可安装应用程序上进行应用开发,从而节省时间、资源和金钱。

将您的 Flash 开发技能转换为 HTML5 是一个有趣的过程,不仅会为您在职业上开启更多机会,还能让您更好地分析什么工具适合当前的工作。Adobe Flash 在短期内不会消失,但同样可以轻松地说 HTML5 也是如此。

由于许多开发人员从以前与 HTML 无关的 Web 和应用程序开发技术转向 Flash 开发,让我们从 HTML5 堆栈的基本知识开始。

什么是 HTML5?

HTML5 是由万维网联盟(www.w3.org/)开发的 HTML 标准的第五个也是最新的版本。作为一个新版本,它为现有的 HTML 规范带来了一些新功能,并删除了一些旧的过时功能。许多这些新功能和现有功能开始与 Adobe Flash 中的功能集紧密相似,从而开启了许多不依赖于付费应用程序或浏览器插件(如 Adobe Flash Player)的新的 Web 开发方面。

HTML5 规范的开发仍在进行中,并计划在 2014 年某个时候完成并发布,但今天大多数现代 Web 浏览器已经支持规范的许多功能。

一般来说,对 HTML5 的引用通常涉及到一套功能和技术,不仅涉及到 HTML,还涉及到层叠样式表CSS)以及JavaScript。如果没有使用 CSS 和 JavaScript,即使是 HTML5 文档,其功能和外观仍然会非常简单。因此,学习 HTML5 实际上是在同时学习三种技术。尽管听起来很繁琐,但 Adobe Flash 的设置方式非常相似。Flash IDE 允许轻松创建、编辑和引用要在应用程序中使用的资产。要将这些资产集成到动态交互式应用程序中,需要使用ActionScript。HTML5 与此非常相似,其中 HTML 和 CSS 将是您的 Flash IDE,而 JavaScript 将是 ActionScript 的替代品。考虑到所有这些,让我们继续审查构建 HTML 的标准。

HTML 标准

万维网联盟www.w3.org/)或W3C负责创建今天 HTML 开发的标准。这些 Web 开发标准是为了统一开发人员创建网页的语法和功能,以及在 Web 浏览器中集成的功能集,以便在 HTML 中启用这些功能。通过以符合 HTML 规范标准的标记编写 Web 应用程序,开发人员可以更好地确保他们的内容将被正确显示,无论用户选择如何查看它。

HTML 语法

尽管看起来微不足道,HTML 语法是所有网页的核心。无论是在 HTML 文件中硬编码,从另一个编程语言源编译,还是在应用程序运行时注入到文档中,HTML 语法都是 HTML 页面中使用的资产的蓝图。开发人员对 HTML 语法及其限制的理解越深入,构建应用程序就会越容易。

HTML 语法是使用包裹在尖括号中的标签元素编写的。HTML 标签有两种不同的类型:成对的或空元素。成对的 HTML 标签是最常见的,也是创建 HTML 文档时通常使用的第一种标签样式。html标签用于声明 HTML 文档中的内容,并通常位于 HTML 文件的第一行和最后一行:

<html>
  Add your HTML content here.
</html>

如前面的例子所示,成对标签打开和关闭一个容器,以便更多的 HTML 元素放置在其中。标签的格式始终相同,成对标签之间唯一的区别是使用斜杠来声明标签正在关闭一个元素。因此,<html>将不会与不包含相同内部值的任何标签配对。HTML 标签不区分大小写,在早期,开发人员在编写标签时通常总是使用大写。这种传统现在已经消失,你几乎总是会看到标签以小写形式书写。

空的 HTML 标签是不使用闭合标签写的。例如,在 HTML 文档中放置图像引用时,没有更多的 HTML 元素内容可以放置在该图像中。因此,在 HTML 中,图像引用的格式如<imgsrc="img/my_image.jpg">。通过将src参数附加到img标签中,并将其值设置为图像位置来引用图像。

提示

如果您已经成功使用Adobe Flex构建任何 Flash 内容并利用MXML用户界面标记语言,您可能已经掌握了使用诸如<imgsrc="img/my_image.jpg" />这样的语法来关闭空标签元素。在 HTML5 中,这个尾部的斜杠是不需要的,但如果您添加它,仍然会正确呈现您的内容。为了最佳使用情况,请尽量养成在 HTML5 项目中不使用它的习惯。

在调试 HTML 时,HTML 是一个棘手的问题;语法错误的文档不会像传统的 Flash 应用程序一样在加载时显示错误。编写干净简洁的 HTML 是保持无错误、标准、符合规范的网页的关键。有许多应用程序和工具可用于帮助开发干净的 HTML 代码,其中一些将在本书的后面部分介绍。W3C 创建了一个强大的 HTML 语法验证服务,可以检查公开可用的网站的 HTML 错误(validator.w3.org/)。

HTML 元素

每个 HTML 规范版本都有一组特定的标签可供开发人员在创建 HTML 文档时使用。W3C 定义的 HTML5 规范中当前的元素列表可以在其语言参考文档中找到(www.w3.org/TR/html-markup/elements.html)。

在 HTML5 规范中,对于媒体集成到网页中,开发人员可以使用一些非常有趣的新元素。例如,通过添加audiovideo标签,现在可以避免嵌入音频或视频时对 Flash 的要求。这些令人兴奋的新媒体标签将在第三章中更深入地介绍,可伸缩性、限制和效果

引入样式

层叠样式表或 CSS 是用于为 HTML 元素设置样式的主要方法。与 HTML 一样,在 CSS 中有一组样式列表,您可以将其应用于 HTML 文档中的元素。要了解可用于您的 CSS 属性的想法,请转到www.w3schools.com/cssref/查看完整列表。CSS 可以以多种不同的方式应用于 HTML 元素。传统上,CSS 语法存储在外部的.css文件中,并从 HTML 文档的head元素中引用。但是,CSS 也可以直接附加到 HTML 文档中的元素中,方法是在body标记内的几乎任何元素中添加style参数:

<imgsrc="img/my_image.jpg" style="border:5px solid #000000;">

在上一个示例中,使用style参数在图像元素上应用了一个 5 像素宽的黑色边框,该图像在src参数中引用。

提示

下载示例代码

您可以从www.packtpub.com的帐户中下载您购买的所有 Packt 图书的示例代码文件。如果您在其他地方购买了本书,可以访问www.packtpub.com/support并注册以直接通过电子邮件接收文件。

如果您的页面中有五个图像,甚至 100 个图像需要应用相同的样式到每个元素,那该怎么办?将完全相同的style参数应用于每个图像标记不仅耗时,而且会导致代码过大,可能极难维护或更新。CSS 可以通过使用classid HTML 参数来针对单个元素或一组元素:

<div id="photo-gallery">
  <imgsrc="img/photo1.jpg" class="photo">
  <imgsrc="img/photo2.jpg" class="photo">
  <imgsrc="img/photo3.jpg" class="photo">
  <imgsrc="img/photo4.jpg" class="photo">
  <imgsrc="img/photo5.jpg" class="photo">
</div>

在前面的示例中,我们尝试在 HTML 文档中显示一组不同的图像。每个图像,使用img元素标记引用,并附加了一个值为photoclass参数。class HTML 参数可以在几乎任何可用的元素上使用和重复使用,并允许您引用一组元素,而不是直接修改每个元素。所有图像也都包含在一个div元素中。div元素用作显示内容的容器。在这种情况下,div元素的id参数设置为photo-galleryid HTML 参数与class非常相似,但可以在同一 HTML 文档中重复使用相同的id值。

提示

通过使用设置为辅助 HTML5 语法的代码编辑应用程序,可以简化编辑 HTML、CSS 和 JavaScript。推荐使用Aptanaaptana.com/)、Dreamweaveradobe.com/products/dreamweaver.html)和Sublime Textsublimetext.com/)等应用程序。然而,如果你喜欢简单,可以随意使用其他工具。

考虑到所有这些,编写 CSS 来为这个相册添加样式可以按以下方式完成:

<!DOCTYPE html>
<html>
  <head>
    <title>My Photo Gallery</title>

    <!-- Our Photo Gallery CSS Styles -->
    <style type="text/css">
      body {
        background-color:#000000;
      }

      #photo-gallery {
        width:100%;
      }

      #photo-gallery .photo {
        width:200px;
        border:4px solid #ffffff;
      }
    </style>
  </head>
  <body>
    <div id="photo-gallery">
      <imgsrc="img/photo1.jpg" class="photo">
      <imgsrc="img/photo2.jpg" class="photo">
      <imgsrc="img/photo3.jpg" class="photo">
      <imgsrc="img/photo4.jpg" class="photo">
      <imgsrc="img/photo5.jpg" class="photo">
    </div>
  </body>
</html>

现在,我们可以在head元素内使用style标签来放置原始的 CSS 代码,而不是将style参数应用到文档中的每个元素。在前面的例子中,HTML 元素以三种不同的方式被选中。首先,通过使用其十六进制值,将文档的背景颜色设置为黑色。我们通过简单地使用标签引用来选择body标签元素。这种选择原始元素的方法可以用于文档中的各种元素,但会影响具有该引用的所有元素。第二种选择方法是查找具有特定 ID 的元素。为了指定使用 ID,需要在 ID 值前面加上#。因此,#photo-gallery将选择具有id参数设置为photo-gallerydiv元素。我们将相册容器的width参数设置为100%,这是根据查看 HTML 文档时的浏览器宽度计算的。最后,为了为相册中的每个图像添加样式,我们为应用到 HTML 文档的每个图像标签的类添加样式。由于class HTML 参数可以应用于 HTML 文档中无限数量的元素,我们可以通过将 CSS 元素选择链接在一起来具体地定位另一个元素中的类。在 CSS 中,通过在类名前面添加.来选择类。因此,#photo-gallery .photo将仅选择具有photo类名的元素,这些元素位于具有idphoto-gallery的元素内:

引入风格

交给 JavaScript

仅使用 CSS 来为 HTML 元素添加样式只能让你走得更远。从 Flash 开发者的角度来看,HTML 和 CSS 在效果上等同于 Flash IDE。缺少的是驱动应用程序内交互和功能的代码。在使用 Flash 时,创建强大应用程序时,ActionScript 是首选的武器。在开发 HTML5 内容时,JavaScript 将会发挥作用,使你的 HTML 元素焕发生机。

什么是 JavaScript?

JavaScript 自上世纪 90 年代中期就存在,并已成长为最流行的脚本语言之一。JavaScript 通过添加内容修改、动画、3D 图形、表单提交和数据检索等功能,为 HTML 文档增添了生机,而无需重新加载活动的 HTML 文档。这些功能使得 Web 内容更像传统软件应用程序,而不是静态网页。

与 ActionScript 3 不同,JavaScript 是一种真正的脚本语言,因为它不需要预处理或编译就可以运行。就像 HTML 和 CSS 一样,JavaScript 文档的源代码在用户请求时发送到用户端并在客户端执行。因此,与 ActionScript 等技术不同,JavaScript 源代码是公开可见的。

JavaScript 的作用

回顾我们的我的照片库示例,一个重要的缺失功能是查看所选照片的大尺寸。JavaScript 是一个完美的平台,可以通过它的主要用途将交互性带入 HTML 文档。使用现有的代码示例,我们可以通过在页面主体底部添加一个新的div元素来扩展其功能,以包含更大的图像视图。这个元素可以是空的,因为我们不希望在页面加载时默认显示照片。最后,我们在div标签上设置一个标识符id="photo-display",这将允许我们从 CSS 和 JavaScript 中定位该元素中的内容:

<div id="photo-display"></div>

在集成 JavaScript 功能之前,我们需要使用#photo-display附加一些 CSS 样式到div,以允许所选照片以更高分辨率填充浏览器窗口,这是大多数照片库显示的典型特征。在 CSS 样式中,我们已经为此示例设置了一些样式属性,我们将在#photo-display元素中附加一些更多的样式属性:

#photo-display {
  display:none;
  position:absolute;
  top:0;
  width:100%;
  height:100%;
  background-color:#000000;
  text-align:center;
}

#photo-display img {
  margin:auto;
  margin-top:50px;
  max-height:800px;
  border:4px solid #ffffff;
}

这个 CSS 将只针对一个特定的div,因为我们使用了#photo-display语法来定位它。为了开始样式,我们从最重要的参数display:none开始,这会在页面加载时隐藏元素。这在我们的情况下是完美的,因为我们不希望在页面加载时看到全屏显示。通过为#photo-display元素的样式定义添加position:absolutetop:0,我们将在 HTML 主体中声明的先前元素的顶部显示该元素。在#photo-display上设置的其余样式都很容易理解。CSS 的下一行专门针对具有photo-display类的div中的img元素。我们可以在 CSS 中通过链接标识符来做到这一点。在这种情况下,我们为自定义命名的元素 ID 内的图像标签元素指定这些样式。

在 HTML 和 CSS 中显示所选照片的大尺寸版本后,下一步是添加 JavaScript 代码,以便在用户交互时在#photo-display容器中显示所选照片。为了将这个示例整合到一个文件中,我们将在 HTML 的script元素中添加 JavaScript:

<!-- Our Photo Gallery JavaScript Source -->
<script>
  var largeImage = new Image();

  // Display a specific photo in the large
  // photo display element.
  var displayPhoto = function(source) {
    // If there is already an image inside the display
    // remove it.
    if(largeImage.src != '') {
      document.getElementById("photo-display").removeChild(largeImage);
    }

    // Update the source location of the image
    largeImage.src = source;
    document.getElementById("photo-display").appendChild(largeImage);

    // Display the large photo element.
    document.getElementById("photo-display").style.display = 'block';
  }

  // Closes the large photo display element.
  var closePhotoDisplay = function() {
    document.getElementById("photo-display").style.display = 'none';
  }
</script>

作为 Flash 开发人员,以前的函数语法应该看起来很熟悉。在函数范围内的一个重大变化是变量语法。与 AS3 不同,HTML 以及源变量都不是严格类型的。这适用于 JavaScript 语法中的所有变量,这可能是 Flash 开发人员对 JavaScript 最大的问题之一。

除了对源变量进行一些字符串操作以生成img HTML 元素之外,该方法还引用了文档对象。加载到浏览器中的每个 HTML 文档都成为 JavaScript 中可访问的文档对象。JavaScript 中的文档对象具有许多内置属性和方法,可用于访问视图 HTML 文档中的信息和元素。在我们的示例中,我们利用了易于定义的文档对象方法getElementById()。正如方法名称所暗示的那样,当提供 HTML 元素的 ID 时,将返回对 HTML 文档中元素的引用,以便在脚本中使用。由于 JavaScript 支持属性的链接,我们可以应用innerHTML属性来操作 HTML 元素的内部内容,以及style属性来更改元素的 CSS 属性。

为了使用户在查看完照片后能够关闭图像,我们将在示例中添加第二个 JavaScript 函数,以恢复显示照片时所做的所有更改。由于当用户点击新图像时,photo-display图像将被更新,我们的closePhotoDisplay方法所需做的就是隐藏可见元素,以再次显示完整的照片库:

functionclosePhotoDisplay() {
  document.getElementById("photo-display").style.display = 'none';
}

#photo-display元素的style.display设置回none会隐藏整个元素,并将用户界面恢复到初始状态。

将事件添加到每张照片中可以通过向目标元素附加onclick参数来轻松实现。添加如下:

<imgsrc="img/photo1.jpg" class="photo"onclick="displayPhoto('photo1.jpg')">

现在,当单击图像时,onclick事件将被触发并运行参数中声明的 JavaScript 代码。在这种情况下,我们利用这个机会来调用我们之前编写的 JavaScript 块中的displayPhoto方法。在调用中,我们提供所需的源变量,这将是图像文件名作为String数据类型。这将允许在#photo-display元素中使用正确的图像引用。将所有内容放在一起,我们更新的带有id="#photo-gallery"div标签现在看起来像下面这样:

<div id="photo-gallery">
  <imgsrc="img/photo1.jpg" class="photo"onclick="displayPhoto('photo1.jpg')">
  <imgsrc="img/photo2.jpg" class="photo"onclick="displayPhoto('photo2.jpg')">
  <imgsrc="img/photo3.jpg" class="photo"onclick="displayPhoto('photo3.jpg')">
  <imgsrc="img/photo4.jpg" class="photo"onclick="displayPhoto('photo4.jpg')">
  <imgsrc="img/photo5.jpg" class="photo"onclick="displayPhoto('photo5.jpg')">
</div>

最后,为了使用户能够关闭#photo-display元素中的打开图像,我们将应用一个onclick事件来调用我们的closePhotoDisplay方法。我们将事件应用于#photo-display元素中的图像,而是将其定位到显示本身,允许用户在浏览器中的任何位置单击以关闭显示:

<div id="photo-display" onclick="closePhotoDisplay()"></div>

将所有这些代码片段放在一起,画廊源现在看起来像下面这样:

<!DOCTYPE html>
<html>
  <head>
    <title>My Photo Gallery</title>

    <!-- Our Photo Gallery CSS Styles -->
    <style type="text/css">
      body {
        background-color:#000000;
      }

      #photo-gallery {
        width:100%;
      }

      #photo-gallery .photo {
        width:200px;
        border:4px solid #ffffff;
      }

      #photo-display {
        display:none;
        position:absolute;
        top:0;
        width:100%;
        height:100%;
        background-color:#000000;
        text-align:center;
      }

      #photo-display img {
        margin:auto;
        margin-top:50px;
        max-height:800px;
        border:4px solid #ffffff;
      }
    </style>

    <!-- Our Photo Gallery JavaScript Source -->
    <script>
      var largeImage = new Image();

      // Displays a specific photo in the large
      // photo display element.
      var displayPhoto = function(source) {
        // If there is already a image inside the display
        // remove it.
        if(largeImage.src != '') {
          document.getElementById("photo-display").removeChild(largeImage);
        }

        // Update the source location of the image
        largeImage.src = source;
        document.getElementById("photo-display").appendChild(largeImage);

        // Display the large photo element.
        document.getElementById("photo-display").style.display = 'block';
      }

      // Closes the large photo display element.
      var closePhotoDisplay = function() {
        document.getElementById("photo-display").style.display = 'none';
      }
    </script>
  </head>
  <body>
    <div id="photo-gallery">
      <!-- Place all of the images inline with a 'photo' class for CSS manipulation. -->
      <imgsrc="img/photo1.jpg" class="photo"onclick="displayPhoto('photo1.jpg')">
      <imgsrc="img/photo2.jpg" class="photo"onclick="displayPhoto('photo2.jpg')">
      <imgsrc="img/photo3.jpg" class="photo"onclick="displayPhoto('photo3.jpg')">
      <imgsrc="img/photo4.jpg" class="photo"onclick="displayPhoto('photo4.jpg')">
      <imgsrc="img/photo5.jpg" class="photo"onclick="displayPhoto('photo5.jpg')">
    </div>

    <!-- An empty DIV element to contain the user selected photo in large scale. -->
    <div id="photo-display" onclick="closePhotoDisplay()"></div>
  </body>
</html>

将文本保存到.html文件中,并在 Web 浏览器中启动它,现在将显示出我们所有的辛勤工作。就像以前一样,画廊应该从默认显示图像列表开始。一旦单击图像,选择将传递到#display-window元素,并以浏览器宽度的 100%显示:

JavaScript in action

最后,在文档中的任何位置单击将关闭大图像,并将您返回到初始画廊显示。

尽管此示例不包含 HTML5 的新功能,但这是展示 HTML 的一些关键技术和引用 HTML 中资产的一些方法的简单方式。

为什么要学习 HTML5?

作为 Flash 开发人员,进入 HTML5 开发领域是一个非常合乎逻辑的步骤,原因有很多。使用 HTML5 构建应用程序可以让您轻松地在桌面或移动设备上接触到用户,而无需插件即可获得丰富的集成和交互式内容。HTML5 开发最有益的一个方面是开发环境的可访问性。由于 HTML5 不需要特殊的编译器或软件来编写代码,开发人员可以自由选择他们喜欢的设置来编写和测试他们的项目。应用程序可以在任何兼容的 Web 浏览器中轻松运行和测试,并且可以在本地进行测试,而无需 Web 服务器。这使得 HTML5 成为 Web 上最易访问和易用的技术之一。

一次编写,到处部署

与 Flash 应用程序不同,任何具有现代 Web 浏览器的设备都可以与 HTML5 Web 内容进行交互。因此,借助 CSS 对可视内容进行动态调整,您的 HTML5 应用程序可以在不需要在桌面或移动平台上安装应用程序或依赖的情况下使用。开发人员还可以使用诸如Phone Gapphonegap.com/)或Appceleratorwww.appcelerator.com/)等技术,将其现有的 HTML5 Web 内容轻松转换为打包的移动应用程序,以在所有现代移动操作系统中上市。打包的应用程序可以通过诸如苹果的App Storestore.apple.com)和Google Playplay.google.com)等常见移动应用程序服务进行集成和销售。此外,Microsoft Windows 8桌面应用程序开发现在支持一整套不同的编程语言,其中之一就是 HTML5(msdn.microsoft.com/en-us/library/windows/apps/br211386.aspx)。通过将 HTML5 内容打包成可安装的应用程序,开发人员现在可以通过各种应用程序分发渠道轻松地将其作品进行销售。

令人兴奋的新功能

正如刚才提到的,HTML5 拥有一系列新的令人兴奋的功能,其中许多将在本书的后续章节中介绍。然而,为了让您更好地理解为什么 HTML5 对 Flash 开发人员和 Web 开发社区如此令人兴奋和重要,这里是一份更详细的一些功能列表。

canvas - 2D 绘图 API

Flash 开发人员可以在新的canvas元素和 2D 绘图 API 中充分发挥他们的编程能力。就像 ActionScript 3 中的绘图 API 一样,canvas HTML5 元素允许开发人员在运行时创建动态图形,所有这些都可以通过 JavaScript 完成。转向 HTML5 的 Flash 游戏开发人员通常会在使用 HTML5 时找到他们的家园,因为canvas元素是传统 Flash 开发的最接近的表示。

媒体播放

Web 开发人员不再需要使用 Flash 或 Quicktime 等平台来开发他们的媒体播放元素。音频和视频现在可以通过audiovideo标签轻松集成到 HTML 文档中。这不仅使播放元素更容易、更便宜地集成到网页中,而且移动设备在其集成浏览器中读取和显示这些元素时也没有问题。

离线存储

传统上,当 Web 开发人员需要在用户的计算机上本地保存数据时,他们使用cookies。HTML5 添加了一种新的离线存储方法,称为Web Storagedev.w3.org/html5/webstorage),可以大大增加应用程序的能力,当您需要保存数据以供重复使用时。像客户端用户特定的应用程序配置这样的大量数据现在可以以更安全、更快的方式存储。

现在还可以设置 HTML5 内容在用户离线时可用,方法是利用 HTML5 的缓存清单。缓存清单只是一个简单的文本文件,放在您的 Web 服务器上。如果 Web 浏览器支持使用 HTML5 缓存清单(所有现代浏览器目前都支持),则在清单中放置的文件和资产的引用都会被缓存在客户端。根据您的清单是否设置为缓存运行应用程序所需的所有内容,用户可以在离线状态下继续使用应用程序。结合使用 HTML Web 存储将数据存档以在重新连接到互联网时重新发送到 Web 服务器,您可以开发应用程序,使用户可以在连接中断的情况下无缝地使用它们。HTML5 功能的一个完美示例是 Google 的Gmailmail.google.com)。通过在用户访问时在设备上存档消息数据,例如电子邮件,在用户在地铁地下时打开消息时,仍然可以查看重要信息。

文档编辑

许多 HTML5 元素现在允许使用参数contenteditable="true",这允许用户编辑元素内的所有内容。这个功能直接将所见即所得的环境带到了 HTML 内容中。在 HTML5 项目中操作内容时,内联文档编辑对开发人员来说非常方便。

拖放

HTML5 元素现在具有可拖动的能力。诸如此类简单但重要的用户体验增强功能,可以带来更多类似应用程序的交互性,传统上需要使用 JavaScript 来构建。就像内联文档编辑一样,在开发过程中将元素设置为可拖动可以帮助找到正确的位置属性。

地理定位

地理定位 API 使用户可以允许将其当前位置发送到 HTML5 文档中供 JavaScript 使用。除了在地图应用程序中使用用户位置的明显用途外,地理定位值还可以为允许更交互式用户体验的 Web 文档添加许多新功能。

文件 API

HTML5 中的文件 API 允许在处理用户本地计算机上的文件时获得更交互式的体验。现在可以将本地文件拖入浏览器,并在 HTML 文档中预览,而无需将数据上传到 Web 服务器。

提示

要更深入地了解 HTML5 中的完整功能集,请访问在线 W3C API 文档(www.w3.org/TR/html5/)。

移动设备可访问性

随着越来越多的设备集成了互联网功能,需要流畅、多平台的应用程序,可以实现低开销和集成设备访问的需求达到了历史最高点。几乎所有现代移动浏览器已经支持 HTML5 的许多功能,Web 开发人员可以利用这些功能来构建与特定移动平台上许多原生应用程序相媲美的移动应用程序。地理定位、本地文件访问和离线存储等功能使应用程序能够轻松地整合到运行它们的设备硬件中。

提示

本书中的任何示例都可以在运行 HTML5 兼容的现代移动设备上运行。如果您有 iPhone、Android 或 Windows 手机,可以在设备上测试示例,查看移动平台如何运行 HTML5 内容。

HTML5 出现的最大推动力之一是移动设备。移动应用程序开发需要与典型应用程序开发略有不同的方法,因为运行应用程序的平台不仅资源较少,而且还需要考虑诸如电池寿命、屏幕分辨率和触摸界面等因素。在开发 Flash 应用程序时处理所有这些要求可能会有些棘手。Flash 应用程序传统上在资源使用上有些沉重,尽管可以进行优化来弥补在移动平台上运行应用程序时的一些问题。

移动设备上的 Flash Player

自 iPhone 问世以来,Flash 开发人员不得不面对这样一个事实,即他们基于 Web 的 Flash 内容永远无法在集成的 iOS Web 浏览器中查看。苹果在 2010 年 4 月史蒂夫·乔布斯公开信中明确表明了对 Adobe Flash Player 使用的立场,指出 Flash Player 无法在他们的设备上提供所需的性能。

2012 年 6 月,Adobe 发布了一份关于 Adobe Flash Player 在移动设备上的未来的公开声明。截至 2012 年 8 月 15 日,Android 版 Flash Player 只能在经过认证可以运行 Flash Player 的设备上使用,因为 Adobe 已经暂停了移动版 Flash Player 的开发。运行 Android 4.1+版本的用户将无法在其浏览器中运行 Flash 内容,所有 Web 内容将依赖于 HTML5 中的技术。

随着 Flash Player 从移动市场上被移除,目前 Flash 开发人员创建移动应用程序的唯一资源是使用Adobe AIR开发并将他们的工作打包为独立应用程序,而不是在 Web 上运行。

建立在现有的技能基础上

Flash 开发人员转向 HTML5 开发时,学习使用纯 HTML、CSS 和 JavaScript 创建令人惊叹的应用程序的技巧会更容易一些。不仅所有关于处理和优化媒体元素的经验都会转移过来,而且他们的 ActionScript 3 技能也将使他们能够充分理解和使用 JavaScript。

ECMAScript

开发人员投入学习诸如 ActionScript 3 之类的编程语言的时间远非短暂。幸运的是,JavaScript 和 ActionScript 3 都是基于ECMAScript脚本语言标准构建的(www.ecmascript.org)。简而言之,这意味着许多方法、变量和属性的语法设置在外观、感觉和使用上都非常相似。当我们深入挖掘并看到 HTML5 的更多实例时,如果你有 ActionScript 3 的经验,你将立即注意到在使用 JavaScript 时有许多相似之处。

避免最初的障碍

所有 Flash 开发人员在转向 HTML5 开发时通常都会遇到相同的问题。大多数问题都源于平台语法之间的差异,以及处理 HTML5 堆栈内每个元素之间的交互。

舞台与 DOM

转向 HTML5 开发时最明显的变化之一是缺少了重要的 Flash 舞台。在 HTML5 中处理元素布局、资产动画和交互性都纯粹通过代码来实现。尽管有许多带有拖放式界面的 Web 开发 IDE,为了更好地理解如何构建更干净的网页,本书将涵盖所有手写代码示例。

在 DOM 中定位资产

许多 Flash 开发人员在转向 Web 开发时最初遇到的一个最大问题是在 DOM 中定位内容和资产的概念。除非指定,HTML 元素不会简单地使用 X 和 Y 位置值放置在 DOM 中。由于 HTML 文档中的元素默认以内联方式显示,全局 X 和 Y 位置值是无关紧要的。在 DOM 中使用 CSS 定位元素时,而是使用诸如 margin、padding、top、left、right 和 bottom 等属性。如前所述,如果元素被特别设计为绝对位置或在canvas元素中使用,则可以使用 X 和 Y 值。除了简单地控制项目中元素放置的问题之外,还有确保可能查看内容的所有 Web 浏览器都按照您的规格显示内容的问题。

处理媒体元素

媒体优化是提供 Web 内容的关键。在使用 Flash 时,许多使用的资产是基于矢量的,因此在编译后的 SWF 文件大小上轻量级。Flash SWF 中使用的位图数据在编译期间被压缩,因此自动帮助您最小化文件大小。由于大多数 HTML 文档所做的是引用公开可访问的原始文件,因此每个使用的资产都应该针对最小文件大小进行优化,同时尽可能保持预期的质量接近原始质量。随着本书各章节中涵盖 HTML5 开发的不同方面,将涵盖用于网页中使用的不同类型媒体的许多优化方法。

保护您的代码

在 Adobe Flash 中发布内容会输出一个编译后的二进制 SWF 文件,该文件已准备好在兼容的 Flash Player 中播放。应用程序中使用的代码和资产免受窥视,因为应用程序已编译为单个二进制文件。但是在处理 Web 上的代码和资产时,整个游戏都会发生变化。几乎您在 HTML5 项目中创建和交付的所有内容,与任何网站一样,都可以公开查看。

代码混淆是一些开发人员在交付生产级别客户端代码时使用的一种做法。许多网站和应用程序可用于通过以难以阅读的压缩格式重写代码来混淆 JavaScript 代码。尽管这并不是保护代码的绝对方法,但在用户查看文档源代码时,它增加了一定程度的威慑力。

理解客户端代码的使用和限制是编写安全 JavaScript 应用程序的关键。敏感信息不应该硬编码到可以在客户端查看的文档中。第二章,“准备战斗”,比 ActionScript 3 更深入地涵盖了客户端脚本的使用。

浏览器和平台兼容性

从 Flash 转向 HTML5 开发时的主要变化之一是需要使用相同的代码库针对多个平台进行开发。在使用 Adobe Flash 开发应用程序时,您最初为应用程序设置 Flash Player 的目标版本。通过将应用程序编译为打包的 SWF,Flash 运行时将无法在任何兼容的 Flash Player 中渲染您的应用程序。由于每个浏览器和平台都倾向于以稍微不同的方式显示 Web 内容,因此在开发 HTML5 内容和应用程序时,必须注意可能用于查看内容的平台和浏览器,以更好地优化查看体验。

可以将浏览器功能检查写入 JavaScript 条件中,以便使那些不支持特定 HTML5 功能的浏览器的用户仍然可以查看你的 HTML5 内容。例如,如果用户访问一个包含 HTML5 视频播放元素的页面,而他的浏览器不支持它,JavaScript 可以选择替代地嵌入 Flash 视频播放应用程序。

提示

找到一个不支持 HTML5 的现代 Web 浏览器变得越来越困难。在阅读本书时,选择一个用于测试代码的浏览器时,Firefox(www.getfirefox.net/)、Chrome(www.google.com/chrome)、Safari(www.apple.com/safari/)和 Opera(www.opera.com/)都是很好的选择,并且可以在线免费使用。

在本书的章节中,将使用许多这些流行的 Web 浏览器来展示内容在外观和使用上有时可能会有所不同。由于浏览器更新和变化的速度很快,尽可能在每个平台的每个浏览器中测试你的网站是 Web 开发的一个非常重要的方面。许多这些 Web 浏览器现在都有内置的开发和调试工具,可以更轻松地优化你的 HTML5 项目。还有其他应用程序和服务可用于简化浏览器测试的痛苦,其中一些将在本书中使用和介绍。

总结

在这一章中,我们已经涵盖了 HTML5 技术栈的关键方面,以及如何以简单的方式使用它们。通过创建一个简单的相册网页,我们不仅使用了 HTML、CSS 和 JavaScript,还使用了它们之间引用元素的方法。一些 HTML5 中的新功能也被解释并与传统上在 Flash 资产中创建的功能进行了比较。回顾了 Flash 开发人员转向 Web 开发时的典型问题,让你在发现问题之前就意识到这些问题。希望这一章能进一步激发你对学习 HTML5 更多可能性的兴趣。

W3C 维护的 HTML5 标准的制定是一个有趣但非常深入的话题,这超出了本书的范围。如果你对了解 HTML5 标准的制定和维护更感兴趣,我强烈建议查看并关注 W3C 在其网站上发布的规范和语法开发信息(www.w3.org)。

在完成平台概述后,我们将继续深入研究 HTML5 技术栈中最重要的方面 JavaScript,以及它与你已经了解的使用 ActionScript 开发的知识的关系。

第二章:为战斗做准备

现在您了解了 HTML5 的构成技术,我们可以开始动手了。但在我们开始编写 HTML、CSS 和 JavaScript 之前,我们需要覆盖项目开发的第一步基础知识,即资产准备。没有设计、资产和内容,您的网页将不会很吸引人,或者说,功能不完善。在准备过程中,我们还将深入研究 JavaScript 的语法规范,以及它与 ActionScript 3 的关系,为我们在第三章 可扩展性、限制和效果中进行全面开发做准备。

在本章中,我们将涵盖:

  • 准备常见资产,如图像、音频和视频,以在 HTML5 文档中使用

  • 在浏览器中代码输出和调试

  • JavaScript 的基础知识和与 ActionScript 3 的语法变化

  • JavaScript 在实际操作中的示例以及代码执行的正确方法

准备资产

在 Flash 中开发应用程序时,可以通过几种不同的方式将资产(如图像、音频和视频)集成到典型项目中。您可以选择通过直接在 Flash 项目库中导入它们的典型方式来集成资产。将资产添加到 Flash 项目中会导致资产包含到编译的 SWF 文件中。由于所有资产都编译在一个文件中,因此无需从互联网等外部资源获取资产。编译在编译的 Flash 项目中的资产本质上受到保护,不会被获取或被公开引用。

不幸的是,项目库内部引用的资产一旦项目被导出就无法更新或更改。开发应用程序,如视频播放 UI 或照片库,需要动态集成资产,从而产生一个可以无限使用的单个应用程序实例。可以通过请求外部文件来集成外部资产,这些文件在互联网上是公开可访问的。外部集成可以减小应用程序的大小,并且可以修改外部资产而无需进行应用程序更新。不幸的是,如果文件不可用或用户未连接到互联网,则无法集成资产,可能会导致应用程序失败。

外部资产集成是将内容包含到基于 Web 的文档中的标准方式。将被 HTML 文档引用的文件通常放在与嵌入它们的 HTML 文档相同的 Web 服务器上。

资产也可以从互联网上的其他 Web 服务器引用,但内容取决于具有访问权限的开发人员或管理员。开发人员的懒惰或试图降低带宽成本可能导致图像或其他资产被嵌入到外部来源,将带宽费用转移到您自己以外的 Web 服务器。这个过程被称为热链接,在 Web 开发社区中被视为不良行为,因为您迫使其他网站所有者承担资产分发的成本。

由于 Web 内容没有像 Flash 中的编译器那样自动优化,Web 开发人员必须自行准备其内容和资产以供 Web 使用。由于 Web 内容根据用户的可变互联网连接速度按需传送,资产的文件大小应尽可能小,以便最终用户能够以尽可能少的延迟进行播放和查看。让我们回顾一下常见资产类型以及为将它们嵌入到我们的 HTML 文档中准备的正确方法。

提示

所有用于优化和转换的资产都可以在书籍的可下载示例文件中的Chapter 02_examples目录中找到。

图像

将图像添加到项目中通常是每个基于 Web 的项目中使用的第一种资产集成技术。Web 上的所有图像通常以 JPEG、PNG 或 GIF 三种不同格式之一出现。每种格式都有特定的用途,应根据设计和功能的要求来使用。尽管这些图像格式在日常使用中很常见,但重要的是要了解每种格式的优缺点,以便将图像优化地集成到 HTML 文档中。

注意

可以在 Packt Publishing 网站(www.packtpub.com)上下载示例文件,跟着本书学习。如果你没有 Photoshop CS6 的副本,可以从www.adobe.com/cfusion/tdrc/index.cfm?product=photoshop&loc=en_us&promoid=IICUB免费下载和安装演示版。

考虑一下这张高质量的未压缩 CR2 格式图像,直接从佳能数码单反相机拍摄而来。这张原始图像的大小为 27 兆字节,因此不适合在任何现代 Web 浏览器中查看或嵌入。

Images

即使 Web 浏览器可以处理将图像放入 HTML 文档中,下载图像所需的时间也将是巨大的。尽管如今普通的高速互联网连接很常见,但不多的用户愿意等待几分钟以上来查看加载网页时的单个图像。因此,在将图像用于 Web 之前,必须对其进行优化。当在 Photoshop 中打开这个 CR2 图像时,Photoshop Camera RAW 窗口将显示照片数据和文件大小,还有图像尺寸。

Images

看起来用于拍摄这张照片的数码相机以 17.9 百万像素的分辨率保存了这张图像,使得这张图像宽 5184 像素,高 3456 像素。这张图像在 Web 上永远不会以这个分辨率使用,因为它不适合在计算机显示器上,需要缩小才能查看。为了在 Web 上使用图像,需要将其缩小,使其在 Web 上更小更容易查看,但用于显示它的文件仍然是巨大的主版本,加载速度慢。让我们继续通过在 Camera RAW 导入窗口中选择“完成”来在 Photoshop 中打开这个文件。

将网页设计所需的分辨率导出为网页版本的图像是一个很好的做法。在 Photoshop 中,可以通过在“图像”选项卡下选择“图像大小”来轻松地将图像分辨率更改为适合 Web 的合适尺寸。

Images

在“图像大小”窗口中,我们可以输入一些更现实的值到“宽度”和“高度”参数中,以查看我们可以实现什么样的优化。通过使用 1920 x 1280 这样的值,这仍然是一个非常高分辨率的图像,可以查看预期输出图像源文件大小将显示在“宽度”和“高度”参数上方的文本中。

Images

在“图像大小”窗口中更新“宽度”和“高度”参数后,生成的文件大小将立即显示在它们的上方。请记住,显示的文件大小变化不会是最终输出大小,因为我们仍然可以使用 JPEG 压缩等技术来优化这个图像源。

提示

在为特定网页设计优化图像大小时,通常不需要导出比设计中设置的尺寸更大的图像。如果设计中需要缩略图,最好导出两张图像,一张大一张小,而不是在两种情况下使用同一张图像。

位于文件选项卡下的 Photoshop 的保存为 Web功能可以说是网页开发人员的好帮手。这个工具允许您轻松地从 Photoshop 中导出图像,特别是为了优化 Web 而设计。无论是为设计增添活力还是将资产转换为单个实例,每当您要从 Photoshop 导出东西供 Web 使用时,这个工具都是实现的最佳方式。

图片

单击保存为 Web选项将打开一个专用窗口,帮助您选择最佳格式和压缩方法来导出数据。因此,让我们导出这张照片的几个版本,看看在尽量保留图像质量的同时可能的最小文件大小是多少。

将格式类型设置为JPG以进行更好的压缩,然后在窗口顶部选择4-Up选项卡,以便在图像数据的不同压缩级别之间进行并排比较。尝试调整这些值,看看在看到图像发生显著变化之前,您可以将质量水平降低到多低。在这样做的同时,密切关注预期文件大小,以了解压缩水平如何影响文件大小。

图片

随着压缩级别的提高,这张狗照片的背景质量特别受到影响。这是因为长草创造了一个非常动态和繁忙的区域,可以看到像素化。狗身体内的固体颜色区域保持了更多原始质量,因为同一区域的像素颜色非常相似。保存为 Web窗口中的另一个有趣特性是每个图像版本的预期下载时间。您可以轻松更改预期带宽级别,以查看将此图像传递到互联网用户可能需要多少时间。

提示

由于每个图像都不同,没有单一的完美优化设置。花时间确保每个图像在最小文件大小下看起来最好,将为您带来一个外观更好、加载速度更快的网站。

举例来说,我使用 JPEG 格式以不同的分辨率和压缩级别导出了这张照片。

图片

正如您从文件列表中看到的,我们最初从数码单反相机直接拍摄了一张 27MB 的照片。在 Photoshop 中使用不同的导出方法,我们可以轻松获得相同图像的较小分辨率版本,文件大小远远低于 500 千字节。考虑到在完全开发的网页中,这张图片可能是众多图片之一,一般的经验法则是尽可能保持每个图像文件的大小。这将使您的内容快速加载,并为您创建的设计正确显示。

当然,正如之前提到的,JPG 并不是网页中唯一可用的图像格式。让我们快速介绍每种格式以及它们各自的特点。

JPEG

将图像输出为.jpeg或更常见的.jpg允许进行有损图像压缩,旨在通过丢弃图像内的一些数据来减小文件大小。在 JPEG 格式中保存时使用的压缩量通常由用户定义,允许设计师和开发人员创建比原始文件更小的文件,使其尽可能接近其源。JPEG 格式的主要缺点之一是缺乏透明度支持,因为该格式不包含 alpha 通道。

PNG

便携式网络图形PNG)是一种位图图像格式,在保存图像数据时不使用压缩。PNG 图像非常适合设计和资产图像,因为它们保留了设计中使用的质量和调色板,并且支持透明度。然而,它们通常不用于照片等图像,因为由于图像中的细节数量,生成的文件大小将会太大。

GIF

心爱的 GIF 文件,或者如今更常见的动画 GIF自 1987 年 CompuServe 发布该格式以来一直可供使用。GIF 图像支持 256 种颜色、透明度,以及通过多个图像帧进行动画。尽管直到今天它仍然在 Web 上使用,但由于对动画图像的时间轴控制的缺乏,诸如精灵表(我们将在接下来的章节中更多地介绍)的技术正在变得更受欢迎,用于动画图像的集成。

音频

为 Web 准备音频相对来说非常简单,因为大多数 Web 浏览器支持 HTML5 新音频元素中的MP3音频格式。除了 MP3,一些浏览器还支持使用OGG音频文件。因此,以任一格式导出音频将允许您针对所有现代 HTML5 兼容的浏览器,并确保您的最终用户无论选择何种浏览器查看您的内容,都能听到音频。

音频元素

创建audio元素,与大多数元素的 HTML 语法一样,都非常简单。与 HTML 元素中传统的源引用的一个主要区别是使用了source元素,该元素被包含在audio元素中。通过利用这个新的source元素,我们可以在同一个元素中引用多个资产,并且只加载与之兼容的第一个文件:

<audio controls>
  <source src="img/horse.ogg" type="audio/ogg">
  <source src="img/horse.mp3" type="audio/mp3">
  Your browser does not support the audio tag.
</audio>

如果用户尝试在不支持 HTML5 音频的浏览器中打开此元素,则将显示audio元素内的其余内部内容。在这种情况下,我们只显示文本,但您也可以轻松地附加对 Flash 音频播放应用程序的引用或使用 CSS 进行样式化的警告。但是,如果浏览器按照给定的要求一切正常,页面将显示类似以下内容的音频播放 UI:

音频元素

音频播放控件用户界面是特定于显示数据的浏览器的。上述图像是 Google Chrome 当前呈现的内部音频播放控件用户界面。可以通过在audio标签元素中排除controls参数来移除默认音频控件。没有默认控件 UI,您可以使用图像、HTML 和 CSS 构建自己的控件,并使用 JavaScript 进行控制。

视频

如第一章 为什么选择 HTML5?中所述,将视频集成到 HTML5 文档中现在比以往任何时候都更容易。虽然将视频集成到 HTML5 文档中很简单,但一切都始于为 Web 准备视频。这个过程不仅应该最小化视频文件的大小,还应该使用特定的编解码器对其进行编码,并将其保存在特定的视频容器中。

HTML5 的video标签支持包含多种视频容器格式。在尝试支持完整范围的 HTML5 兼容浏览器时,开发人员必须包含对同一视频的多种格式的引用,因为并非每个浏览器都支持所有允许的视频文件类型。因此,对视频容器和编解码器的扎实理解对于网页开发人员来说是必要的,以便将视频正确集成到其文档中。

视频

视频编解码器

编解码器用于压缩和解压视频,以减小文件大小并允许使用更少的带宽共享大型视频文件。如果不对视频文件进行压缩,用户将不得不等待很长时间才能通过典型的互联网连接传输视频。以高清晰度为例,大约 5 分钟的原始视频可能超过 25GB 的数据。视频编解码器由先进的算法组成,可以删除从一帧到另一帧混合的相似数据。编码视频不是将每个单独的帧存储为单独的图像,而是存储一个通常比原始源材料小得多的专门数据集。为了观看,编码数据需要从精简的数据源解码回可视的基于帧的视频。编解码器是完成这项任务的一体化技术。HTML5 中支持的每个视频容器只支持一个视频编解码器,因此选择一个相当简单。然而,由于视频通常伴随着音频,音频也必须通过特定的音频编解码器运行。

视频容器

在尝试将视频嵌入 HTML5 文档时的一个主要问题是支持所有现代浏览器以相同的内容。不幸的是,并非所有兼容 HTML5 的浏览器都支持相同的视频格式。因此,为了支持最广泛的浏览器范围,开发人员必须嵌入多个版本的相同视频文件,以多种格式进行编码。由于这个问题在不久的将来不太可能改变,了解可用的视频容器及其相应的编解码器是准备 HTML5 文档中的视频的重要步骤。

MP4

从 Flash 开发者的角度来看,MP4 容器应该是最熟悉的,因为它们与 FLVF4V 文件非常相似。目前,MPEG-4 或 MP4 容器受到 Internet Explorer 9+、Google Chrome 和 Safari 的支持,可以嵌入视频元素。MP4 视频必须使用 H.264 编解码器进行编码,这也是 Flash 中 FLV 和 F4V 视频所使用的。

WebM

WebM 音频和视频格式是由 Google 赞助的项目,旨在为 Web 带来完全开放的多媒体容器和编解码器。WebM 文件受到 Firefox、Google Chrome 和 Opera 的支持。在为 WebM 容器内的视频进行编码时,使用了同样由 Google 拥有的 VP8 视频编解码器。

GG

OGG 容器受到 Firefox、Google Chrome 和 Opera 的支持。在为 OGG 容器内的视频进行编码时,使用 Theora 编解码器。由于只需使用 MP4 和 WebM 视频即可覆盖所有浏览器,因此在 OGG 格式中进行编码并不是完全必要的。无论如何,将其添加为备用并不会有害;浏览器在源列表中找到的第一个视频文件格式在显示时被利用,所有其他文件都会被忽略并且不会被下载。

提示

可以在Chapter 02_examples目录中找到示例编码视频文件以及高质量的主视频文件。

视频编码软件

有许多在线可用的优秀应用程序可以将您的视频内容编码为与 HTML5 兼容的格式。只要满足容器和编解码器的规范,任何应用程序或方法都可以用于完成任务。为了帮助您快速上手,在本章和本书的其余部分,以下是一些最受欢迎的编码工具和应用程序,供 Web 开发人员使用以快速将视频发布到网络上。

Miro 视频转换器

如果您正在寻找一个简单的方法来准备 Web 视频,那么不妨试试 Miro Video Converter,它可以在 Miro 的网站www.mirovideoconverter.com上找到。这个软件不仅免费和开源,而且还支持以所有 HTML5 兼容格式导出视频和音频。Miro Video Converter 适用于 Windows 和 OS X,可能是为 HTML5 项目准备音频和视频的最简单的方法。

Miro Video Converter

安装并打开应用程序后,下一步就是简单地将源视频文件拖放到应用程序中进行排队。如果您有多个视频,也可以将它们添加到队列中,所有视频将依次进行编码。

Miro Video Converter

一旦您需要编码的所有视频都添加到队列中,选择窗口底部的格式选项,并在转换器中选择三种可用格式之一。请记住,为了在每个浏览器中启用视频元素播放支持,您需要在每种格式中编码您的视频一次。如果您需要更深入地配置编码作业,Miro Video Converter 允许我们控制基本参数,如宽高比和视频尺寸。

Adobe Media Encoder

在其项目中包含视频的 Flash 开发人员可能已经使用了 Adobe Media Encoder。这个方便的软件与 Flash 捆绑在一起,可以轻松地对 Flash 和 HTML5 项目中使用的视频进行编码。不幸的是,该应用程序只能原生输出 Flash 视频格式的 HTML5-ready MP4 视频。

Adobe Media Encoder

Handbrake

如果您无法访问 Adobe Media Encoder,那么免费编码 MP4 视频的下一个最简单的方法就是前往handbrake.fr并下载 Handbrake。Handbrake 不仅是开源的,而且还适用于 Windows、OS X 和 Linux,因此很难被忽视。

Handbrake

FFMPEG

最后,我个人最喜欢的是 FFMPEG。如果您像我一样喜欢命令行,那么这个令人惊叹的软件一定适合您。在媒体方面,很难说 FFMPEG 不能用来完成什么。如果您需要高级视频转码,请务必访问ffmpeg.org了解更多信息。

视频元素

一旦我们的视频内容已经以必要的格式进行了编码,剩下的就是在 HTML 文档的正文中引用视频。与audio元素一样,video元素支持在video元素内部使用source标签来引用多个资产,而不是在标签中使用典型的src参数来创建对文件的引用。值得注意的是,如果您只嵌入了单个视频引用,则可以在video标签中使用src参数,而不是添加source标签:

<video width="800" height="600" controls>
  <source src="img/my_video.mp4" type="video/mp4">
  <source src="img/my_video.webm" type="video/webm">
  <source src="img/my_video.ogg" type="video/ogg">
  Your browser does not support the video tag.
</video>

audio元素一样,video元素允许通过在video标签中添加controls参数来进行播放控制集成。视频可以通过在video标签中添加autoplay="true"来在页面加载时自动播放。

现在我们已经准备好所有资产并准备好行动,是时候开始进入开发环境了。由于 Web 浏览器是我们的目标平台,让我们花点时间来了解今天的现代 Web 浏览器在 Web 开发工具方面为我们提供了什么,以帮助我们在开发周期中进行开发。

调试和输出方法

随着 HTML5 和其他大量客户端驱动的网页内容的流行,需要一个强大的开发者工具集来方便地调试和测试网页。幸运的是,每个现代浏览器都已经适应或集成了一些非常相似的设置来做到这一点。在这个工具集中最重要的功能之一就是 JavaScript 控制台。JavaScript 控制台对于网页开发者来说就像 Flash 开发者的 Flash 输出窗口一样重要。这是一个非常重要的区域,用于打印初始化应用程序或网站中的数据以及代码中指定的打印语句或值。在 ActionScript 中,通过使用trace()函数来将数据打印到输出窗口。在 JavaScript 中,我们利用console对象的内置方法来做同样的事情。考虑以下示例:

function calculateSum(a, b) {
  sum = a + b;
  console.log("The sum of " + a + " + " + b + " = " + sum);
}

calculateSum(2, 3);

提示

这个例子可以在Chapter 02_examples目录中的Console-Example目录中找到。

这个代码示例创建了一个 JavaScript 函数来计算数字的总和,并使用示例参数调用该方法,以在浏览器控制台中显示输出。与 ActionScript 中的跟踪类似,JavaScript 中的控制台集成在幕后,与实际网页分开。控制台的主要功能是在运行时帮助开发者调试 JavaScript、CSS 或 HTML 属性。开发者控制台不仅可以用于从应用程序中打印数据,还可以用于触发代码中特定函数的执行,而无需特定事件或交互的发生。

控制台同样重要,整个用户界面和交互取决于使用何种浏览器来查看文档。因此,了解在所有流行浏览器中找到和如何使用控制台是帮助您构建健壮代码的重要一步。让我们快速在一些常见的浏览器中运行我们简单的计算总和示例,看看它们如何处理输出。

谷歌浏览器

所有版本的谷歌浏览器都内置了开发者工具集,可以通过右键单击网页并在对话框中选择检查元素选项来轻松打开。这将显示附加到浏览器窗口底部的开发者工具窗口。选择控制台选项卡将显示 JavaScript 控制台,以查看网页的输出。在 Chrome 中打开我们的 JavaScript calculateSum函数示例,并打开控制台,应该显示类似下面的图像:

谷歌浏览器

正如你所看到的,console.log()调用的输出已经显示出来,还有调用是从哪个文件和行号发出的。即使从简单的角度来看,我相信你已经开始看到这个工具有多么方便,如果你有 100 甚至 1000 行代码在多个文件中处理。与 ActionScript 中的跟踪输出窗口类似,这个工具的另一个亮点是它能够直接从控制台窗口调用进一步的 JavaScript 执行。在控制台中,我们可以继续调用calculateSum函数,并直接从控制台传入必要的值来找到新数字的总和。

谷歌浏览器

一些浏览器,比如 Chrome,甚至具有自动完成功能,当你输入方法或属性名称时会展开文本,这是我相信大多数 Flash 开发者希望在 Flash IDE 中拥有的功能。

火狐浏览器的 Firebug

由于 Firefox 没有预装强大的开发者工具集,网页开发者的常见选择是安装Firebug扩展来启用此功能。可以通过访问getfirebug.com在几秒钟内将扩展轻松添加到 Firefox 安装中。安装并激活后,右键单击页面的任何位置,然后选择使用 Firebug 检查元素

火狐浏览器的 Firebug

这应该感觉非常熟悉,就像我们在 Chrome 中所做的一样。Firebug 是一个几乎所有我认识的开发人员都使用的很棒的项目。所有这些工具集中都有很多很棒的功能,我们将在本书中介绍其中许多功能。由于我们打开了一个非常简单的 HTML 页面,几乎没有什么内容,现在可能是一个很好的时机来查看更原始的网页的 UI 和输出,所以随时随地点击并查看一下。

Firefox 的 Firebug

Safari

启用 Safari 中的开发者工具,请打开首选项窗口,然后选择高级选项卡。选择窗口底部标有在菜单栏中显示开发菜单的复选框,然后可以关闭窗口。

Safari

从这一点开始,您可以像往常一样在任何网页上右键单击,然后选择检查元素以显示工具窗口。

Safari

如果您留意的话,您可能会注意到这个控制台几乎与 Google Chrome 中的控制台相同。当然,它具有命令行集成,就像我们在其他浏览器中看到的那样。

Safari

Opera

与 Google Chrome 类似,Opera 中的开发者工具可以通过右键单击网页并选择检查元素来轻松访问。一旦开发者工具窗口在浏览器底部打开,选择控制台选项卡以打开开发者控制台。最初,控制台将是空白的,没有任何来自当前正在查看的网页的交互。

Opera

与始终处于活动状态的控制台不同,Opera 决定仅在控制台实际打开时才读取控制台命令。因此,刷新页面将显示控制台交互:

Opera

Internet Explorer

从 Internet Explorer 9 开始,微软已经开始在浏览器中直接包含开发人员工具集。可以通过在查看页面时按下F12随时打开开发人员工具窗口。与 Opera 一样,Internet Explorer 需要刷新页面才能在活动页面上启用控制台的使用,因为当关闭时它保持不活动状态。

Internet Explorer

当然,就像其他控制台一样,我们可以从命令行调用我们的 JavaScript 方法和变量。

Internet Explorer

语法差异

既然我们有一些媒体可以使用,并且浏览器工具也可以使用,让我们开始玩弄 JavaScript 并将其语法与您已经了解的 ActionScript 3 进行比较。

变量

与在 ActionScript 3 中声明的变量不同,JavaScript 变量没有严格类型。这将从熟悉的 ActionScript 3 变量声明转换为:

var myVariable:String = 'abc123';

转换为 JavaScript 中的更简单的语法,如下所示:

var myVariable = 'abc123';

这种缺乏严格类型称为动态类型。JavaScript 中的变量可以随时用作任何类型。考虑以下示例:

var exampleVar;                      // A undefined variable
exampleVar = "Some example text";    // Variable is now a String
exampleVar = 12345;                  // Variable is now a Number

动态类型允许代码更快地编写,因为它需要开发人员的输入更少,但这种开发便利性是以调试大型应用程序为代价的。ActionScript 3 的严格类型允许编译器在导出新版本应用程序之前就捕获问题。JavaScript 不会在本地执行此操作,这可能是先前具有 ActionScript 3 经验的大多数开发人员使用该语言时最大的抱怨之一。

变量类型转换

尽管 JavaScript 中的变量没有严格类型,但有方法可以确保变量数据以正确的形式进行所需的操作。可以对变量进行类型转换以确保其格式正确:

myString = String('12345');   // Convert to a String
myBoolean = Boolean('true');  // Convert to Boolean
myNumber = Number('12345');   // Convert to Number

条件和循环

我们将一起涵盖这两个方面,因为 JavaScript 中的条件和循环语法几乎与 ActionScript 3 中您习惯的一样。Ifif... elseif... else if条件与 ActionScript 中的条件没有什么不同:

if(cats > dogs) {
  // Code for cat people...
} else if (cats < dogs) {
  // Code for dog people...
} else {
  // Code for everyone else...
}

另外,switch语句也可以使用,就像if语句一样;语法与 ActionScript 中的完全相同:

switch(animal) {
  case 'cat':
    // Code for cat people...
    break;
  case 'dog':
    // Code for dog people...
    break;
  default:
    // Code for everyone else...
}

循环与 ActionScript 中的循环没有什么不同。考虑这些forwhile循环:

for(var n = 0; n < myArray.length; n++) {
  // Code within loop...
}

while(n < 100) {
  // Code within loop...
}

do {
  // Code within loop...
} while(n < 100);

函数

与 ActionScript 3 一样,JavaScript 中的函数是用大括号({})括起来的代码块。每个函数都与一个关键字相关联,用于调用函数并运行其中的代码。通常情况下,函数可以将值返回到最初调用的地方。这是通过使用return语句来实现的。

JavaScript 函数的语法与 ActionScript 函数非常相似,但不需要严格类型化预期参数和函数返回类型。作为 Flash 开发人员,您的 ActionScript 3 函数可能看起来像下面这样:

function getCoffee (owner:String, milks:int, sugars:int):void {
  // Code...
}

这种语法可以很容易地转换为 JavaScript,只需删除变量和返回类型声明,以便 JavaScript 中的相同函数可以写成如下形式:

function getCoffee (owner, milks, sugars) {
  // Code...
}

对象

从技术上讲,JavaScript 中声明的所有内容都是对象,但是,总有一天你会需要创建自己的自定义对象。可以通过以下两种方式之一来实现。第一种方式,应该非常熟悉 ActionScript 开发人员,如下所示:

player = new Object();
player.name = "John Smith";
player.lives = 5;
player.posX = 10;
player.posY = -30;

您还可以通过将它们定义为函数来创建对象,如下所示:

function player(name, lives, posX, posY) {
  player.name = name;
  player.lives = lives;
  player.posX = posX;
  player.posY = posY;
}

var teddyBear = new player("Teddy", 5, 10, 10);
console.log(teddyBear.name);

DOM 事件

集成 DOM 事件允许您使用 JavaScript 处理在 HTML 文档中发生的事件。

鼠标事件

DOM 公开了鼠标事件,用于鼠标指针的基本用户交互。通过在 HTML 标记中使用onclick事件参数,我们可以在用户单击特定元素时执行 JavaScript:

<img src="img/my-image.jpg" id="my-image" onclick="PLACE YOUR JAVASCRIPT HERE">

然而,我们也可以完全从 JavaScript 中定位元素,并在 HTML 源代码之外处理事件处理程序,以保持项目清晰易懂:

document.getElementById("my-image").onclick=function() {
  // Place your JavaScript here...
};

当然,您不仅仅局限于鼠标点击事件。事件也可以处理鼠标悬停、鼠标移出、鼠标按下和鼠标释放。在本书的示例中,我们将利用所有这些事件以及扩展它们的方法。

JavaScript 示例

在涵盖了所有 JavaScript 语法规范之后,让我们将其中一些用于一个工作示例,并看看会发生什么。看一下以下简单的 HTML 文档,其中包含 JavaScript 来对随机数组进行排序:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Insertion Sort - JavaScript Syntax Example</title>

    <script type="text/javascript">
      // Number of elements to sort.
      elementCount = 10000;	
      // The array which will be sorted.
      sortlist = new Array();

      /**
      * Called on button click.
      */
      function init() {
        // Prepare random array for sorting.
        for(i = 0; i < elementCount; i++)
          sortlist.push(i);

        //shuffle(sortlist);
        sortlist.sort(function() {
          return 0.5 - Math.random();
        });

        // Display the random array prior to sorting.
        console.log(sortlist);

        // Start a timer.
        console.time('Iteration Sort Timer');

        // Sort the randomized array.
        insertionSort(sortlist);

        // Stop the timer.
        console.log('Sorted ' + elementCount + ' items.');
        console.timeEnd('Iteration Sort Timer');

        // Display the sorted array.
        console.log(sortlist);
      }

      /**
      * The popular Insertion Sort algorithm.
      */
      function insertionSort(list) {
        // It's always smart to only lookup array size once.
        l = list.length;

        // Loop over supplied list and sort.
        for(i = 0; i < l; i++) {
          save = list[i];
          j = i;

          while(j > 0 && list[j - 1] > save) {
            list[j] = list[j - 1];
            j -= 1;
          }

          list[j] = save;
        }
      }
    </script>
  </head>

  <body>
    <p>
      Click the button below to begin.
      Be sure to open up your browsers developer console.
    </p>
      <button onclick="init()">Start Sorting</button>
  </body>
</html>

这个示例涵盖了我们刚刚涵盖的 JavaScript 的许多特性和语法规范。在 HTML 文档的head标记中声明的 JavaScript 块中,我们创建了两个函数。第一个函数是我们的初始化方法,用于准备和运行应用程序。第二个函数包含了流行的插入排序算法,它将对我们的随机数组进行排序。为了使两个函数能够使用相同的变量,我们在每个函数的作用域之外创建了elementCountsortlist作为全局变量。在 HTML 的body标记中是一个button元素,它在页面上呈现一个典型的表单按钮元素,当用户单击此按钮时,onclick处理程序调用init函数。

这个示例并不华丽,但正如我上面提到的,它涵盖了 JavaScript 语法规范的许多不同方面。

定时 JavaScript 执行

处理 JavaScript 执行时间的一个重要注意点是确保整个页面在允许 JavaScript 开始执行其代码之前已经完成加载。等待页面加载的原因是为了在尝试操作它们之前允许页面上的所有资产和外部引用加载。如果您的 JavaScript 尝试对不存在的元素执行操作,您的应用程序流程可能会失败。为了避免这个问题,我们可以向 DOM 添加一个事件侦听器,使其仅在页面完全加载并显示后运行。利用 DOM 事件为 JavaScript 提供了一个简单的方法来做到这一点,如下面的代码所示:

window.addEventListener("load", init, false);

var init = function() {
  // Start everything from in here.
}

现在,当窗口完成加载过程后,将调用init函数,应用程序代码的其余部分可以开始执行。实际上,JavaScript 有许多方法可以在页面加载完成后执行代码。本书的后续章节将使用示例来使用和解释其中许多方法。

总结

在本章中,我们花了一些时间来更好地熟悉为我们的 HTML5 项目准备媒体资产所涉及的过程。此外,本章还涵盖了每种典型多媒体格式的准备和集成技术,以及一些流行的软件,可帮助完成这些工作。我们迅速地比较了 ActionScript 3 和 JavaScript 语法,以便更熟悉在编写 JavaScript 时与 ActionScript 3 相比的细微但重要的差异。这使我们完美地准备好进入第三章,“可扩展性、限制和效果”,在那里我们将开始将 HTML5 推到极限,以查看它的限制和缺点,以及它可以做的所有令人惊讶的事情。

第三章:可扩展性、限制和效果

准备好用于 HTML5 集成的媒体资产后,让我们继续这个旅程,通过查看 CSS3 和 JavaScript 中一些新的和令人兴奋的对象操作功能,以及它们与 Flash 开发人员熟悉的内容的关系。在本章的过程中,我们将回顾 HTML5 的许多特定功能,这些功能使其获得了广泛的使用和受欢迎程度,变得更像典型的 Flash 开发。

本章将涵盖以下内容:

  • 初始开发限制及避免它们的方法

  • 一些新的和令人兴奋的 CSS3 新增功能

  • 为移动和桌面开发响应式布局

  • 使用 CSS 媒体查询为特定显示目标 CSS 样式

  • 控制和流式传输音频和视频,以及与 Flash 相比的限制

  • 客户端文件集成和操作

  • 使用 HTML5 Web Workers 将繁重的进程发送到后台

  • 介绍使用 WebSockets 进行服务器端通信

  • 了解 Canvas 元素是什么以及它的重要性

  • WebGL 简介及其与 Stage3D 的关系

HTML5 的限制

如果您现在还没有注意到,您将使用的许多 HTML5 功能都具有故障保护、多个版本或特殊语法,以使您的代码覆盖整个浏览器范围和其中支持的 HTML5 功能集。随着时间的推移和标准的巩固,人们可以假设许多这些故障保护和其他内容显示措施将成熟为所有浏览器共享的单一标准。然而,实际上,这个过程可能需要一段时间,即使在最好的情况下,开发人员仍然可能不得不无限期地利用许多这些故障保护功能。因此,对何时、何地以及为什么使用这些故障保护措施有坚实的理解,将使您能够以一种方式开发您的 HTML5 网页,以便在所有现代浏览器上都能按照预期查看。

为了帮助开发人员克服先前提到的这些问题,许多框架和外部脚本已经被创建并开源,使得在开始每个新项目时,可以拥有更普遍的开发环境,从而节省了开发人员无数的时间。Modernizr(modernizr.com)已经迅速成为许多 HTML5 开发人员必不可少的补充,因为它包含了许多条件和验证,使得开发人员可以编写更少的代码并覆盖更多的浏览器。Modernizr 通过检查客户端浏览器中 HTML5 中可用的大多数新功能(超过 40 个)并在几毫秒内报告它们是否可用来实现所有这些。这将使您作为开发人员能够确定是否应该显示内容的备用版本或向用户发出警告。

让您的网络内容在所有浏览器中正确显示一直是任何网络开发人员面临的最大挑战,当涉及创建尖端有趣的内容时,挑战通常变得更加艰巨。本章不仅将涵盖许多新的 HTML5 内容操作功能,还将在代码示例中进行演示。为了让您更好地了解这些功能在没有使用第三方集成的情况下是什么样子,我们将暂时避免使用外部库。值得注意的是,这些功能和其他功能在所有浏览器中的外观。因此,请确保在不仅是您喜欢的浏览器中,而且在许多其他流行的选择中测试示例以及您自己的工作。

使用 CSS3 进行对象操作

在 CSS3 出现之前,Web 开发人员使用了一长串的内容操作、资源准备和资源呈现技术,以便在每个浏览器中获得他们想要的网页布局。其中大部分技术都被认为是“黑客”技术,因为它们基本上都是一种解决方案,使浏览器能够执行通常不会执行的操作。诸如圆角、投影阴影和变换等功能都不在 Web 开发人员的工具库中,而且要达到想要的效果的过程可能会让人感到无聊。可以理解的是,CSS3 对所有 Web 开发人员的兴奋程度都非常高,因为它使开发人员能够执行比以往更多的内容操作技术,而无需事先准备或特殊的浏览器黑客技术。尽管 CSS3 中可用属性的列表很庞大,但让我们来介绍一些最新和最令人兴奋的属性。

box-shadow

一些设计师和开发人员说投影阴影已经过时,但在 HTML 元素中使用阴影仍然是许多人的流行设计选择。在过去,Web 开发人员需要进行一些技巧,比如拉伸小的渐变图像或直接在背景图像中创建阴影,以在其 HTML 文档中实现这种效果。CSS3 通过创建box-shadow属性来解决了这个问题,允许在 HTML 元素上实现类似投影阴影的效果。

为了提醒我们 ActionScript 3 中是如何实现这种效果的,让我们回顾一下这段代码:

var dropShadow:DropShadowFilter = new DropShadowFilter();
dropShadow.distance = 0;
dropShadow.angle = 45;
dropShadow.color = 0x333333;
dropShadow.alpha = 1;
dropShadow.blurX = 10;
dropShadow.blurY = 10;
dropShadow.strength = 1;
dropShadow.quality = 15;
dropShadow.inner = false;
var mySprite:Sprite = new Sprite();
mySprite.filters = new Array(dropShadow);

如前所述,CSS3 中的新box-shadow属性允许您相对轻松地附加这些阴影效果,并且具有许多相同的配置属性:

.box-shadow-example {
  box-shadow: 3px 3px 5px 6px #000000;
}

尽管在此样式中应用的每个值都没有属性名称,但您可以看到许多值类型与我们在 ActionScript 3 中创建的投影阴影所附加的值相符。

这个box-shadow属性被赋予了.box-shadow-example类,因此将被应用到任何具有该类名的元素上。通过创建一个带有box-shadow-example类的div元素,我们可以改变我们的内容,使其看起来像下面这样:

<div class="box-shadow-example">CSS3 box-shadow Property</div>

box-shadow

尽管这个 CSS 属性很容易添加到您的项目中,但它在一行中声明了很多值。让我们按顺序回顾每个值,以便我们更好地理解它们以备将来使用。为了简化属性中每个变量的识别,这些变量已经被更新为不同的值:

box-shadow: 1px 2px 3px 4px #000000;

这些变量的解释如下:

  • 初始值(1px)是阴影的水平偏移,或者阴影是向左还是向右。正值将把阴影放在元素的右侧,负偏移将把阴影放在左侧。

  • 第二个值(2px)是垂直偏移,与水平偏移值一样,负数将生成向上的阴影,正数将生成向下的阴影。

  • 第三个值(3px)是模糊半径,控制阴影的模糊程度。声明一个值,例如0,将不会产生模糊,显示出一个非常锐利的阴影。放入模糊半径的负值将被忽略,与使用 0 没有任何不同。

  • 第四个值(4px)也是数字属性的最后一个,是扩展半径。扩展半径控制了投影阴影模糊超出初始阴影大小声明的距离。如果使用值0,阴影将显示默认的模糊半径并且不会应用任何更改。正数值将产生更模糊的阴影,负值将使阴影模糊变小。

  • 最后一个值是十六进制颜色值,表示阴影的颜色。

或者,您可以使用box-shadow将阴影效果应用于元素的内部而不是外部。使用 ActionScript 3,可以通过在DropShadowFiler对象的参数列表中附加dropShadow.inner = true;来实现这一点。在 CSS3 中应用box-shadow属性的语法方式非常相似,只需要添加inset关键字。例如,考虑以下代码片段:

.box-shadow-example {
  box-shadow: 3px 3px 5px 6px #666666 inset;
}

这将产生一个看起来像下面截图的阴影:

box-shadow

提示

本章的代码示例中包含了一个 box-shadow 工具,它将帮助您更好地理解每个属性的影响。

text-shadow

就像box-shadow属性一样,text-shadow通过为文本创建相同的投影效果,实现了其名字的含义。

text-shadow: 2px 2px 6px #ff0000;

box-shadow一样,text-shadow的初始两个值是阴影放置的水平和垂直偏移量。第三个值是可选的模糊大小,第四个值是十六进制颜色:

text-shadow

border-radius

就像元素或文本阴影一样,在 CSS3 之前为元素添加圆角是一件苦差事。开发人员通常会附加单独的图像或使用其他对象操作技术来实现这种效果,通常是在典型的正方形或矩形形状元素上。通过在 CSS3 中添加border-radius设置,开发人员可以轻松动态地设置元素的角落圆度,只需几行 CSS 代码,而无需像 Flash 中那样使用矢量 9 切片。

由于 HTML 元素有四个角,当附加border-radius样式时,我们可以单独针对每个角落,或者一次性针对所有角落。为了轻松地一次性附加边框半径设置到所有角落,我们将创建我们的 CSS 属性如下:

#example {
  background-color:#ff0000; // Red background
  width: 200px;
  height: 200px;
border-radius: 10px;
}

前面的 CSS 不仅将 10px 的边框半径附加到#example元素的所有角落,还使用了现代浏览器使用的所有属性,我们可以确保这种效果对所有试图查看此内容的用户都是可见的:

border-radius

如上所述,可以针对元素的每个单独角落,只附加半径到元素的特定部分:

#example {
  border-top-left-radius: 0px; // This is doing nothing
  border-top-right-radius: 5px;
  border-bottom-right-radius: 20px;
  border-bottom-left-radius: 100px;
}

前面的 CSS 现在通过将左边框半径设置为0px来移除我们的#example元素,并为其他每个角落设置了特定的半径。值得注意的是,在这里将边框半径设置为0与完全不在 CSS 样式中留下该属性没有任何区别:

border-radius

字体

多年来,在 Flash 中处理自定义字体一直有着起伏。任何需要在其 Flash 应用程序中合并和使用自定义字体的 Flash 开发人员可能都知道选择字体嵌入方法以及确保它对没有在其计算机上安装字体的用户正常工作的痛苦。

CSS3 字体嵌入已经实现了一种“无忧无虑”的方式,可以通过@font-face声明将自定义字体包含到 HTML5 文档中:

@font-face {
  font-family: ClickerScript;
  src: url('ClickerScript-Regular.ttf'),
    url('ClickerScript-Regular .otf'),
    url('ClickerScript-Regular .eot');
}

CSS 现在可以直接引用您的 TTF、OTF 或 EOT 字体,这些字体可以放在您的 Web 服务器上以实现可访问性。在我们的 CSS 文档中声明了字体源,并为其应用了唯一的font-family标识后,我们可以通过使用font-family属性在特定元素上开始使用它:

#example {
  font-family: ClickerScript;
}

由于我们在@font-face属性中声明了特定的字体系列名称,因此以后几乎可以在任何元素上使用该自定义名称。自定义字体可以应用于 HTML 文档中几乎包含文本的任何内容。表单元素,如按钮标签和文本输入,也可以被设计为使用您的自定义字体。您甚至可以使用纯 HTML 和 CSS 重新制作网站标志等资产,并使用原始资产创建时使用的相同自定义字体。

可接受的字体格式

与在线资产的许多其他嵌入方法一样,字体需要转换为多种格式,以使所有常见的现代浏览器能够正确显示它们。几乎所有可用的浏览器都能够处理常见的 True Type 字体(.ttf 文件类型)或 Open Type 字体(.otf 文件类型),因此嵌入其中一种格式就足够了。不幸的是,Internet Explorer 9 没有内置对这两种流行格式的支持,需要将字体保存为 EOT 文件格式。

外部字体库

在过去几年中,出现了许多优秀的在线服务,使 Web 开发人员可以轻松地准备和嵌入字体到他们的网站中。Google 的 Web 字体存档可在www.google.com/webfonts找到,其中托管了一大批开源字体,可以添加到您的项目中,而无需担心许可或付款问题。只需在 HTML 文档中添加几行额外的代码,您就可以开始使用了。

值得一提的另一个很棒的网站是 Font Squirrel,可以在www.fontsquirrel.com找到。与 Google Web Fonts 一样,Font Squirrel 托管了一个大型的网页可用字体存档,并提供了复制粘贴就绪的代码片段,以将它们添加到您的文档中。该网站上的另一个很棒的功能是@font-face生成器,它可以让您将现有字体转换为所有网页兼容格式。

在沉迷于将所有喜爱的字体转换为网页可用格式并将它们整合到您的工作中之前,值得注意的是最初随字体附带的最终用户许可协议或 EULA。将许多可用字体转换为网页使用将违反许可协议,并可能在未来给您带来法律问题。

不透明度

对于 Flash 开发人员来说,更常见的是“alpha”,设置元素的不透明度不仅可以改变设计的外观和感觉,还可以添加诸如淡入淡出的内容等功能。尽管这个概念看起来很简单,但它相对于 Web 开发人员可用的 CSS 属性列表是相对较新的。设置元素的不透明度非常容易,看起来像下面这样:

#example {
  opacity: 0.5;
}

正如您从上面的示例中看到的那样,与 ActionScript 3 一样,不透明度值是介于 0 和 1 之间的数值。上面的示例将以 50%的透明度显示一个元素。CSS3 中的不透明度属性现在在所有主要浏览器中都得到支持,因此在声明时无需担心使用替代属性语法。

RGB 和 RGBA 着色

在处理 CSS 中的颜色值时,许多开发人员通常会使用十六进制值,类似于#000000来声明使用黑色。颜色也可以在 CSS 中以 RGB 表示法实现,通过使用rgb()rgba()调用来代替十六进制值。通过方法名称,您可以看到 CSS 中的rgba颜色查找还需要第四个参数,它声明颜色的 alpha 透明度或不透明度量。在 CSS3 中使用 RGBA 而不是十六进制颜色有几个好处。假设您刚刚创建了一个div元素,它将显示在网页布局中现有内容的顶部。

如果您曾经想要将div的背景颜色设置为特定颜色,但希望只有该背景是半透明的,而不是内部内容,那么 RGBA 颜色声明现在可以轻松实现这一点,因为您可以设置颜色的透明度:

#example {
  // Background opacity
  background: rgba(0, 0, 0, 0.5); // Black 50% opacity

  // Box-shadow
  box-shadow: 1px 2px 3px 4px rgba(255, 255, 255, 0.8); // White 80% opacity
  // Text opacity
  color: rgba(255, 255, 255, 1); 	// White no transparency
  color: rgb(255, 255, 255);	// This would accomplish the same styling

  // Text Drop Shadows (with opacity)
  text-shadow: 5px 5px 3px rgba(135, 100, 240, 0.5);
}

正如在前面的示例中所看到的,您可以在 CSS 语法中的任何需要颜色值的地方自由使用 RGB 和 RGBA 值,而不是十六进制。

元素变换

就我个人而言,我发现 CSS3 变换是 CSS 中最令人兴奋和有趣的新功能之一。在 Flash IDE 中以及使用 ActionScript 转换资产一直是非常容易访问和易于实现的。在 CSS 中转换 HTML 元素是 CSS 的一个相对较新的功能,并且仍在逐渐得到所有现代浏览器的全面支持。

变换元素允许您通过打开大量动画和视觉效果的可能性来操纵其形状和大小,而无需事先准备源。当我们提到“变换元素”时,实际上是在描述可以应用于变换的一系列属性,以赋予它不同的特性。如果您以前在 Flash 或可能在 Photoshop 中转换过对象,这些属性可能对您来说很熟悉。

翻译

作为一名主要处理 X 和 Y 坐标来定位元素的 Flash 开发人员,CSS3 Translate Transform 属性是放置元素的一种非常方便的方法,它的工作原理与 Flash 相同。translate属性接受两个参数,即 X 和 Y 值,用于平移或有效地移动元素:

transform:translate(-25px, -25px);

不幸的是,为了使您的变换在所有浏览器中都能正常工作,您需要在附加变换样式时针对每个浏览器进行定位。因此,标准的变换样式和属性现在看起来会像这样:

transform:translate(-25px, -25px);
-ms-transform:translate(-25px, -25px);     /* IE 9 */
-moz-transform:translate(-25px, -25px);    /* Firefox */
-webkit-transform:translate(-25px, -25px); /* Safari and Chrome */
-o-transform:translate(-25px, -25px);      /* Opera */

旋转

旋转是相当不言自明的,而且非常容易实现。rotate属性接受一个参数,用于指定要应用于特定元素的旋转量(以度为单位):

transform:rotate(45deg);
-ms-transform:rotate(45deg);       /* IE 9 */
-moz-transform:rotate(45deg);      /* Firefox */
-webkit-transform:rotate(45deg);   /* Safari and Chrome */
-o-transform:rotate(45deg);        /* Opera */

值得注意的是,尽管提供的值始终意味着度数值,但值必须始终附加deg以便正确识别该值。

比例

就像rotate变换一样,缩放也非常简单。scale属性需要两个参数,分别声明 X 和 Y 的缩放量:

transform:scale(0.5, 2);
-ms-transform:scale(0.5, 2);      /* IE 9 */
-moz-transform:scale(0.5, 2);     /* Firefox */
-webkit-transform:scale(0.5, 2);  /* Safari and Chrome */
-o-transform:scale(0.5, 2);       /* Opera */

倾斜

倾斜元素将导致 X 和 Y 轴的倾斜:

transform:skew(10deg, 20deg);
-ms-transform:skew(10deg, 20deg);      /* IE 9 */
-moz-transform:skew(10deg, 20deg);     /* Firefox */
-webkit-transform:skew(10deg, 20deg);  /* Safari and Chrome */
-o-transform:skew(10deg, 20deg);       /* Opera */

以下插图是对使用前述属性倾斜图像的表示:

倾斜

矩阵

matrix属性将所有前述变换组合成一个属性,并且可以轻松消除源代码中的许多额外 CSS 行:

transform:matrix(0.586, 0.8, -0.8, 0.586, 40, 20);
/* IE 9 */
-ms-transform:matrix(0.586, 0.8, -0.8, 0.586, 40, 20);
/* Firefox */
-moz-transform:matrix(0.586, 0.8, -0.8, 0.586, 40, 20); 
/* Safari and Chrome */  
-webkit-transform:matrix(0.586, 0.8, -0.8, 0.586, 40, 20);
/* Opera */
-o-transform:matrix(0.586, 0.8, -0.8, 0.586, 40, 20); 

前面的示例利用了 CSS 变换矩阵属性来在单个调用中应用多个变换样式。matrix属性需要六个参数来旋转、缩放、移动和倾斜元素。只有当您实际上需要一次实现所有变换属性时,使用矩阵属性才真正有用。如果您只需要利用元素变换的一个方面,最好只使用该 CSS 样式属性。

3D 变换

到目前为止,我们审查过的所有变换属性都是二维变换。CSS3 现在还支持 3D 和 2D 变换。CSS3 3D 变换最好的部分之一是许多设备和浏览器支持硬件加速,从而允许在您的视频卡 GPU 上进行复杂的图形处理。在撰写本书时,只有 Chrome、Safari 和 Firefox 支持 CSS 3D 变换。

提示

在开始开发之前,想知道哪些浏览器将支持所有这些出色的 HTML5 功能吗?请访问caniuse.com查看流行浏览器在一个简单易用的网站上支持哪些功能。

在处理 3D 世界中的元素时,我们使用 Z 坐标,这允许使用一些新的变换属性。

transform:rotateX(angle)
transform:rotateY(angle)
transform:rotateZ(angle)
transform:translateZ(px)
transform:scaleZ(px)

让我们从 HTML 元素创建一个 3D 立方体,将所有这些属性放入一个工作示例中。要开始创建我们的 3D 立方体,我们将首先编写包含立方体的 HTML 元素,以及构成立方体本身的元素:

<body>
  <div class="container">
    <div id="cube">
      <div class="front"></div>
      <div class="back"></div>
      <div class="right"></div>
      <div class="left"></div>
      <div class="top"></div>
      <div class="bottom"></div>
    </div>
  </div>
</body>

这个 HTML 通过创建每个具有特定类名的六个面的元素,以及整个立方体的容器以及显示所有页面内容的主容器,为我们的立方体创建了一个简单的布局。当然,由于这些容器中没有内部内容,也没有样式,将此 HTML 文件在浏览器中打开将得到一个空白页面。因此,让我们开始编写 CSS,使所有这些元素可见,并将每个元素定位以形成我们的三维立方体。我们将首先设置我们的主容器,这将定位我们的内容并包含我们的立方体面:

.container {
  width: 640px;
  height: 360px;
  position: relative;
  margin: 200px auto;

  /* Currently only supported by Webkit browsers. */
  -webkit-perspective: 1000px;
  perspective: 1000px;
}
#cube {
      width: 640px;
      height: 320px;
      position: absolute;

/* 
Let the transformed child elements preserve 
the 3D transformations: 
*/
  transform-style: preserve-3d;
      -webkit-transform-style: preserve-3d;
      -moz-transform-style: preserve-3d;
}

container类是我们的主要元素,它包含此示例中的所有其他元素。在附加了宽度和高度后,我们将顶部边距设置为200px,以将显示向下推移一点,以便更好地查看页面,并将左右边距设置为自动,这将使该元素在页面中居中对齐。

#cube div {
  display: block;
  position: absolute;
     border: 1px solid #000000;
     width: 640px;
     height: 320px;
     opacity:0.8;
}

通过为#cube div定义属性,我们为#cube元素内的每个div元素设置样式。我们还通过将宽度和高度设置为矩形比例来欺骗立方体系统,因为我们的意图是在结构和位置确定后向立方体的每一面添加视频。

附加了基本的立方体面样式后,现在是时候开始变换每个面,形成三维立方体了。我们将从立方体的前面开始,通过在 Z 轴上进行平移,使其靠近视角:

#cube .front  {
-webkit-transform: translateZ(320px);
   -moz-transform: translateZ(320px);
   transform: translateZ(320px);
}

为了将这种样式附加到所有现代浏览器中的元素上,我们需要为每个不支持默认transform属性的浏览器指定多种语法的属性:

3D transforms

在附加了 Z 轴平移 320px 后,前面的屏幕截图显示了.front div 发生的变化。较大的矩形是.front div,现在离我们的视角近了 320px。为了简单起见,让我们对.back div 执行相同的操作,将其推离视角 320px:

#cube .back   {
  -webkit-transform:
      rotateX(-180deg) 
      rotate(-180deg) 
      translateZ(320px);
  -moz-transform: 
      rotateX(-180deg) 
      rotate(-180deg) 
      translateZ(320px);
  transform: 
      rotateX(-180deg) 
      rotate(-180deg) 
      translateZ(320px);
}

如前面的代码所示,为了正确将.back元素移动到位而不使其倒置,我们在 X 轴上将元素翻转 180 度,然后像.front一样将 Z 平移 320px。请注意,我们没有在 translate Z 上设置负值,因为元素被翻转了。有了.back CSS 样式,我们的立方体应该看起来像下面这样:

3D transforms

现在可见的最小矩形是具有类名.back的元素,最大的是我们的.front元素,中间的矩形是剩下的要变换的元素。

为了定位立方体的各个面,我们需要绕 Y 轴旋转侧面元素,使其面向正确的方向。一旦它们旋转到位,我们可以在 Z 轴上平移位置,使其从中心推出,就像我们对前面和后面的面做的那样:

#cube .right {
    -webkit-transform: rotateY(90deg) translateZ( 320px );
    -moz-transform: rotateY(90deg) translateZ( 320px );
    transform: rotateY(90deg) translateZ( 320px );
}

3D transforms

右侧就位后,我们可以对左侧执行相同的操作,但是将其朝相反方向旋转,使其面向另一侧:

#cube .left {
-webkit-transform: rotateY(-90deg) translateZ( 320px );
   -moz-transform: rotateY(-90deg) translateZ( 320px );
   transform: rotateY(-90deg) translateZ( 320px );
}

3D transforms

现在我们已经正确对齐了立方体的四个面,我们可以通过对齐顶部和底部来最终确定立方体的位置。为了正确设置顶部和底部的大小,我们将设置它们自己的宽度和高度,以覆盖#cube div 样式中设置的初始值:

#cube .top {
   	width: 640px;
   height: 640px;

   -webkit-transform: rotateX(90deg) translateZ( 320px );
   -moz-transform: rotateX(90deg) translateZ( 320px );
   transform: rotateX(90deg) translateZ( 320px );
}
#cube .bottom {
   	width: 640px;
   height: 640px;

   -webkit-transform: rotateX(-90deg) translateZ( 0px );
   -moz-transform: rotateX(-90deg) translateZ( 0px );
   transform: rotateX(-90deg) translateZ( 0px );
}

为了正确定位顶部和底部,我们需要在 X 轴上将.top.bottom元素旋转+-90 度,使它们朝上和朝下,只需要在 Z 轴上将顶部平移到正确的高度,以连接所有其他面。

在我们的布局中添加了所有这些变换后,生成的立方体应该如下所示:

3D 变换

尽管看起来是 3D 的,但由于容器中没有内容,透视并没有很好地展示我们的立方体。因此,让我们在立方体的每一面添加一些内容,比如视频,以更好地可视化我们的工作。在每一面中,让我们添加相同的 HTML5 视频元素代码:

<video width="640" height="320" autoplay="true" loop="true">
  <source src="img/cube-video.mp4" type="video/mp4">
  <source src="img/cube-video.webm" type="video/webm">
  Your browser does not support the video tag.
</video>

由于我们尚未添加元素播放控件以显示立方体的更多可见区域,我们的视频元素被设置为在完成后自动播放视频以及循环播放。现在我们得到了一个正确展示 3D 变换能做什么并且更具视觉吸引力的结果:

3D 变换

由于我们设置了每个立方体面的不透明度,现在我们可以看到所有四个视频在每一面播放,非常酷!既然我们已经在这里,为什么不再加一点,为这个立方体添加用户交互,这样我们就可以把它转过来,看到每一面的视频。

要执行这种用户交互,我们需要使用 JavaScript 将页面文档上的鼠标坐标转换为立方体的 X 和 Y 3D 旋转。因此,让我们开始创建 JavaScript 来监听鼠标事件:

window.addEventListener("load", init, false);

function init() {
  // Listen for mouse movement
  window.addEventListener('mousemove', onMouseMove, false);
}

function onMouseMove(e) {
  var mouseX = 0;
  var mouseY = 0;

  // Get the mouse position
  if (e.pageX || e.pageY) {
    mouseX = e.pageX;
    mouseY = e.pageY;
  } else if (e.clientX || e.clientY) {
    mouseX = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
    mouseY = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
  }

  console.log("Mouse Position: x:" + mouseX + " y:" + mouseY);
}

从上述代码示例中可以看出,当mousemove事件触发并调用onMouseMove函数时,我们需要运行一些条件语句来正确解析鼠标位置。由于像网页开发的许多其他部分一样,从浏览器中检索鼠标坐标各不相同,我们添加了一个简单的条件来尝试以几种不同的方式收集鼠标 X 和 Y。

鼠标位置准备好被转换为立方体的变换旋转后,我们需要在设置 CSS 样式更新之前完成最后一点准备工作。由于不同的浏览器支持不同语法的 CSS 变换应用,我们需要在 JavaScript 中找出在运行时使用哪种语法,以允许我们的脚本在所有浏览器上运行。以下代码示例就是这样做的。通过设置可能属性值的预定义数组,并尝试检查每个属性的类型作为元素样式属性,我们可以找到哪个元素不是未定义的,并知道它可以用于 CSS 变换样式:

// Get the support transform property
var availableProperties = [
      'transform',
      'MozTransform',
      'WebkitTransform',
      'msTransform',
      'OTransform'
      ];
// Loop over each of the properties
for (var i = 0; i < availableProperties.length; i++) {
  // Check if the type of the property style is a string (ie. valid)
  if (typeof document.documentElement.style[availableProperties[i]] == 'string'){
    // If we found the supported property, assign it to a variable
    // for later use.
        var supportedTranformProperty = availableProperties[i];
      }
}

现在我们已经获得了用户的鼠标位置和立方体的 CSS 变换更新的正确语法,我们可以把它们放在一起,最终实现对我们的视频立方体的 3D 旋转控制:

<script>
  var supportedTranformProperty;

  window.addEventListener("load", init, false);

  function init() {
    // Get the support transform property
    var availableProperties = ['transform', 'MozTransform','WebkitTransform', 'msTransform', 'OTransform'];
    for (var i = 0; i < availableProperties.length; i++) {
      if (typeof document.documentElement.style[availableProperties[i]] == 'string'){
                supportedTranformProperty = availableProperties[i];
          }
}

    // Listen for mouse movement
    window.addEventListener('mousemove', onMouseMove, false);
  }

  function onMouseMove(e) {
    // Get the mouse position
    if (e.pageX || e.pageY) {
      mouseX = e.pageX;
      mouseY = e.pageY;
    } else if (e.clientX || e.clientY) {
      mouseX = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
      mouseY = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}

    // Update the cube rotation
    rotateCube(mouseX, mouseY);
  }

  function rotateCube(posX, posY) {
    // Update the CSS transform styles
  document.getElementById("cube").style[supportedTranformProperty] = 'rotateY(' + posX + 'deg) rotateX(' + posY * -1 + 'deg)';
  }

</script>

尽管我们已经尝试允许多浏览器使用此示例,但最好在每个浏览器中打开它,看看类似 3D 变换的重型内部内容是如何运行的。在撰写本书时,所有 WebKit 浏览器都是查看此类内容的简单选择,因为诸如 Firefox 和 Internet Explorer 之类的浏览器以更慢和更低质量的输出渲染此示例:

3D 变换

过渡

使用 CSS3,我们可以在从一种样式更改到另一种样式时添加效果,而无需使用 Flash 动画或 JavaScript:

div {
  transition: width 2s;
  -moz-transition: width 2s;    /* Firefox 4 */
  -webkit-transition: width 2s; /* Safari and Chrome */
  -o-transition: width 2s;      /* Opera */
}

如果未指定持续时间,过渡将不会产生任何效果,因为默认值为 0:

div {
  transition: width 2s, height 2s, transform 2s;
  -moz-transition: width 2s, height 2s, -moz-transform 2s;
  -webkit-transition: width 2s, height 2s, -webkit-transform 2s;
  -o-transition: width 2s, height 2s,-o-transform 2s;
}

提示

值得注意的是,目前 Internet Explorer 不支持 CSS3 过渡。

浏览器兼容性

如果你还没有注意到,浏览器兼容性的斗争是网页开发人员工作的最重要方面之一。随着时间的推移,许多出色的服务和应用程序已经被创建,以帮助开发人员以比试错技术更简单的方式克服这些障碍。网站,如css3test.comcaniuse.comhtml5readiness.com都是保持 HTML5 规范开发人员和浏览器对所有功能的支持的重要资源。

帧速率

一个人会假设,因为你,读者,来自 Flash 开发背景,当开发 HTML5 应用程序时,应该花点时间谈论帧速率或每秒帧数。由于 Flash 应用程序中的每个资源都是基于时间轴模型的,计算每秒显示多少帧时间轴的帧是一个相当简单的计算。然而,组成 HTML5 开发的所有技术在运行时都不依赖于时间轴的使用。因此,计算网页的每秒帧数或 FPS 值并不总是衡量 HTML5 项目性能的准确指标。

提示

我们在章节代码示例中找到的 CSS 3D 变换示例包括使用一个名为Stats.js的优秀 JavaScript 代码,用于监视每秒帧数以及毫秒数。Stats.js 是一个开源项目,可以在github.com/mrdoob/stats.js找到。

为移动设备开发

HTML5 受到现代移动浏览器的全面支持,这是 HTML5 流行的另一个推动力。随着 Flash Player 在所有移动平台上的失去,使用 HTML5 传递内容的使用率达到了历史最高水平,并且每天都在增长。应用程序、框架和模板,如 jQuery Mobile (jquerymobile.com)、Phone Gap (phonegap.com)、Appcelerator (www.appcelerator.com)和 Mobile Boilerplate (html5boilerplate.com/html5boilerplate.com/dist/mobile),所有这些都将在第五章中详细介绍,一次编码,到处发布,都是专门为帮助网页开发人员构建专门针对移动视图的网页内容而构建的。CSS 可以以响应的方式设置,以便根据用户查看内容的设备和视口配置以优化的格式显示相同的页面内容。

响应式布局

“响应式布局”这个术语似乎在 HTML5 开发日益普及的情况下被更频繁地使用。对一些人来说,它已经成为定义良好的 HTML5 开发的关键特性之一的关键词。无论术语如何使用,归根结底,当我们在网页开发中提到“响应式布局”时,我们指的是使用现代网页开发技术来使同一页面内容能够在用户设备和视图分辨率上进行布局和内容的过渡调整。换句话说,确保您的页面内容以优化的方式设置,适用于所有视图分辨率,并且能够在任何一个布局之间进行过渡,而无需刷新页面内容。

CSS 媒体查询

创建响应式布局时最重要的资产之一是使用 CSS 媒体查询。媒体查询允许您根据用户的设备、分辨率、旋转等目标特定的 CSS 样式。尽可能了解加载 HTML 文档的设备和软件将使您不仅能够指定特定设备和浏览器如何显示内容,还可以使您的代码监视查看方法的实时更改。例如,以下媒体查询示例根据设备旋转更改背景颜色:

@media screen and (orientation:portrait) {
  background-color: #FF0000;
}

@media screen and (orientation:landscape) {
  background-color: #0000FF;
}

CSS 媒体查询属性列表很短,但在创建条件时了解可用的内容非常重要。因此,让我们快速回顾一下在编写媒体查询时可以使用的属性:

  • width:描述目标显示区域的宽度。

  • height:描述目标显示区域的高度。

  • device-width:描述输出设备的渲染显示的宽度。

  • device-height:描述输出设备的渲染显示的高度。

  • orientation:当高度媒体特征的值大于或等于宽度媒体特征的值时,为portrait。否则,方向为landscape

  • aspect-ratio:定义为width媒体特征值与height媒体特征值的比率。

  • device-aspect-ratio:定义为device-width媒体特征值与device-height媒体特征值的比率。

  • color:描述输出设备颜色组件的每位数。如果设备不是彩色设备,则该值为零。

  • color-index:描述输出设备颜色查找表中的条目数。如果设备不使用颜色查找表,则该值为零。

  • monochrome:描述单色帧缓冲区中每像素的位数。如果设备不是单色设备,则输出设备值将为0

  • resolution:描述输出设备的分辨率,即像素的密度。在查询具有非方形像素的设备时,在min-resolution查询中,最不密集的维度必须与指定值进行比较,在max-resolution查询中,最密集的维度必须进行比较。没有“min-”或“max-”前缀的resolution查询永远不匹配具有非方形像素的设备。

  • scan:描述“tv”输出设备的扫描过程。

  • grid:用于查询输出设备是否为网格或位图。如果输出设备是基于网格的(例如“tty”终端或仅具有一个固定字体的手机显示器),则值将为1。否则,值将为0

音频和视频播放控制

正如我们在上一章中看到的,将音频和视频资产与基本控件集成到 HTML5 文档中非常容易。但是,如果您打算以除了直接视频播放元素之外的其他形式使用多媒体,您需要了解用于自定义播放代码集成的可用属性。

预加载

默认情况下,在 HTML5 文档中显示音频或视频元素时,其中声明的源资产将被预加载,以便在用户启动播放器时进行即时播放。资产将仅在浏览器认为必要的情况下进行预加载,以实现流畅的不间断播放。要覆盖此设置,我们可以在音频元素中使用preload参数来声明用户查看页面时希望预加载的内容。

preload参数设置为auto将在页面加载时预加载整个音频,并且可能是用户在页面加载后某个时刻几乎肯定会观看的任何音频的有用补充。使用设置了preload参数后,我们的音频元素将如下所示:

<audio controls preload="all">
  <source src="img/my-audio .mp3" type="audio/mpeg">
  <source src="img/my-audio.ogg" type="audio/ogg">
  Your browser does not support the audio element.
</audio>

除了预加载所有内容,我们还可以通过设置preload="none"而不是auto来完全不预加载任何内容。从音频中删除预加载将允许用户在不需要进行不必要的音频下载的情况下浏览您的页面,但会导致用户启动音频播放后加载时间更长。最后,我们还可以通过设置preload="metadata"在预加载时仅加载音频元数据。这将允许音频元素查看它即将加载的数据,这在动态添加音频到音频元素并在尝试播放之前需要验证其是否适合播放时非常有用。

自动播放

如第二章准备战斗中所述,将autoplay设置附加到视频元素后,视频将在能够播放而无需停止视频进行进一步缓冲时开始播放。与 HTML 中许多其他元素参数不同,autoplay参数不需要值。因此,只需将autoplay附加到元素即可完成任务。值得注意的是,几乎所有移动浏览器加载时都会忽略autoplay设置。移动浏览器倾向于忽略此设置,以节省无线连接的带宽。

循环

将循环设置附加到音频元素后,视频将在每次完成时重新开始。与autoplay参数一样,loop参数不需要值。如果您只想让视频循环播放特定次数,可以使用设置loop参数并在必要时删除它,或者从 JavaScript 控制整个播放以控制视频元素中的循环计数而不使用循环参数。

音效

在特定时刻播放音效可以通过使用 HTML5 音频元素和 JavaScript 的多种方式来实现。在其最简单的形式中,播放音效可以实现为以下代码示例所示的方式。

<body>
  <audio src="img/ping.mp3" preload="auto" id="audio-ping">
  </audio>

  <script>
    window.addEventListener("load", init, false);

    function init() {
      window.addEventListener(
          'mousedown', 
          onMouseDown, 
          false
      );
    }

    function onMouseDown(e) {
      document.getElementById('audio-ping').play();
    }
  </script>
</body>

当音频元素在 HTML 文档主体内创建时,我们设置preload="auto",这将确保音频尽快完全预加载。我们这样做是为了在需要音效时没有延迟。音频元素还被赋予一个 ID,以便在 JavaScript 中引用。通过窗口加载事件监听器,我们等待页面加载,然后对浏览器窗口中的任何mousedown事件应用事件监听器。当这发生时,我们通过 ID 选择我们的音频元素,并调用内置的play()方法,从而在每次单击浏览器窗口时播放音频。

媒体播放操作

除了前面示例中的play()方法外,JavaScript 还可以直接控制音频和视频元素的许多其他方面。如下例所示,音频音量可以设置为01之间的值。

document.getElementById('audio-ping').volume = 0.5; // Set the volume to 50%

我们还可以利用其中的以下公开对象来收集元素的所有统计信息:

var media = document.getElementById('audio-ping');
media.seekable.start(); // Start time (seconds)
media.seekable.end(); 	  // End time (seconds)
media.currentTime = 20; // Seeks playback to 20 seconds
// Total amount of seconds the playback has displayed
media.played.end();

使用文件 API 读取本地文件

将 HTML5 内容带入更类似应用程序的功能集的另一个功能是添加文件 API。用户现在可以以比以往更深入的方式与本地内容进行交互。用户可以以传统的 HTML 表单方式导入文件,或者现在只需将文件拖放到 HTML5 布局中指定的拖放区域。一旦用户向网页提交了文件,您的 JavaScript 文件 API 使用可以允许您在将文件提交到服务器之前查看、编辑和操作文件数据。我们将在接下来的章节中深入探讨文件 API 的许多示例中。

Web Workers

在过去,当执行处理器密集型 JavaScript 时,浏览器经常会在处理完成并返回结果之前冻结。随着 HTML5 Web Workers 的出现,您现在可以将处理器密集型的 JavaScript 代码作为后台进程来执行,这不会影响活动文档的性能。用户将能够在等待 Web Worker 在后台完成其工作时继续使用网站。

要轻松检查用户的浏览器是否支持 HTML5 Web Workers,我们可以检查Worker对象的类型是否未定义或不是:

if(typeof(Worker) == "undefined") {
  // This browser doesn't support Web Workers...
}

根据浏览器是否支持 Web Workers 的使用,我们可以随时通过实例化一个新的Worker对象和其 JavaScript 源的引用来轻松创建一个新的 worker:

worker = new Worker("worker.js");

在前面的示例中,我们创建了一个新的 worker,并将其引用到worker.js文件中的源代码。下一步是为当 worker 发布更新时创建事件侦听器。为了创建这个侦听器,我们在onmessage属性上创建一个函数,并从event.data属性中检索消息:

// Create an event listener for worker updates.
worker.onmessage = function (event) {
  console.log('New worker event - ' + event.data);
};

worker 中的代码可以是任何内容,尽管最合理的做法是使其成为通常会在短时间内冻结浏览器的内容。无论您的 worker 正在做什么,为了使回调到您的代码生效,您将使用内置的postMessage函数:

postMessage(YOUR_DATA);

提示

由于您的 Web Worker 代码位于外部文件中,因此它将无法访问其 JavaScript 源中的 window、document 或 parent 对象。

在本章的示例文件中,以及在我们开始构建更大的 JavaScript 项目时,您将在即将到来的章节中找到更多 Web Workers 的用法。

WebSockets

向您的网页添加服务器端通信以启用诸如多用户交互或推送通知等功能,随着 WebSockets 的出现越来越受欢迎。简而言之,当您需要服务器与客户端进行通信而不需要客户端的请求时,WebSockets 填补了这一空白。

在构建 Flash 应用程序时,通常会使用诸如实时媒体流协议RTMFP)或 SmartFoxServer(www.smartfoxserver.com)等技术和框架,以实现基于服务器的多用户应用程序。现在,通过使用 WebSockets,这个概念已经可以实现,这真正证明了 HTML 规范的发展已经走了很远。

在即将到来的章节中,我们将继续深入研究 WebSockets 的更多示例,以及一些其他有趣的方法,用于连接查看您的 HTML5 内容的用户,例如 Socket.io(socket.io)、Node.js(nodejs.org)和 Google V8(code.google.com/p/v8)。

Canvas 元素

在没有至少提及 HTML5 Canvas 元素的情况下,我们无法完成本章。Canvas 允许开发人员使用 Canvas 2D 绘图 API 将图形实时绘制到一个可控制的空白区域中。从 Flash 开发人员的角度来看,理解 Canvas 元素的功能集最简单的方法是,它使用类似于 ActionScript 3 绘图和图形 API 的功能,在 HTML 布局中的一个空白区域中,这与 Flash 舞台非常相似。

为了更好地理解这一切的意义,让我们使用 Canvas 创建一个简单的绘图应用程序。首先,我们需要将 Canvas 元素附加到 HTML 文档的主体中。元素标签内不需要包含任何内容,因为只有在用户尝试从不支持 Canvas 元素的浏览器中查看此内容时才能看到它:

<body>
  <canvas id="example" width="640" height="480" style="border:1px  solid #000000;">
    Your browser does not support the HTML5 Canvas element.
  </canvas>
</body>

在这个例子中,Canvas 中添加了两个重要的内容,它们是元素 ID,将在接下来的步骤中在 JavaScript 中使用,以及宽度和高度声明。如果没有在元素中设置宽度和高度,大多数浏览器将以 300px x 150px 渲染 Canvas。为了帮助我们开发这个应用程序,在 Canvas 中添加了 1px 的边框,以便我们准确地看到它在浏览器窗口中的边界。最后,正如前面提到的,Canvas 元素内部的内容只有在浏览器中不支持该元素时才会显示。如果应用程序也被编写为 Flash 应用程序,Flash SWF 的对象嵌入可以用来替代我们在这个例子中使用的文本警告。

下一步是在 JavaScript 中设置对我们的 Canvas 及其 2D 上下文的引用,由于我们在元素上设置了一个 ID,我们可以在我们的代码中轻松地引用它到一个变量中:

var canvas, context; // Variables to hold Canvas references

window.addEventListener("load", init, false);

function init() {
  // Set the canvas reference to a JavaScript variable.
  canvas = document.getElementById('example');

  // Get the 2D canvas context to allow for 2D Drawing API integration
   context = canvas.getContext('2d');
      if(!context) {
        alert("Failed to get canvas context!");
    return;
}

  canvas.addEventListener('mousemove', onMouseMove, false);
  canvas.addEventListener('mousedown', onMouseDown, false);
  canvas.addEventListener('mouseup', onMouseUp, false);
}

function onMouseDown(e) {
  isDrawing = true;
}
function onMouseUp(e) {
  isDrawing = false;
}

通过引用我们的 Canvas 并设置鼠标事件监听器来监视用户何时按下鼠标按钮,让我们通过编写我们的onMouseMove函数来完成这个例子,以在isDrawing变量为true时画一条线:

function onMouseMove(e) {
    var x, y;

  if (e.pageX || e.pageY) {
    x = e.pageX;
    y = e.pageY;
  } else if (e.clientX || e.clientY) {
    x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
    y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
  }

    if(!isDrawing) {
    // Since the mouse isn't down, just move 
// the context to the latest mouse position.
        context.beginPath();
        context.moveTo(x, y);
  } else {
        // The mouse is down so draw the line to 
// the current mouse position.
        context.lineTo(x, y);
        context.stroke();
  }
}

如果您注意到,我们onMouseMove函数中的初始代码取自我们的 3D 变换示例,并允许我们在不同的现代浏览器上读取鼠标 X 和 Y 位置。如果您曾在 ActionScript 3 中使用过绘图 API,那么跟随鼠标位置查找的条件应该看起来有点熟悉。假设鼠标按下,我们会画一条线到当前鼠标位置,并设置默认的描边。在这种情况下,默认的描边是 1px 的黑色实线。当鼠标未按下时,我们只是将上下文移动到鼠标位置,但不画任何线条。这样可以让我们不断重置并等待开始新的线条。在浏览器中测试时,这个例子会看起来像这样:

Canvas 元素

这个例子只是开发人员可以使用的开始,但希望能让您了解它是如何工作的。我们将在下一章继续研究 Canvas 元素。

Stage3D 与 WebGL

在结束本章之前,应该提到一些关于 WebGL 的可用性以及它与 Adobe Flash 中的 Stage3D 的相似之处和不同之处。WebGL 是一个跨平台的 Web 标准,允许开发人员创建和操作低级 3D 图形,将无插件的 3D 开发带到 Web 上。WebGL 可以在所有现代浏览器中实现和查看,唯一的例外是 Internet Explorer。

提示

请注意,微软似乎计划发布支持 WebGL 的 Internet Explorer 11。

WebGL 和 Stage 3D 的关键方面是它们都支持使用硬件加速。这可以在设备和浏览器上查看内容时大大提高图形处理负载的性能,前提是有适当的支持。尽管本书没有足够的空间深入研究 WebGL 的使用,但我们将在第六章中查看一些支持和使用它的框架和库,HTML5 框架和库

提示

要了解更多信息并查看您当前的网络浏览器是否支持使用 WebGL,请访问get.webgl.org,WebGL 公共维基(www.khronos.org/webgl/wiki),或在www.khronos.org/webgl/wiki/Demo_Repository上查看一些有趣的示例。

摘要

虽然在本章的课程中我们已经涵盖了许多有趣的功能,但在接下来的章节中,我们还将看到许多非常酷的 HTML5 新增功能。到目前为止,我们所涵盖的关键思想是 Flash 和 HTML5 之间的特性关系在某种程度上是相似的,但又有所不同。了解什么样的技术能够让你以最佳方式开发应用程序是任何优秀开发者的关键特质。在下一章中,我们将深入探讨 JavaScript 的使用以及在面向对象编程时与 ActionScript 3 的关系。

第四章:使用 HTML5 构建健壮的应用程序

自从 ActionScript 3 出现以来,Flash 开发人员已经习惯于以面向对象编程(OOP)范式进行开发。当转换到 JavaScript 时,许多具有 OOP 经验的开发人员可能会对 JavaScript 用于实现相同功能的语法感到不满。然而,对语法的误解可能导致对功能的误解。因此,在本章中,我们将介绍 JavaScript OOP、事件和事件监听器的使用。

在本章中,我们将涵盖以下内容:

  • JavaScript 类结构、用法和语法

  • 对象继承

  • 类构造函数

  • 辅助开发的工具和框架

  • 创建自定义事件和监听器

编写面向对象的 JavaScript

2006 年,当 Adobe 发布带有 ActionScript 3 支持的 Flash Player 9 时,Flash 开发社区看到了他们开发 Flash 应用程序的一次重大范式转变。在使用 ActionScript 3 之前,开发人员被要求用 ActionScript 2 编写他们的应用程序,这主要用作脚本编程语言。ActionScript 3 被设计为一个真正的面向对象编程语言,具有严格的类型,允许以可重用、更受控制的方式编写代码。

通过使用 ActionScript 3 和为 Flash Player 9 发布的新 ActionScript 虚拟机编译器,Flash 应用程序中的代码不仅以 OOP 结构编写,而且还可以比以前的传统 Flash 应用程序运行快 10 倍。随着时间的推移,Flash 开发人员已经习惯于编写适当的 OOP 结构化代码,这使他们能够轻松地将他们的编程技能转移到其他语言,如 Java、C++或 C#。

在 JavaScript 中使用 OOP 范式起初有点难以理解,因此让我们创建一个在 ActionScript 3 中的示例类结构,并直接将其移植到 JavaScript 中,以查看视觉语法的差异。在这个示例中,我们将创建一个名为Player的示例类,它将模拟游戏中角色的基本功能。我们将使用Player类来创建我们游戏中所需的任意数量的玩家,并通过构造函数、getter、setter 和公共变量来改变它们的属性,而不是根据功能为我们的游戏中的每个玩家设置单独的代码。为了更好地理解这个概念,请考虑以下代码示例:

package {
public class Player {
    // Private Variables
    private var lives:int; // How many lives our player has.
    private var xPosition:int; // The players X position.
    private var yPosition:int; // The players Y position.

    // Public Variables
    public var name:String = 'John'; // The players name.

    /**
     * The Player constructor. 
     * This function is called when a new Player is     * instantiated.
     *
     * @param playerName: The name to give to our player.
     * @param lives: How many lives to give our player.
     */
    public function Player(playerName:String, playerLives:int = 5):void {
        // Update the player variables with 
        // the supplied parameters.
        name = playerName;
        lives = playerLives;
    }

    /**
     * Return the current amount of lives the player has.
     */
    public function get lives():int {
        return lives;
    }

    /**
     * Move the players x and y position.
     *
     * @param	x: The new X position to move the player to.
     * @param	y: The new Y position to move the player to.
     */
    public function move(x:int, y:int):void {
        // Update the player position variables.
        xPosition = x;
        yPosition = y;

        updatePosition();
    }

    /**
     * The would be the function that actually moves the
     * displayed player display object on the stage. 
     * This would get called every time a players X and Y   
     * position values are updated.
     */
    private function updatePosition():void {
        // Code to update the players display object...
    }
  }
}

尽管为了示例目的而简化,这个类示例对于任何有 ActionScript 3 编码经验的开发人员来说应该看起来非常熟悉。在我们的 Flash 项目中声明了这个类,我们可以随时导入它并实例化它。在对象内部声明了可以被父对象调用以操纵特定Player对象数据的属性和方法,这是任何类的典型特征。当你准备在你的应用程序中添加一个新的玩家时,我们可以用以下 ActionScript 3 代码实例化一个新对象:

var player:Player = new Player('John', 10);

通过将所需的值附加到构造函数中,我们为我们的玩家提供了一个独特的名称,以及这个玩家将拥有多少生命的初始值。现在我们有了我们的新玩家,在我们的想象游戏中可以操纵它。

现在让我们看一下在 JavaScript 中重新编写的相同类:

function Player(playerName, playerLives) {
  // Private variables
  var lives = playerLives;
  var xPosition = 0;
  var yPosition = 0;

  // Public variables
  this.name = playerName;

  // Return the current amount of lives the player has.
  this.lives = function() {
    return lives;
  }

  /**
   * Move the players x and y position.
   *
   * @param	x: The new X position to move the player to.
   * @param	y: The new Y position to move the player to.
   */
  this.move = function(x, y) {
    xPosition = x;
    yPosition = y;

    updatePosition();
  }

  /**
   * The would be the function that actually moves the displayed
   * player display object on the stage. This would get called 
   * every time a players X and Y position values are updated.
   */
  function updatePosition() {
    //
  }
}

乍一看,人们会发现没有类声明,这是许多其他编程语言中熟悉的。相反,在 JavaScript 中创建“类”时,使用函数来模拟传统类结构的使用。包声明也从等式中移除,JavaScript 的包含也附加到 HTML 文档中,渲染网页。在逻辑上,可以将所有 JavaScript 类分开放在单独的文件中以便于开发。但是,当将大量 JavaScript 发布到公共托管环境时,为了节省对 Web 服务器的数据请求,应尽可能合并 JavaScript。我们将在第十章中更深入地探讨为生产环境准备 HTML5 项目,准备发布

提示

此时必须注意一点。由于缺乏严格的类型和许多其他特定的类结构规则,同样的 JavaScript 功能可以用多种方式编写。在本章的过程中,我已经尽可能地编写了示例,以便让我们更好地检查语法。

类语法

在初始的 JavaScript 类示例中,我们比较了 ActionScript 3 的类结构与 JavaScript 在创建面向对象代码时的差异。该示例使用了 JavaScript 中创建类的更传统方法之一,即使用函数代替典型的类声明。

函数

到目前为止,我们在代码示例中使用的许多 JavaScript 函数示例已经展示了定义新函数的不同方式。正如我刚才提到的,根据开发人员的舒适程度,他们可能选择以以下一种方式之一在 JavaScript 中编写函数:

function isAlive1() { return true; }
var isAlive2 = function() { return true; };
window.isAlive3 = function() { return true; };

console.log(isAlive1());
console.log(isAlive2());
console.log(isAlive3());

在前面的示例中,三个console.log输出中的每一个都将产生正确的布尔返回值true。值得注意的是,函数定义和函数使用的顺序不需要像前面的代码示例中所示的那样。以下示例将产生完全相同的结果:

console.log(isAlive1());

function isAlive1() { return true; }
var isAlive2 = function() { return true; };
window.isAlive3 = function() { return true; };

console.log(isAlive2());
console.log(isAlive3());

尝试调用未定义的函数时,将会触发ReferenceError错误,并在 JavaScript 控制台中显示。

函数

尽管我们的应用程序可能会继续运行,尽管出现此运行时错误,但这通常意味着我们的代码存在问题。我们可以使用简单的条件来处理代码中的此问题,而不是将默认的ReferenceError错误发送到 JavaScript 控制台。

try {
  console.log(isAlive4());
} catch(error) {
  console.log('Failed to call isAlive4() - ' + error);
  // Run alternate code here...
}

变量范围

正确理解 JavaScript 中变量范围是理解语言核心方面的关键步骤。范围是指变量在代码的其他部分创建时对其可访问性。变量可以根据用于声明它们的语法进行实例化和引用。JavaScript 利用许多人所说的函数范围,在其中所有变量和函数的范围都相同。在范围链的顶部是全局变量和函数。与所有编程范式一样,全局意味着一切,全局变量或函数可以在代码的任何其他地方访问。

var name = 'John';

function getName() {
  return name;
}

// Both calls return the name as it is accessible globally.
console.log(name);
console.log(getName());

此代码演示了全局变量的使用。由于变量名在使用它的函数范围之上声明,因此运行此代码时不会出现错误。但是,变量也可以在函数内部局部声明。

function getName() {
  var name = 'John';
  return name;
}

console.log(name);     // Error
console.log(getName());   // Success

由于 Player 的名称(在这种情况下是 John)是在getName函数内创建的,因此无法全局访问该函数之外的任何代码。全局和局部变量的概念虽然简单,但当你开始考虑严格类型的缺乏以及全局和局部变量的确切变量名称时,你可能会感到头晕。不用担心,这是 JavaScript 开发人员在典型学习曲线问题中遇到的另一个问题。但正如之前提到的,掌握 JavaScript 中的变量作用域是每个优秀的 HTML5 开发人员必须具备的基本技能之一。

为了演示一些问题,并允许您完全查看作用域的运行情况,让我们回顾以下示例:

// We will start with a globally scoped variable which is accessible by everything.
var alpha = 'a';

// Global scope example.
function a() {
    console.log(alpha); // Reference the global alpha variable.
}

// Local scope using a supplied variable.
function b(alpha) {
    console.log(alpha); // Reference the supplied alpha variable.
}

// Local scope using a variable created within the function.
function c() {
  var alpha = 'c';
  console.log(alpha);
}

// Update the global object property.
function d() {
    this.alpha = 'd'; // Create an internal object property.
}

function e() {
    var n = 'e';

    this.alpha = function() {
        console.log(n);
    }
};

function f() {};

a();    // A
b('b'); // B
c();    // C

console.log(new d().alpha); // D

var e = new e().alpha();    // E

f.prototype.alpha = 'f';    
console.log(new f().alpha); // F

尽管上面的例子是一个不合逻辑的输出字符 A 到 F 的方式,但它展示了许多变量和函数可以被操纵以访问应用程序作用域链中特定区域数据的方式。

公共和私有变量和函数

理解 JavaScript 中的变量和函数的下一步是学习如何创建和利用公共和私有成员。与 ActionScript 3 不同,变量不是私有或公共类型的,因此语法稍微难以理解。

局部或私有变量

通过在对象内创建变量时使用var关键字声明私有(或局部)变量。结果变量只能在特定对象内部访问,并且需要 getter 和 setter 方法来允许外部操作。这种变量声明方式类似于在 ActionScript 3 中创建变量时使用private关键字。面向对象编程开发的一个一般准则是尽可能使用私有变量,因为这将大大减少变量损坏或其他错误使用的问题。

function Example() {
  var foobar = 'abc'; // Only accessible within the Example scope.
}

公共变量

通过使用this.myVariableName语法声明公共变量或属性。与在 ActionScript 3 中创建变量时使用public关键字类似,JavaScript 中的公共变量不仅可以在对象作用域链内的代码中访问,还可以在创建它的对象之外访问。

function Example() {
  this.foobar = 'abc'; // Accessible outside the Example scope.
}

私有函数

只能在对象作用域内访问的私有函数可以用几种不同的方式编写。

function Example() {
  function TestOne() {
    return true;
  }

  var testTwo = function() {
    return true;
  };
}

先前演示的两个例子都产生了一个私有函数。任何尝试从对象作用域之外调用该函数的尝试都会导致运行时错误。

公共函数

公共或特权函数,像公共变量一样,可以从创建它们的对象之外访问,并使用this.myFunctionName = function() {...}语法创建。

function Example() {
  this.test = function() {
    return true;
  }
}

原型

JavaScript 对象语法中更令人困惑的一个方面是原型对象的使用。正如我们在此之前的示例和解释中所看到的,JavaScript 中的一切都是对象,而每个 JavaScript 对象中都有一个原型属性。

提示

如果您在 ActionScript 1 或 ActionScript 2 的时代使用 Flash,您可能熟悉原型对象的概念(help.adobe.com/en_US/as2/reference/flashlite/WS5b3ccc516d4fbf351e63e3d118ccf9c47f-7ec2.html)。这个对象在两种编程语言中都被使用,但在 ActionScript 3 发布时被放弃。

为了看到这个概念的运行情况,让我们从简单开始,逐步向更复杂的原型对象用法前进。我们将首先查看一个新空对象的原型对象。

var player = {}; // Create a new empty object.
console.log(Object.getPrototypeOf(player)); // Return the prototype object.

在 Web 浏览器中运行此代码将导致 JavaScript 日志,其结果非常接近以下代码,如果不是相同的话:

Object
__defineGetter__: function __defineGetter__() { [native code] }
__defineSetter__: function __defineSetter__() { [native code] }
__lookupGetter__: function __lookupGetter__() { [native code] }
__lookupSetter__: function __lookupSetter__() { [native code] }
constructor: function Object() { [native code] }
hasOwnProperty: function hasOwnProperty() { [native code] }
isPrototypeOf: function isPrototypeOf() { [native code] }
propertyIsEnumerable: function propertyIsEnumerable() { [native code] }
toLocaleString: function toLocaleString() { [native code] }
toString: function toString() { [native code] }
valueOf: function valueOf() { [native code] }

这个输出可以让我们更加了解原型对象的真正含义。正如你已经从之前的 ActionScript 3 开发中了解到的,对象变量类型带有许多内置方法来操作其中的内容。查看我们的Player对象的原型的输出,你可能会注意到列出了许多这些熟悉的方法和属性。

我们可以使用原型对象随时向对象附加新属性。考虑以下示例,我们创建了一个简化的Player对象,并通过原型对象而不是直接在对象本身内部附加移动功能:

function Player(name) {
  this.name = name;
  this.lives = 0;
  this.xPosition = 0;
  this.yPosition = 0;
}

Player.prototype.move = function(x, y) {
  this.xPosition = x;
  this.yPosition = y;
}

相同的概念可以用于覆盖默认对象行为。通过修改原型对象的移动属性,对移动方法的任何进一步调用都将导致新附加的行为。

function Player(name) {
  this.name = name;
  this.lives = 0;
  this.xPosition = 0;
  this.yPosition = 0;

  this.move = function(x, y) {
    this.xPosition = x;
    this.yPosition = y;
  }
}

Player.prototype.move = function(x, y) {
  this.xPosition = x + 5;
  this.yPosition = y + 5;
}

请记住,这些更改是针对对象本身而不是实例。因此,这些更改将影响到每个“Player”实例,如下所示:

function Player(name) {
  this.name = name;
  this.lives = 0;
  this.xPosition = 0;
  this.yPosition = 0;

  this.move = function(x, y) {
    this.xPosition = x;
    this.yPosition = y;
  }
}

function init() {
  var susan = Player('Susan');
  var john = Player('John');	

  // Modify the move function for ALL Player instances.
  Player.prototype.move = function(x, y) {
    this.xPosition = x + 5;
    this.yPosition = y + 5;
  }
}

那么为什么这很重要呢?根据你的应用程序是如何构建的,通过利用原型对象,你可以直接向对象附加共享代码,而无需多次编写相同的代码。代码越少意味着内存占用越少,这将使你在维护项目时更加轻松。

实例类型

当你开始在这些示例和自己的 JavaScript 代码中浮动时,添加检查和条件以获取实例类型将证明是一个重要的补充。缺乏严格的类型将要求你编写和维护干净和优化的代码,以使你的应用程序正常工作。考虑一些以下代码片段,以获取有关我们的Player对象实例类型的信息:

// Create a player instance
var player = new Player('John');

// Check the type of player.
console.log(typeof(player));

// Output the constructor data from the player.
console.log(player.constructor);

// Check if the player is a Object - returns a Boolean.
console.log(player instanceof Object);

console.log方法的每次调用都执行查找Player对象实例的不同方式。当我们在 Web 浏览器中运行此代码并打开开发者控制台时,我们会得到以下输出:

实例类型

初始的object输出是对Player对象的typeof()函数调用的结果。第二个输出是调用Player对象的构造函数时返回的代码块。最后,条件控制台调用(console.log(player instanceof Object))在控制台中返回布尔值,让我们知道instanceof条件为真。

对象字面量

字面量只是在 JavaScript 中定义数组和对象的一种更简短的方法。我们已经使用以下语法在 JavaScript 中创建新对象:

var player = new Object();
var player= Object.create(null);
var player = {};

我们可以通过在变量声明中直接创建对象的内部来进一步扩展前面的语法。

var player = {
  name: "John",
  lives: 5,
  xPosition: 0,
  yPosition: 0,
  move: function(x, y) {
    xPosition = x;
    yPosition = y;

    // Update the position display...
  }
}

然而,这种Object语法在重用方面存在重大问题,因为它的实例在创建时已经存在。由于不需要实例化对象,我们可以通过引用其属性来继续我们的代码。

  player.name = "Susan";
  player.move(5, 5);

构造函数

正如我们在之前的一些示例中所看到的,与 ActionScript 3 相比,对象构造函数的语法与通常使用的有些不同。由于类只是函数,我们可以直接将通常在构造函数中找到的代码放在类内部。

通过调用对象的constructor属性,你可以轻松查找最初创建对象的函数的引用。

console.log(player.constructor);

当尝试查找内置对象类型(如数组、字符串或日期)的构造函数时,输出将隐藏内部代码,并显示使用原生代码的警告。

var test = Array();
console.log(test.constructor);

这将在浏览器的 JavaScript 控制台中生成以下内容:

function Array() { [native code] }

继承

通过在现有对象上使用call()方法,我们可以将对this的引用从对象本身更新到代码中的其他位置。

function Player(name) {
  this.name = name;
  this.age = 20;
}

function John() {
  Player.call(this, 'John');

  this.age += 35;
}

function Jill() {
  this.age += 20;
}
Jill.prototype = new Player('Jill');

function init() {
  var john = new John();
  console.log(john.name + ' is ' + john.age);

  var jill = new Jill();
  console.log(jill.name + ' is ' + jill.age);
}
window.addEventListener("load", init);

此示例演示了在新的John对象中简单继承Player对象。这使我们能够访问Player中的内部值,并在John类中外部使用它们。在这个例子中,我们让 John 比默认的Player大 35 岁。我们还可以使用原型对象来声明对象继承。

function Player(name) {
  this.name = name;
  this.age = 20;
}

function Jill() {
  this.age += 20;
}
Jill.prototype = new Player('Jill');

function init() {
  var jill = new Jill();
  console.log(jill.name + ' is ' + jill.age);
}
window.addEventListener("load", init);

创建新的jill对象时,它继承了来自Player的所有基本属性和函数,因为原型引用已经声明了对象继承。在页面加载时,此示例的输出将显示以下内容:

John is 55
Jill is 40

列出对象属性

在任何时候,您都可以使用Object.getOwnPropertyNames()方法查找对象中可用的属性。在考虑私有和公共语法的情况下,让我们回顾以下示例,查看查找对象属性时的输出:

function Player() {
  // Private variables
  var _this = this; 	// Reference to this object
  var lives;
  var positionX = 0;
  var positionY = 0;
  var playerElement;

  // Public variables
  this.name = '';	// The players name.
  this.age = 10;	// The players age (default = 10).

  this.move = function(x, y) {
    positionX = x;
    positionY = y;

    // Move the player...
  };

  function blink() {
    // Blink players eyes...
  }
}

var player = new Player();
var properties = Object.getOwnPropertyNames(player);

console.log(properties);

再次,考虑到公共和私有变量,只有使用关键字this创建的变量才能在对象范围之外可见。执行前面的代码示例将提供以下属性名称数组作为输出:

["name", "age", "move"]

使 JavaScript 中的面向对象编程更容易

对于许多开发人员来说,开发面向对象 JavaScript 所需的所有这些变通和语法更改似乎就像是一长串“黑客”清单。无论您对此事的看法如何,都有许多选项可用于帮助构建具有更传统开发流程的大规模基于 JavaScript 的应用程序。在第六章中,HTML5 框架和库,我们将深入研究许多流行的 JavaScript 库和框架,以及它们如何不仅可以加快您的开发时间,还可以在所有现代浏览器和设备上提供更可靠的最终结果。

JavaScript 事件

与大多数语言一样,事件是真正使应用程序活跃起来的。如果没有事件的使用,程序通常只会从开始到结束运行,而不需要人类交互。JavaScript 事件建立在异步事件模型的基础上。事件可以使用回调结构,例如在 ActionScript 中,来执行一旦事件被触发就执行代码,而不是不断地检查是否满足条件。这方面的一个很好的例子,你可能已经在许多其他示例中看到过:

window.addEventListener("load", init, false);

我们在window对象上使用此事件监听器,以便让我们的代码确定window对象何时完成加载,以便我们可以调用我们的初始方法来开始 JavaScript 操作我们的文档。

键盘和鼠标事件

鼠标事件是几乎所有 JavaScript 项目都包含的核心元素之一。虽然我们将在整本书中的示例中使用这些事件,但值得回顾一下不仅是鼠标,还有键盘和触摸事件的整合列表,这样你就可以更好地了解可供您用于输入事件监听的内容。不同的键盘和鼠标事件列举如下:

  • mousedown:鼠标按钮已被按下

  • mouseup:鼠标按钮已被释放

  • click:鼠标按钮已被单击

  • dblclick:鼠标按钮已被双击

  • contextmenu:已触发某个操作以显示上下文菜单

  • scrolling:上下文已在滚动轴上移动

  • keydown:键盘键已被按下

  • keypress:键盘键已被按下并释放

  • keyup:键盘键已被释放

触摸事件

触摸事件支持正逐渐接近一套标准;但是,您会注意到根据您正在测试的设备和浏览器的支持和功能集的差异。值得注意的是,就像在触摸界面上运行的 Flash 应用程序一样,您可以使用鼠标事件而没有问题,并且仍然支持实际鼠标的使用。但是,由于鼠标一次只能点击一个点,如果您的应用程序需要多点触控支持,您将不得不开始使用触摸事件结构。在接下来的两章中,我们将进一步探讨在触摸设备上使用 HTML 的方式以及开发的区别。支持的触摸事件如下:

  • touchstart:用户已开始触摸元素

  • touchmove:用户自 touchstart 以来移动了触摸位置

  • touchend:用户已经从元素中移开手指

自定义事件

拥有创建自定义事件并将其分派给等待事件监听器的能力,可以进一步扩展应用程序的面向对象编程语法和结构。通常,作为使用 ActionScript 3 的 Flash 开发人员,您会利用flash events类来创建自定义事件,以便从一个类到另一个类创建通信流。在其最简单的形式中,它看起来像下面这样:

import flash.events.Event;
import flash.events.EventDispatcher;

var myEvent:Event = new Event("myEvent", false);
dispatchEvent(myEvent);

与 ActionScript 3 中的大多数功能一样,为了优化应用程序的文件大小和执行速度,在使用扩展的内部功能时,必须直接导入包。因此,在 ActionScript 3 中创建和分派事件时,我们应该始终导入EventEventDispatcher类,之后我们可以用提供为字符串的自定义事件类型实例化一个新事件。当事件被分派时,将需要一个事件监听器来执行进一步的代码,以完成应用程序中的事件序列。我相信您非常清楚,ActionScript 3 中的典型事件监听器描述如下语法:

addEventListener("myEvent", myCustomEventHandeler, false, 0, true);

使用与所有事件监听器设置相同的 ActionScript 3 语法,自定义事件类型再次以字符串形式提供给标识符。提供的第二个参数始终是一旦此监听器触发将要调用的函数。最后三个参数控制事件控制和内存清理的冒泡和弱引用。

幸运的是,JavaScript 中自定义事件的设置和结构与一些明显的区别非常相似。考虑这个工作示例,与我们刚刚审查过的内容以及您已经了解的 ActionScript 3 中的事件相比。

function init() {
  // Create an event listener
  document.addEventListener("myEvent", myEventHandeler, false);

  // Create our custom event
  var myEvent = document.createEvent("Event");

  // initEvent(event type, allow bubbling, allow prevented)
  myEvent.initEvent("myCustomEvent", true, true);
  myEvent.customData = "We can add more data into our event easily!";
  document.dispatchEvent(myEvent);
}

function myEventHandeler(event) {
  console.log('The custom event has been dispatched - ' + event);
  console.log('And retrieve our appended data - ' + event.customData);
}

window.addEventListener("load", init);

除了缺少导入的类之外,这个事件示例在任何 Flash 开发人员眼中应该看起来非常熟悉。与本书中大多数示例一样,我们等待窗口加载一个我们到目前为止经常看到的事件监听器。在init函数中,我们首先创建我们的事件监听器。在这种情况下,我们将监听器附加到文档;但是,这可以附加到代码中的任何对象。请注意,不仅创建新事件监听器的方法完全相同(addEventListener),而且初始两个参数的语法也是相同的。提供的最后一个布尔值控制事件冒泡,我们稍后将回顾。init函数中的剩余代码包含我们的事件实例化以及该事件的分派。再次感谢 ECMAScript 的荣耀,自定义事件的语法和结构几乎是相同的。

在 JavaScript 中使用createEvent()方法创建我们的事件类型时,事件模块的可用性取决于浏览器的 DOM 级别支持。在撰写本书时,大多数浏览器都在向全面支持 DOM 级别 3 事件迈进,其中包括 UIEvent、DOMFocus、DOMActivate、Event、MouseEvent、MutationEvent、TextEvent、KeyboardEvent 和 HTMLEvent。您可以通过访问www.w3.org/TR/DOM-Level-3-Events/来查看当前可用或未来指定的 DOM 级别 3 事件的完整列表。

addEventListener方法的第三个参数指定注册的事件处理程序是否捕获指定的事件。如果事件处理程序捕获事件,那么每次事件发生在元素或其后代上时,事件处理程序都将被调用。

事件冒泡

当事件被分派时,它将遵循对象的父树直到绝对父对象,直到它被处理或停止。这种行为称为事件冒泡,它也存在于 ActionScript 3 和 JavaScript 事件结构中。在 Flash 项目中,事件将一直冒泡到主对象或在大多数情况下是舞台。在 JavaScript 中,事件将冒泡到文档对象。

在下面的例子中,我们将研究如何通过控制事件的传播来处理文档和对象上的mousedown事件:

function init() {
  // Add a mouse click listener to the document.
  document.addEventListener("mousedown", onDocumentClick, false);

  // Add a mouse click listener to the box element.
  var box = document.getElementById('box');
  box.addEventListener("mousedown", onBoxClick, false);
}

function onDocumentClick(event) {
  console.log('The document has been clicked - ' + event);
}

function onBoxClick(event) {
  console.log('The box has been clicked. - ' + event);

  // Stop this event from reaching the document object
  // and calling the document level event listener.
  event.stopPropagation();
}

window.addEventListener("load", init);

这个 JavaScript 片段为文档中的元素以及文档本身都应用了mousedown事件监听器。如果用户在页面中的元素上点击,那么两个事件监听器都会被调用,导致两个不同的处理程序处理相同的鼠标点击。虽然在某些应用程序中这可能很方便,但处理这个问题的自然方式是通过使用stopPropagation()方法停止事件冒泡的流程,从而只允许调用单个处理程序。

在 JavaScript 中处理事件传播与在 ActionScript 3 中所习惯的方式是相同的。在事件流的任何时刻,您都可以通过调用stopPropagation()stopImmediatePropagation()方法轻松地停止事件的传播。如果您熟悉 ActionScript 3 开发中的这些方法,您将知道它们在本质上几乎是相同的。唯一的区别是stopImmediatePropagation()调用会阻止事件流到达当前节点中的任何其他事件侦听器。

把所有东西放在一起

归根结底,所有这些代码示例只是定义了 JavaScript 功能的各个部分。继续使用本章节中一直在使用的Player类概念,我们对我们的示例“游戏中的玩家”类结构进行了一些润色。

function Game() {
  // An array to hold all our player objects
  var players = new Array();

  // Game Constructor
  // Reference to the game element in the document.
  var gameElement = document.getElementById('game'); 

  // Get the game element size.
  var gameElementWidth = gameElement.offsetWidth;
  var gameElementHeight = gameElement.offsetHeight;

  // Be sure to update these values if the window is 
  // to ever be resized.
  window.onresize = function() {
    console.log("NOTICE: Browser Resize: " + gameElementWidth + " x " + gameElementHeight);
    gameElementWidth = gameElement.offsetWidth;
    gameElementHeight = gameElement.offsetHeight;
  };

  // Player Class.
  function Player(name) {
    this.name = name;

    // Create the element for the player.
    var playerElement = document.createElement("div");
    playerElement.class = 'element';
    playerElement.style.position = "absolute";
    playerElement.style.left = Math.floor((Math.random() * gameElementWidth - 100) + 1) + 'px';	// Random position within viewabled bounds
    playerElement.style.top = Math.floor((Math.random() * gameElementHeight - 100) + 1) + 'px';
    playerElement.style.color = "#000000";
    playerElement.style.display = "block";
    playerElement.style.padding = "5px";
    playerElement.style.border = "1px solid #000000";
    playerElement.style.width = "100px";
    playerElement.style.height = "100px";
    playerElement.innerHTML = this.name;
    gameElement.appendChild(playerElement);

    // Move this players X and Y positions.
    this.move = function(x, y) {
      playerElement.style.left = x + "px";
      playerElement.style.top = y + "px";
    }

    // Return the current position of this player 
    // as a object.
    this.getPostion = function() {
      var position = {};
      position.x = parseInt(playerElement.style.left);
      position.y = parseInt(playerElement.style.top);
      return position;
    }
  }

  // Public Methods
  this.addNewPlayer = function(name) {
    // Check if this player name is already created
    var l = players.length;
    for(var i = 0; i < l; i++) {
      if(name == players[i].name) {
        console.log('Error: Player with the name ' + name + ' already exsits.');
        return;
      }
    }

    // Create the new player instance
    var player = new Player(name);

    // Add a reference to the global players array.
    players.push(player);
  }

  this.getPlayer = function(name) {
    // Check if this player name is already created
    var l = players.length;
    for(var i = 0; i < l; i++) {
      if(name == players[i].name) {
        return players[i];
      }
    }

    return false;
  }
}

function init() {
  // Create the game instance.
  var game = new Game();

  // For this game we will automatically create two players.
  game.addNewPlayer('Jack');
  // Try to add another Jack to view name check.
  game.addNewPlayer('Jack'); 
  game.addNewPlayer('Jill');

  document.addEventListener('keydown', onKeyDown);

  // Called when the user presses a key on the keyboard.
  function onKeyDown(event) {
    // The key that was just pressed (ID).
    var key = event.keyCode; 

    // Lookup the player to reference.
    var player = game.getPlayer('Jack'); 

    // Make sure the player exsists.
    if(player == false) return;

    // Get the players current position.
    var position = player.getPostion();

    // Forward
    if(key == 38) player.move(position.x, position.y - 10);

    // Backwards
    if(key == 40) player.move(position.x, position.y + 10);

    // Left
    if(key == 37) player.move(position.x - 10, position.y);

    // Right
    if(key == 39) player.move(position.x + 10, position.y);
  }
}

window.addEventListener("load", init);

尽管这仍然是一个简单的例子,展示了在一个单一包中可能具备的许多我们在本章中看到的特性:

把所有东西放在一起

总结

尽管本章涵盖了 JavaScript 开发中对象的语法和结构的主要概念,但实际上我们可以写一整本书来讨论这个主题。话虽如此,我们将继续在本书中涵盖这些以及更多 JavaScript 开发的高级方面。正如本章的介绍中所提到的,许多高级 JavaScript 开发中呈现的差异和范式变化可能对一些 Flash 开发人员来说有些令人生畏。然而,一旦您掌握了这些核心概念中的大部分,其他的拼图就会变得轻而易举地拼合在一起。在下一章中,我们将探讨一些可用于将现有 Flash 内容移植到 HTML5 的工具。

提示

寻找更高级的 JavaScript 语法和结构技巧?您可以查看 Packt Publishing 出版的这些书籍,Object-Oriented JavaScript by Stoyan StefanovLearning jQuery, Third Edition by Jonathan Chaffer and Karl Swedberg,以及jQuery for Designers: Beginner's Guide by Natalie MacLees

第五章:一次编码,到处发布

我相信您现在可能已经注意到,尽管所有现代浏览器都支持指定的 HTML5 功能集的许多方面,但在许多情况下,开发人员必须以特定方式编写其代码,以实现对其项目的适当跨浏览器支持。这不仅是一项耗时的任务,需要大量的冗余和调整,而且要求开发人员保持与 HTML5 规范的当前浏览器支持的最前沿;不仅针对每个目标浏览器,还要针对每个浏览器的每个更新。随着 HTML5 开发的流行度以极快的速度增长,许多开发人员已经创建了库和框架,以帮助使用单个代码实例针对所有平台。

在本章中,我们将学习:

  • CreateJS 工具包的概述,这是 Flash 开发人员在 HTML5 开发中的最佳伙伴,以及其内部库 EaselJS、SoundJS、PreloadJS 和 TweenJS

  • 使用 Modernizr 检测客户端浏览器功能

  • 深入了解 CSS3 媒体查询

覆盖所有基础

Flash 和 HTML5 开发范式之间最大的区别之一是开发人员对最终用户的期望。通常,Flash 开发人员会在启动项目时预设项目的发布设置,以便将项目发布到最能支持应用程序内置功能集的 Flash Player。当然,这意味着用户计算机上安装的 Flash Player 必须是最新的,以满足先决条件版本。在开发任何基于 HTML 的 Web 项目时,这个问题会更加严重,因为开发人员失去了对用户如何查看其内容的控制。尽管有些浏览器比其他浏览器更常见,但如今有大量的互联网浏览器软件可用,不仅适用于台式机和移动设备,还适用于电视等设备。不幸的是,每个浏览器的规格并不完全相同,如果您忽略在每个浏览器中测试您的项目,就无法保证您的内容将被显示并且将按照您创建的方式运行。

随着微软发布的 Internet Explorer 10 版本的发布,开发人员不再抱怨为 Internet Explorer 6 版本开发网页的日子已经一去不复返。然而,随着 HTML5 的出现,一系列新问题也随之而来。网页和基于 Web 的应用程序现在可以访问许多您已经习惯于在本机桌面应用程序中使用的功能。新的系统集成,如文件访问权限,外围支持以及硬件加速,要求现代 Web 浏览器实现对这些功能的支持,以便为查看这些新 HTML5 内容的用户提供适当的支持。

那么哪个浏览器是最好的呢?从开发人员的角度来看,尽管有一个最喜欢的浏览器是很好的,但如果您希望每个人都能查看您的内容,这并不重要。了解差异以及它们如何已经改变,以及将来会如何改变,将使您的 HTML5 技能保持最新并领先于潮流。如前所述,如果您使用今天可用的流行和现代 Web 浏览器,大多数基础将得到覆盖。在撰写本书时,即将推出的功能,如我们将在本书后面介绍的 WebRTC,只在 Google Chrome 等浏览器中得到支持。

CreateJS

由于这本书是专门为 Flash 开发人员编写的,他们正在用 HTML5 扩展他们的技能,我们首先要介绍的是 CreateJS。CreateJS 是一组开源的、模块化的 JavaScript 库,可以单独工作,以实现从 ActionScript 3 到 JavaScript 的更无缝的过渡。CreateJS 专门为了让 Web 开发人员能够轻松地在他们的 HTML5 项目中创建、嵌入和操纵媒体资产。如果你来自 Flash 开发背景,这一点尤其正确。

注意

CreateJS 中所有元素的最新版本以及完整的文档可以在www.createjs.com找到。

CreateJS 专注于资产集成和操纵,以便让您,开发人员,花更多的时间来确保您的项目像素完美。最近有一些很棒的例子,展示了一些令人惊叹的项目,这些项目利用了这个库,产生了一些令人惊叹的 HTML5 体验,比如www.findyourwaytooz.comwww.atari.com/arcadeshinobicorp.com/retro-soccer

虽然我们可以详细介绍 CreateJS 包中每个令人兴奋的功能,但我们可能会填满一半的书。因此,为了确保您至少可以初步了解 CreateJS 提供了什么,让我们回顾一下包中的每个元素以及它们如何在您的 HTML5 项目中使用。

EaselJS

EaselJS 是一个旨在模仿 Flash 中 ActionScript 3 的显示列表语法的 JavaScript 库。它通过使用 HTML5 Canvas 元素来实现这一点,就像 Flash 中的舞台一样。作为 HTML5 和 JavaScript 语法的新手,EaselJS 可能是一个不仅可以让你继续以与你到目前为止一直在开发的方式相似的方式创建应用程序,而且还可以让你相对轻松地将你现有的 Flash 应用程序移植到 HTML5 的库。

注意

最新的 EaselJS 文档可以在www.createjs.com/Docs/EaselJS上轻松找到。

EaselJS 可以用来处理 HTML5 项目中的所有图形元素,如位图、矢量和精灵表。EaselJS 的最佳用例之一是将现有的 ActionScript 3 类移植到 JavaScript 中。由于 EaselJS 被设置为模拟 Flash 中的显示列表,一旦我们的 ActionScript 3 类被转换,我们就可以开始在我们的 JavaScript 项目中使用它,方式几乎与在 Flash 项目中使用它的方式相同。

每个使用 EaselJS 或任何其他 CreateJS 库的项目都需要将库源导入到他们的 HTML5 项目中。一旦你从 CreateJS 网站获取了必要的 JavaScript 源文件,就可以按照下面的例子设置你的 HTML 文档:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>CreateJS - EaselJS Example</title>

    <!-— Import the EaselJS Library --> 
    <script src="img/easeljs-0.5.0.min.js"></script>

    <script>
      function init() {
        // We can place our custom code here.
      }
    </script>
  </head>

  <body onload="init()">
    <canvas id="exampleCanvas" width="800" height="600">
  </body>
</html>

正如您从上面的例子中所看到的,EaselJS 库是在<script>标签中导入到我们的文档中的。我们还在文档的主体中添加了一个空的 Canvas 元素。由于 EaselJS 使用 HTML5 Canvas 元素来替代 Flash 中的舞台,这在这个例子和所有使用这个库的项目中都是必需的。

在我们的例子中,我们将首先回顾一个基本的 ActionScript 3 类,这个类可以在任何 Flash 项目中使用。这个类是一个简单的演示,将一个位图图形应用到舞台上鼠标的位置,并在鼠标移动时更新图形的位置。这个例子不仅涵盖了对外部图形引用的使用,还涵盖了基于鼠标和时间的事件:

package {

  import flash.display.*;

  public class MouseLine() {

    private var oldX:int;
    private var oldY:int;

    public function MouseLine() { }

    public function update(container:Sprite, x:int, y:int):void {
      container.graphics.setStrokeStyle(1);
      container.graphics.moveTo(oldX, oldY);
      container.graphics.lineTo(x, x);

      oldX = x;
      oldY = y;
    }
  }
}

如果你花了一些时间使用 ActionScript 3 类,所有这些都应该看起来非常熟悉,所以让我们直接进入转换过程。正如我们在整本书中所看到的例子一样,在 JavaScript 中创建类时,语法、布局和用法都有一些明显的不同。首先是包声明以及导入语句。JavaScript 中不存在包;因此,该代码可以被移除。代码目录和文件结构仍然可以被使用;然而,在代码中不需要引用来区分哪个代码在哪个包中。导入语句也可以完全移除,因为它们在 JavaScript 中也不被使用。相反,项目中需要的任何外部代码应该在 HTML 文档内的<script>标签元素中导入。

由于我们打算将所有课程作为项目源结构中的单独文件保留,因此我们可以用以下自执行匿名函数替换 ActionScript 3 类中的典型包语法:

(function(window) {
  // Place Your Code here
})(window);

当我们的类源代码放置在这个函数内时,它将在加载后自动执行,允许我们从项目的其余代码中利用这个类。在删除函数和变量的严格类型以及将公共和私有变量转换为 JavaScript 语法之后,我们的类将看起来像下面这样:

(function(window) {

  function MouseLine() {
    this.oldX = 0;
    this.oldY = 0;
  };

  MouseLine.update = function(container, x, y) {
    container.graphics.setStrokeStyle(1);
    container.graphics.moveTo(this.oldX, this.oldY);
    container.graphics.lineTo(x, y);

    this.oldX = x;
    this.oldY = y;
  }

  window.MouseLine = MouseLine;

})(window);

注意在自执行匿名函数中附加的最后一行,window.MouseLine = MouseLine;

类的最后添加允许我们从应用程序基础实例化一个新的MouseLine对象,并在类中使用功能。但在我们开始使用这个类之前,我们需要将其导入到我们的项目中,如下所示:

<script type="text/javascript" src="img/MouseLine.js"></script>

将我们的类保存为MouseLine.js后,我们现在可以像往常一样将其导入到我们的 HTML 文档中,通过在 HTML5 文档的头部使用<script>标签。在这个例子中,我们还将在文档的head中打开另一个<script>标签,在那里我们将放置利用我们的新类的自定义 JavaScript 代码:

<script type="text/javascript">
  var stage;
  var line;

  function init() {
    stage = new createjs.Stage("exampleCanvas");
  }
</script>

在前面的例子中,我们开始构建 EaselJS 项目的stage。我们首先创建两个全局变量,一个用于我们的stage元素,另一个用于我们的鼠标图形元素。在全局变量之后是我们的init()函数,该函数将在页面加载时调用。在我们的init函数内的下一步是设置 Canvas 元素,我们将其应用到这个 HTML 文档的 body 上。我们使用new.createjs.Stage('canvas-element')语法告诉 EaselJS,我们的 ID 为exampleCanvas的 Canvas 是我们预期的 stage。

将 EaselJS 应用到我们的项目中并引用我们的 Canvas 元素后,下一步是应用一个 ticker,以允许我们模拟 ActionScript 3 中的onEnterFrame事件。由于我们打算让MouseGraphic类中的图形在 Canvas 上跟随鼠标移动,我们需要不断检查鼠标的位置,将这些值转换为图形的 x 和 y 位置值。如前所述,在 ActionScript 3 中,传统上会使用onEnterFrame事件;然而,在 JavaScript 中没有 MovieClips 和 frames 的概念,因此设置使用了 EaselJS 的Ticker对象。

在我们刚刚创建的init()函数中,我们现在可以应用以下代码来设置我们的Ticker对象:

createjs.Ticker.setFPS(60);
createjs.Ticker.addListener(window);

我们不仅为我们的Ticker对象创建了一个新的事件监听器,并且还通过使用 CreateJS 内部对象方法之一来设置了 Canvas 渲染的预期每秒帧数。然而,通过创建我们的事件监听器,我们需要一个在每次渲染新帧时调用的函数。在使用 CreateJS 中的Ticker对象时,我们可以简单地在与Ticker对象相同的范围内附加一个tick()函数,这将在每个间隔调用:

function tick() {
  stage.update();
}

在这个 tick 函数中,我们还添加了对我们在init()函数中创建的 Stage 对象的全局变量引用的调用。正如您可能猜到的那样,这个调用实际上告诉stage对象通过渲染舞台进度中的下一个间隔来更新自己。因此,任何在 ActionScript 3 中通常附加在onEnterFrame事件中的代码都将在调用stage.update()方法之前放置。

有了我们基本的 EaselJS 结构,我们的示例现在应该看起来像下面这样:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>CreateJS - EaselJS Example</title>

    <script src="img/easeljs-0.5.0.min.js"></script>
    <script src="img/MouseLine.js"></script>

    <script>
      var stage;
      var mouseImage;

      function init() {
        stage = new createjs.Stage("exampleCanvas");

        createjs.Ticker.setFPS(60);
        createjs.Ticker.addListener(window);
      }

      function tick() {
        stage.update();
      }

    </script>
  </head>

  <body onload="init()">
    <canvas id="exampleCanvas" width="800" height="600">
  </body>
</html>

最后,我们需要导入我们的自定义类,并在Ticker对象的每个间隔中读取鼠标位置属性,以便重新定位图像:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>CreateJS - EaselJS Example</title>

    <style>
      canvas {
        border:1px solid #000;
      }
    </style>

    <!-- Import the EaselJS library. -->
    <script type="text/javascript" src="img/easeljs-0.5.0.min.js"></script>

    <!-- Import our custom classes. -->
    <script type="text/javascript" src="img/MouseLine.js"></script>

    <script type="text/javascript">
      // A global reference to our stage object.
      var stage; 
      var line;

      /**
       * Called on body load.
       */
      function init() {
        // Initialize the stage.
         stage = new createjs.Stage("exampleCanvas"); 
         line = new createjs.Shape();
         stage.addChild(line);

        // Create our ticker (ie. onEnterFrame).
        // Sets the target frames per second.
        createjs.Ticker.setFPS(60); 
        createjs.Ticker.addListener(window);
      }

      /**
       * The 'tick' function is continuously called on the specified interval set by Ticker.setFPS()
       */
      function tick() {
      line.graphics.beginStroke(createjs.Graphics.getRGB(0, 0, 0));

        MouseLine.update(line, stage.mouseX, stage.mouseY);

        stage.update();
      }

    </script>
  </head>

  <body onload="init()">
    <!— Canvas element to be used as our Stage. -->
    <canvas id="exampleCanvas" width="800" height="600">
  </body>
</html>

这个简单的示例只是使用 EaselJS 时的冰山一角,但它展示了如何使用 Canvas 元素作为舞台的核心流程。EaselJS 实际上是 CreateJS 捆绑包的核心,因为当它与捆绑包中的任何或所有其他库一起使用时,一切都会变得生动起来。让我们继续查看 CreateJS 中的库列表,看看下一个库 TweenJS。

TweenJS

对于 Flash 开发人员来说,对对象进行缓动应该并不陌生。然而,在 ActionScript 3 中处理对象动画要比使用 CSS3 动画或编写自己的缓动引擎要容易得多。这就是 TweenJS 发挥作用的地方。TweenJS(www.createjs.com/#!/TweenJS)使用了 ActionScript 和 TweenMax(www.greensock.com/tweenmax)等库中常用的缓动语法,可以让您轻松创建适用于 HTML5 的动画,通过允许 TweenJS 在特定时间段内执行所有对象属性操作。虽然 TweenJS 是一个非常简单的库,但它在开发新项目或转换现有 Flash 项目时所能节省的时间可能是无价的。与 CreateJS 包中的所有元素一样,TweenJS 与 EaseJS 库非常配合,我们可以在下面的代码示例中演示:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>CreateJS - TweenJS Example</title>

    <style>
      canvas {
        border:1px solid #000;
      }
    </style>

    <!-- Import the TweenJS library. -->
    <script type="text/javascript" src="img/easeljs-0.6.1.min.js"></script>
    <script type="text/javascript" src="img/tweenjs-0.4.1.min.js"></script>
<!-- Import the TweenJS Ease library as well. -->
    <script type="text/javascript" src="img/Ease.js"></script>

    <script type="text/javascript">
      var canvas, stage;

      // Called on body load.
      function init() {
        stage = new createjs.Stage("exampleCanvas");

        var circle = new createjs.Shape();
        circle.graphics.beginFill("#00FF00").drawCircle(100, 100, 100);

        stage.addChild(circle);

        createjs.Tween.get(circle, {loop:true})
          .to({
            x:600,
          }, 1000)
          .wait(500)
          .to({
          scaleX:0.2,
            scaleY:0.2
          }, 500)
          .to({
            x:600,
            y:400
          }, 1000)
                .to({
            scaleX:1,
            scaleY:1
          },1000)
          .to({
            x:0,
            y:0
          }, 1000);

        createjs.Ticker.setFPS(30);
        createjs.Ticker.addEventListener("tick", stage);
      }
    </script>
  </head>

  <body onload="init()">
    <!-- Canvas element to be used as our Stage. -->
    <canvas id="exampleCanvas" width="800" height="600">
  </body>
</html>

正如您在前面的示例代码中所看到的,在 EaselJS 创建的舞台中对元素进行缓动非常简单和熟悉,对于任何 Flash 开发人员来说都是如此。与 CreateJS 中的所有元素一样,TweenJS 可以与 CreateJS 套件的其余部分一起使用,也可以单独使用。因此,如果您需要一个简单但功能强大的缓动引擎来节省大量时间和开销,同时在 HTML5 项目中对元素进行动画处理,那么 TweenJS 绝对值得一试。

PreloadJS

就像在 Flash 应用程序中一样,在 HTML5 项目中预加载资产可以是一个关键步骤,以确保您的内容以适当的方式传递给最终用户。PreloadJS(www.createjs.com/#!/PreloadJS)允许轻松设置多个资产的预加载,实时进度反馈和队列支持。正如我们在 EaselJS 示例中看到的,CreateJS 已经设置了自己的资产管理系统,可以轻松集成到 PreloadJS API(www.createjs.com/Docs/PreloadJS/modules/PreloadJS.html)中。考虑以下简化的示例,它从 Web 加载外部音频和图像资产。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>CreateJS - PreloadJS Example</title>

    <!-- Import the PreloadJS library. -->
    <script type="text/javascript" src="img/preloadjs-0.3.1.min.js"></script>
    <!-- Import the SoundJS library. -->
    <script type="text/javascript" src="img/soundjs-0.4.1.min.js"></script>

    <script type="text/javascript">

      // Called on body load.
      function init() {
        var loadCount = 0;

        var queue = new createjs.LoadQueue(false);
        queue.installPlugin(createjs.Sound);
        queue.addEventListener("complete", handleComplete);
        queue.addEventListener("fileload", handleFileLoad);

        // We can load a specific external file...
        queue.loadFile({id:"sound", src:"http://www.w3schools.com/html/horse.mp3"});

        // Or create a manifest which lists all of the files to load.
        queue.loadManifest([
        // Load some Google Doodles from the Google Servers.
           	{ id: "doodle1", src:"http://www.google.com/logos/2013/first_day_of_summer_2013-1536005-hp.gif" },
           	{ id: "doodle2", src:"http://www.google.com/logos/2013/first_day_of_winter_2013-1985005-hp.gif" },
           	{ id: "doodle3", src:"http://www.google.com/logos/2013/140th_anniversary_of_the_rcmp-1580006-hp.jpg" }
        ]);

        // Called on LoadQueue file load complete.
        function handleFileLoad(event) {
          var item = event.item;
              console.log("File Loaded: " + item.id);

          loadCount++;
            	console.log((loadCount / 4) * 100 + "% completed.");
        }

        // Called on LoadQueue load complete.
        function handleComplete() {
            console.log("File Loading Completed!");

            createjs.Sound.play("sound");

            var d1 = queue.getResult("doodle1");
            document.body.appendChild(d1);

            var d2 = queue.getResult("doodle2");
            document.body.appendChild(d2);

            var d3 = queue.getResult("doodle3");
            document.body.appendChild(d3);
        }
      }

    </script>
  </head>

  <body onload="init()">
  </body>
</html>

正如我们在本书的前面示例中所看到的,等待所有文档及其资产在与其交互之前加载是几乎每个 JavaScript 应用程序都会使用的关键步骤。然而,在页面加载期间下载资产时,没有简单的方法来监视下载或完成过程。尽管我们典型的onload调用仍将等待我们的资产准备就绪,但在许多应用程序中,进度条的使用可以极大地增强用户体验,尤其是在较长的应用程序加载时间内。

再次审查前面的示例,您会发现我们在每个文件加载时都添加了事件侦听器,以及在所有资产加载完成时。将要加载的资产数量的数值与已加载的资产数量相结合,我们可以轻松地找到当前的预加载完成百分比。为了避免冗长的代码示例,我只是使用开发者控制台来补充了一些预加载用户界面:

PreloadJS

SoundJS

在撰写本书时,处理所有现代 HTML5 兼容浏览器上的音频和音频交互支持仍然非常困难。当前的 HTML5 音频支持水平在不同浏览器之间甚至在大多数移动平台上都可能存在极大的差异。正确地调整音频交互和操作,以在每台设备和浏览器上运行几乎似乎是一项不可能的任务。幸运的是,SoundJS 可以帮助解决许多与 HTML5 音频开发相关的常见问题。SoundJS 允许您轻松地查询客户端浏览器的功能,以确保您使用的音频具有用户设备支持的正确功能和插件:

<script>
  var preload;

  function init() {
    if (window.top != window) {
      document.getElementById("header").style.display = "none";
    }

    createjs.FlashPlugin.BASE_PATH = "../src/soundjs/" // Initialize the base path from this document to the Flash Plugin
    if (!createjs.SoundJS.checkPlugin(true)) {
      document.getElementById("error").style.display = "block";
      document.getElementById("content").style.display = "none";
      return;
     }

    document.getElementById("loader").className = "loader";
    var assetsPath = "assets/";
    var manifest = [
        {src:assetsPath+"Game-Break.mp3|"+assetsPath+"Game-Break.ogg",id:1, data: 1},
        {src:assetsPath+"Game-Spawn.mp3|"+assetsPath+"Game-Spawn.ogg",id:2, data: 1},
        {src:assetsPath+"Game-Shot.mp3|"+assetsPath+"Game-Shot.ogg", id:3, data: 1},

        {src:assetsPath+"GU-StealDaisy.mp3|"+assetsPath+"GU-StealDaisy.ogg", id:4, data: 1},
        {src:assetsPath+"Humm.mp3|"+assetsPath+"Humm.ogg", id:5, data:1},
        {src:assetsPath+"R-Damage.mp3|"+assetsPath+"R-Damage.ogg", id:6, data: 1},

        {src:assetsPath+"Thunder1.mp3|"+assetsPath+"Thunder1.ogg", id:7, data: 1},
        {src:assetsPath+"S-Damage.mp3|"+assetsPath+"S-Damage.ogg", id:8, data: 1},
        {src:assetsPath+"U-CabinBoy3.mp3|"+assetsPath+"U-CabinBoy3.ogg", id:9, data: 1},

        {src:assetsPath+"ToneWobble.mp3|"+assetsPath+"ToneWobble.ogg",id:10, data: 1},
        {src:assetsPath+"Game-Death.mp3|"+assetsPath+"Game-Death.ogg", id:11, data: 1},
        {src:assetsPath+"Game-Break.mp3|"+assetsPath+"Game-Break.ogg",id:12, data: 1}
		];

      preload = new createjs.PreloadJS();
      //Install SoundJS as a plugin, then PreloadJS will initialize itautomatically.
      preload.installPlugin(createjs.SoundJS);

      //Available PreloadJS callbacks
      preload.onFileLoad = function(event) {
        // Show the icon on loaded items.
        var div = document.getElementById(event.id);
        div.style.backgroundImage = "url('assets/audioButtonSheet.png')";
      };
    preload.onComplete = function(event) {
      document.getElementById("loader").className = "";
    }

      //Load the manifest and pass 'true' to start loading immediately. Otherwise, you can call load() manually.
      preload.loadManifest(manifest, true);
  }

  function stop() {
    if (preload != null) { preload.close(); }
    createjs.SoundJS.stop();
  }

  function playSound(target) {
      //Play the sound: play (src, interrupt, delay, offset, loop, volume, pan)
      var instance = createjs.SoundJS.play(target.id, createjs.SoundJS.INTERRUPT_NONE, 0, 0, false, 1);
    if (instance == null || instance.playState == createjs.SoundJS.PLAY_FAILED) { return; }
    target.className = "gridBox active";
    instance.onComplete = function(instance) {
      target.className = "gridBox";
    }

  }
</script>

CreateJS Toolkit

CreateJS 最伟大的方面之一是由Grant Skinnerwww.gskinner.com)和 Adobe 创建的 CreateJS Toolkit。这个工具包是 Adobe Flash Professional 的插件,可以让您轻松地在 Flash Professional 环境中创建 CreateJS-ready 的动画和元素,这是每个 Flash 开发人员已经习惯的。

提示

您可以在www.adobe.com/devnet/createjs.html获取 CreateJS Toolkit 的最新消息和文档。

设置工具包

首先,您需要前往 Adobe 网站上的 Adobe CreateJS Toolkit 页面(www.adobe.com/devnet/createjs.html)下载插件的最新版本以安装到您的计算机上。找到此页面的最简单方法之一是单击打开 Flash CS6 时可能已显示的链接:

设置工具包

一旦您下载了扩展程序,请确保退出所有正在运行的 Flash CS6 实例,并在 Adobe Extension Manager CS6 应用程序中打开下载的文件,将其安装到您的计算机的创意套件设置中。阅读并接受条款和条件以完成安装。

安装完成后,您应该能够在 Flash 扩展下看到 CreateJS Toolkit 扩展程序的列表,就是这样,我们已经准备好再次在 Flash 中开始使用 Toolkit 了:

设置工具包

安装了扩展程序并重新打开 Flash 后,启动一个新的 ActionScript 3 项目,并通过从窗口下拉菜单中选择 CreateJS Toolkit 来打开 CreateJS Toolkit 窗口。生成的 Toolkit 窗口将类似于以下图像。从这个窗口,您将能够在 Flash Professional 中使用 CreateJS Toolkit 配置和发布当前项目,而不是传统的导出到 SWF 设置:

设置工具包

在深入发布内容之前,值得查看一下工具包窗口中的配置设置。点击工具包窗口中的“编辑设置”按钮,打开 CreateJS Toolkit 的“发布设置”窗口:

设置工具包

从 Flash 项目中发布内容的配置设置相对简单。默认的“输出”值将在与 FLA 文件相同的目录中,您的项目保存在其中,并设置了资产路径。选项部分中的最终值再次非常简单,除了以下值:

  • 紧凑形状:此值将代码压缩为绘图 API 类的最小版本

  • 多帧边界:此值计算资产的boundsRect

发布您的资产

一旦您的资产都准备好了,可以在工具包窗口中点击“发布”按钮。结果将是典型的应用程序输出,而不是编译成 SWF,结果完全设置在 HTML5 中:

发布您的资产

这个 CreateJS Toolkit 编译器最好的部分是能够轻松地获取导出源代码的一部分,并在应用程序的特定部分中使用它。这个过程极大地提高了设计师和开发人员轻松地处理 HTML5 内容和资产,并轻松更新现有媒体的能力。

审查 CreateJS Toolkit 的输出

在完成工具包之前,值得审查一下从其编译器中导出的一些代码。让我们看看它为我们的太空游戏示例创建了什么:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>CreateJS export from SpaceAssets</title>

<script src="img/easeljs-0.5.0.min.js"></script>
<script src="img/SpaceAssets.js"></script>

<script>
var canvas, stage, exportRoot;

function init() {
	canvas = document.getElementById("canvas");
	exportRoot = new lib.SpaceAssets();

	stage = new createjs.Stage(canvas);
	stage.addChild(exportRoot);
	stage.update();

	createjs.Ticker.setFPS(30);
	//createjs.Ticker.addListener(stage);
}
</script>
</head>

<body onload="init();" style="background-color:#D4D4D4">
	<canvas id="canvas" width="800" height="600" style="background-color:#ffffff"></canvas>
</body>
</html>

正如您所看到的,通过上面的所有示例和库,CreateJS 是一组 JavaScript 功能的大集合,打包成了几个非常良好维护的开源 JavaScript 库。如前所述,在这本书中我们没有时间涵盖的内容远远超过这些,所以一定要前往 CreateJS 网站(www.createjs.com)并阅读最新版本的文档。

Modernizr

我们在 CreateJS 捆绑包中看到的一个重要功能是轻松地检查客户端的 Web 浏览器是否支持在您的网页上使用的 HTML5 功能。然而,只有 CreateJS 具有检查库中使用的功能的兼容性的能力。如果您需要更深入地检查用户的 Web 浏览器是否具有适当的功能,Modernizr 项目绝对值得一看。Modernizr 允许您仅使用几行代码和一个仅有几千字节的外部 JavaScript 文件轻松地检查 HTML5 功能集中的每个功能。

使用 Modernizr

首先,您需要前往modernizr.com并下载库的最新版本。与许多 JavaScript 库一样,您可以选择下载生产版本或开发版本的代码,以便节省文件大小和带宽:

使用 Modernizr

为了方便示例和学习的缘故,我们将下载开发版本的代码,其中包括整个 Modernizer 库。一旦 JavaScript 文件下载完成,它可以像包含任何 JavaScript 引用一样包含到您的 HTML 文档中。

提示

如果您仍然在寻找 Modernizr 设置方面遇到问题,请前往官方安装文档,网址为[modernizr.com/docs/#installing](http:// http://modernizr.com/docs/#installing)。

理解 Polyfills

在网页开发中,Polyfill 的概念一旦在实际项目中处理起来就非常简单。幸运的是,即使您来自 100%的 Flash 开发背景,您以前可能也有过这个概念的经验。在 HTML 页面中嵌入 Flash 内容时,即使在 Flash Professional 中使用了自动发布设置,生成的代码也会创建一个带有对编译的 SWF 文件的引用的 HTML 对象元素。但是,如果您仔细观察,或者在 Web 浏览器中禁用 Flash,您会注意到仍会显示警告,提示您需要下载 Flash 播放器以及指向 Flash 播放器下载页面的链接。这个内容只在 Flash 内容无法显示时显示,并且是 Polyfill 的一个最简单形式的示例。

在 HTML5 中使用 Polyfills 可能是为了在特定浏览器和平台上达到预期受众而必不可少的。但是,并不总是需要使用 Polyfills。如果您试图提供尽可能最佳的体验,可能不值得尝试使用您的尖端 HTML5 功能来针对像 IE7 这样的浏览器。

Modernizr.load()

Moderizr 中的load方法可能是库中最强大但又易于使用的实用工具之一。简而言之,load方法允许您有选择地选择应加载哪些脚本和数据,这取决于用户是否能够利用 HTML5 功能集的特定部分。考虑以下示例:

Modernizr.load({
  test: Modernizr.geolocation,
  yep : 'geo.js',
  nope: 'geo-polyfill.js'
});

这个简单的例子展示了我们如何根据用户是否能够在其浏览器中使用地理位置功能来轻松选择要加载的 JavaScript 文件。如果客户端能够在其浏览器中使用地理位置 API,则将加载geo.js文件并继续执行。如果用户无法使用地理位置,则使用nope值,并加载geo-polyfill.js文件。

正如您在此演示中所看到的,Modernizr 是一个简单的库,其主要目标是简化处理多个浏览器和平台尝试查看您的 HTML5 内容的混乱,并且它做得非常好。

Modernizr 可以检测到的内容

感谢世界各地许多 JavaScript 开发人员的贡献,Modernizr 自豪地宣称它能够检测并为当前指定的每个 HTML5 功能创建 Polyfill。由于有太多功能要列出,我将把 Modernizr API 文档的研究留给你,并给你以下代码示例,以演示这个伟大库的进一步用途:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Modernizr Example</title>

    <!-- Import the Modernizr library. -->
    <script type="text/javascript" src="img/modernizr-latest.js"></script>

    <script>
      function init() {
        // Touch
        if(Modernizr.touch){
           console.log('You are on a touch enabled device.');
        } else {
           console.log('You are not on a touch enabled device.');
        }

        // WebGL
        if(Modernizr.webgl){
           console.log('You are on a WebGL enabled browser.');
        } else {
           console.log('You are on a WebGL enabled browser.');
        }

        // Display all values
        console.log(Modernizr);
      }
    </script>
  </head>

  <body onload="init()">
  </body>
</html>

如上例所示,Modernizr 的实现非常简单。条件易于识别,因为它们的命名约定几乎直接匹配可以测试的功能集。要更好地了解 Modernizr 提供的不断增长的 API,请访问官方项目文档modernizr.com/docs

CSS 媒体查询

尽管我们已经在前几章中涉及了 CSS3 媒体查询,但在本章中也值得停下来注意一下。如果活动视口在设置内容以便在所有设备上可见并具有响应式布局和显示设置时发生大小变化,CSS3 媒体查询可以轻松地允许您避免操纵任何站点内容,只操纵附加到它们的样式。这个概念不仅非常适合在所有桌面和移动项目上实施,而且还可以用于更多其他用途。考虑以下一些可以直接从 CSS 源中查询的属性列表:

  • 全部:此属性允许所有设备监听此属性

  • 盲文:此属性用于盲文触觉反馈设备

  • 浮雕:此属性用于分页盲文打印机

  • Handheld:此属性用于手持设备(智能手机和平板电脑适用于此!)

  • Print:此属性用于分页材料和在屏幕上打印预览模式中查看的文档

  • Projection:此属性用于投影演示,例如投影仪

  • Screen:此属性主要用于彩色计算机屏幕和智能手机

  • Speech:此属性用于语音合成器

  • tty:此属性用于使用固定间距字符网格的媒体,例如电传打字机、终端或具有有限显示功能的便携设备

  • Tv:此属性用于电视类型设备,例如低分辨率、彩色、有限滚动功能的屏幕,带有可用音频

媒体查询的正确使用可以轻松地让您针对各种设备进行定位,使您的内容能够根据浏览器的特定大小、平台和设置做出响应:

#mycontent {
    background-repeat: no-repeat;
    background-image:url('image.gif');
}

@media screen and (min-width: 1200px) {
    # mycontent {
        background-image:url('large-image.gif');
    }
}

@media print {
    #mycontent {
        display: none;
    }
}

总结

在本章中,我们介绍了将现有的 Flash 应用程序转换或移植到 HTML5 时可用的一些选项,以及使用户能够在任何设备上正确查看其内容的方法。我们深入研究了构成 CreateJS 的各个优秀库,从在 JavaScript 中模拟 Flash 显示列表,到使用传统的 ActionScript 3 tweening 语法对元素进行动画处理。我们了解了 CreateJS 工具包对于任何具有 Adobe Flash 专业版 IDE 的先前知识的人来说是多么有用,以及如何直接从舞台和库编译资产以供在 Web 文档中使用。我们还学习了如何通过使用 Modernizr 等库来统一开发体验。通过查询浏览器功能支持,您可以轻松决定是否需要使用替代显示方法或 shim 来使用户获得良好的体验。

第六章:HTML5 框架和库

与任何编程语言一起工作最令人兴奋的方面之一是发现可以用来扩展和简化驱动应用程序的代码的新库和框架。随着 HTML5 在许多不同平台和设备上的开发日益受欢迎,为帮助任何人进行 HTML5 开发而公开提供的代码量以惊人的速度增长。在本章中,我们将概述一些最受欢迎的库和框架,您可以随时利用它们,不仅可以节省时间,还可以让您更多地专注于用户体验,而不是编写复杂的 JavaScript 以在每个现代浏览器中运行。

在本章中,我们将涵盖以下内容:

  • 框架和库如何让你的生活更轻松

  • 使用框架或库可以创建的东西

  • 对广受欢迎的 jQuery 库和 jQuery 移动框架进行概述

  • 使用HTML5 Boilerplate 模板构建HTML页面

  • 使用 Bootstrap 创建响应式统一页面布局

  • 使用 GreenSock 的动画平台来使用熟悉的缓动引擎对内容进行动画处理

  • 使用Backbone.js在流行的MVC结构中开发您的 JavaScript

  • 使用 WebGL 和Three.js编程硬件加速的 3D 图形

  • 通过查看 Google 的 V8 项目来概述 JavaScript 编译器

  • 使用Node.js将 JavaScript 推动到应用程序开发的极限

框架和库如何让你的生活更轻松?

从外部人或非开发人员的角度来看,在项目中使用他人的代码的想法可能会引起许多负面联想。如果您使用的是由您不认识的人创建并自由分发的代码,您如何能相信它的性能与所宣传的一样,并且不会显示恶意功能?传统上,在任何编程语言中导入库和使用框架时,只会使用整个代码库的一小部分。这会导致更大的开销,并可能对应用程序运行时执行速度产生影响。尽管所有这些论点都是有效的,但许多关于使用外部库和框架的流行关注点已经得到解决。在HTML5项目中使用外部资源的概念已经变得如此普遍,以至于 JavaScript 已经轻松成为社交编码网站 GitHub(github.com/languages)上最受欢迎的编程语言。

由于 GitHub 等网站(github.com)的出现,分享和为开源项目做出贡献的概念已经爆炸式增长。借助外部库和框架,开发人员可以轻松地从头脑中的概念或想法转移到在几分钟到几小时内构建原型。开发人员还可以更多地专注于实际的概念集成,而不是处理诸如浏览器优化和平台支持等小问题。因此,随着开发人员周围的环境扩大和支持项目的开源项目增长,升级外部依赖项将导致应用程序功能集获得最新和最好的支持。

JavaScript 框架和库可以做什么?

正如你在本书前面章节中所看到的,现代 Web 浏览器对 JavaScript 的支持每天都在变得更好。将所有应用程序类型的典型应用程序流程移动到 Web 的想法随着时间的推移变得更加现实。JavaScript 现在开始进入桌面和移动操作系统应用程序。有了对这么多平台的新覆盖,JavaScript 可以做很多你现在可能还不知道的事情。作为一个有经验的 Flash 应用程序开发者,你可能会发现,在理解和实现许多新颖的 JavaScript API 方面,当涉及到转向 HTML5 时,你会有另一个优势。从麦克风和摄像头集成到触摸设备上的多点触控手势,你可以用 JavaScript 做的事情每天都在增加。为了让你对可能的事情更加兴奋,这里是一些 HTML5 应用程序可以做的一些伟大的事情的简短列表:

  • 动态控制 CSS 属性以创建 2D 和 3D 动画

  • 从客户端摄像头和麦克风实时音频和视频流传输

  • 使用硬件加速渲染 3D 图形和高帧率

  • 将 JavaScript 直接编译成机器码,以便作为服务器或应用程序运行

寻找适合项目的正确库或框架

当涉及到寻找适合项目的外部资源时,可能会变成一个繁琐的任务,需要筛选各种项目,因为它们似乎都在做同样的事情。随着 JavaScript 开发的当前流行,人们只能期望这个问题会随着越来越多的开发者发布他们的项目而变得更加严重。幸运的是,开发社区支持你!那么,一个人应该去哪里找到最新和最好的开源项目,以在他们的 HTML5 项目中使用呢?嗯,和互联网上的任何东西一样,没有一个地方可以找到所有这些可用的项目。然而,随着时间的推移,越来越多的项目被托管在 GitHub 上(github.com),这样开发者就可以轻松地分享和贡献项目,同时利用 Git 版本控制系统。

在尝试寻找新项目时,像 GitHub 这样的社交编码网站的最好之处不仅在于能够按特定的编程语言对项目进行排序,还在于项目的当前流行程度(github.com/explore)。GitHub 通过关注、派生和对相关项目的贡献数量来排名项目的流行程度。因此,通过排序这些值,将显示无数受欢迎和最新的项目。当然,使用站点搜索只会在寻找特定主题和平台时进一步细化您的结果:

寻找适合项目的正确库或框架

因此,在进行一些调查后,您可能已经找到了一些您认为是适合您需求的库或框架。下一个决定是将选择范围缩小到您可以开始使用的内容。那么,您该如何选择呢?显然,对于这个问题也没有简单的答案。但是,在下载和实施您找到的库或框架之前,有一些重要的考虑值得考虑。第一个考虑因素应该始终是您期望的最终结果是什么。如果您只是为了自己编写代码来学习新的框架,那么您几乎可以自由下载和测试任何您希望的内容。如果您考虑将此代码用于专业用途或可能向公众开放的项目,花一些时间研究有关所涉及项目的更多具体信息将有助于您避免日后的麻烦。如果您发现了一个感兴趣的开源项目,但该项目几乎没有或没有任何开发活动,无论是错误修复还是更新,那么该项目背后的开发团队很可能已经转移到了新项目。因此,您将下载和使用的代码版本将需要您维护和更新,以便在没有任何问题的情况下实施和使用它。如果最初创建项目的开发人员已经放弃了它,他们很可能不会很快回来专门帮助您解决问题。另一方面,如果您正在查看一个刚刚诞生或仍处于早期开发阶段的项目,那么如果您将该项目实施到您的项目中,您将需要在每次进行关键依赖项的手动更正时将其重新引入您的项目中。尽管这在大多数开发情况下都是典型的,但在早期阶段(选择和使用外部资产)时,始终值得记住,您可能会在项目的生命周期中使用这些代码。

为了让您了解一些可用功能,让我们概述一些等待在您下一个项目中使用的优秀开源项目。

jQuery

在列出 JavaScript 库的清单时,我们不可能不从 jQuery 开始。在本书的这一部分,我们还没有在任何示例中使用 jQuery。但是,如果您在阅读本书之前花了一些时间研究 Web 开发,那么您很可能已经听说过这个项目。jQuery 最初发布于 2006 年,已经成为最受欢迎的 JavaScript 库,截至目前(撰写本书时)在互联网上访问量最高的 10,000 个网站中,超过 55%使用了 jQuery。由于 jQuery 在互联网上的大大小小项目中被广泛使用,它已经成为HTML5开发者工具包中几乎必不可少的技能。

提示

为了让您了解与 jQuery 相关的一切,请访问项目网站jquery.com

当然,随着 jQuery 的压倒性流行,文档、示例和教程的数量也是压倒性的。因此,与其花费大量时间查看 jQuery 的所有功能,我们将简要概述它的基本原理以及您可以用它做什么。

与本书中的所有主题一样,如果您有兴趣了解更多,快速的谷歌搜索将非常有帮助:

jQuery

那么 jQuery 到底是什么呢?嗯,jQuery 是一个相对较小的 JavaScript 库,用于帮助解决各种常见的 JavaScript 开发任务和问题。通过轻松选择文档中的元素,创建和处理各种事件,在文档中对元素进行动画处理,使用 Ajax 调用和检索外部数据,jQuery 可以为您提供一个更简单、易于使用和统一的语法,可在大量的 Web 浏览器上运行。jQuery 最好的部分是,它可以在只导入一个小于 50 KB 的 JavaScript 文件的情况下完成所有这些工作。

将 jQuery 投入实践

与所有 JavaScript 项目一样,了解 jQuery 的最佳方法是通过示例。因此,让我们快速了解如何将 jQuery 正确添加到您的项目中,以及如何开始在您的代码中使用其功能。

一切都始于前往 jQuery 项目网站获取项目的最新稳定版本(jquery.com)。值得注意的是,几乎所有积极开发的开源项目都有许多不同的构建类型可供您下载和使用。通常情况下,当访问 jQuery 等项目网站时,您通常会找到一个链接,用于下载项目的最新稳定版本。积极开发的项目的稳定版本通常不是最新版本,但稳定版本经过测试并获得批准供公众使用。随着开发人员对项目的贡献不断增加,它们将继续增加,直到开发团队批准当前代码库已准备好供公众使用。因此,在软件的每个版本发布之间的整个时间内,项目将有一个开发版本,在许多情况下,您也可以下载和使用,当然可能会遇到新的未记录问题的可能性。

在到达 jQuery 下载页面(jquery.com/download)后,您可以选择下载当前版本的压缩或未压缩版本。压缩代码的原因是为了减小文件大小,并在您的 Web 服务器请求时实现更快的加载时间。压缩或压缩 JavaScript 实际上是您可以轻松完成的工作,我们将在后面的章节中继续深入探讨这个主题。现在,您可以将这些 jQuery 源 JavaScript 文件中的任何一个保存在计算机上,最好是在您将创建HTML5项目的目录中。创建空的 HTML 文档后,导入 jQuery 就像导入任何其他外部 JavaScript 文档一样简单:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>jQuery Importing Example</title>
    <script src="img/jquery.min.js"></script>
  </head>

  <body>
  </body>
</html>

完成了这项艰苦的工作后,您现在可以在项目中利用 jQuery 的所有功能。但是,通常需要从一个关键位置开始,那就是控制代码准备执行的时间点。到目前为止,我们已经使用了许多常见的技术来完成这项任务,比如将body onload参数设置为 JavaScript 函数。

<body onload="init()">

或在window对象上设置onload事件:

window.onload = function() {
	// Start executing your code here...
}

使用这种方式调用 JavaScript 的一个问题是,等待文档加载的方式包括等待所有图像资产加载,包括不受控制的外部资产,如横幅广告。因此,jQuery 创建了自己的文档就绪事件处理程序语法来规避这个问题。通常情况下,对于所有基于 jQuery 的项目,要追加的第一段代码将是文档就绪处理程序:

$( document ).ready( function() {
  // Start executing your code here...
});

使用 jQuery 选择元素

jQuery 最伟大的方面之一是其选择器引擎,也被称为 Sizzle(sizzlejs.com)。选择器引擎之所以如此出色,是因为在处理 HTML 文档中的交互元素时,它使整个开发过程变得非常简单。考虑一下我们在 body 中添加一些简单内容的工作示例:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>jQuery Importing Example</title>

    <!-- Always import external libraries before your custom site code. -->
    <script src="img/jquery.min.js"></script>

    <script>
      $( document ).ready( function() {
        // Start executing your code here...
      });
    </script>
  </head>

  <body>
    <div>
        <p>
        <a href="http://www.google.com">Go to Google</a>
        </p>
    </div>
  </body>
</html>

尽管这个页面布局很简单,但页面中的每个元素都可以通过使用 jQuery 选择器来轻松控制。要为我们的链接到 Google 添加事件监听器,我们可以在文档准备好的回调函数中添加它:

$( document ).ready(function() {
  $("a").click(function(event) {
    alert("Tell Google I said hello!");
  });
});

尽管前面的示例非常简单,但有几个关键方面应该立即介绍。jQuery 选择器语法依赖于$()语法。在我们的示例中,在选择器语法括号内,我们提供参数"a"来选择文档主体中的所有<a>元素标记。仅仅选择一个元素不会让你走得太远;因此,示例中的下一步是将点击事件监听器链接到所选元素。当然,点击事件远非您可以应用于元素的唯一可用事件,您可以参考事件文档以查看整个列表(api.jquery.com/category/events)。最后一步是定义要在事件回调中使用的方法,在我们的示例中,我们只是直接将函数定义到回调参数中。

通过添加、保存并在浏览器中重新加载此更改后,当单击链接时,将显示一个警报对话框,然后是页面位置(google.com))。如您所见,我们的事件监听器在锚标签中引用的URL移动到之前已经被触发。选择器引擎与大量的 jQuery 事件一起使用,可以让您控制页面中可能发生的大量用户和网络交互。

覆盖预定义的操作也很容易。正如您在我们的示例中定义的回调函数中所看到的,当它被调用时,它将事件变量传递给方法。这个事件属性用于控制事件,可以很容易地被操纵或完全覆盖:

$(document).ready(function() {
	$("a").click(function(event) {
		alert("You're not going anywhere!");
		event.preventDefault();
	});
});

通过在事件对象上调用preventDefault()方法,我们可以禁用事件的默认操作,并使用我们自己的代码来控制结果。

通过 jQuery 控制 CSS

jQuery 的另一个伟大之处是可以轻松地使用 CSS3 属性来控制元素的外观和感觉。使用 jQuery 选择器和 CSS 方法在任何元素上获取和设置 CSS 值非常简单:

$("#example").css("width", 200);
$("#example").css("height", 300);

正如您在前面的示例中所看到的,设置特定元素的宽度和高度的 CSS 属性非常简单。我们可以通过将 CSS 属性传递给对象而不是单独传递它们来简化这两行 CSS 属性更新为一行:

$("#example").css({ width:200, height:300 });

这相当于在文档的 CSS 结构中添加以下内容:

#example {
  width:200px;
  height:300px;
}

在 jQuery 中进行 CSS 操作不仅仅是为了设置文档中元素的宽度和高度。jQuery 现在完全支持 CSS3 属性,其中包括圆角、文本效果、不透明度、2D 和 3D 变换以及滤镜等属性。

CSS 动画

由于通过 jQuery 可以控制几乎任何元素的 CSS 属性,因此也可以轻松地对它们进行动画处理。

在查看一些示例之前,有一些重要的要点需要注意。如第二章中所述,准备战斗,以及第三章中所述,可扩展性、限制和影响,在涵盖 CSS 属性及其值与 ActionScript 3 API 相比时,定位文档中元素的值不是基于传统的 x 和 y 值集。因此,在对元素位置进行动画处理时,应注意元素位置值,以正确定义移动元素的正确值:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>jQuery CSS Animation Example</title>

    <style>
      html, body {
        margin:0;
        padding:0;
        height:100%;
      }
      #example {
        width:200px;
        margin:auto;
        background-color:#EFEFEF;
        border:1px solid #000;
        text-align:center;
        cursor:pointer;
      }
    </style>

    <script src="img/jquery.min.js"></script>
    <script>
      $( document ).ready( function() {
        $('#example').click(function(event){ 
          // Animate the #example element
          $("#example").animate({
              marginLeft: '0',
              width:'100%',
              height:'100%',
              fontSize:'40px'
          }, 500, function(event) {
          // Update the element paragraph inner HTML.
            $("#example p").html('Animation Complete!');
          });
        });
      });
    </script>
  </head>

  <body>
    <div id="example">
      <p>Click To Begin Animation</p>
  </div>
  </body>
</html>

使用 jQuery Ajax 请求外部数据

由于 ActionScript 3 中内置的URLLoaderURLRequest类,请求外部数据(无论是项目内部还是 Web 上的外部数据)都非常简单。创建了一个URLLoader,以及一个包含数据路径引用的URLRequest对象。最后,将URLRequest对象传递到加载器对象中,并调用load方法:

Var loader:URLLoader = new URLLoader();
var request:URLRequest = new URLRequest("data.xml");
loader.load(request);

当然,要正确完成此示例,您需要添加事件处理程序来捕获数据的返回,并知道何时可以开始操作或利用它。然而,从应用程序中调用和检索外部数据的概念对您可能并不陌生。

在 JavaScript 中开发应用程序时,这种功能的首选是AJAXAJAX(异步 JavaScript 和 XML)是在客户端使用网页时与 Web 服务器交换数据的概念,而无需重新加载页面。如今在 Web 上使用AJAX是如此普遍,以至于几乎不可能一天不在许多网站或服务中使用它。一个完美的例子是在查看 Facebook 时间线或 Twitter 动态时的无限滚动。当您向下滚动页面查看内容时,底层运行的 JavaScript 会检测到您即将到达页面底部,并调用服务器以获取更多数据以不断填充列表。传统上,这将通过将数据应用于多个页面并要求用户为每个视图刷新页面来完成。

那么 jQuery 在开发应用程序的AJAX功能方面能做些什么呢?在 jQuery 库中有许多方法专门设计用于处理AJAX请求和请求类型。

在其最基本的形式中,jQuery 的load方法可以在一行 JavaScript 中检索外部数据并将其放置在所选元素中:

$('#myElement').load('example.html');

当然,外部资产不需要是HTML文档。XML、JavaScript、JSON、纯文本和 HTML 文档都支持在 AJAX 请求中使用。

可以理解的是,您可能并不总是希望将AJAX请求中的传入数据直接放入文档中,因此响应处理程序通常会放置在这些类型的调用中。这可以通过使用AJAX方法本身以自我实例化的 jQuery 语法来实现:

$.ajax({
    url: 'example.html'
}).done(function(data) {
  if(data != '') {
    $("body").append(data);
  }
});

现在,通过返回的数据,您可以在将其包含在文档中之前轻松操作和验证AJAX调用返回的数据。

数据也可以在调用外部数据时提供。根据所引用文档中脚本的要求,您可以选择通过HTTP GET 请求发送数据:

$.get("getmyphotos.php", { user:"johnsmith", id:"200" })
.done(function(data) {
	console.log(data);
});

或者,您可以选择通过 HTTP POST 请求发送数据:

$.post("getmyphotos.php", { user:"johnsmith", id:"200" })
.done(function(data) {
	console.log(data);
});

jQuery Mobile

在最近,jQuery 团队发布了 jQuery Mobile (jquerymobile.com),它为开发人员创建了一个统一的HTML5用户界面,可以在现代移动设备的广泛范围内正确显示内容。就像 jQuery 本身一样,jQuery Mobile 非常轻量,甚至带有预构建的主题包,可以在可主题化的元素设计中使用。jQuery Mobile 旨在通过允许您更多地专注于应用程序内容而不是为浏览器支持编写特殊的 shims 和条件代码来简化您的移动开发过程。更新由开发团队发布的速度能够跟上移动设备市场惊人的速度。因此,您可以编写能够在尽可能多的设备上运行的移动 Web 应用程序,而无需专门针对每个设备进行定位:

jQuery Mobile

自发布以来,jQuery Mobile 已经被大大小小的网站广泛使用。jQuery Mobile 框架的核心方面包括页面、对话框、工具栏、列表视图和按钮的使用。通过围绕框架中的这些核心元素开发您的网页和内容,您可以为移动设备布局您的页面,而无需打开 Photoshop。

jQuery Mobile 充分利用了自定义数据属性,这是HTML5中的一个新功能。如果您查看下面的示例多页面 jQuery Mobile 布局,您会看到许多元素属性使用了data-*语法。这些是任何人现在都可以将其实现到他们的HTML5项目中的自定义数据属性。它们可以有任何字符串,至少是一个字符,并且可以用于在设置元素属性时轻松声明值:

<body>
  <div data-role="page" id="one">
    <div data-role="header">
      <h1>Page 1</h1>
    </div>
    <div data-role="content" >
      <h2>Page One</h2>
      <p><a href="#two" data-role="button">Show Page 2</a></p>
      <p><a href="page3.html" data-role="button">Show Page 3</a></p>
    </div>
    <div data-role="footer" data-theme="d">
      <h4>Page Footer</h4>
    </div>
  </div>

  <div data-role="page" id="two" data-theme="a">
    <div data-role="header">
      <h1>Page 2</h1>
    </div>

    <div data-role="content" data-theme="a">	
      <h2>Page Two</h2>
      <p><a href="#one" data-direction="reverse" data-role="button" data-theme="b">Back to Page 1</a></p>	
    </div>

    <div data-role="footer">
      <h4>Page Footer</h4>
    </div>
  </div>
</body>

正如你所看到的,这个单个的HTML文件实际上是两个页面,分别用data-role="page"元素分隔成 DIV 元素。现在当 jQuery Mobile 框架加载包含这两个页面的HTML文件时,只有初始页面会显示,第二个页面会等待用户交互滑动到视图中。在第一个页面中,你可以看到到我们第二个页面的链接实际上只是一个锚标签,因为它在引用当前HTML文档中另一个页面的 ID 之前使用了#字符。为了进一步说明这种差异,在初始页面的导航中还有一个到第三个页面的链接,它以传统的方式链接到外部HTML文档。

默认情况下,当请求新页面时,数据会被加载(如果尚未加载),并显示在一个 DIV 元素中,实际上对于最终用户是不可见的。当数据加载和文档准备工作完成后,新页面会从右向左动画显示给用户。这种内容动画是许多现代移动设备应用程序用户界面的典型特征,因此使您的应用程序更加熟悉于最终用户:

jQuery Mobile

jQuery Mobile 在实际操作中最简单、最精致的例子之一是该框架的文档。正如您在上面的截图中所看到的,默认情况下,jQuery Mobile 的用户界面看起来非常适合移动设备。按钮很大,可以轻松地拉伸以适应页面,让用户可以轻松选择菜单项而不必担心误点。标题和段落文本易于阅读,并且位置完美。在文档导航中的特定元素添加了图标。在打印的截图中看不到的是布局的响应性。为了更好地说明移动设备上响应性的重要性,这里是同一 jQuery Mobile 文档网页在更大的窗口大小下的截图:

jQuery Mobile

正如您所看到的,同一个页面现在已经响应了更大的浏览器窗口大小,并重新调整了页面布局以更好地适应可见的显示区域。jQuery Mobile 使用CSS媒体查询来定义当前的视口大小,并将页面内容定向到适当的位置,而不是在不同的浏览器大小上显示相同页面的多个设计。使用 jQuery Mobile 构建网站的最大优势是,您无需编写一行CSS或自己定义特殊的CSS媒体查询。

HTML5 模板

像 jQuery 这样的库非常适合帮助您轻松编写 JavaScript 代码,但让您的项目启动运行是另一个问题。页面布局、浏览器故障保护和跟踪代码通常是您最终会添加到项目中的所有内容,这些只是HTML5模板(html5boilerplate.com)中的一些出色功能。HTML5 模板在技术上并不是一个库或框架,因为在其核心,它只是创建 HTML5 文档的起点。

然而,由于其简单性、渴望跟上网络周围的所有变化以及在其背后有大量开源贡献的支持,这个HTML5模板在处理任何大小的项目时都是一个很好的起点:

HTML5 模板

从项目网站下载最新版本的HTML5模板后,您会发现一系列文件,其中不仅包括基本的 ready-to-go index.html文件及其引用文件,还包括一组其他常见文件,通常在公共网络服务器的基本网站目录中找到。

要了解这个模板的确切外观和它对您的实际作用,让我们快速浏览一下以下默认的index.html文件:

<!DOCTYPE html>
<!--[if lt IE 7]>      <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]>         <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]>         <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
        <title></title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width">

        <!-- Place favicon.ico and apple-touch-icon.png in the root directory -->

        <link rel="stylesheet" href="css/normalize.css">
        <link rel="stylesheet" href="css/main.css">
        <script src="img/modernizr-2.6.2.min.js"></script>
    </head>
    <body>
        <!--[if lt IE 7]>
            <p class="chromeframe">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> or<a href="http://www.google.com/chromeframe/?redirect=true"> activate Google Chrome Frame</a> to improve your experience.</p>
        <![endif]-->

        <!-- Add your site or application content here -->
        <p>Hello world! This is HTML5 Boilerplate.</p>

        <script src="img/jquery.min.js"></script>
        <script>window.jQuery || document.write('<script src="img/jquery-1.9.0.min.js"><\/script>')</script>
        <script src="img/plugins.js"></script>
        <script src="img/main.js"></script>

        <!-- Google Analytics: change UA-XXXXX-X to be your site's ID. -->
        <script>
            var _gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];
            (function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
            g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';
            s.parentNode.insertBefore(g,s)}(document,'script'));
        </script>
    </body>
</html>

正如您所看到的,这个模板 HTML 文件做了很多事情,而且幸运的是它有非常完善的文档。从头到尾,这个示例充满了浏览器检查和故障保护,网站图标的引用,用于清理和设置开发环境的 Modernizr 的引用,以及对 jQuery 和包括 Google Analytics 访客跟踪的默认代码的引用。

HTML5 模板是在 MIT 许可下开发的,甚至包括一些精制和优化的 Web 服务器配置,如果您有兴趣优化您的 Web 服务器提供内容的方式。

Bootstrap

如果你和我一样喜欢编写代码而不是处理在 Photoshop 中设计和创建页面,你可能会对 Bootstrap 很感兴趣,这是由两名 Twitter 员工创建的。Bootstrap 是一个 HTML5 框架,旨在让开发人员轻松创建基于 12 列网格系统的强大和响应式页面布局和设计。Bootstrap 支持在所有现代设备和浏览器上正确呈现页面布局,减少了大量编写 CSS 和 JavaScript 的需求,以便以统一的方式为所有用户显示内容,无论他们如何尝试查看您的内容:

Bootstrap

使用 Bootstrap 开发新项目并使其运行起来,就像本章涵盖的许多项目一样,非常简单。只需转到项目网站twitter.github.com/bootstrap并下载最新版本。下载并解压缩后,将下载的目录内容移动到项目目录的根目录。您会注意到下载的 Bootstrap 文件不包含用于开始工作的 HTML 文件,而是期望您生成自己的文件。原因是页面没有定义特定的布局模板。Bootstrap 利用网格布局系统,使开发人员能够轻松地将其网站内容放置在网格格式中,以便轻松地响应动态浏览器窗口大小的正确定义布局:

Bootstrap

默认的 Bootstrap 布局是建立在一个具有无限行数的 12 列网格布局上的,因为行的溢出将导致典型的网页滚动。通过查看前面图像中的示例网格布局,您几乎可以想象出您每天使用的每个网站以及它是如何在这样的网格中排列的。由于这种网格布局系统对于几乎任何网页设计都非常有价值,它几乎可以在您将来遇到的几乎每个 HTML5 项目中为您提供帮助。

如果您仍然不确定 Bootstrap 是否适合您的网站,请转到 Bootstrap 项目网站的示例部分,查看正在使用该项目的最新热门网站列表twitter.github.com/bootstrap/getting-started.html#examples

Bootstrap 附加组件

随着 Bootstrap 的流行度迅速增长,用户贡献的数量也开始跟随。许多这些第三方外部插件和功能可以添加到现有的 Bootstrap 设置中,以扩展其基本功能。让我们快速浏览一下其中一些最受欢迎的项目,以便让您对可用的内容有所了解。

StyleBootstrap.info

尽管 Bootstrap 在创建元素时附带了许多不同的颜色选择,但您可能希望进一步定制的机会相当高。StyleBootstrap.info (stylebootstrap.info)是一个很好的在线资源,可以通过简单的点击和选择用户界面轻松定制 Bootstrap 设置的外观和感觉。完成设计后,该网站将为您生成必要的CSS文件,供您下载并包含在项目中。

Font Awesome

另一个扩展 Bootstrap 已有功能集的优秀库是 Font Awesome (fortawesome.github.com/Font-Awesome)。虽然听起来像是这是框架的新字体添加,但实际上它是一个额外的图标集,可以轻松地实现到您的设计中。之所以提到字体的概念是因为图标集实际上是在打包的字体中实现的,以实现可伸缩的矢量图形,而不是在HTML文档中找到的典型位图图形。由于您来自 Flash 背景,您可能已经了解了在缩放图像时矢量图形有多么重要,您可能已经明白为什么使用字体打包概念会使库变得非常易于使用。包中的所有图标都有特定的名称,并且可以通过在HTML元素中调用该唯一图标名称作为类来轻松实现到您的页面中:

Font Awesome

正如前面的屏幕截图所示,其中仅显示了包中可用图标的一小部分,每个图标都有一个特定的名称。根据项目文档的规定,将图标附加到文档中的最佳方法是在<i>或斜体HTML标记的类属性中调用唯一的图标名称。例如,如果我们想在文档中的“书籍”旁边放置一个书籍图标,HTML 语法将表示如下:

<p><i class="icon-book"></i> Books</p>

由于斜体标记可以放置在几乎任何 HTML 元素中,这使您可以将图标放在需要的任何位置,比如放在 Bootstrap 自定义按钮内部:

<a href="books.html" class="btn">
<i class="icon-book"></i> Books
</a>

同样值得注意的是,由于字体包以矢量格式保存,以允许动态字体大小,因此默认的 Bootstrap 设置以及此项目中的图标也都是矢量格式。要更改文档中图标的大小,只需设置font-size属性或将其附加到已配置字体样式的元素中。

bootstrap-wysihtml5

如果您计划构建需要大量基于文本的用户输入的 Web 应用程序,那么 Bootstrap WYSIWYG(所见即所得)库值得一看(jhollingworth.github.com/bootstrap-wysihtml5)。只需几行代码,您就可以为用户构建格式化的HTML文本内容的优雅工具输入表单:

bootstrap-wysihtml5

尽管简单,这只是互联网上许多免费分发的众多优秀示例之一,随时可以在您的项目中使用。

Hammer.js

如果您计划进入快速发展的移动 Web 开发世界,处理新的事件,如触摸交互,将是必不可少的。尽管传统的 JavaScript 鼠标事件在触摸设备上直接转换为基本的触摸事件,但是像滑动和捏合这样的事件并不常见于传统的桌面用户交互(eightmedia.github.com/hammer.js)。

Hammer.js 目前支持轻触、双击、滑动、按住、捏合(变换)和拖动事件,并且可以轻松地实现到任何现有网站中,无论您是否使用 jQuery。由于库的简单性,压缩后的文件大小仅为 2KB:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Hammer.js Example</title>

    <style>
      body {
        padding:10px;	
      }
          #touch-area {
              border: 5px dashed #000;
              text-align: center;
              width: 100%;
              line-height:10px;
              padding-top:200px;
              padding-bottom:200px;
          }
          #touch-area p {
          font-size: 30px;
          }
          #touch-area p.subtext {
          	font-size:12px;
          	color:#666;
          }
      </style>

      <script type="text/javascript" src="img/jquery-1.9.1.min.js"></script>
    <script type="text/javascript" src="img/hammer.js"></script>
    <script type="text/javascript"  src="img/jquery.specialevent.hammer.js"></script>
    <script>
      function hammerLog(event){
          event.preventDefault();
          $('#output').prepend( "Type: " + event.type + ", Fingers: " + event.touches.length + ", Direction: " + event.direction + "<br/>" );
      }

      $(document).ready(function() {
        var events = ['hold', 'tap', 'swipe', 'doubletap', 'transformstart', 'transform', 'transformend', 'dragstart', 'drag', 'dragend', 'swipe', 'release'];

        $.each(events, function(key, val) {
          console.log('NOTICE: Applying Touch Event: ' + val);
          $('#touch-area').on(val,  hammerLog);
        });  
      });
    </script>
  </head>

  <body>
    <div id="touch-area">
      <p>Touch here to see results<p>
      <p class="subtext">For best results, open this page on a touch enabled device.</p>
    </div>

    <p id="output"></p>
  </body>
</html>

GreenSock 动画平台

如果您花了足够的时间开发 Flash 应用程序,很可能您以前已经接触过 GreenSock TweenMax 或 TweenLite 库。TweenMax 和 TweenLite 库可以轻松地让您在舞台上移动 Flash 对象,并支持 ActionScript 2 和 ActionScript 3 项目。GreenSock 现在已经开发并发布了他们的纯 JavaScript 库,没有依赖关系,为您的 HTML5 项目带来了许多伟大的熟悉功能。

因此,在查看 jQuery 动画方法及其功能后,为什么您需要使用这样的库呢?与 jQuery 不同,GSAP JS 专注于非常出色地完成一件事。诸如按顺序动画以启用适时的动画、覆盖控制以随时停止运行的动画以及能够对几乎任何内容进行动画处理等功能,将相对轻松地增强您的 Web 应用程序的视觉吸引力。

就像 ActionScript 伴侣一样,GreenSock JavaScript 库(www.greensock.com/v12)包含大量的最新文档和示例,将指导您正确的起步方向。事实上,他们专门创建了一个视觉快速入门指南,让您轻松上手并在浏览器中演示代码的结果:

GreenSock 动画平台

GSAP JS 文档的最佳补充是交互式入门指南,可以在www.greensock.com/jump-start-js/找到。这个简单易用的交互式应用程序可以让您在几分钟内从未使用过该库,了解它的功能;我无法强调这个功能有多么棒。

再次,如果您在以前的 Flash 项目中使用过 GreenSock TweenMax 或 TweenLite 库,那么您将非常容易地转移到 GSAP JS。如前所述,大多数 ActionScript 3 开发人员在使用此库时将面临的主要问题是正确处理为 Tween 提供的CSS3属性,以便正确运行。

Three.js

如果您喜欢硬件加速的 3D 图形世界,Three.jsmrdoob.github.com/three.js)绝对值得一看。这个轻量级的 3D 库非常容易上手,并且在网络上有大量的示例和文档。Three.js不仅使用<canvas>元素进行渲染,还使用<svg>CSS3DWebGL,从而支持各种现代浏览器和设备。

为了让您对Three.js在打印中的功能有所了解,请查看我在查看Three.js项目网站上找到的一些示例项目时拍摄的一些美丽的屏幕截图:

Three.jsThree.jsThree.js

从前面的截图中可以看出,JavaScript 和 WebGL 在很短的时间内取得了长足的进步。再次强调,所有这些截图都是从Three.js项目网站上找到的示例中获取的,所以一定要去那里尝试一下,看看它们在您的设备和浏览器上运行得如何。请记住,许多现代移动设备的网络浏览器都渴望获得更强大的 WebGL 支持,因此也可以在手机或平板电脑上尝试一下。

在开始开发您的Three.js项目之前,最好确保您熟悉 3D 编程的许多常见方面和原则。在其核心,典型的Three.js应用程序将包括一个场景、一个渲染器、一个摄像机和一个对象。这些元素将相互配合,以创建一个 3D 环境。学习关于Three.js的最佳入门教程之一是Paul LewisGetting Started with Three.js文章(www.aerotwist.com/tutorials/getting-started-with-three-js)。在这篇文章中,他涵盖了 3D 编程的所有原则以及如何在Three.js框架中利用它们。

不要深入细节,因为已经有许多优秀的书籍和在线资源可以学习 Three.js 开发,这里是一个在Three.js中渲染场景的非常简单的代码布局:

// Scene sizes
var WIDTH = 500;
var HEIGHT = 300;

// set some camera attributes
var VIEW_ANGLE = 45,
    ASPECT = WIDTH / HEIGHT,
    NEAR = 0.1,
    FAR = 10000;

// get the DOM element to attach to
var $container = document.getElementById('example');

// create a WebGL renderer, camera
// and a scene
var renderer = new THREE.WebGLRenderer();
var camera = new THREE.PerspectiveCamera(  VIEW_ANGLE,
                                ASPECT,
                                NEAR,
                                FAR  );
var scene = new THREE.Scene();

// the camera starts at 0,0,0 so pull it back
camera.position.z = 300;

// start the renderer
renderer.setSize(WIDTH, HEIGHT);

// attach the render-supplied DOM element
$container.append(renderer.domElement);

// create the sphere's material
var sphereMaterial = new THREE.MeshLambertMaterial({ color: 0xCC0000 });

// Set up the sphere vars
var radius = 50, segments = 16, rings = 16;

// Create a new mesh with sphere geometry -
// we will cover the sphereMaterial next!
var sphere = new THREE.Mesh(
   new THREE.SphereGeometry(radius, segments, rings),
   sphereMaterial);

// Add the sphere to the scene
scene.add(sphere);

// and the camera
scene.add(camera);

// create a point light
var pointLight = new THREE.PointLight( 0xFFFFFF );

// set its position
pointLight.position.x = 10;
pointLight.position.y = 50;
pointLight.position.z = 130;

// add to the scene
scene.add(pointLight);

// draw!
renderer.render(scene, camera);

从前面的Three.js代码示例的顶部开始,我们可以看到最初将舞台大小附加到WIDTHHEIGHT变量中。这些属性对每个 Flash 开发人员来说都很熟悉,它们定义了内容将被渲染的可视区域。在舞台配置之后是初始摄像机配置。创建 3D 场景时,渲染前端的视图将来自已放置在场景中的摄像机的透视。就像任何其他对象一样,摄像机可以根据 x、y 和 z 值以及属性(如视角、摄像机方面和缩放能力)移动。摄像机配置之后,我们需要将文档中的特定元素作为我们的舞台目标,并且document.getElementById查找我们在 HTML 文档中已经创建的元素就可以了。配置值设置好,选择了一个准备好设置我们场景的元素,我们实际上可以开始初始化我们的scene元素。

当然,Three.js项目并不需要 100%使用 JavaScript。一旦您的场景设置好并准备好查看,转到 Blender 或 Maya 等 3D 建模软件将允许您创建极其详细的 3D 对象,这些对象可以轻松地导入到您的 HTML5 项目中。正如您之前在一些示例图像中看到的那样,可以获得的细节水平简直令人惊叹。

关于在 JavaScript 中使用 3D 或 WebGL 的最后一点说明:目前在桌面环境中,浏览器对 WebGL 的支持已经非常广泛。您几乎不会在几乎所有现代桌面 Web 浏览器中遇到任何问题,但是在移动浏览器中可能仍然会遇到许多限制。谷歌 Chrome 浏览器在桌面和移动端都试图通过 Chrome 实验网站(www.chromeexperiments.com/webgl/)来推动 WebGL 的极限。该网站包含大量出色的示例和项目,可以让您轻松测试您正在运行的浏览器、设备或平台对硬件加速图形的处理能力。

编译 JavaScript

早已过去了将 JavaScript 仅视为用于 HTML 元素操作的前端开发语言的日子。随着 JavaScript 编译器的出现,仅仅编写一些 JavaScript 代码就可以做一些难以想象的事情。就像您在 Flash 中习惯的方法,其中 ActionScript 被编译成二进制包一样,JavaScript 编译器将纯 JavaScript 转换为机器代码,可以在计算机上像任何其他应用程序一样运行。尽管这个概念可能看起来很遥远,但实际上有很多很好的原因,其中最好的原因是用于 Web 浏览器。

谷歌的 V8 引擎

2008 年底,谷歌发布了 Chrome 的初始版本,随之而来的是 V8 引擎的初始版本。V8 将 JavaScript 直接编译成本机机器代码,甚至在这样做时优化代码。结果是应用程序可以像用 Python 或 C++编写的应用程序一样运行。V8 是用 C++编写的,并自原始发布以来一直是开源和免费提供给公众的。您可以通过访问项目网站code.google.com/p/v8了解更多关于 Google V8 项目的信息。

Node.js

从谷歌 V8 引擎诞生的最酷的新项目之一就是 Node.js(nodejs.org)。Node.js允许您完全使用 JavaScript 编写服务器端应用程序(通常是 Web 服务器),这通常是用PHPPerlPython甚至CC++等编程语言完成的。

与本章涵盖的许多框架和库一样,Node.js拥有大量的优秀文档和示例,遍布整个网络。然而,由于一些项目的开发速度,可用的文档很容易过时。Node.js最好的资源之一是由Node.js项目的早期核心贡献者之一Felix Geisendörfer创建的nodeguide.com,该资源不断更新到项目的当前稳定版本。

由于Node.js是用于服务器端运行的,而不是将其包含到您的HTML5项目中,您必须在计算机上安装它以便作为应用程序运行。因此,在您选择的计算机上下载并安装Node.js文件后,您现在可以像运行任何其他命令行应用程序一样从命令行运行Node.js准备好的 JavaScript 文件。

为了演示如何启动Node.js应用程序的基本用法,我们将使用流行的Node.js Web 服务器示例,该示例可以在官方文档中找到。创建一个名为example.js的新 JavaScript 文件,并使用以下 JavaScript 填充它:

var http = require('http');

http.createServer(
  function (request, response)
  {
    response.writeHead(200, {'Content-Type': 'text/plain'});
    response.end('Hello World\n');
  }
).listen(8000);

console.log('Server running at http://localhost:8000/');

示例代码的第一行是在Node.js框架中导入HTTP模块。包含 HTTP 模块后,将调用createServer方法并提供成功函数。该函数包含一个简单的“Hello World”问候,并将Content-Type设置为text/plain,因此查看它的浏览器知道它只是纯文本。最后,将listen方法链接到服务器声明中以指定端口,HTTP服务器将在该端口上监听请求。

保存example.js文件后,在运行Node.js的系统上打开命令行,并将当前工作目录指向您的新 JavaScript 文件的位置,然后输入以下命令。使用Node.js执行文件就像使用node应用程序引用 JavaScript 文件一样简单:

% node example.js

执行此命令将产生以下响应:

Server running at http://localhost:8000/

命令行将等待物理终止。在停止服务器之前,我们必须测试以确保它正常工作。因此,在执行node命令后,转到响应中指定的 URL:

Node.js

尽管这个输出很简单,但事实上,您只是使用 JavaScript 创建了一个简单的自定义 Web 服务器。这只是Node.js所提供的众多功能的开始,最好的部分是Node.js可以做的许多伟大的事情已经存在,并且随时可供您查找和使用。Node.js不需要在 Google 上花费数小时搜索要包含在项目中的模块,它使用自己的系统来查找并安装更多功能到您的Node.js服务器中。

Node 包管理器

如果您仍然不确定Node.js对您有什么作用,NPMNode 包管理器)可能能够帮助您。包管理器是一个在线收集的包,可以轻松地在您的节点项目中下载和使用。由于Node.js已安装在您的计算机上,当涉及到检查依赖关系、版本和平台支持时,包管理器可以完成所有繁重的工作。要轻松搜索当前的Node.js NPM目录,请转到npmjs.org并浏览,直到找到您有兴趣安装的内容:

Node 包管理器

在注册表中找到的任何包都可以通过在命令行中运行安装命令来轻松安装到您的系统上:

% npm install PACKAGE-NAME

如前所述,如果您请求安装的软件包需要注册表中的其他软件包,它们将自动下载和安装,而无需您自己去寻找适当的版本。随着开发人员对您可能使用的软件包进行更新并发布更新,NPM 注册表将自动负责向您提供更新信息,并允许轻松更新您安装的任何过时软件包。

托管公共 Node.js 服务器

由于您需要一个服务器来运行您的Node.js项目,您需要设置一个面向公众的服务器来托管您希望对互联网开放的任何项目。由于在计算机上安装Node.js进行测试无法实现这一点,您需要设置自己的服务器,并进行适当的网络设置,或者从Node.js托管公司购买此服务。由于您用于网页托管的公司通常不允许您完成此操作,因此研究 Nodejitsu(nodejitsu.com)等服务可能会有用:

托管公共 Node.js 服务器

与传统的网络托管公司一样,Nodejitsu 提供您自己的面向公众的Node.js服务器,可用于在线的任何网页项目。您可以始终从免费试用账户开始,以了解该服务如何允许您在世界各地使用您的Node.js服务器,然后根据您的需求转入付费账户。

摘要

本章仅仅触及了作为 HTML5 开发人员可用的一部分内容。这里介绍的是目前最流行的一些库和框架的集合。全世界开发人员公开发布并积极开发的惊人代码数量似乎以超指数速度增长。分配和利用本章列出的外部资产需要您作为开发人员在将其实施到公开网站之前了解使用库或框架的影响和好处。

花时间去研究、测试和贡献许多您感兴趣的项目,不仅有利于您现有的开发技能,还可以让您利用手头的最佳工具。随着时间的推移和网络开发的变化,跟上这些流行的库和框架将始终有助于您了解整个 HTML5 开发环境的最新情况。

在下一章中,我们将把这些新获得的 HTML5 框架和库的知识扩展到直接将您现有的 Flash 应用程序转换为 HTML5 网页项目的领域。

第七章:选择开发方式

进入 HTML5 开发流程将需要您摆脱在 Flash 开发周期中熟悉的应用程序。诸如 Flash 专业版、Flash Builder 和 Flash Develop 等应用程序都是专门设计用于处理 Flash 内容的。尽管这些应用程序非常出色,但有许多类似的 HTML5 开发应用程序可以让您以非常类似的方式构建丰富的网络体验。本章将介绍摆脱使用 Adobe Flash 专业版开发环境的过程,并开始艰难决定在创建 HTML5 项目时使用什么新的开发软件。虽然有许多优秀的软件可供选择,本章将介绍许多当前网页开发人员正在使用的新型和流行的应用程序。

在本章中,我们将涵盖以下内容:

  • 了解 HTML5 IDE 所需的内容

  • 资产创建和操作工具

  • 使用 Adobe Edge 创建交互式动画 HTML5 元素

  • 一些最受欢迎的 HTML5 代码编辑器概述

  • 代码执行和运行时测试工具

替换 Flash 开发环境

在 Flash 环境中开发应用程序的最大优势在于 Adobe 花费大量时间构建了 Creative Suite 中包含的工具和应用程序,使您能够在其应用程序集中创建整个应用程序。尽管有人认为这个系统限制性强,更新速度慢。事实是,当所有正确的工具都可用于帮助您构建应用程序时,进入下一个项目就会更容易。由于 HTML5 开发不受特定公司的监管或控制,它是一个更加开放的开发平台,开发人员可以自由选择如何以及使用什么来构建他们的项目。

我必须强调,在本章中,我们将概述许多 HTML5 开发人员用来完成工作的常见方法和应用程序。当然,这些应用程序或方法都不是构建 HTML5 项目的绝对正确方式,希望您能找到符合您需求的应用程序。随着时间的推移,您构建越来越多与 HTML5 相关的项目,一定要在研究最适合手头工作的最佳工具时付出额外的努力。快速变化的环境导致许多项目在短时间内兴起和衰落。了解市场上的情况将有助于保持您的竞争力,并继续扩展您的 HTML5 开发技能。

HTML5 开发环境的要求

当在同一个代码编辑器中编写 HTML、CSS 和 JavaScript 时,大多数开发人员通常会考虑一些一般性的因素,以确保他们获得适合自己需求的功能集。由于整个 HTML5 堆栈都是以纯文本文档的形式呈现,从技术上讲,任何文本编辑器都可以完成工作。尽管每个开发人员都有自己独特的设置和开发风格,但总是值得留意许多常见的功能。

资产和文件管理

具有预览甚至操纵项目中包含的资产的能力,如图像、视频、音频和其他外部资产,直接在开发环境中可以帮助您加快开发流程,将焦点集中在特定应用程序内。我们将在稍后介绍的 Adobe Dreamweaver 等应用程序是设计用来将设计和开发过程结合在一起的绝佳例子。值得注意的是,本章将概述的许多简单的代码编辑器可能不包含支持轻松文件和资产管理的功能。然而,当像这样的大型功能集成不包含在代码编辑器中时,一般的最终结果是更快速、轻量级的应用程序。

代码高亮

与任何编程语言一样,代码高亮或着色是代码编辑器中必不可少的功能。轻松理解代码的各个部分在做什么的能力,不仅可以让您更轻松地开发应用程序,还可以帮助您更轻松地理解其他开发人员的代码。代码高亮也是确保您以正确的语言语法编写代码的关键。为了让代码编辑器能够正确地着色或高亮显示您的代码,应用程序必须能够正确识别和解析您的代码所编写的特定语言。因此,在寻找最佳选择时,要密切关注支持您打算使用的特定编程语言的代码编辑应用程序是至关重要的。幸运的是,在我们的情况下,HTML5 开发或 HTML、CSS 和 JavaScript 开发得到了许多可用的代码编辑应用程序的广泛支持,因此您拥有的选择非常丰富。

代码完成

在您选择使用的代码编辑器中内置良好的代码完成功能可以帮助您学习新的编程语言。如果您在 Flash 开发职业生涯中使用 Flash Builder,我相信您已经看到了您可以编写良好、有效的代码的速度有多快。尽管一些开发人员认为代码完成只会让开发人员变得懒惰,避免记住语言语法的具体细节。事实是,从新手到经验丰富的老手,开发人员都使用代码完成来避免在编写代码时出现延迟,以及加快编写长代码片段所需的时间。

在使用 Flash Builder(www.adobe.com/products/flash-builder.html)或 Flash Develop(www.flashdevelop.org)等开发环境开发 Flash 应用程序时,您将面临着利用代码完成的最佳情况之一。由于这些代码编辑器专门用于编写 ActionScript 3,它们可以通过专注于 ActionScript 3 API 中可用的内容来优化开发体验。

实际上,在开发 HTML5 或其他许多语言时,存在两种形式的代码完成。这种明显的形式是在您输入时自动完成文本。例如,当您键入声明的变量名称并按下.键以准备指定该对象上的属性时。一些编辑器将在活动代码行下生成一个下拉菜单,列出您可以附加到当前对象上的可用属性列表。当学习一种新语言时,这种代码完成形式非常方便,因为它在您开发时就在您面前展示了可用的可能性。代码完成的第二种形式是生成更大的代码片段。例如,当您尝试通过在代码编辑器中键入单词function来声明一个新函数时,一些编辑器会识别这一点,并自动生成默认的函数布局。您所要做的就是填写内部代码,然后完成。一些开发人员对此功能有意见,因为它可能无法生成符合其精确规范的代码,但现在许多编辑器都支持修改预先存在的代码片段甚至添加自己的代码。

创建和操作资产

作为以前在 Flash 中创建应用程序的开发人员,您可能已经习惯于使用 Flash Professional 开发环境,不仅可以将应用程序资产文件存储在 SWC 文件中以供项目包含,还可以构建整个项目。在功能丰富的开发环境中使用项目资产的能力,比如 Flash Professional,是 Flash 首先变得如此受欢迎的原因之一。转向 HTML5 开发时,失去这样一个用于构建和操作资产的出色开发环境将是一件遗憾。幸运的是,随着 HTML5 的普及,许多新的令人兴奋的项目和应用程序已经发布,将这种资产控制带到了 Web 开发周期中。

Adobe Edge Animate

随着 Adobe 将其产品发展为完全基于云的软件设置,他们还推出了许多基于 HTML5 的项目,使 Web 开发人员可以在传统的 Adobe 用户友好环境中轻松创建 HTML5 内容。在这个系列中最新和令人兴奋的软件之一是 Adobe Edge。Edge 允许在一个点和点击用户界面中轻松创建交互式和动画的 HTML5 内容。实质上,您可以将 Edge 视为在 HTML5 堆栈中使用 HTML、CSS 和 JavaScript 开发时 Flash IDE 的替代品。尽管这个软件远没有 Flash IDE 支持的功能强大,但自发布以来,其功能支持已经呈指数级增长。

通过登录 Adobe 的应用程序管理器,可以免费下载 Adobe Edge。前往html.adobe.com/edge/animate下载 Edge,并注册 Adobe 帐户(如果尚未注册)。设置完帐户后,在应用程序管理器中找到Edge Tools & Services部分,并将软件下载到您的计算机。值得注意的是,正如前面提到的,这种新的基于云的软件交付系统是 Adobe 正在朝着的新方向,以便更轻松地访问其目录中的软件。您可以通过单击下面截图中显示的每个可用软件描述下的Try链接来轻松测试 Adobe 的任何其他产品。

Adobe Edge Animate

一旦您成功下载并安装了 Edge,启动它,您就可以首次看到用户界面。虽然它不完全像您可能习惯于的 Flash Professional 用户界面,但您可能会看到许多相似之处,这将使您轻松地整合您从 Flash、Photoshop 等软件中获得的现有 Adobe 用户界面技能:

Adobe Edge Animate

让我们花点时间来概述 Edge 对 CSS3 滤镜和动画等独特功能的激动人心的支持和功能。我们可以从一个简单的蓝色框开始,这是用户界面领域的 Hello World。默认情况下,主要工具栏位于窗口顶部,方形形状工具在其中很容易找到:

Adobe Edge Animate

值得一提的是,工具栏中的其他工具也很熟悉和不言自明。无论如何,我们很快就会回来仔细研究它们。现在我们将选择方形形状工具,并在用户界面中呈现的舞台上绘制一个相当大的矩形。请注意,这个过程与在 Flash 中创建内容非常相似:

Adobe Edge Animate

默认情况下,你的形状不会是蓝色的,所以在放置在舞台上后,转到默认情况下位于应用程序窗口左侧的属性面板,并使用颜色选择器修改形状的颜色。

准备好进行动画处理的框后,让我们把焦点转移到默认情况下显示在应用程序窗口底部的时间轴面板。如前所述,如果你花时间在 Flash Professional 和基于时间轴的动画上,Edge 中的这个功能不仅会很熟悉,而且可能会让你兴奋。如果时间轴动画的概念对你来说是新的,那么它是控制应用程序视图中元素在一定时间内的过程。通过定义你的资产如何随时间变化,你可以轻松控制它们在特定播放间隔上的行为。为了演示这一点,我们将使用时间轴来使我们的新蓝色矩形在舞台周围进行动画,同时应用不同的效果和属性。

首先,我们将启用切换销钉,这将轻松地允许我们在动画时间轴的关键帧中设置元素的新状态。切换销钉位于时间轴面板中的顶部按钮导航中。它用蓝色销钉标记表示:

Adobe Edge Animate

启用切换销钉后,当时间轴设置为新位置时,元素的任何更新将仅应用于该新关键帧内。结果将是在一定时间内自动生成的动画补间。

通过将时间轴上的播放头拖动到 1 秒,我们现在告诉 Edge 开始将新属性应用于舞台上的任何元素。因此,让我们将蓝色框从舞台的左上角拖动到右上角,然后按空格键查看结果动画:

Adobe Edge Animate

当然,在文本中你只能相信我的话,但结果是你在 Flash 中对元素进行补间动画时所习惯的。当播放头从 0 秒移动到我们新的 1 秒关键帧时,蓝色框的位置会自动更新,从起点到终点进行动画。尽管这个例子很简单,但它不仅展示了 Edge 与 Flash Professional 非常相似的许多方面,而且突出了 Edge 的核心功能。

让我们花更多时间来检查我们的蓝色框还能做些什么,通过查看属性面板中提供的内容。不要深入每个属性面板的细节,总结起来最简单的方法是,它显然受到了 Flash 专业版中属性面板的启发。虽然 Edge 中可用的一些属性与 Flash 不同,但布局和风格几乎相同。如果 Adobe Edge 引起了您的兴趣,值得花些时间查看当前版本中提供的一些可用属性。我还建议在测试时,打开发布的文件,并在您可以使用的所有浏览器中进行测试,从桌面到移动设备。了解不同设备和平台对 HTML5 动画负载的反应,以更好地判断未来应用程序的推进程度。

当然,在开发或测试阶段的任何时候,您都可以在文件菜单中选择在浏览器中预览选项,查看您当前项目在实际网络浏览器中的外观和感觉。这也是深入生成的源代码以更好地了解 Edge 编译器实际为我们做了什么的绝佳时机。

Edge 将应用程序源代码构建为 HTML、CSS 和最小化的 JavaScript 文件,并将 JavaScript 数据保存为YOUR-PROJECT-NAME_edgePreload.js的文件名。尽管这些最小化的 JavaScript 很难阅读或理解,但它被设置为尽可能小的文件大小,以优化通过互联网由最终用户检索时的加载速度。

Adobe Edge 还包含一个内置的代码编辑器,允许您轻松地将代码附加到您的 Edge 项目中,进一步扩展您的 Web 应用程序的功能。这个代码编辑器,虽然使用方式略有不同,但对于任何在 Flash IDE 中编写过任何 ActionScript 的人来说,它是一个极具辨识度的面板。在代码编辑面板中,您将找到一系列代码片段,可以通过单击将其附加到您的项目中。从在您的元素上添加播放方法调用这样简单的功能,到动态创建和销毁您的元素的新实例,内置的代码片段可以轻松帮助您入门。代码编辑器还可以通过仅显示您操纵元素所需的内容来简化代码显示。这可以通过选择代码窗口右上角的完整代码选项卡来切换,结果将显示整个项目 JavaScript 文档源代码。

编码环境

通常,创建 HTML5 项目的大部分工作将在一个设置允许您在同一位置编写 HTML、CSS 和 JavaScript 的环境中进行。由于所有这些不同的开发语言都包含在纯文本文件中,因此在选择编辑器时没有特定的要求。然而,随着 HTML5 成为一个更成熟的 Web 和应用程序开发平台,支持媒体集成、代码格式化和完成、设备测试和调试等功能的平台几乎已成为必需。许多自 HTML5 之前就存在的软件标题已更新其功能集,以支持 HTML5 开发,并添加了新功能,使 Web 开发变得更加容易。一个很好的例子就是 Adobe Creative Suite 最新版本中包含的最新版本的 Adobe Dreamweaver。

Adobe Dreamweaver CS6

由于 Adobe Creative Suite 应该已经对您来说有些熟悉,我们将从 Adobe 的 Dreamweaver 开始概述 HTML5 代码编辑器。Dreamweaver 自从第 3 版以来一直是 Creative Suite 的一部分。尽管您可能会发现许多网页开发人员对 Dreamweaver 有爱恨交织的关系,因为如果您购买了 Creative Suite,它非常容易获得,许多网页开发人员曾经或另一次使用过它。现在需要注意的重要事情是,无论您以前是否使用过 Dreamweaver,Adobe 都已经添加了大量专门与 HTML5 网页开发相关的新功能,以帮助您整个开发周期中的工作。

Adobe Dreamweaver CS6

我不会深入探讨 Dreamweaver 的使用,因为网络上有许多优秀的书籍和教程可以帮助您学习其功能集和用户界面。然而,我将概述 Dreamweaver CS6 包含的一些新的和令人兴奋的功能,以便让网页开发人员轻松地将 HTML5 元素和功能集成到他们的网页项目中。值得注意的是,写作本书时,这些功能中的许多功能只在 Creative Cloud 上 Dreamweaver CS6 的第二次更新中才可用。目前安装了 Dreamweaver 原生版本的用户目前没有这些功能。如果您有兴趣测试其中一些功能,请从 Creative Cloud 下载 Dreamweaver 的 30 天试用版并尝试一下。

音频和视频嵌入

Dreamweaver CS6 的最新更新增加了一些围绕将 HTML5 准备好的音频和视频文件包含和操作到您的文档中的新功能。与许多可以导入到 HTML5 项目中的媒体形式一样,音频和视频现在可以轻松地从项目源目录中选择,并通过几次点击放置到您的文档中。从 Dreamweaver 用户界面直接设置元素属性,如自动播放,启用播放控件,甚至设置海报图像,都可以轻松完成。这个过程不仅可以确保您正在开发媒体播放代码到正确的语法规范,而且可以轻松地为只支持特定文件类型的浏览器和平台设置播放替代方案。

Adobe Edge 支持

由于您已经了解了 Adobe Edge Animate 的一些功能,您可能会理解为什么直接将 Adobe Edge 集成到 Dreamweaver 中对于网页开发人员来说是一个巨大的胜利。现在,您可以无缝地将交互式和动画元素直接集成到 Dreamweaver 项目中,而不是手动地从 Adobe Edge 项目中提取导出的数据并将其应用到您自己的项目中。如果您曾经有幸在 Flash Professional 中使用“从 Photoshop 导入”功能节省了几个小时,您将很容易理解这种跨应用程序通信如何为您节省无数的开发时间。

PhoneGap 和 jQuery Mobile 支持

您可能会惊讶地发现,Adobe 实际上在 jQuery Mobile 框架的开发中扮演了重要角色。看来两者的关系仍然很密切,因为 Adobe 已经将其对 Dreamweaver CS5.5 的完整 jQuery Mobile 支持延续到了 CS6。最新的 jQuery Mobile 功能更新使得为您的 jQuery Mobile 项目设置主题变得非常容易:

PhoneGap 和 jQuery Mobile 支持

正如您在上一张截图中所看到的,jQuery Mobile Swatches面板允许我们轻松选择项目的实时视图中的特定元素,并通过单击鼠标应用新的主题属性到该元素。生成的代码更新显示为突出显示的更改,向您展示了在现有文档中进行了什么修改。此功能远远超出了仅支持默认 jQuery Mobile 主题。Dreamweaver 将自动检测已附加到项目中的任何自定义主题,并允许您继续在 Dreamweaver jQuery Mobile Swatches面板中操作和实现该主题。您可以将选择范围缩小到网页中的特定元素,并修改图标和字体等资源。最受欢迎的 HTML5 移动框架变得更加易于使用。

PhoneGap 用户也不会被冷落。如果您希望将移动项目构建为原生应用程序,Dreamweaver 已经从开发人员的角度使其非常用户友好。新的 PhoneGap Build Service面板允许您通过单击几下鼠标为任何支持的移动平台构建当前的工作项目。您可以从 Dreamweaver 内部将 PhoneGap 构建发送并从 PhoneGap 构建服务器下载。使用 HTML5 为五种不同的流行移动平台构建原生应用程序从未如此简单或用户友好。

流体网格布局和 HiDPI 支持

Dreamweaver 中的新流体网格布局系统允许您从项目创建开始轻松地针对特定设备定位和自定义网页布局。在页面布局中激活网格系统后,您可以开始指定特定元素可以占用多少列。如果浏览器窗口大小调整或页面加载到高于或低于目标屏幕分辨率的显示器上,网格系统将自动响应更改,更新将显示多少列。在网页中布置元素的概念并不新鲜。然而,随着现在可以访问您的内容的设备的发布,要求跟上现代显示规格可能会很耗费精力。Adobe 已经使 Dreamweaver 用户能够轻松集成优化的条件集,从而实现轻松的响应式 Web 设计。

流体网格布局和 HiDPI 支持

今天我们看到移动和台式屏幕的像素密度越来越高。即使开发人员没有访问权限,也需要适当的环境来测试这些显示器,这已经成为必须。Dreamweaver 现在已经将 HiDPI 支持集成到易于使用的用户界面中,使得针对特定显示类型进行测试变得轻而易举。

Aptana

如果您来自主要在 Flash Builder 中存在的 Flash 开发背景,那么 Aptana(www.aptana.com)可能值得一看。Aptana 建立在与 Flash Builder 相同的 Eclipse(www.eclipse.org)编辑器上,为许多 Flash 开发人员带来了非常熟悉的代码开发用户界面:

Aptana

Aptana 包括许多专门设计用于辅助 Web 开发的出色功能。代码辅助功能可帮助处理 HTML、CSS 和 JavaScript 语法,部署向导可以轻松地将自动文件更新集成到您的公共 Web 服务器中。Aptana 还包含内置的 Git 集成支持,因此您可以轻松地为您的项目集成版本控制支持。与 Flash Builder 一样,Aptana 允许您轻松地将多个项目同时添加到应用程序中。在您面前有来自多个项目的代码可以在引用其他地方添加的功能源代码时轻松节省时间。Aptana 是免费的,开源的,并且由一大群贡献者积极开发。

Brackets

Adobe 的 Brackets(brackets.io/)是目前正在开发中的最新和最令人兴奋的 HTML5 代码编辑器之一。这个开源编辑器不仅专门为 HTML5 开发人员设计,而且应用程序本身实际上是用 HTML5 堆栈编写的,使您可以轻松定制您的编辑体验。

Brackets 实际上是 HTML 开发在过去几年中发展的一个惊人的代表。使用 Web 技术在计算机上创建这样一个丰富的交互环境来操作本地文件,只是朝着完全基于 Web 应用程序的生活方式迈出的又一步。

Brackets 仍处于早期开发阶段,但已经可以被任何人使用。尽管它是用 HTML、CSS 和 JavaScript 编写的,但由于它被打包并作为桌面应用程序运行,它可以轻松地在本地机器上创建和操作文件。

要开始使用 Brackets,您需要前往项目网站获取最新版本的链接(download.brackets.io/)。与许多开源项目一样,作为最终用户,您将被要求下载软件的预打包版本,这通常是最稳定的。或者您可以下载每夜版或最新的开发版本,这是项目贡献者正在积极开发的版本。开发版本可能会不稳定,并可能在使用过程中出现一些问题。然而,如果您愿意冒着使用有 bug 的软件的风险,那么您在使用过程中获得的信息和经验对于项目的开发团队来说可能非常重要。您遇到的问题和 bug 应该记录在 Brackets 的 GitHub 项目账户的Issue Tracker中(github.com/adobe/brackets/issues)。

为了举例说明,我将下载软件的最新预打包稳定版本,以展示 Brackets 提供的一些令人兴奋的功能。下载安装程序并运行后,在 Windows 或基于 OS X 的机器上打开应用程序。在应用程序的初始启动时,您将看到一个默认的示例设置,类似于以下屏幕截图:

Brackets


正如您在上一个屏幕截图中所看到的,界面虽然对许多人来说很熟悉,但却极为简单,同时又具有优雅的风格和布局。

内联编辑

目前内置在 Brackets 中的最酷的功能之一是其易于使用的内联代码编辑系统。作为 Web 开发人员,您会发现自己不仅在开发项目时从一个程序跳转到另一个程序,而且还会从包含完全不同编程语言的文件跳转。为了简化这个过程并加快文档中元素的开发,Brackets 允许您选择 HTML 文件中的元素,并查看它们的相关 CSS 样式。要实现这一点,选择 HTML 示例文件中的一个元素,然后按下Ctrl +ECmd + E(取决于您的操作系统)以在同一 HTML 文件中直接显示该元素的样式。

您不再需要浪费时间在专用的 CSS 文件中逐行查找元素样式。现在 Brackets 可以在您继续编写代码的同时进行所有繁重的搜索工作:

内联编辑

当内联编辑器显示时(如在前一个屏幕截图中在<body> HTML 元素标签下看到的),不仅可以轻松编辑与所选元素相关的样式,还会显示一些重要的数据。在内联样式窗口的左上角是包含相关样式的文档的文件名,文件名旁边是可以找到样式的行号。

内联编辑的概念就像是简单的一样,我们可以通过检查包含多个样式定义的元素的样式来进一步完善它。例如,查看以下链接元素中的多个不同样式的括号显示了所有以某种方式应用于同一元素的不同样式。通过在内联编辑器中选择不同的定义,您可以轻松地在每个样式设置之间切换,编辑它们,然后继续进行。当然,这种内联编辑的概念不仅适用于 HTML 和 CSS,它也适用于您的 JavaScript 开发周期。Brackets 团队仍在将更多的内联编辑功能扩展到应用程序中,扩展功能包括颜色和渐变选择等。

实时预览

Brackets 中已经内置的另一个很棒的功能是实时预览。与传统的编辑代码、保存代码,然后转到浏览器进行测试的方法不同,实时预览系统将这一切简化为在您输入时自动进行测试构建。当在应用程序窗口的右上角激活实时预览按钮时,默认的系统 Web 浏览器将打开包含当前工作的 HTML 页面。如前所述,选择了此功能后,您可以继续修改当前文档并在输入时查看反映的更改:

实时预览

上述截图说明了对 body 元素的 background-color 样式属性的更改,将其从白色更改为红色。这种简单的自动保存和重新加载功能只是 Brackets 的另一个部分,它使 Web 开发人员花费更少的时间来执行重复耗时的任务。

插件

由于 Brackets 是开源的,并且是使用与其预期使用的相同平台创建的,许多开发人员已经开始为 Brackets 创建自己定制的扩展和插件。从使用鼠标进行自定义代码高亮的能力,到增加对代码完成的支持,公众的输入范围已经非常惊人。当然,您不必依赖公众来将新功能引入 Brackets。如前所述,整个项目都是使用您在本书中学习的技术构建的。因此,如果您愿意尝试添加一些新的独特功能到 Brackets 中,这可能是一个很好的学习项目。

贡献

Brackets 最棒的部分不仅在于它完全开源、免费提供并且在积极开发中,而且整个应用程序都是基于 HTML5 堆栈构建的。随着您的 Web 开发技能的增长,Brackets 项目可以成为一个与世界其他地方分享您的开发技能的绝佳场所。开发团队不断追加公众提交的更新和修改,并且始终要求用户提供更多的意见。由于 Brackets 仍在开发中,现在是一个很好的时机来加入并帮助创建可能成为下一个重要的 HTML5 代码编辑器的东西。所有项目信息都可以在项目网站页面以及项目 GitHub 页面中找到。如果您希望深入了解更多,请登录 IRC 并在freenode.net上检查#brackets频道。

Sublime Text

如果像 Brackets 这样的轻量级代码编辑器更适合您,那么 Sublime Text 是另一个值得一试的编辑器(www.sublimetext.com)。Sublime 简单、轻巧,并且支持大量的编程语言,因此它不仅可以用于 HTML5 开发:

Sublime Text

Sublime 的多文本选择功能值得注意。在处理大型代码文档时,经常需要对大量文本进行相同的编辑,例如间距。为了解决这个问题,Sublime 使用多文本选择和编辑功能,允许您轻松修改同一文档的许多部分,只需进行一次更改:

Sublime Text

正如您在上述截图中所看到的,Sublime 还包括代码的最小化布局,以便您根据外观轻松定位代码的特定部分。尽管这听起来有些奇怪,但令人惊讶的是,它确实非常有效。Sublime 还有许多其他出色的功能,使它成为我个人最喜欢的代码编辑器之一。

Sublime 可在 Windows、OS X 和 Linux 上免费下载和永久使用。但是,为了去除购买提醒,可以从 Sublime Text 网站购买 70 美元的许可证(www.sublimetext.com/buy)。

执行和测试

到目前为止,所展示的许多软件标题都包含了它们自己的方法来帮助您测试和调试您的网站和应用程序的过程。然而,用于测试和测试的技术数量正在以难以保持领先地位的速度增长。拥有不同应用程序和服务的库不仅可以让您测试项目的许多不同方面,还可能在此过程中节省大量时间。

Web 浏览器开发人员控制台

尽管我们在本书中已经花了一些时间研究了许多流行浏览器开发人员控制台中包含的功能,但在考虑前端执行基准测试和测试时,还有一些其他方面值得研究。随着项目的规模和复杂性不断增长,您需要花时间优化应用程序的流程和执行。如果您曾有机会使用 Adobe Flash Profiler 或 Adobe Scout 来深入了解应用程序在运行时的操作和情况,您可能已经了解了这种预防措施的好处:

Web 浏览器开发人员控制台

上述截图来自新的 Adobe Scout (gaming.adobe.com/technologies/scout/),游戏开发人员使用这些工具来查看游戏实际进行时的情况。不幸的是,放弃 Flash 开发意味着放弃诸如 Scout 之类的新应用程序和分析器的使用,但是作为 HTML5 开发人员,您有许多可供选择的替代方案,我们只需要去寻找它们。

提示

在 2013 年 Adobe Max 会议期间,讨论了专门为 HTML5 开发而构建的新版本 Adobe Scout 的细节。请在 Adobe 网站上关注这个神奇工具的发布日期。您还可以在会议上观看视频演示tv.adobe.com/watch/max-2013/adobe-scout-profiling-taken-to-the-next-level/

我们已经花了一些时间来研究今天许多流行的 Web 浏览器中的 JavaScript 或 Web 开发人员控制台,但这些面板包含许多其他功能,可以帮助您在发布项目之前对其进行测试和基准测试。

网络分析

许多网络浏览器中的开发者控制台包含网络控制台,允许您从用户的角度可视化您的网页中的数据是如何加载的。在加载页面之前打开控制台,当页面加载完成时,实时数据将传递到一个易于阅读的表格中,该表格可以显示正在加载的文件,它们是否成功加载,资产的文件大小以及加载所需的时间:

网络分析

将所有这些数据结合起来可以很容易地帮助您找到在开发阶段可能忽略的网页中的问题。要注意的一件简单的事情是,一旦页面完全加载,要注意页面的总加载大小是多少。考虑到用户连接到互联网的各种方法和速度,当尝试优化项目中使用的资产的文件大小时,始终首先考虑最终用户是明智的。

时间轴分析

使用许多常见的内置时间轴分析工具,您可以简单地点击记录按钮,捕获应用程序运行时内部发生的情况。当事件被捕获时,它们会实时显示,并显示总内存使用情况。在尝试定位应用程序中任何潜在内存泄漏可能发生的地方时,这些数据非常有帮助:

时间轴分析

如果您对这些应用程序分析方法中的一些形式感到陌生,不用担心,我们将在接下来的章节中更多地涵盖这个主题。花时间检查您的网站和应用程序在不同平台上的实际运行情况,可以在发布后避免许多麻烦。

Stats.js

在 Flash 中处理每秒帧数FPS的概念是经常发生的事情。由于整个平台都建立在时间轴的概念上,使您的应用程序以特定的 FPS 或最大 FPS 运行通常是每个 Flash 项目的最终目标。在您的 Flash 开发生涯中,您可能曾经遇到过或甚至使用过 Mr. Doob 的 Hi-ReS Stats 脚本的版本(github.com/mrdoob/Hi-ReS-Stats)。这段很棒的小代码片段允许您轻松地在应用程序上附加一个覆盖层,显示随时间变化的 FPS 以及您的应用程序当前使用的内存量:

Stats.js

正如您在来自伟大的 Flash 资源网站 WonderFl(wonderfl.net/c/6fCf)上找到的原型示例中所看到的,统计脚本被用来显示魔方应用程序的运行情况。当尝试找到可能导致问题的项目执行位置时,这非常方便。

尽管 HTML5 开发中的 FPS 概念并不完全相同,因为静态 HTML 页面在加载期间或加载后没有活动 FPS。但是,在 JavaScript 中处理动画和定时器间隔时,FPS 概念可以像在 Flash 开发中一样使用。由于 Mr. Doob 的工作,曾经只存在于 Flash 项目中的Stats脚本现在也可以在您的 HTML5 项目中使用了。

访问github.com/mrdoob/stats.js/下载项目的最新版本。在 JavaScript 中实现Stats显示比在 ActionScript 3 中更复杂,但仍然相对简单。看一下项目文档中Stats显示的示例实现:

var stats = new Stats();
stats.setMode(1); // 0: fps, 1: ms

// Align top-left
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';

document.body.appendChild(stats.domElement);

setInterval( function () {
    stats.begin();

    // your code goes here

    stats.end();
}, 1000 / 60 );

这里的主要区别在于你需要自己创建Stats窗口将绘制其计算的时间间隔。正如前面提到的,JavaScript 并不是基于基于帧的开发范式,应用自己的方法来设置应用程序间隔是计算诸如每秒帧数之类的数据的唯一方法。这很容易通过 JavaScript 中内置的setInterval()方法来实现,并手动设置预期的帧速率。由于帧将被有效地模拟渲染,我们可以进行一些简单的数学运算,使事情符合我们已经习惯的方式。在前面的例子中,我们将间隔持续时间设置为1000/60,其中60是预期的每秒帧数值。这个计算等于16.66666666666667,这是在一秒钟内对 60 个间隔求和的毫秒值。因此,在创建Stats对象并使用setMode()方法设置显示模式之后,你还需要手动设置显示位置。

在接下来的章节中,我们将继续深入研究一些这些应用程序,以及概述更多可以帮助项目测试和基准测试的平台。正如我在前面的几章中提到的,仔细检查你的完成项目以测试执行时间、内存使用和浏览器性能的重要性非常重要,以确保你可以相信每个人都可以按照你设计的方式查看你的内容。网页开发缺乏 Flash 编译器在运行之前自动优化我们的应用程序的好处。这项工作取决于你,确保你的程序运行顺畅。

总结

我可能无法再次强调一件事情有多重要,那就是你要去探索尽可能多的不同应用程序和其他服务。你对作为网页开发者可用的内容有越好的理解,你就能更好地判断手头工作的正确工具是什么。尽管在许多方面类似,但 HTML5 开发更多地是一种开放式的开发方式。无需使用特定的应用程序集,你可以自由地做任何你想做的事情。本章仅仅是对一些当今开发者正在使用的流行应用程序的浅显介绍。然而,我希望通过对所解释的软件的概述,你可以开始用最适合工作的工具开发自己的 HTML5 应用程序。

在下一章中,我们将看看一些将 JavaScript 推向更远的流行选项,不仅将 JavaScript 编译为其他编程语言,还将其他编程语言编译为 JavaScript。

第八章:导出到 HTML5

在第五章中,一次编码,到处发布,我们花了一些时间学习了 CreateJS JavaScript 框架以及 Flash Professional CS6 的 CreateJS Toolkit 插件(www.adobe.com/ca/products/flash/flash-to-html5.html),以及它们如何可以轻松地将您对 Flash 开发的现有知识直接整合到 HTML5 项目中。在过去的一年里,Adobe 已经采用了这个框架作为在 HTML5 项目中处理基于 Flash 的资产的官方方式。也就是说,实际上有许多其他方法可以在尝试直接将基于 Flash 的应用程序和游戏移植到纯 HTML5 时实现类似的效果。在本章中,我们将继续探讨一些可能帮助您进行资产和代码开发流程的第三方工具和应用程序。

在本章中,我们将涵盖以下内容:

  • 使用 Google 的 Swiffy 从 Flash SWF 自动生成 HTML5 项目

  • 手动将动画资产转换为 HTML5 准备的精灵表

  • 使用 Jangaroo 在 ActionScript 3 中编写您的 JavaScript 库和框架

  • 使用 Haxe 在单一语言源中定位您所有的平台开发需求

  • 使用 Google 的 Dart 编程语言构建强大的 Web 应用程序

Google Swiffy

由 Google 创建的 Swiffy 项目(www.google.com/doubleclick/studio/swiffy)是将您现有的 Flash 应用程序移植到 HTML5 项目中的最简单的方法之一。该项目的目标是接收已经编译的 Flash SWF 文件,并将其中的数据转换为带有 SVG 矢量动画数据的 JSON 对象。然后,生成的 Swiffy 编译的 JavaScript 可以在现代 Web 浏览器中直接运行,借助 Google Swiffy Runtime 的帮助。

尽管该项目仍处于 Beta 阶段,并且有许多限制,但 Swiffy 支持用 ActionScript 2 和 ActionScript 3 编写的 Flash 项目,使您有可能避免手动将 AS2 转换为 AS3 项目。对于项目中更复杂的 Flash 资产的支持正在稳步增长,但是在使用之前,最好花时间查看项目网站上的当前浏览器和功能支持列表,因为它可能无法完全覆盖您打算转换的应用程序(www.google.com/doubleclick/studio/swiffy/gettingstarted.html)。Swiffy 中的 ActionScript 3 支持仅限于在特定类中使用特定方法,以确保转换可以正确进行。在撰写本书时,Swiffy 中的 ActionScript 3 支持包括以下限制:

  • 不支持异常处理

  • 不支持可选参数

  • 不支持 XML 处理

  • 对象初始化和构造的顺序不是恒定的

您可以在项目网站的 Swiffy ActionScript 3 支持页面上找到当前 ActionScript 3 支持的完整和最新文档(https://www.google.com/doubleclick/studio/swiffy/actionscript3.html)。如果您转到 ActionScript 支持页面,您可以更好地了解哪些类和方法可以在您的 Flash 应用程序中使用。如果您的应用程序超出了项目支持页面中列出的支持属性,那么您的应用程序很可能无法正确转换。

Swiffy 是如何工作的?

为了了解 Swiffy 的工作原理,并亲自看到输出和限制,让我们创建一个简单的 Flash 应用程序,将其转换为 HTML5 并查看结果。我们将从可能是 Swiffy 转换的最佳案例开始。我们的 Flash 项目将包含完全在 Flash Professional IDE 内创建的资产和动画,并暂时避免使用任何 ActionScript。为了使这个示例更接近真实世界的情况,我们可以假装这个 Flash 应用程序是一个现有的横幅广告或其他简单的 Flash 电影,我们希望在移动设备或其他没有访问 Adobe Flash Player 的设备上显示。

Swiffy 是如何工作的?

尽管前面的例子很丑陋,但它实际上代表了一些重要的测试因素。首先,我们有一个在舞台上运动的圆。其次,我们有一个填充有渐变背景颜色的矩形,同样是矢量格式。最后,我们有两行文本:一行是对Times New Roman字体的简单使用,另一行是对更复杂字体如Wingdings的测试。就像两个形状一样,文本在播放时将在舞台上进行动画。这个测试的想法是看看 Swiffy 如何处理只有时间轴修改元素的极为常见的 SWF 设置。为了使这个测试不那么复杂,我们还将省略任何 ActionScript,并假设时间轴将无限循环。

创建了时间轴后,我们可以将这部电影的 SWF 输出到我们的项目目录中。Swiffy 生成 Web 准备好的输出所需的唯一 SWF 来自于你的 Flash 项目创建的单个 SWF,所以打开一个 Web 浏览器,前往 Swiffy 项目网站(www.google.com/doubleclick/studio/swiffy)。

提示

在撰写本书时,Swiffy 允许你上传任何大小等于或小于 1MB 的 SWF 文件。

当你准备好转换你的 SWF 时,使用项目网站首页上的表单将你的 SWF 上传到 Swiffy 服务器。结果应该很快就会出现,就像下面的截图所示:

Swiffy 是如何工作的?

结果应该显示类似于前面的截图。转换后的 SWF 的预览将显示在 Web 准备好的显示中,以及所有的输出消息和下载输出的链接。每个 SWF 转换页面上提供的 QR 码将允许您轻松地在移动设备上测试生成的源代码,以验证它是否正常工作。如输出页面所示,您可以通过右键单击外部输出示例的链接(在本例中为Banner-Test.html)轻松下载 HTML 文档以及所有其他数据,并以这种方式保存引用页面。

检查 Swiffy 生成的代码

将内容保存到本地计算机后,让我们花点时间来审查究竟做了什么,以及我们如何将这个资产移植到现有的网站中。打开 HTML 文件后,首先要注意的是使用外部库:

<script src="img/runtime.js"></script>

这个 JavaScript 调用是从 Google 文件服务器导入 Google Swiffy 运行时,并且需要正确显示其后的数据。就像 CreateJS 一样,已创建的代码是 JavaScript 和需要最终解释器才能正常运行的混合体。这是关于 Swiffy 的一个非常重要的事情。包括runtime.js文件是项目的绝对要求,只要添加了从 Swiffy 生成的任何资产。

在 Swiffy 运行时包含之后,你会注意到更多 HTML <script>标签中包含了大量文本。以下是它的一部分:

swiffyobject = {"tags":[{"frames":[],"scenes":[{"name":"Scene 1","offset":0}],"type":23},{"bounds":[{"ymin":0,"ymax":2240,"xmin":0,"xmax":10399}],"id":1,"fillstyles":[{"transform":["4738D::1056F199e20k"],"type":2,"gradient":{"stops":[{"color":[-65536],"offset":[0]},{"color":[-256],"offset":[42]},{"color":[-16711936],"offset":[93]},{"color":[-16711681],"offset":[127]}….

这些数据是 JavaScript 对象,代表了原始 SWF 中包含的所有资产和动画的数据。由于我们的例子中没有包含任何位图图像,而且其中的一切都是基于矢量的,整个应用程序已经被编译为 100%的代码,并且可以用几行进一步的 JavaScript 来显示:

var swiffyElement = document.getElementById('swiffycontainer');
var stage = new swiffy.Stage(swiffyElement), swiffyobject);
stage.start();

发现 Swiffy 的限制

所有这些都很好,直到我们开始让事情变得更加复杂。在下一个例子中,我创建了一个非常简单的 ActionScript 3 游戏。游戏的想法是通过移动鼠标来控制舞台上方框的位置。随着时间的推移,你的方框会开始增长并占据舞台上更多的空间。游戏的目标是尽可能长时间地让你的方框不要碰到任意移动的黑点。为简单起见,我在这个游戏中没有包含任何用户界面。所有的结果和输出都将暂时发送到 Web 浏览器的开发者控制台。你可以在可下载的章节示例文件中找到这个例子的工作形式。

发现 Swiffy 的限制

是的,这非常粗糙,但它涵盖了 Flash 应用程序中许多常见的方面,并且用可管理的代码行数,非常适合我们的演示目的。如前所述,游戏中没有用户界面,任何游戏输出都将被发送到 Flash 输出调试窗口。在继续之前,让我们先看一下代码,这样你就可以注意到已经使用的特定功能、类和变量类型。

package {

  import flash.display.MovieClip;
  import flash.events.Event;
  import flash.events.MouseEvent;

  // Setting the frame rate is important here as we calculate
  // the users score from how many frames have passed.
  // It's worth noting that the TimerEvent class can be used
  // without any issue by applications converted with
  // Google Swiffy.
  [SWF(backgroundColor="0xEFEFEF", width="1024", height="768", frameRate="30")]

  public class PlayerMoveTest extends MovieClip {
    // Setting a static const variable, defines
    // how many bad guy dots to add to the stage.
    private static const BAD_GUY_COUNT:int = 10;

    // The 'Player' class is a red box created and defined
    // within an SWC included into this project.
    private var _player:Player;

    // An array to hold all of the bad guys created
    // when the game is created.
    private var _badGuys:Array;

    private var _lifeTimer:int;
    private var _playerTarget:Object = new Object();

    /**
     * PlayerMoveTest Constructor
     */
    public function PlayerMoveTest() {
      // Start by creating and adding all of the bad
      // guys to the game stage.
      _badGuys = new Array();
      for(var i:int = 0; i < BAD_GUY_COUNT; i++) {
        // Using MovieClips instead of Sprites
        // as Sprites are not supported by the
        // Google Swiffy compiler.
        var badGuy:MovieClip = new MovieClip();
        badGuy.graphics.beginFill(0x000000, 1);
        badGuy.graphics.drawRect(-5, -5, 10, 10);
        badGuy.graphics.endFill();
        badGuy.x = Math.floor(Math.random() * (1000 + 1));
        badGuy.y = Math.floor(Math.random() * (700 + 1));
        _badGuys.push(badGuy);
        addChild(badGuy);
      }

// Create the users Player object
// Again, this is created within a included SWC.
      _player = new Player();
      _player.x = 100;
      _player.y = 100;
      _playerTarget.x = _player.x;
      _playerTarget.y = _player.y;
      addChild(_player);

      // Add a on enter frame to update the game stage.
      this.addEventListener(Event.ENTER_FRAME, updateEnviroment, false, 0, true);
    }

/**
 * Called on every frame when the game is in a playable
 * state.
 */ 
    private function updateEnviroment(event:Event):void {
      // Update the life timer, used for player score.
      _lifeTimer++;

      // Set the new player position target.
      // this position is based of the current X and Y
      // position of the user's mouse.
      _playerTarget.x = this.mouseX - 50;
      _playerTarget.y = this.mouseY - 50;

// Calculate the distance to the current 
// player target.
      var xDistance:int = _playerTarget.x - _player.x;
      var yDistance:int = _playerTarget.y - _player.y;

      // Update the position of the player object. Use
      // a simple method to ease the position into the 
      // target.
      _player.x = _playerTarget.x - (xDistance * 0.9);
      _player.y = _playerTarget.y - (yDistance * 0.9);
      _player.width += 0.5;
      _player.height += 0.5;

// Randomly move the position of each bad guy on
// every frame.
      for(var i:int = 0; i < BAD_GUY_COUNT; i++) {
        _badGuys[i].x += Math.round(Math.random() * (15 - (-15)) + (-15));
        _badGuys[i].y += Math.round(Math.random() * (15 - (-15)) + (-15));

// Using the common hitTestObject method 
// to check and see if any of these bad guys 
// are currently touching the player object. 
        if(_player.hitTestObject(_badGuys[i])) {
          // The player is touching a bad guy
          // so stop the on enter frame event
          // and alert the users score.
          this.removeEventListener(Event.ENTER_FRAME, updateEnviroment);

          trace('GAME OVER!!!');
          trace('You lasted ' + Math.round(_lifeTimer / 30) + ' seconds.');
        }
      }
    }
  }
}

如果你有兴趣实际编译这个应用程序的源代码,你可以找到所有的文件,在 Flash Builder 中打开它作为一个 ActionScript 项目。将应用程序编译为 SWF 并在本地测试应用程序以确认它是否正常工作。如果一切顺利,让我们尝试将这个文件发送给 Swiffy,看看会发生什么:

发现 Swiffy 的限制

只要你正确地按照步骤操作,当尝试转换这个 SWF 并生成前一个截图中的错误列表时,Swiffy 会失败。让我们快速看一下出了什么问题,限制以及可以采取的解决方法。首先,第一个错误列出了数组不受 Swiffy 编译器支持的通知。在我们的应用程序中,我们使用数组来包含所有坏人实例在一个全局变量中。在这个或任何应用程序中,如果不使用数组来管理数据,就需要以更原始的方式来管理数据。这个问题本身可以成为决定是否使用 Swiffy 进行转换的关键因素。尽管有许多方法可以解决这个问题,事实是,如果你的应用程序中到处都是数组,那么当前版本的 Swiffy 很可能无法帮助你。不管有多少坏消息,让我们继续看第二个问题。毫不奇怪,常见的 ActionScript 3 开发中的hitTestObject方法也不被编译器支持。

这种易于使用的方法在 Flash 开发中需要简单的碰撞检测时可以成为救命稻草,但由于没有直接的 JavaScript 等效方法来转换它。再次弥补这一点是可以的,但结果代码会比在典型的 ActionScript 3 开发中调用单个方法要大得多。因此,这可能被视为一个问题,但并不是一个死胡同,只要你的碰撞检测只使用支持的方法和属性。我们转换尝试中列出的最终错误是使用Sprite.graphics类。如果你还记得,代码示例明确使用了 MovieClips 而不是 Sprites,因为 Sprites 不受 Swiffy 编译器支持。然而,与最初在 Flash Professional IDE 中创建并保存到 SWC 中的Player对象不同,坏人对象是在代码中使用内部 ActionScript 3 Graphics API 创建的。

badGuy.graphics.beginFill(0x000000, 1);
badGuy.graphics.drawRect(-5, -5, 10, 10);
badGuy.graphics.endFill();

这三行是最终错误的原因。由于 Flash 中的MovieClip对象是建立在Sprite类之上的,所以结果错误也是如此。由于 SWC 处理了Player对象的创建,因此该对象不会出现错误。然而,值得注意的是,即使通过移除所有坏人并只让一个方块在周围移动来修复这些错误,成功转换的结果在游戏的 HTML 版本中仍然没有显示任何内容。目前看来,Swiffy 不支持在 ActionScript 3 项目中使用 SWC,它更倾向于使用旧式的纯 Flash IDE 开发风格的应用程序开发。

尽管 Swiffy 缺乏多年来在 Flash 开发中常用的许多功能,但它仍然可以成为集成网站动画或广告横幅等资产的非常方便的工具。实际上,Swiffy 可靠地为你做的大部分工作是简单的 Flash 应用程序和电影转换,而不是你典型的 Flash 游戏或应用程序。

在 Flash Professional CS6 中生成精灵表

如果你打算将一些现有的基于 Flash 的时间轴动画移植或复制到 HTML5 项目中,你将不得不进行一些自己的转换。正如你在本书的示例中所看到的,时间轴动画在 HTML5 堆栈中根本不存在。因此,你需要将动画序列转换为一种新的格式,以便在 Web 上正确显示。最简单的选项之一是将动画转换为视频文件,并使用<video>标签元素进行播放。不幸的是,将位图或矢量资产转换为可以在 Web 上正确播放的视频文件将导致大量的质量损失。更糟糕的是,视频播放将非常沉重,导致应用加载时间变慢。最后,HTML5 中的视频缺少许多重要功能,比如不支持 alpha 透明度,导致所有资产都包含在一个完全可见的矩形容器中。为了解决所有这些问题,许多网页开发人员正在转向经过验证的精灵表方法。精灵表背后的概念非常简单。将动画序列中的所有帧放在同一张图像上(带有透明背景),并将图像保存为未压缩的 PNG 文件。这样,当客户端在 Web 上加载时,只需下载一个文件就可以将整个动画序列加载到内存中准备播放。将基于 Flash 的时间轴动画手动转换为精灵表,通过将每一帧复制并粘贴到 PNG 文档中,是一项漫长而繁琐的工作。幸运的是,这是一项你不需要处理的工作,因为 Flash Professional CS6 在 IDE 中已经集成了精灵表生成器。

在 Flash CS6 中使用精灵表生成器非常简单。Adobe 的工程师们成功地创建了一个工具,可以让您在几分钟内轻松地在 HTML5 项目中使用 Flash 动画。虽然使用简单,但该功能可能有点隐藏,因此让我们快速看一下精灵表生成器的操作,并将一些结果放入工作中的 HTML5 文档进行测试。

举例来说,我创建了一个非常简单的 Flash 动画示例,时间轴上只包括三种不同的形状。每种形状仅显示 5 帧,总共有 15 帧动画:

提示

和往常一样,您可以在可下载的章节示例中找到所有示例文件。

在 Flash Professional CS6 中生成精灵表

在 Flash 项目的库中找到 MovieClip,右键单击它。在右键单击任何 MovieClip 时显示的上下文菜单中,您将找到生成精灵表选项。选择此选项,将会出现新的、功能丰富的生成精灵表窗口:

在 Flash Professional CS6 中生成精灵表

初步检查时,您会看到动画中的每一帧都自动添加到同一文档中,并以网格格式排列。如前所述,该动画包含 15 帧,因此每帧都已添加到精灵表预览窗口中,并显示默认配置。在保存此输出之前,让我们查看一些可用的选项,看看是否可以进一步优化这个精灵表。

我们可以从对即将导出的内容进行概述开始。在生成精灵表窗口的左下角,您将找到当前 MovieClip 的详细信息,包括基于特定帧速率的帧数和持续时间。在窗口的右侧,您可以看到一个易于查看的预览,显示了在当前配置下生成的精灵表的外观。选择第二个预览选项卡将显示以其原生形式运行的动画。

在预览窗口下方是在导出动画资产和数据集时可用的所有配置属性。导出图像的尺寸可以由 Flash 自动调整,也可以手动配置以设置动画帧的可用区域。图像格式也可以配置为 PNG 或 JPG 格式,以便对导出图像进行进一步压缩。建议将其设置为 PNG 格式,无背景,除非需要允许正确的图像背景透明度:

在 Flash Professional CS6 中生成精灵表

配置属性的正确大小包含了数据集导出的设置。由于为精灵表导出的图像将只包含帧资产而没有动画数据,因此精灵表将需要某种形式的数据才能正确播放。当使用可用的切片算法中的基本算法时,通常不会出现问题。使用基本设置时,精灵以漂亮的统一行排列在易于使用的网格布局中。这是处理任何简单动画时的最佳输出设置。目前算法的另一个选项是MaxRects选项。此选项用于尝试尽可能紧密地打包帧。这样做的原因是为了最小化导出图像文件大小,以便在互联网连接上实现更快的下载时间。选择算法后,我们可以继续进行此导出窗口中可能最重要的设置。数据格式选择允许您将数据导出格式设置为特定于您正在开发的 HTML5 应用程序的工作方式。已包括对 iOS 开发的The Sparrow Frameworkgamua.com/sparrow)、用于 ActionScript 3 的The Starling Frameworkgamua.com/starling/)以及Cocos2Dcocos2d.org/)的支持。作为 HTML5 开发人员,您可能最感兴趣的三个主要导出设置是JSONJSON-Arrayeaseljs选项。将数据集导出为简单的 JSON 导出将允许您将数据通用地用作 JSON,这是人类可读的数据存储的开放标准。JSON-Array设置非常相似,不同之处在于将数据存储在 JSON 数组中而不是直接对象中。这两者之间的区别实际上只会影响您在代码中如何解释数据。最后,easlejs导出设置允许您自动准备导出的动画以包含在您的 CreateJS 或 EaselJS 项目中。当您尝试将外部资产包含到现有的基于 CreateJS 工具包的项目中时,这种导出设置非常方便:

在 Flash Professional CS6 中生成精灵表

配置中的最终设置是修剪堆叠帧选项。修剪精灵表中的帧将删除每个元素之间的未使用空白空间。这将通过最小化导出图像文件大小再次优化您的最终结果。最后,堆叠帧选项允许您通过删除或堆叠动画中相同的帧来进一步优化您的动画。

由于导出的数据集将包含时间轴信息,因此无需存储相同的图像两次,因此可以毫无问题地删除这些资产:

在 Flash Professional CS6 中生成精灵表

所有这些设置都已覆盖,让我们使用前面截图中的设置导出这个动画,看看我们得到了什么输出。当单击导出按钮时,窗口完成后将关闭,您将能够在项目目录的根目录中找到导出的材料。在数据格式选项中附加JSON设置后,将导出两个文件。第一个文件是 PNG 格式的精灵表图像:

在 Flash Professional CS6 中生成精灵表

第二个文件是我们的 JSON 输出,其中包含动画的前三帧的所有帧位置和大小的数据。以下是导出的 JSON 中包含的动画数据的片段:

{"frames": {

"ShapeAnimation0000":
{
  "frame": {"x":0,"y":0,"w":100,"h":100},
  "rotated": false,
  "trimmed": false,
  "spriteSourceSize": {"x":0,"y":0,"w":100,"h":100},
  "sourceSize": {"w":100,"h":100}
},
"ShapeAnimation0001":
{
  "frame": {"x":0,"y":0,"w":100,"h":100},
  "rotated": false,
  "trimmed": false,
  "spriteSourceSize": {"x":0,"y":0,"w":100,"h":100},
  "sourceSize": {"w":100,"h":100}
},
"ShapeAnimation0002":
{
  "frame": {"x":0,"y":0,"w":100,"h":100},
  "rotated": false,
  "trimmed": false,
  "spriteSourceSize": {"x":0,"y":0,"w":100,"h":100},
  "sourceSize": {"w":100,"h":100}
},
"ShapeAnimation0003":
{
  "frame": {"x":0,"y":0,"w":100,"h":100},
  "rotated": false,
  "trimmed": false,
  "spriteSourceSize": {"x":0,"y":0,"w":100,"h":100},
  "sourceSize": {"w":100,"h":100}
},

数据非常简单易懂,这很好,因为从这一点开始,如果没有使用游戏开发框架或 CreateJS,我们必须自己解释和显示这些数据和资产:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>ShapeAnimation Sprite Sheet Example</title>

    <style>
      #animation {
        width:100px;
        height:100px;
        overflow:hidden;
      }
      </style>

      <script type="text/javascript" src="img/jquery-1.9.1.min.js"></script>
    <script>
      var animationData = Array();
      var currentFrame = 0;

      $(document).ready(function() {
        // Get the Sprite Sheet JSON
        $.getJSON('ShapeAnimation.json', function(data) {
          // Save each of the objects into an array.
          $.each(data['frames'], function(key, val) {
            animationData.push(val);
          });

          // Start the animation.
          runAnimation();
        });
      });

      function runAnimation() {
        // Update the CSS properties of the Sprite Sheet image.
        $('#animation img').css('margin-left', animationData[currentFrame]['frame']['x'] * -1);
        $('#animation img').css('margin-top', animationData[currentFrame]['frame']['y'] * -1);

        // Update the frame counter and reset if needed.
        currentFrame++;
        if(currentFrame == animationData.length) currentFrame = 0;

        // Keep calling this method every 200ms.
        setTimeout(runAnimation, 200);
      }
    </script>
  </head>

  <body>
    <div id="animation">
      <img src="img/ShapeAnimation.png">
    </div>
  </body>
</html>

由于 Flash Professional CS6 对 CreateJS 的巨大支持,使用 EaselJS 设置来导出和使用精灵表绝对是最简单的方法。然而,正如前面的代码片段所示,通过标准化的 JSON 导出方法,你可以相对容易地将任何 Flash 动画实现为精灵表,用于你的 HTML5 项目。

提示

如果你对精灵表感兴趣,但又不想花时间创建所有的资源,可以前往 Google 图片搜索精灵表。你会发现无穷无尽的精灵表资源,可以用来测试你的应用程序。当然,在公共网站上使用任何资产时,你应该确保拥有权限或所有权。

Jangaroo

Jangaroo(www.jangaroo.net)的开发背后实际上非常有趣。Jangaroo 是由 CoreMedia(www.coremedia.com)的开发团队创建的,它是由内部开发团队对当前 JavaScript 开发能力的挫折而构建的。CoreMedia 的开发团队并没有处理 JavaScript 所具有的许多常见语法问题,而是着手创建了一个用 Java 编写的 ActionScript 3 到 JavaScript 编译器。尽管这听起来可能很荒谬,但实际情况是,Flash 开发人员可以轻松地继续使用熟悉的语法,同时专门针对基于 HTML5 的 Web 开发。Jangaroo 旨在允许开发人员使用 ActionScript 3 的强大功能编写高质量的 JavaScript 框架和应用程序。简而言之,它将接收 ActionScript 3 文件,并借助其用 Java 编写的编译器将它们转换为可用的 JavaScript:

Jangaroo

那么,为什么有人想要避免编写原生 JavaScript,开始为下一个 HTML5 项目编写 ActionScript 3 呢?嗯,作为一个有过编写 ActionScript 3 经验的开发者,你可能已经可以从本书中到目前为止的所有示例和概述中回答这个问题。在编写大型强大的 HTML5 应用程序时,JavaScript 中缺少包、类和适当的继承可能会开始创建一堆代码的雷区,这些代码可能很难管理。通过允许自己继续使用一种你不仅习惯了的语言来开发应用程序,而且可以更容易地管理项目中的类,你可以克服许多在纯 JavaScript 开发周期中可能出现的常见障碍。

Jangaroo 项目的核心是名为jooc的 Jangaroo ActionScript 3 到 JavaScript 编译器。编译器将接收你的 ActionScript .as文件,并将它们导出为编译后的 JavaScript .js文件。要安装和运行 Jangaroo,你需要首先确保已安装最新版本的 Java 运行环境(www.oracle.com/technetwork/java/javase/downloads/index.html)以及 Maven(maven.apache.org)。安装和设置这两个软件可能看起来令人生畏,但请放心,这个过程非常简单直接,并且有很好的文档记录,所以我会把这个过程留给你自己。

提示

在安装 Java 运行环境时,值得注意的一点是要确保JAVA_HOME环境变量已正确设置。如果在安装或测试 Jangaroo 过程中遇到任何问题,这将是一个很好的调试起点。

为了给你一个用 ActionScript 3 创建并使用 Jangaroo 编译的 JavaScript 驱动应用程序的简化开发周期的例子,让我们使用可以在项目网站上找到的 HelloWorld 示例(www.jangaroo.net/tutorial)。

package {
/**
 * The most simple Jangaroo class on earth.*/
public class HelloWorld {
  /**
   * Let the browser display a welcome message.*/
  public static function main():void {window.document.body.innerHTML = "<strong>Hello World from Jangaroo!</strong>";
  }
}
}

正如您在代码示例中所看到的,您的 ActionScript 类可用的语法是常见的 ActionScript 3 和一些特殊的窗口和文档对象引用的混合,以便您可以正确地将应用程序集成到浏览器中。如果您对准备将 ActionScript 3 编译为 Jangaroo 编译器感兴趣,可以前往官方 Jangaroo 文档的编写代码页面了解有关语言和代码语法选项的更多信息(www.jangaroo.net/tutorial/writing_code)。

Jangaroo 的大部分是开源的,项目代码和资产可以在 CoreMedia 的 Github 页面上找到(github.com/CoreMedia)。

Haxe

继续讨论将应用程序和其他编程语言直接编译为 JavaScript 的话题,我应该花点时间介绍一下 Haxe 开发世界中一些令人兴奋的功能:

Haxe

Haxehaxe.org)是一种独立的开源编程语言。大多数编程语言都是为特定的应用类型而构建的,JavaScript 用于 Web,ActionScript 用于 Flash,而 Haxe 可以从相同的源代码编译和运行在各种平台和设备上。Haxe 源代码可以有选择地编译成 JavaScript、Flash、PHP、C++、C#和 Java,结合您之前对 ActionScript 3 的经验和您在 JavaScript 中学到的新技能,学习 Haxe 语法是轻而易举的。

尽管跨平台开发现在可能不是您的兴趣所在,但至少对诸如 Haxe 等语言提供的基本了解可能会让您填补开发技能中的一些空白。在我们继续之前,让我们快速看一下 Haxe 项目网站的代码片段页面上可以找到的 Haxe 代码示例。以下代码是实现流行排序方法 Quicksort 的示例(en.wikipedia.org/wiki/Quicksort)。由于我们已经了解了这个排序算法试图实现的目标,让我们主要审查这段代码,以了解 Haxe 编程语言中的类、方法和变量语法:

class Quicksort {

    static var arr = [4,8,0,3,9,1,5,2,6,7];

    static function quicksort( lo : Int, hi : Int ) : Void {
        var i = lo;
        var j = hi;
        var buf = arr;
        var p = buf[(lo+hi)>>1];
        while( i <= j ) {
            while( arr[i] > p ) i++;
            while( arr[j] < p ) j--;
            if( i <= j ) {
                var t = buf[i];
                buf[i++] = buf[j];
                buf[j--] = t;
            }
        }
        if( lo < j ) quicksort( lo, j );
        if( i < hi ) quicksort( i, hi );
    }

    static function main() {
        quicksort( 0, arr.length-1 );
        trace(arr);
    }
}

正如您可以直接在第一行看到的那样,Haxe 具有完整的类支持,不像 JavaScript。这个概念本身可能是一个卖点,因为从 ActionScript 转到 Haxe 的开发人员会发现许多在 JavaScript 中不可用的相似之处。其他功能,如静态函数、严格的变量类型和常见的调试方法,比如trace(),只是 Haxe 中让具有先前 ActionScript 3 开发经验的开发人员脱颖而出的众多出色功能之一。

提示

如果您对了解 Haxe 开发的激动人心世界感兴趣,请查看《Haxe 2 初学者指南》,Packt Publishingwww.packtpub.com/haxe-2-beginners-guide/book)。

Haxe 本身是一个庞大的项目。直接将应用程序源代码交叉编译到几乎所有现代平台上的能力是一个非常宝贵的资产,尤其是当您开发具有非常特定平台要求的项目时。即使您只打算使用 Haxe 源代码针对 HTML5 Web 项目,只需点击几下鼠标即可将应用程序移植到另一个平台的能力是非常惊人的。此外,就像我们在本章中审查的许多其他平台和编译器一样,Haxe 可以减轻许多 Web 开发人员对 JavaScript 语法的常见抱怨。该项目仍然相对较新,尽管许多开发人员已经加入了这一行列。如果在 Haxe 中开发您的下一个应用程序听起来像一个有趣的挑战,我强烈建议您进一步了解一下。

Google Dart

为了帮助来自各个平台的开发人员构建现代 Web 的复杂、高性能客户端应用程序,谷歌的 Dart(code.google.com/p/dart/)是推动 Web 开发的又一个很好的例子,更具体地说是 JavaScript 开发。就像 Haxe 一样,Dart 是一个开源项目,使用自己特定的编程语言编译成 Web-ready JavaScript 文档,就像 Jangaroo 一样,Dart 是基于对当前 Web 开发平台限制的不满而构建的。为了引入新的结构化、单一语言工作流程,谷歌发布了 Dart 项目的技术预览,以便早期测试和来自 Web 开发社区的反馈:

Google Dart

当然,由于 Dart 是基于自己的语法构建的,刚开始时会有一个学习曲线。为了帮助减轻学习新语言的压力,我强烈建议查看官方的 Dart Editor。Dart Editor(www.dartlang.org/docs/editor/)可能是最简单的开始和运行 Dart 开发的方法。

它支持实时错误和语法检查功能,以在编译之前提醒您任何问题,同时还支持代码完成功能,以帮助您了解每个方法和属性可以做什么。

Google Dart

Dart Editor,就像许多其他编辑器一样,是基于流行的 Eclipse IDE 构建的。尽管代码编辑器简化了,但对于有 Flash Builder 经验的人来说,这可以被认为是又一个胜利,因为界面会非常熟悉。我说这个编辑器简化了,因为这个编辑器不是作为 Eclipse 的插件提供的,而是作为自己独立的基于 Eclipse 的编辑器打包,删除了所有不必要的元素。

就像 Haxe 的概述一样,我会保持简短,因为 Dart 仍然是一个非常新的项目,我还没有亲自遇到任何使用它开发流行 Web 应用程序的人。也就是说,绝对没有理由贬低 Dart 这样的语言。随着 JavaScript 规范的发展和浏览器支持的跟进,对这些项目的需求可能会减少。然而,就像任何 Flash 开发人员知道的,使用适当的调试和输出流编译项目可以让他们比许多传统的客户端脚本编写方法更快地找到和修复问题。

总结

在本章的过程中,我们花了一些时间研究了一些正在推动网页应用程序开发极限的项目,例如谷歌的 Swiffy 项目,它可以轻松地将简单的 Flash SWF 文件直接转换为 Web 友好的 HTML 和 JavaScript 配置,以及从 Flash Professional IDE 中直接导出 Flash 矢量和位图动画到 Web 准备好的精灵表。诸如 Haxe、Dart 和 Jangaroo 之类的项目为开发人员在尝试创建他们的 HTML5 项目时提供了新的选择。驱动他们应用程序的本机 JavaScript 实际上可以用完全不同的语言编写。最初,将 JavaScript 的能力扩展到其他语言可能看起来有些反向,但创建这些项目的原因通常都归结为在编写 JavaScript 时缺乏通用语法和开发流程问题。正如前面提到的,开发下一个 HTML5 项目时,并不需要使用本章中提到的特定应用程序中的项目或功能。了解当前网页开发人员可以使用的项目和平台的知识将使您能够更好地得出结论,找到最佳的方式来处理下一个 HTML5 项目。

我必须强调,本章提到的应用程序、功能和编译器列表只是在使用 JavaScript 时可用的一小部分。如果您有兴趣了解更多可以编译到 JavaScript 或扩展 JavaScript 的项目,请访问altjs.org。在那里,您将找到针对初学者到高级开发人员风格的项目列表,所以我相信那里一定会有一些能够吸引您的东西。许多这些项目都是基于 CoffeeScript(coffeescript.org)开发的,这是另一种直接编译为 JavaScript 的专用语言,也是我推荐您了解的另一个很棒的项目。与扩展 JavaScript 的开发流程和能力相关的项目数量似乎是无穷无尽的,并且每天都在增长。没有人能指望您了解所有这些项目,但是对现有项目以及许多这些平台能做什么有一个基本的了解,将使您在着手开发下一个项目时能够做出更快更好的决策。

在接下来的两章中,我们将开始将我们到目前为止所涵盖的所有内容融入到实际的 HTML5 应用程序开发流程中。我们将涵盖每个开发人员在为 Web 开发时应该注意的许多重要方面,以及在开发过程中正确测试应用程序的方法。最后,为了总结一切,我们将把该应用程序发布到互联网,并介绍一些在应用程序上线后发布和维护项目的方法。

第九章:避免路障

到目前为止,在本书中,我们已经涵盖了许多 HTML5 网页开发的新颖方面。然而,在此期间,我们只是将每个功能视为一个独立的方面,而不是一个更大项目的一部分。在构建真实世界的应用程序时,您将不可避免地不得不开始合并这些新功能,以构建应用程序中的适当功能集。在本章中,我们将使用一些新的和旧的功能来构建一个可玩的游戏,作为 Web 开发人员可用的一些功能。我们将从 Flash 内置版本开始,然后将应用程序直接移植到 HTML5,并讨论在处理您已经习惯于 ActionScript 和 Flash 开发世界中的资产和功能时可能遇到的差异和问题。

喷气背包游戏

我们将构建的应用程序是一个简单的 2D 横向滚动游戏,具有简单的控件,可以轻松转换为移动设备的触摸事件。为了使解释和理解变得简单,我将此游戏建模为流行的直升机游戏(www.helicopter-game.org/)的简化版本,这个游戏已经在互联网上玩了很多年。

以下屏幕截图显示了直升机游戏:

The Jet Pack game

尽管互联网上已经有许多版本的这个游戏,但最受欢迎的版本是用 Flash 编写的,这使得许多移动设备和桌面浏览器的用户无法玩这个游戏。游戏玩法和控件非常简单,只需要玩家使用键盘上的一个键或使用鼠标进行点击。

与其直接复制这个流行的游戏,我们将把它作为游戏设计的起点。为了为游戏增添我们自己的个人风格,我们将把直升机角色换成一个带喷气背包的小人。在开始之前,看一下游戏运行的屏幕截图,以更好地了解我们最终的结果会是什么样子。当然,您也可以通过查看本章示例文件中的 Flash 和 HTML 版本来玩这个游戏。

The Jet Pack game

您将扮演左侧屏幕截图中显示的角色,即喷气背包人。您的目标是通过激活他的喷气背包来控制角色的高度。随着角色深入洞穴系统,洞穴不仅会变得越来越狭窄,而且还会随机放置障碍物,您需要绕过这些障碍物。重力也起着重要作用,因为当您的喷气背包未激活时,您的角色将开始下落到地面。随着难度的增加,关卡对象会在玩家深入洞穴时实时动态生成,并受到更严格的限制。在游戏源代码的每个间隔期内,将检查关卡的每个部分与玩家的碰撞,以确定游戏是否结束。游戏的每个间隔期还会增加玩家的当前得分,因此您深入洞穴旅行越深,得分就越高。高分将在游戏实例中进行跟踪,并在 UI 中显示,以便玩家轻松查看下一次尝试的目标。

正如我之前提到的,我们将首先查看这个游戏的 ActionScript 3 源代码,以了解我是如何编写的。当然,需要声明的是,尽管这个游戏代码的某些方面可能适用于你在其他项目中,但这个游戏的源代码已经设置好,以帮助我们学习和理解 ActionScript 3 到 HTML5 的转换。这个游戏还没有被优化到应该发布给公众的程度,我会在本章尽力指出其中一些不足之处。有了这些想法,让我们直接进入这个喷气背包游戏 Flash 版本的源代码结构。

在 Flash 中构建游戏

为了让事情易于理解,我们将保持这个游戏的行数和文件数最少。我们将从我们 ActionScript 项目的基类Game开始。查看以下精简的基类示例,以了解游戏代码的结构。你可以随时在可下载的章节示例文件中查看完整版本:

package {
  import flash.display.Sprite;
  import flash.events.Event;
  import flash.events.KeyboardEvent;

  [SWF(width='1000', height='800', backgroundColor='#000000', frameRate='60')]
  public class Game extends Sprite {
    private var _levelBlocks:Array = new Array();
    private var _interval:int;
    private var _blockInterval:int;
    private var _player:JetPackMan;
    private var _difficulty:int = 10;
    private var _speed:int = 10;
    private var _keyDown:Boolean;
    private var _score:int;
    private var _highScore:int;
    private var _scoreBoard:ScoreBoard;
    private var _gameOver:Boolean;

    public function Game() {
      generateLevel();
      createPlayer();
      displayScore();

      this.addEventListener(Event.ENTER_FRAME, updateI terval, false, 0, true);
      stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown, false, 0, true);
      stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp, false, 0, true);
    }

    // Starts a new game.
    private function startNewGame():void {
      // Reset the game switches and counters.
      _gameOver = false;
      _score = 0;
      _speed = 10;
      _difficulty = 10;

      for each(var block:LevelBlock in _levelBlocks) {
        block.destroy();
        removeChild(block);
      }
      _levelBlocks = new Array();

      removeChild(_player);
      _player.destroy();
      _player = null;

      generateLevel();
      createPlayer();

      this.addEventListener(Event.ENTER_FRAME, updateInterval, false, 0, true);
    }

    // Adds the score board to the stage.
    private function displayScore():void {
      _scoreBoard = new ScoreBoard();
      _scoreBoard.x = -40;
      _scoreBoard.y = -20;
      addChild(_scoreBoard);
    }

    // Generates the beginnings of a new level.
    private function generateLevel():void {
      for(var i:int = 0; i < 20; i++) {
        var randomHeight:int = (Math.floor(Math.random() * (_difficulty - (_difficulty - 10) + 1)) + (_difficulty - 10));
        var levelBlock:LevelBlock = new LevelBlock(_speed, 800 + (20 * i), randomHeight - 10, 20, 100);
        _levelBlocks.push(levelBlock);
        addChildAt(levelBlock, 0);

        randomHeight = (Math.floor(Math.random() * (_difficulty - (_difficulty - 10) + 1)) + (_difficulty - 10));
        var bottomLevelBlock:LevelBlock = new LevelBlock(_speed, 800 +(20 * i), (randomHeight * -1) + 750, 20, 100);
        _levelBlocks.push(bottomLevelBlock);
        addChildAt(bottomLevelBlock, 0);
      }
    }

    // Creates and adds a player object to the stage.
    private function createPlayer():void {
      _player = new JetPackMan();
      _player.x = 80;
      _player.y = 400;
      addChild(_player);
    }

    // Called on keyboard key down.
    private function onKeyDown(e:KeyboardEvent):void {
      if(_gameOver) startNewGame();
      _keyDown = true;
    }

    // Called on keyboard key up.
    private function onKeyUp(e:KeyboardEvent):void {
      _keyDown = false;
    }

    // Game interval. Called on each frame.
    private function updateInterval(e:Event):void {
      _score = _score + 1;
      _scoreBoard.score.text = String(_score);

      if(_keyDown) {
        _player.jetPackOn();
        _player.y = _player.y - 5;
      } else {
        _player.jetPackOff();
        _player.y = _player.y + 5;
      }

      _interval++;
      var increaseSpeed:Boolean = false;
      if(_interval >= 100) {
        increaseSpeed = true;
        _difficulty = _difficulty + 5;
        //_speed++;
        _interval = 0;
      }

      // Update every block
      for each(var block:LevelBlock in _levelBlocks) {
        block.update();

        if(block.hitTestObject(_player)) {
          _player.explode();
          _gameOver = true;

          if(_score > _highScore) {
            _highScore = _score;
            _scoreBoard.highscore.text = String(_highScore);
          }

          this.removeEventListener(Event.ENTER_FRAME, updateInterval);
        }

        if(increaseSpeed) block.setSpeed(_speed);
      }

      // Check if new blocks need to be added
      if(_levelBlocks[_levelBlocks.length - 1].currentX() < 2000) {
        var randomHeight:int = (Math.floor(Math.random() * (_difficulty - (_difficulty - 10) + 1)) + (_difficulty - 10));
        var levelBlock:LevelBlock = new LevelBlock(_speed, _levelBlocks[_levelBlocks.length - 1].currentX() + 20, randomHeight - _difficulty, 20, 100 + _difficulty);
        _levelBlocks.push(levelBlock);
        addChildAt(levelBlock, 0);

        randomHeight = (Math.floor(Math.random() * (_difficulty - (_difficulty - 10) + 1)) + (_difficulty - 10));
        var bottomLevelBlock:LevelBlock = new LevelBlock(_speed, _levelBlocks[_levelBlocks.length - 1].currentX(), (randomHeight * -1) + 750, 20, 100 + _difficulty);
        _levelBlocks.push(bottomLevelBlock);
        addChildAt(bottomLevelBlock, 0);
}

      // Add a random block every now and then to make things interesting.
      // We will base the frequency of random blocks on the // current difficulty in the game.
      _blockInterval++;
      if(_blockInterval > 5000 / _difficulty) {
        _blockInterval = 0;

        randomHeight = (Math.floor(Math.random() * (750 - 5 + 1)) + 5);
        var randomY:int = (Math.floor(Math.random() * (750 - 5 + 1)) + 5);
        var randomBlock:LevelBlock = new LevelBlock(_speed, _levelBlocks[_levelBlocks.length - 1].currentX(), randomY, 20, 100 + _difficulty);
        _levelBlocks.push(randomBlock);
        addChildAt(randomBlock, 0);
      }
    }

  }
}

这个单一的类负责处理大部分的游戏玩法和用户交互。游戏会根据当前设置的难度因素不断更新。随着游戏的进行,难度被转化为滚动速度,因为每个关卡中的资产都会被更新,并设置为以更大的数值改变其 x 位置值。虽然还有另外两个类来支持玩家和所有的关卡块的创建,但我会留给你自己去深入研究。

转换游戏资产

为了开始准备将这个游戏移植到 HTML5,我们将从游戏中使用的资产开始,将它们转换并准备好用于网络。为了在游戏中创建用户界面和玩家对象,我创建并发布了一个 Flash SWC,它包含在 Flash Builder 中的 ActionScript 项目中。SWC 中的资产不仅包括游戏中使用的对象和动画,还包括如下截图中显示的声音:

转换游戏资产

如前面的截图所示,我粗略地手绘了一个喷气背包人,他有一个基于时间轴的关键帧动画序列,使他能够在视觉上打开和关闭喷气背包,并在撞到墙壁时爆炸。这个时间轴动画在一些帧上附加了一些简单的 ActionScript,允许动画序列轻松停止和循环。

为了将玩家 MovieClip 转换为 HTML5 准备好的资产,我们将使用我们在之前章节中讨论过的精灵表生成工具,如下截图所示:

转换游戏资产

在将图像压缩到最小布局并优化输出属性后,在生成精灵表窗口中将数据格式属性设置为JSON并导出数据。编译后的精灵表以及JSON文件将用于在 HTML5 转换中复制玩家的动画。一旦我们准备好了玩家精灵表,我们可以将其放在一边,同时转换其余的资产。

现在让我们继续准备游戏中使用的音频。Flash SWF 中的音频资产可以通过简单地定位源 MP3 文件并将它们放置在 HTML5 游戏项目目录中来轻松准备用于 HTML5。由于 MP3 文件在 HTML5 中可以在<audio>元素中使用,因此在进行转换时不需要进一步准备音频。HTML5 音频的棘手之处只有在需要在代码中处理它时才会出现,我们很快就会涉及到。

在没有使用 Flash 时间轴或任何其他 HTML5 精灵表库的情况下,我们将不得不自己导入和操作我们的玩家的 Sprite Sheet 数据。像gotoAndPlay();stop();这样方便的调用在 JavaScript 中是不存在的,因此我们需要自己重新创建这些功能。这可能听起来是一项艰巨的任务,但请放心,Sprite Sheets 图像和数据集包含了显示它们所需的所有必要信息。您只需要了解数据格式和需要引用的属性。由于 Sprite Sheets 被分解为帧,通过数字帧引用进行播放控制的概念对您来说应该非常熟悉。在 Flash 中,Sprite Sheet 数据集中导出的帧将直接对应于 Flash 中源 MovieClip 中帧的数字值。然而,由于JSON文件中的帧数据是一个数组,如果我们不补偿数字数组键从 0 开始而不是 1,我们将遇到一个“差一”错误。再次强调,值得一提的是,这种显示 Sprite Sheet 的方法只是 HTML5 中成千上万种 Sprite Sheet 实现方法中的一种。许多开发人员已经创建了自己的开源项目,以减轻在每个项目中都必须自己完成这项工作的压力。

提示:

网络上有很多优秀的开源资源,可以帮助将 Sprite Sheet 集成到您的 HTML5 项目中。一个值得一试的库是www.spritely.net,这是一个完全基于 HTML5 和 jQuery 的 Sprite Sheet 库。

看一下转换后的JetPackMan类的精简版本,如下所示的 JavaScript。这将让您对这个 Sprite Sheet 的帧控制有一个更清晰的理解:

var JetPackMan = function() {
  var _jetPackSound = document.getElementById("jetpack");
  _jetPackSound.addEventListener("ended", onSoundDone, false);

  var _explosionSound = document.getElementById("explosion");
  var _soundPlaying = false;

  var _animationData = Array();
  var _imageSource = 'imgs/JetPackMan.png';
  var _currentFrame = 0;
  var _canvas = document.getElementById("player");
  var _context = _canvas.getContext("2d");

  var _imageObj = new Image();
  _imageObj.src = _imageSource;

  var _currentY = 0;
  var _jetPackOn = false;
  var _explode = false;
  var _interval;

  var _xVal = 0;
  var _yVal = 0;
  var _widthVal = 0;
  var _heightVal = 0;

  // Append the Sprite Sheet JSON to a local Array.
  $.each(JetPackManData['frames'], function(key, val) {
    _animationData.push(val);
  });

  _widthVal = _animationData[_currentFrame]['frame']['w'];
  _heightVal = _animationData[_currentFrame]['frame']['h'];

  // Starts the animation sequence.
  this.startAnimation = function() {
    runAnimation();
  }

  // Turns the Jet Pack on.
  this.jetPackOn = function() {
    _jetPackOn = true;

    if(!_soundPlaying) {
      _jetPackSound.play();
      _soundPlaying = true;
    }
  }

  // Runs the character's animation sequence.
  function runAnimation() {
    // Clear the entire canvas as the player sits in its own.
    _context.clearRect(0, 0, 1000, 800);

    if(_jetPackOn) {
      if(_currentFrame == 0) _currentFrame = 1;

      if(_currentFrame == 5) {
        _currentFrame = 0;
      } else {
        _currentFrame++;
      }
    }

    if(_explode) {
      if(_currentFrame < 7) {
        _currentFrame = 7;
      } else {
        if(_currentFrame < 13)
          _currentFrame++;
        else
          return; // The explosion has finished, stop the interval.
      }
    }

    _currentY = _yVal;

    _context.drawImage(
      _imageObj, 
      _animationData[_currentFrame]['frame']['x'], 
      _animationData[_currentFrame]['frame']['y'], 
      _animationData[_currentFrame]['frame']['w'], 
      _animationData[_currentFrame]['frame']['h'], 
      _xVal,
      _yVal,
      _animationData[_currentFrame]['frame']['w'],
      _animationData[_currentFrame]['frame']['h']
    );

    _widthVal = _animationData[_currentFrame]['frame']['w'];
    _heightVal = _animationData[_currentFrame]['frame']['h'];

    // Call the animation interval again.
    setTimeout(runAnimation, 1000/60);
  }

  // Called on jetpack sound effect complete.
  function onSoundDone(e) {
    _soundPlaying = false;
  }

  // Turns the Jet Pack off.
  this.jetPackOff = function() {
    _jetPackOn = false;
    _currentFrame = 0;
  }

  // Explodes the JetPackMan.
  // To be called on level collision.
  this.explode = function() {
    _explode = true;
    _jetPackOn = false;
    _explosionSound.play();
  }

  this.setX = function(x) { _xVal = x; }
  this.setY = function(y) { _yVal = y; }
  this.currentX = function() { return _xVal; }
  this.currentY = function() { return _yVal; }
  this.currentWidth = function() { return _widthVal; }
  this.currentHeight = function() { return _heightVal; }

  // Destroys the JetPackMan.
  this.destroy = function() {
    _explosionSound = null;
    _jetPackSound = null;
  }
}

这种图形操作方法,虽然与 Flash 时间轴中使用的概念类似,但不同之处在于我们使用了 100%的位图数据,这要求我们在每个帧间隔手动重绘 Sprite Sheet 的可见区域。在每个更新间隔到动画序列时,我们利用内置的drawImage();方法将更新后的帧位置附加到 HTML <canvas>元素上。这意味着如果在前一帧中已经绘制到画布上的可见数据没有被手动从画布上移除,下一个drawImage();调用将只是在旧的图形上附加新的帧图形。为了避免这个问题,我们调用另一个内置方法clearRect();,使用前一个图形的位置值。这可能听起来令人困惑,但有一个非常简单的方法来看待这种 Sprite Sheet 操作。如果您打开本章示例文件中包含的 Jet Pack 游戏的 HTML5 版本,并在runAnimation函数中注释掉clearRect();,您现在可以玩游戏,看看在运行时玩家元素会是什么样子。随着时间的推移,越来越多的帧叠加在一起,很快就会变成一团永远不会更新的层叠图像。尽管这很烦人,但它确实有一个很好的用途,因为它可以用于一些非常有趣的可视化。然而,由于我们游戏中只想要一个角色,我们必须确保在每个间隔上清理画布显示。

虽然在这个例子中避免了这种情况,但另一个重要的注意事项是,在尝试导入包含 Sprite Sheet 帧属性的JSON数据集时可能会遇到的问题:

$.getJSON('json/JetPackMan.json', function(data) { 
  // Your code here…
});

如果用于外部数据加载,流行的 jQuery getJSON方法(api.jquery.com/jQuery.getJSON/)将尝试通过 AJAX 请求到外部数据源,以读取数据并将其传递回返回方法。然而,如果您在本地计算机上执行 JavaScript 而没有使用 Web 服务器,您的浏览器将不接受该请求,并显示类似以下控制台截图的警告:

转换游戏资产

解决这个问题的一种方法是简单地将您的 HTML5 项目发布到一个工作的 Web 服务器上。从那里,您可以在浏览器中打开从网站 URL 引用的数据。您还可以在计算机上设置自己的 Web 服务器,以便能够连接到http://localhost来查看您的内容。像 WAMP(www.wampserver.com/en/)和 MAMP(www.mamp.info/en/index.html)这样的项目都是一体化的软件包,包括 Apache Web 服务器、MySQL 服务器和 PHP。这些服务器设置通常在 Linux 服务器上运行,允许您轻松模拟您的网站最终将驻留的环境,并从上面描述的软件堆栈中获得所有的好处。

当然,您在本地 Web 服务器上运行的内容不会在互联网上公开,所以除非您开始执行一些网络配置(如果您感兴趣,可以在 Google 上搜索“端口转发”),否则您将无法与朋友分享。在尝试在没有 Web 服务器的情况下本地运行此代码时,最终并且可能更合乎逻辑的解决此问题的方法是从 JSON 精灵表输出中剥离数据,并将其附加到包含在游戏 HTML 文档中的 JavaScript 源代码中,如下所示:

var JetPackManData = {
  "frames": {
    "Player0000": {
      "frame": {"x":0…

通过简单地将从 Flash 导出的 JSON 对象附加到标准 JavaScript 变量中,我们现在可以调用JetPackManData变量,而不是 jQuery 的getJSON()调用,从而使我们可以在本地运行这个游戏,而不会出现进一步的访问控制问题。

提示

Mozilla 开发者网络有一个很好的资源,可以更详细地解释为什么可能会出现访问控制问题(developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS)。

您可能会遇到的另一个问题,取决于您用来玩这个游戏的浏览器大小,是浏览器页面滚动或用户试图在游戏中控制玩家时发生的其他不需要的操作。

转换 ActionScript 类

由于您已经通过查看已经修改为处理精灵表而不是 Flash 时间轴的Player类,已经对转换后的游戏源代码有了一瞥,让我们继续沿着这条路,看看我们如何在 HTML5 中设置应用程序的基类。为了帮助一些事件侦听器和元素选择,我在这个项目中包含了 jQuery 库。我已经在本书的前几章中注意到了这个有争议的决定,但值得再次注意的是,许多开发人员不喜欢使用 jQuery 仅仅用于元素选择。由于我们目前正在转换为 HTML5 的游戏非常简单,所以在这个例子中真的没有必要使用 jQuery。然而,如果您将这个简单的游戏源代码扩展为一个更发达的游戏,具有丰富的用户界面和更好的游戏玩法,像 jQuery 这样的库的使用将很容易变得非常有效。

以下源代码是包含在这个游戏的 HTML5 版本中的Game.js文件的简化副本。我简化了代码,并进一步添加了注释文档,以帮助理解这个游戏源代码的流程:

// Wait for the page load to finish before starting the game.
$( document ).ready( function() {
  // Create a new instance of our Game object.
  // This is effectively like declaring our base class
  // in our Flash Builder ActionScript project.
  var game = new Game();
});

// Declaring the Game Object
var Game = function() {
  // Setup all of the private object variables.
  var _levelBlocks = Array();
  var _interval = 0;
  var _blockInterval = 0;

  // Calls the (JetPackMan) object source we just reviewed above
  var _player = new JetPackMan();

  var _difficulty = 10;
  var _speed = 10;
  var _keyDown = false;
  var _score = 0;
  var _highScore = 0;
  var _gameOver = false;
  var _gameInterval;

  // Use the 'game' canvas for level assets.
  var _canvas = document.getElementById("game");

  // Append the stats display to the stage for benchmarking.
  var _stats = new Stats();
  _stats.setMode(0);
  document.body.appendChild(_stats.domElement);
  _stats.domElement.style.position = 'absolute';
  _stats.domElement.style.left = '960px';
  _stats.domElement.style.top = '41px';
  _stats.domElement.style.zIndex = '2';

  // Prepare the initial aspecs of the level.
  generateLevel();

  // Create a player object for the user to control.
  createPlayer();

  // Set up Enter Frame and keyboard even listeners.
  // To emulate the 60 frames per second runtime in the Flash
  // version of this game, we divide 1000 by the specific frame
  // rate.
  _gameInterval = setInterval(
    function() {
      // On every frame interval
      updateInterval(); 
    }
  , 1000 / 60);

  // Set up the keyboard event listeners as they were set up
  // in the Flash version. 
  $(document).keydown(function() { onKeyDown(); });
  $(document).keyup(function() { onKeyUp(); });

  // Do the same for the mouse click events.
  window.addEventListener('mousedown', onKeyDown, false);
  window.addEventListener('mouseup', onKeyUp, false);

  // Finally add support for touch events.
  window.addEventListener('touchstart', onKeyDown, false);
  window.addEventListener('touchend', onKeyUp, false);

  /**
  * Starts a new game.
  * Note: The structure of this function declares it as a 
  * private function within the Game object scope.
  */
  function startNewGame() {
    // Reset the game switches and counters.
    _gameOver = false;
    _score = 0;
    _speed = 10;
    _difficulty = 10;

    // Clear all level blocks by calling the destroy method
    // on each of the active blocks.
    // NOTE: By appending the _levelBlocks.length to a 
    // variable prior to looping over the Array, we avoid
    // having to duplicate the same lookup multiple times.
    var l = _levelBlocks.length;
    for(var i = 0; i < l; i++) {
      _levelBlocks[i].destroy();
    }
    _levelBlocks = new Array();

    // Remove player.
    _player.destroy();
    _player = null;

    generateLevel();
    createPlayer();

    // Start the game interval event again.
    _gameInterval = setInterval(function() { updateInterval(); }, 1000 / 60);
  }

  /**
  * Generates the beginnings of a new level.
  * By running this prior to starting the game, we can
  * assure that there is some level already created for the
  * Player to initially fly into.
  */
  function generateLevel() {
    // Run a loop to generate LevelBlocks on both the top 
    // and bottom of the level.
    for(var i = 0; i < 20; i++) {
      // Top LevelBlocks
      // Base the height of the LevelBlock on the
      // current game difficulty.
      var randomHeight = (Math.floor(Math.random() * (_difficulty - (_difficulty - 10) + 1)) + (_difficulty - 10));
      var levelBlock = new LevelBlock(_speed, 800 + (20 * i), randomHeight - 10, 20, 100);
      // Once created, add the LevelBlock reference to
      // the _LevelBlocks Array.
      _levelBlocks.push(levelBlock);

      // Bottom LevelBlocks
      // We will perform the same operation as above
      // with some some changes to place this block on
      // the bottom of the level.
      randomHeight = (Math.floor(Math.random() * (_difficulty - (_difficulty - 10) + 1)) + (_difficulty - 10));
      var bottomLevelBlock = new LevelBlock(_speed, 800 + (20 * i), (randomHeight * -1) + 750, 20, 100);
      _levelBlocks.push(bottomLevelBlock);
    }
  }

  /**
  * Creates and adds a player object to the stage.
  */
  function createPlayer() {
    _player = new JetPackMan();
    _player.setX(80);
    _player.setY(400);
    _player.startAnimation();
  }

  /**
  * Called on keyboard key down.
  */
  function onKeyDown(e) {
    // Start a new game if the current one has ended.
    if(_gameOver) startNewGame();

    _keyDown = true;
  }

  /**
  * Called on keyboard key up.
  */
  function onKeyUp(e) {
    _keyDown = false;
  }

  /**
  * Game interval. Called on each frame.
  */
  function updateInterval(e) {
    _stats.begin();

    // Update the player's score.
    _score = _score + 1;
    $('#scoreboard .score').html(String(_score));

    // Move player
    if(_keyDown) {
      _player.jetPackOn();
      _player.setY(_player.currentY() - 5);
    } else {
      _player.jetPackOff();
      _player.setY(_player.currentY() + 5);
    }

    // Speed game up as it plays
    _interval++;

    // Check if the interval is far enough to increase
    // the difficulty factor.
    var increaseSpeed = false;
    if(_interval >= 100) {
      increaseSpeed = true;
      _difficulty = _difficulty + 5;
      //_speed++;
      _interval = 0;
    }

    // Update every block
    var l = _levelBlocks.length;
    for(var i = 0; i < l; i++) {
      _levelBlocks[i].update();

      // This is where things get really interesting.
      // Since we do not have access to the 
      // hitTestObject() method like in ActionScript 3
      // we will need to do our own collision detec-tion.
      // This process is further explained after this
      // code example.
      if(
        _levelBlocks[i].currentX() < _player.currentX() + _player.currentWidth()  && 
        _levelBlocks[i].currentX() + _levelBlocks[i].currentWidth()  > _player.currentX() &&_levelBlocks[i].currentY() < _player.currentY() + _player.currentHeight() && _levelBlocks[i].currentY() + _levelBlocks[i].currentHeight() > _player.currentY()
      ) {
        _player.explode();
        _gameOver = true;

        // Check for highscore.
        if(_score > _highScore) {
          _highScore = _score;
          $('#scoreboard .highscore').html(String(_highScore));
        }

        // Like the Flash version, we need to stop
        // the game interval once the user has hit
        // a part of the level. To do this, we can 
        // utilize the clearInterval() method and
        // pass in the _gameInterval reference.
        window.clearInterval(_gameInterval);
      }

      // If the condition for the game speed to increase
      // was met and the increaseSpeed Boolean is set to 
      // true, call the setSpeed method on each of the 
      // LevelBlocks.
      if(increaseSpeed) _levelBlocks[i].setSpeed(_speed);
    }

    // As time passes and the LevelBlocks scroll across
    // the screen we need to check if new blocks need to be
    // added in order to keep the level filled with blocks.
    // To do this, we just monitor the X position value of
    // last block in the _levelBlocks Array.
    if(_levelBlocks[_levelBlocks.length - 1].currentX() < 2000) {
      // Just as in the generateLevel() method, we
      // create a block on both the top and bottom 
      // and set its height based on the _ difficulty.
      var randomHeight = (Math.floor(Math.random() * (_difficulty - (_difficulty - 10) + 1)) + (_difficulty - 10));
      var levelBlock = new LevelBlock(_speed, _levelBlocks[_levelBlocks.length - 1].currentX() + 20, randomHeight - _difficulty, 20, 100 + _difficulty);
      _levelBlocks.push(levelBlock);

      // Bottom
      randomHeight = (Math.floor(Math.random() * (_difficulty - (_difficulty - 10) + 1)) + (_difficulty - 10));
      var bottomLevelBlock = new LevelBlock(_speed, _levelBlocks[_levelBlocks.length - 1].currentX(), (randomHeight * -1) + 750, 20, 100 + _difficulty);
      _levelBlocks.push(bottomLevelBlock);
    }

    // To make the game interesting, we add a random block
    // every now and then to make things interesting.
    // We will base the frequency of random blocks on the
    // current difficulty in the game. 
    _blockInterval++;
    if(_blockInterval > 5000 / _difficulty) {
      // When the condition is met, reset the counter.
      _blockInterval = 0;

      // Create random height and Y position values
      // to keep things interesting. This method of
      // generating a random number within a range
      // is one of the most common.
      // Math.floor(Math.random()*(max-min+1))+min;
      randomHeight = (Math.floor(Math.random() * (750 - 5 + 1)) + 5);
      var randomY = (Math.floor(Math.random() * (750 - 5 + 1)) + 5);
      var randomBlock = new LevelBlock(_speed, _levelBlocks[_levelBlocks.length - 1].currentX(), randomY, 20, 100 + _difficulty);
      _levelBlocks.push(randomBlock);
    }

    _stats.end();
  }
};

在打印时,这可能看起来像是大量的代码,但实际上这是一个非常简单的 JavaScript 构建的 2D 游戏的例子。确保阅读代码中包含的注释,这些注释突出了代码的每个部分的功能以及其布局。当一切都说完并且主index.html文件在符合 HTML5 的网络浏览器中打开时,您将希望看到以下截图中所示的 Jet Pack 游戏的结果:

转换 ActionScript 类

请注意,我们甚至成功在游戏区域的右上角获得了类似于 Flash 版本的统计信息。就像我们在第七章中审查的那样,选择开发方式,我们利用了相同开发者(Ricardo Cabello Miguel,也被称为 Mr. Doob)提供给我们在 Flash 中使用的统计显示功能。查看Stats.js的 GitHub 页面(github.com/mrdoob/stats.js/)以了解更多关于该项目的信息。

处理音频和播放

利用我们在本书中对 HTML5 音频文件类型和编解码器支持的概述中获得的知识,我们可以很容易地为这个游戏设置音频。与我们在 Flash 项目中使用的 SWC 导入方法不同,当将音频导入 HTML5 项目时,我们可以直接将引用附加到 HTML 文档的<audio>元素的主体中,如下所示:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Jet Pack</title>

    <link rel="stylesheet" type="text/css" href="css/Game.css">

    <script src="img/jquery.min.js"></script>
    <script src="img/Stats.js"></script>
    <script src="img/LevelBlock.js"></script>
    <script src="img/JetPackMan.js"></script>
    <script src="img/JetPackManData.js"></script>
    <script src="img/Game.js"></script>
  </head>

  <body>
    <canvas id="game" width="1000" height="800"></canvas>
    <canvas id="player" width="1000" height="800"></canvas>

    <div id="scoreboard">
      <p>Score: <span class="score">0</span></p>
      <p>HighSchore: <span class="highscore">0</span></p>
    </div>

 <audio id="explosion" src="img/explosion.mp3"></audio>
 <audio id="jetpack" src="img/jetpack.mp3"></audio>
  </body>
</html>

默认情况下,音频不会播放,因为我们排除了创建播放控件的选项,在页面渲染期间不会创建任何可见元素在 HTML 文档中。因此,要开始在 JavaScript 中利用音频,允许我们按需播放,我们首先通过引用附加到 HTML 文档中的元素来开始:

var _jetPackSound = document.getElementById("jetpack");

通过引用音频文件,我们可以通过在 _jetPackSound 变量上调用play()方法来轻松开始控制播放。然而,为了避免在游戏过程中多次重叠同一音轨,我们还可以附加一个事件侦听器到元素,以在声音播放完成时调用如下:

_jetPackSound.addEventListener("ended", onSoundComplete, false);

通过为此侦听器创建事件处理程序,我们还可以更新位于JetPackMan类中的_soundPlaying布尔变量。从这一点开始,我们现在可以轻松确定声音效果是否正在JetPackMan对象内的任何位置播放。

提示

您可以在 Mozilla 开发者网络网站上找到有关 JavaScript 中 HTML5 音频和视频播放控件的更详细信息developer.mozilla.org/en-US/docs/HTML/Using_HTML5_audio_and_video

重写无法直接转换的 AS3 代码

正如我在之前的一个代码示例中提到的,ActionScript 3 中常用的hitTestObject()方法在任何 HTML5 项目中都没有,这意味着许多这些常见但复杂的功能需要自己编写。在 HTML5 中 Jet Pack 游戏的示例中,我们使用了更简单的碰撞检测方法之一,通过检查玩家的边界框或玩家占据的区域,并将其与生成的每一个 LevelBlock 进行比较。简化后,这个条件看起来像下面的代码:

if(
  a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y
) {
  // 'a' is touching 'b'
}

实现碰撞检测这样的功能的一个棘手之处在于,随着 HTML5 中 Canvas 元素的出现,有多种方法可以在 HTML 文档中创建和操作元素。Flash 应用程序具有利用一些出色的内置功能的优势,因为开发结构更加结构化。在开发严重依赖 JavaScript 的 HTML5 应用程序时,开发流程变得更加像是在西部的野外。

提示

当查找其他开发者如何实现碰撞检测这样的功能时,一个很好的资源始终是 Github。Github 项目搜索允许您将搜索范围缩小到特定的编程语言,从而更容易找到可能帮助您的开源项目(github.com/search?l=JavaScript&q=Collision+Detection&ref=cmdform&type=Repositories)。

移动平台支持

由于我们现在有了一个 HTML5 版本的游戏,我们可以考虑的一个最初的好处是,大多数移动设备浏览器都支持播放这个应用程序。然而,由于我们专门设置了游戏以通过用户的键盘进行控制,我们没有为那些无法访问键盘的用户创建控制方法。在我们的 Jet Pack 游戏中解决这个问题实际上相当容易,因为我们的控制系统只使用一个输入。实际上,我们有两种选择来实现这个功能,因为几乎每个支持触摸事件的移动浏览器都将它们注册为点击事件。这意味着我们可以将相同的键盘事件监听器实现为特定的触摸事件或常见的鼠标点击事件。设置鼠标事件而不是触摸事件的好处是,鼠标事件也将为任何使用鼠标玩游戏的用户注册。只使用默认的 JavaScript 语法,我们可以用以下代码实现这一点:

window.addEventListener('mousedown', onKeyDown, false);
window.addEventListener('mouseup', onKeyUp, false);

如您所见,由于我们没有在onKeyDownonKeyUp方法中对按键进行条件判断,我们可以简单地将鼠标事件绑定到相同的事件处理程序中并结束。根据您是否使用触摸事件而不是鼠标事件,我们只需要修改addEventListener调用中的事件参数,如下所示:

window.addEventListener('touchstart', onKeyDown, false);
window.addEventListener('touchend', onKeyUp, false);

提示

Mozilla 开发者网络还包含了一个详细的概述,介绍了在开发支持触摸的 HTML5 应用程序时可用的触摸事件和属性(developer.mozilla.org/en-US/docs/DOM/Touch_events)。虽然这是一个 Mozilla 网站,但大部分文档都是跨浏览器兼容的。

在移动设备上测试这个游戏最令人困扰的一个方面可能是,用于显示游戏元素的画布元素具有固定的宽度和高度。我最初选择创建游戏的宽度和高度为 1000px 乘以 600px。这些尺寸可能在一些移动设备上正确呈现,但如果您在手机上玩这个游戏,画布的某些区域可能会被切断显示,或者以缩放画布以适应屏幕的方式呈现,如下面的截图所示:

Mobile platform support

前面的截图直接来自三星 Galaxy S2,清楚地显示了游戏在横向显示中如何从默认视图中被切断。页面的放大和缩小仍然可以作为交互式浏览器功能,但即使在游戏处于横向显示时缩小页面,我仍然无法查看整个可视游戏区域。现在看看在同一手机上以纵向显示拍摄的游戏的下一个截图:

Mobile platform support

现在,页面视图完全缩小,我们可以看到整个游戏区域,但可视区域的底部一半被浪费了,因为游戏设置为在 1000px x 600px 的显示中显示。尽管这个问题很痛苦,但实际上对许多 Flash 开发人员来说是非常常见的。如果您在 Flash 中开发了任何需要可伸缩显示的 Web 应用程序,您将不得不在应用程序中编写事件侦听器,以便监视窗口调整大小并相应地移动应用程序中的元素位置。您可以使用以下代码轻松从 JavaScript 更新画布大小,调用元素的宽度和高度属性:

myCanvas.width = 1920;
myCanvas.height = 1080;
myCanvas.style.width = "1920px";
myCanvas.style.height = "1080px";

这个设置涵盖了更新元素的 CSS 样式以及基本元素的宽度和高度值。当然,通过这个更新,您还必须手动更新在游戏中显示的所有可玩元素的位置值。如果您希望继续深入挖掘这个游戏的源代码,我建议尝试扩展游戏玩法或使用 Canvas 元素调整动态窗口显示大小。

总结

通过使用最初在 Flash 中构建的应用程序,在本章中,我们涵盖了 HTML5 开发的一些更有趣的方面。通过在不使用任何第三方 JavaScript 库的情况下开发我们游戏的 HTML5 版本,我们可以轻松地比较这两种开发范式。实际上,Flash 和 HTML5 开发非常相似。资产准备和操作突出了将您的 ActionScript 和 Flash 开发经验转移到 HTML5 开发的最复杂的方面之一。了解您的限制,无论是浏览器还是设备特定的,都是确保您准备好向尽可能多的用户显示您的内容的关键因素。

在本书的下一章和最后一章中,我们将继续组合工作的 HTML5 应用程序,更加重视为公开发布做准备。我们将尝试推动 HTML5 在外围设备上的潜力,如网络摄像头和麦克风,并讨论一旦您完成本书后,您在 HTML5 开发中的下一步可能是什么。

第十章:准备发布

在本书的各章中,我们已经涵盖了许多不同的应用程序和工具,用于开发你的 HTML5 内容。与你习惯的 Flash 开发周期一样,你通常需要花一些时间来优化你的应用程序,以便在 HTML5 应用程序开发流程的最后阶段在网络上使用。值得庆幸的是,就像我们的 HTML5 开发的资产管理和编码阶段一样,有大量的优秀资源可以帮助你准备将你的应用程序移动到公共面向网络服务器上。在本地计算机上测试 HTML5 应用程序时,你正在开发它们的计算机上的本地网络,除非经过特殊配置,否则不允许在任何其他计算机上查看内容,无论是在你的本地网络上还是在互联网上。如果你在你的计算机上安装了一个 Web 服务器并配置了你的网络以允许传入连接,那么你才能与互联网上的其他人分享你的工作。为了避免任何域名或安全问题,将你的工作托管到互联网上的常见方法是从在线供应商那里购买 Web 托管计划。这样,你的内容就不再是你的工作版本,而是放置在专门用于托管你的 Web 内容的服务器上。这个过程对于 HTML5 来说并不新鲜,对于你的 Flash SWF文件来说也是典型的,只要你有相同的意图。然而,由于 HTML5 项目不会被编译成 Flash 那样的单个二进制文件,我们需要非常小心地确保我们的项目资产和代码被正确设置,以向我们的最终用户提供快速和安全的体验。在本章中,我们将通过审视许多最佳的方式来为 Web 准备你的项目来结束本书。

针对支持的 Web 浏览器

许多开发人员在 HTML5 开发过程中未能不断在多个 Web 浏览器中测试他们的工作,要么是因为他们懒得花时间去做,要么是因为他们完全忘记了。当你测试和发布最终应用程序供公众使用时,你将遇到的最大问题之一很可能是浏览器支持。如果你没有在不同平台的各种浏览器中测试你的项目,当用户抱怨你的应用程序对他们不起作用时,你可能会感到惊讶。由于 HTML5 仍然是新生力量,许多浏览器仍在努力赶上支持显示你的 HTML5 内容所需的功能。

跟上当前 Web 浏览器在 HTML5 功能支持方面的支持是作为开发人员获得的一个极其重要的资产。让自己和你的项目远离即将到来的厄运,因为你能够早早地发现特定设备或平台上的支持不足,不仅可以节省大量的时间,还可以节省金钱。一个伟大的“一站式”Web 浏览器 HTML5 功能支持检查的网站是caniuse.com。在 Web 浏览器中打开该网站后,你可以轻松地选择任何指定的 HTML5 功能,并查看哪些浏览器,更重要的是哪些浏览器版本支持该特定功能:

针对支持的 Web 浏览器

在撰写本书时,看一下前面的Canvas标签元素支持统计的屏幕截图,我们可以看到Canvas元素现在被大约 85%的浏览器支持。这个网站几乎是在开始为项目创建初始技术大纲时必不可少的。只要稍微了解一些 HTML5 中可能在项目中使用的功能,你就可以简单地跳转到caniuse.com并确定你的最终用户需要使用什么来正确查看你的内容。

html5readiness.com是一个很好的 HTML5 项目的示例,因为该网站使用了来自caniuse.com的数据,以便以易于查看的单页交互式图形显示 HTML5 的准备情况

目标支持的 Web 浏览器

这可以是一个很好的资源,就像caniuse.com一样,但是你的客户或设计师可能更喜欢它,因为它易于使用,并且支持功能的时间线可视化。

了解全球浏览器支持的概况是一个很好的资源,但是如果你对你的电脑上使用的浏览器的规范和统计数据感兴趣,可以前往html5test.com让该网站评估你的浏览器。

目标支持的 Web 浏览器

该网站生成的评分可以作为该网站测试你的浏览器的所有 HTML5 功能的概览。在结果页面向下滚动,你会找到每个被测试功能的完整概述,以及它是否通过了测试。

目标支持的 Web 浏览器

这些易于使用的工具都可以免费在线访问,让你可以轻松获取所有数据,以便正确规划和概述你的 HTML5 应用。请记住,随着新浏览器的发布和当前版本的更新,这些数据将继续发展。你甚至可能会发现,在项目开发初期参考的一些数据在你完成时已经发生了变化。因此,在开发过程中测试项目的浏览器兼容性总是值得额外的努力。

提示

一定要查看移动浏览器的 HTML5 测试,网址是html5test.com/results/mobile.html。在那里,你可以轻松找出当前哪些移动设备最好地支持你的 HTML5 应用所使用的功能,以及整个 HTML5 规范。这个资源在购买下一个手机或平板电脑时也会非常有帮助。

客户端测试

随着支持 HTML5 的移动和桌面设备数量呈指数增长,开发人员需要在各种不同的设置上测试应用程序或网络内容的需求也在增加。不幸的是,花费在所有这些设备上设置测试环境所需的时间可能会非常快速。为了在测试阶段节省大量时间,你可能会对查看www.browserstack.com感兴趣。

客户端测试

注册 BrowserStack 后,你可以轻松地从该服务中访问所有现代桌面和移动浏览器。与必须独立配置每台机器和浏览器不同,BrowserStack 已经为你做好了所有的艰苦工作,只需提供预先配置了开发工具的特定浏览器的访问权限。BrowserStack 是一个付费服务,但如果你感兴趣,也可以获得免费试用。我个人建议至少手动进行一次这种类型的测试,以便看看你能否证明使用这样的服务的成本。如果你是那种在地下室有 10 台电脑的人,那么请设置自己的测试环境。你也可以在开发电脑上设置多个虚拟机,以便你可以从一台电脑上打开和关闭你的测试机器。无论你如何做,通常只是时间的问题,你会厌倦花费无数小时来维护你的测试环境,而 BrowserStack 等服务可能是最终的解决方案。

浏览器 Nightly 版本

如果您真的在挑战 HTML5 的极限,测试您的内容在将要查看您网站的所有预期浏览器的最新版本上可能是一个明智的决定。对于像 Internet Explorer 这样的私人开发的浏览器,您将不得不等待微软发布或预发布下一个版本,但对于像 Google Chrome(www.google.com/intl/en/chrome/browser/)或 Mozilla 的 Firefox(www.mozilla.org/en-US/firefox/new/)这样的开源浏览器,您可以下载 Nightly 或积极开发的软件版本,以了解下一个版本的方向。

浏览器 Nightly 版本

Google Chrome 浏览器的 Nightly 版本被称为 Canary 版本,可以在www.google.ca/intl/en/chrome/browser/canary.html找到。从前面项目网站截图的标语中可以看出,这个 Chrome 版本是绝对最新的,并且在前面明确警告了其可能的不稳定性。值得记住的是,您可以安全地在计算机上安装标准版本的 Google Chrome 以继续正常使用。Canary 版本可以随时打开,并且预设为在 Google 发布新版本供公众使用时从其分发服务器上自动更新。

当然,正如前面提到的,Mozilla 也有他们自己的 Firefox Nightly 版本可供下载,网址是nightly.mozilla.org

浏览器 Nightly 版本

Firefox 的 Nightly 版本目前提供了桌面和移动版本的网络浏览器,并且可以轻松地与您计算机上现有的稳定版本的 Firefox 并存。使用这些流行网络浏览器的开发版本不仅可以让您在即将推出的软件上测试您的代码,而且还可以让您深入了解一些新的令人兴奋的功能,这些功能一般公众可能要等到下一个稳定版本才能使用。

那么,真的值得在这些浏览器上进行测试吗?实际上,没有人真正使用这些浏览器吗?嗯,对于可能在互联网上不受更新或补丁影响的应用程序来说,提前进行开发确实可以带来回报。如果您没有注意到您的 HTML5 网络应用程序在您发布应用程序几个月后发布的浏览器中无法工作,那可能是一件非常糟糕的事情。此外,花时间与这些新的网络浏览器一起将使您更加了解和熟悉在开发应用程序时可以利用的功能。您将开始更多地了解特定浏览器的开发和发布周期的内部工作。

WebRTC

利用媒体输入,如摄像头和麦克风,在您的 HTML5 应用程序中的使用达到了历史最高水平,WebRTC 已经提供了解决这个问题的解决方案。WebRTC 项目目前得到 Google、Mozilla 和 Opera 的支持,项目网站可以在www.webrtc.org找到。从 Flash 开发者的角度来看,使用网络摄像头和麦克风而无需第三方插件的概念应该让您对 HTML5 开发更加兴奋。WebRTC 已经开始为 HTML5 开发者带来的功能将 HTML5 的极限推向了与 Flash 更加接近的地步。

提示

如果您有在线朋友,并且想测试一个简单的 WebRTC 网络摄像头聊天应用程序,请转到apprtc.appspot.com并连接到将返回用户 ID 的服务器。将该 ID 与站点的 URL 一起发送给朋友,并在纯 HTML5 音频和视频聊天中在线连接到彼此。

由于 WebRTC 仍然是一个非常新的规范,目前仅有一些现代浏览器支持它,而且不幸的是,对于开发人员来说,每个浏览器的支持方法都有些不同。任何使用 WebRTC 的开发人员应该做的初始步骤是检查当前浏览器是否支持此功能。可以使用一些 JavaScript 函数来完成这个任务,比如以下代码片段:

function hasGetUserMedia() {
  return !!(navigator.getUserMedia || 
            navigator.webkitGetUserMedia ||
            navigator.mozGetUserMedia || 
            navigator.msGetUserMedia
          );
}

正如您所看到的,与 JavaScript 中的用户媒体对象元素交互的方法从getUserMediamsGetUserMedia都不同,具体取决于尝试加载它的 Web 浏览器的类型。通过将查找用户媒体对象的所有方法条件化为一个单一的返回语句,这个函数将返回用户媒体对象(如果浏览器支持),或者如果没有找到任何内容,则返回 false。将这个hasGetUserMedia函数应用到 HTML5 项目中后,如果可用,我们可以轻松地将函数调用应用到进一步的代码中,将用户的网络摄像头视频流应用到网页上。

var video = document.getElementById('camera');
if(hasGetUserMedia()) {
  navigator.webkitGetUserMedia(
{
audio: true, 
video: true
}, function(stream) {
video.src = window.URL.createObjectURL(stream);
      }, onConnectionFail);
} else {
      alert('WebRTC is not supported!');
}

这个代码示例的初始行将 ID 为camera的元素应用到变量video上。HTML 文档中的这个元素实际上是一个视频标签元素,看起来像下面这样:

<video id="camera" autoplay></video>

您可能还记得,视频元素可以通过 CSS3 属性进行样式设置,为显示网络摄像头视频提供了大量的设计选项。因此,让我们将这些与 WebRTC 相关的代码片段扩展为一个带有一些 CSS3 样式的工作示例。我们将从创建初始的 HTML 页面开始,以在 Web 浏览器中显示。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Web RTC Demo</title>
    <style>
      .grayscale {
        -webkit-filter:grayscale(1);
      }
      .sepia {
        -webkit-filter:sepia(1);
      }
      .blur {
        -webkit-filter:blur(10px)
      }
    </style>
  </head>

  <body>
    <video id="camera" autoplay></video>
    <p>
      <button id="button0">Clear Styles</button>
      <button id="button1">Grayscale</button>
      <button id="button2">Sepia</button>
      <button id="button3">Blur</button>
    </p>
    <script src="img/webrtc.js"></script>
  </body>
</html>

在这个 HTML 页面中,我们还添加了一些简单的 CSS3 过滤器,为每个过滤器添加了唯一的类名。每个不同的 CSS 样式都附加到 HTML 文档中,并在文档的主体中有一个对应的 HTML 按钮元素。最后,我们引用将处理 WebRTC 功能的外部 JavaScript 文件,接下来我们将创建这个文件。

保存了 HTML 文件并打开一个新文件来编写我们的 JavaScript 代码,我们可以将已经查看过的代码片段附加到视频元素上,以及一些代码来附加 CSS 样式,当用户点击 HTML 页面上的按钮时。

var video = document.getElementById('camera');
var clearBtn = document.getElementById('button0');
var grayscaleBtn = document.getElementById('button1');
var speiaBtn = document.getElementById('button2');
var blurBtn = document.getElementById('button3');

if(hasGetUserMedia()) {
  navigator.webkitGetUserMedia({audio: true, video: true}, function(stream) {
    video.src = window.URL.createObjectURL(stream);
  }, onConnectionFail);
} else {
  alert('getUserMedia() is not supported in your browser');
}

// Checks if the users browser has Web RTC support.
function hasGetUserMedia() {
  return !!(navigator.getUserMedia || 
            navigator.webkitGetUserMedia ||
            navigator.mozGetUserMedia || 
            navigator.msGetUserMedia
          );
}

// Called if the connection to the video stream fails.
var onConnectionFail = function(e) {
  console.log('ERROR: User Media connection failure!', e);
};

// Clear Styles
clearBtn.addEventListener('click', function() {
  video.className = '';
});

// Grayscale
grayscaleBtn.addEventListener('click', function() {
  video.classList.add('grayscale');
});

// Sepia
speiaBtn.addEventListener('click', function() {
  video.classList.add('sepia');
});

// Blur
blurBtn.addEventListener('click', function() {
  video.classList.add('blur');
});

正如您所看到的,这个 JavaScript 的大部分内容都在处理视频元素的 CSS 操作,而不是设置网络摄像头流。只要用户的浏览器支持,通过几行 JavaScript 代码就可以轻松地为最终用户建立 WebRTC 连接。

WebRTC

幸运的是,我足够聪明,添加了模糊效果来隐藏自己,就像在前面的截图中使用的例子一样。请记住,就像 Flash 中的摄像头和麦克风连接一样,流完全是用户本地的,除非您手动将该功能添加到应用程序中,否则不会发送到任何其他服务器。对于诸如多用户聊天和公共流媒体之类的应用程序,您将需要一个服务器来发送您的音频或视频流。如果您对这种 HTML5 开发感到兴奋,Node.js 将是一个完美的开始。甚至有一个专门用于处理 WebRTC 的 Node.js 包(npmjs.org/package/webrtc.io)可以帮助您朝着正确的方向开始。

隐私可能是一个巨大的问题,当涉及到在互联网上流式传输音频和视频而不使用 Flash Player 等第三方插件时。因此,当尝试通过 WebRTC 连接到其媒体通道时,Web 浏览器将始终提示最终用户进行批准。如果您正在开发计划利用 WebRTC 功能的 HTML5 应用程序,这是一个重要的事实需要记住。如果用户决定不启用其麦克风或网络摄像头,您的应用程序的用户体验可能会完全中断。一些浏览器(如 Google Chrome)正在采取的另一步骤是为任何具有活动媒体流的浏览器标签应用一个独特的图标,以保护您的在线安全。无论流是否将内容传送到第三方服务器或仅在本地使用,该图标始终可见。

WebRTC

正如您在前面的截图中所看到的,当 Google Chrome 中激活了 WebRTC 并且用户正在向网站传送音频或视频时,浏览器标签会获得一个发光的红色录制图标,以提醒您流正在进行。

提示

如果您对了解 WebRTC 项目的起源及其在现代浏览器上的支持感兴趣,以及如何在项目中正确利用它,可以查看www.html5rocks.com/en/tutorials/getusermedia/intro/上关于在 HTML 中捕获音频和视频的精彩文章。

关于 WebRTC 更令人兴奋的是技术的可能未来。正如您迄今为止在本技术概述中所看到的,目标实际上是在没有对网络的依赖的情况下使音频和视频工作。然而,还有一个目标是在 WebRTC 中启用专用数据通道,这可能会使一系列令人惊叹的新基于 Web 的技术成为可能,因为您的计算机在使用特定 Web 应用程序时可以真正充当自己的服务器。

WebGL 支持

WebGL 和 WebRTC 一样,是许多现代桌面和移动 Web 浏览器开始看到高质量支持的新功能之一。利用客户端计算机或设备上的硬件加速图形,可以使您在开发 Flash 项目时可能使用的许多完整功能的 2D 和 3D 动画和交互成为可能。在尝试验证您所使用的浏览器是否支持 WebGL 时,最简单的验证工具可以在get.webgl.org找到。

WebGL 支持

如果您的浏览器通过了 WebGL 验证测试,该网站将显示如前面的截图所示,呈现一个旋转的 3D 线框立方体,并显示结果的文本提示。当然,您可以使用caniuse.com来检查当前支持 WebGL 的浏览器,以及查看www.khronos.org/webgl/wiki/Main_Page上的官方 WebGL 公共 wiki。

提示

如果您对在 HTML5 中的令人兴奋的 WebGL 世界想了解更多,请查看 Packt Publishing 网站上的WebGL 初学者指南www.packtpub.com/webgl-javascript-beginners-guide/book)。

移动浏览器上的 WebGL

根据您正在查看的 WebGL 应用程序的强度,您可能会注意到极慢的帧速率,以及可能变得无响应的应用程序。由于 WebGL 在少数设备和浏览器上得到支持,开发社区正在慢慢地为利用这项技术的移动体验创建优化。不幸的是,大多数桌面和移动设备之间的处理能力仍然相差甚远,因此,如果打算让移动浏览器查看它们,开发人员需要优化其体验是至关重要的。

在移动设备上查看 WebGL 内容的一个很好的资源是 Three.js 项目页面,网址为threejs.org。从主页的项目和示例索引中,您可以轻松地了解一些最酷的 WebGL 示例是如何在任何设备上运行的。

提示

如果您对任何网络浏览器的 WebGL 功能感兴趣,可以查看webgl-bench.appspot.com,这是一个在线基准测试工具,可帮助检查和显示您的 WebGL 渲染统计信息。

让用户找到您的作品

虽然搜索引擎索引与 HTML5 没有直接关系,但从 Flash 开发者的角度来看,这点绝对值得注意。许多搜索引擎在解析 Flash 内容以获取搜索结果方面已经变得越来越好,但归根结底,创建一个完整的 Flash 网站将需要您进行一些工作,以确保您的内容在搜索时能够正确显示。搜索引擎爬虫将更容易地读取您的 HTML5 项目,因为它们对于读取网站页面上的重要数据有更直接的方法。

随着 HTML 元素标签所能包含的内容的增长,特别是针对搜索引擎的网站上特定元素的标记,是优化您的网站搜索引擎优化的一种新颖而令人兴奋的方式。谷歌搜索引擎近年来寻找的网络内容的关键方面之一是与作者相关的内容。例如,如果您的网站上有一篇特定作者撰写的文章或作品,您可以轻松地将作者的信息作为 HTML 页面中的元数据集成进去。

当正确标记时,这不仅可以让搜索引擎轻松解析这些特定页面的内容,也可以让社交网站轻松解析。例如,考虑一些在 HTML 源代码中定义特定元素的方法:

<!-- 
    Link to a Spanish version of your site, defined by the
    'hreflang' parameter.
-->
<link hreflang="es" href="index_es.html" rel="alternate">

<!-- The link to the authors website (external) -->
<link rel="author" href="https://www.johnsmith.com″>

在这些示例中,我们附加了relhreflang标签元素,以帮助预先描述链接的内容。

<a rel="license" href="licensing.html">License information</a>
<a rel="next" href="index3.html">Next</a> 
<a rel="prev" href="index1.html">Previous</a>

<link rel="search" href="http://www.johnsmith.com/search.xml">

<figure id="myfigure>
  <img src="img/cat.jpg" alt="Kitty Cat">
  <figcaption>A Photo of my Cat</figcaption>
</figure>

虽然在开发 HTML 文档时,这些标签都不是技术上必需的,但是在您的内容中放入元数据的努力,特别是如果它将会被动态提供,很可能会导致更好的流量和搜索引擎结果排名,一旦您的网站已经发布在线。

正如前面提到的,许多社交网站,如 Google+或 Facebook,将尝试解析您的网页内容,以创建详细的链接结构指向您的网站。您可以向这些尝试读取内容的网站和系统提供更多有价值的内容和元数据,这将导致更好的链接结构和信息输出,当用户链接回您的网站时。

HTML5 历史 API

如果您以前完全使用 Flash 构建过网站,可能已经使用过 JavaScript 库,如 SWFAddress(www.asual.com/swfaddress)来模拟正常网页显示中典型的 URL 更新功能。如果您不了解这个项目,或者 URL 更新的概念,那么简单来说,当您在互联网上加载新页面时,地址栏中的 URL 会根据您正在查看的页面的位置而更改。对于典型的 HTML 网页内容,每个页面都显示在一个新的 HTML 文档中,因此可以为网站的每个部分轻松定义 URL。对于使用 Flash 构建的网站,所有内容将被编译到一个单独的 SWF 中,该 SWF 将嵌入到单个 HTML 页面中。因此,当 Flash 中的内容发生变化时,由于没有请求新页面,因此 Web 浏览器的地址栏不会发生变化。通过在 Flash 项目中包含 SWFAddress,您可以轻松通过 SWFAddress JavaScript API 发送命令,以将自定义更新附加到 Web 浏览器的地址栏。最重要的是,如果用户要点击应用程序使用的自定义 URL 之一,您可以轻松地将用户体验直接跳转到他们最初打算访问的应用程序部分。随着 HTML5 中 Canvas 元素和 AJAX 请求等功能的日益流行,许多网站在每次请求时都需要重新加载网页的需求正在减少。因此,需要类似于 SWFAddress 的系统来正确地将您的网站集成到 Web 上。

HTML5 历史 API(www.whatwg.org/specs/web-apps/current-work/multipage/history.html)不仅可以控制客户端 Web 浏览器的地址栏,还可以控制许多浏览器历史记录的元素,以使浏览器的后退和前进功能继续工作。尽管一些较旧版本的流行现代浏览器不支持 HTML5 历史 API,但目前可用的绝大多数最新版本的所有浏览器似乎都支持此功能(caniuse.com/#feat=history)。然而,当尝试使用任何您不确定每个用户是否支持的功能时,通常值得进行一些初始检查。要检查历史 API 的支持情况,我们可以尝试查找history.pushState对象的类型,该对象负责将更改写入浏览器的页面历史记录。

if (typeof history.pushState === 'undefined') {
  // The HTML5 History API is Unavailable...
} else {
  // The HTML5 History API is Available!
}

如果一切顺利,您就可以读取和写入客户端浏览器的历史记录。Mozilla 已经对历史 API 及其用法进行了出色的概述,可以在developer.mozilla.org/en/docs/DOM/Manipulating_the_browser_history找到。

提示

要了解 HTML5 历史 API 的实际示例,请查看 HTML5 Demos(html5demos.com/history)的在线演示和源代码。

关于历史 API 和搜索引擎优化,需要注意的重要因素是,当应用程序在浏览器地址栏中引用应用程序状态时,应用程序具有的自由度。为内容设置唯一的 URL 以指向特定位置和元素将有助于更容易地进行调试和生成用户统计和分析数据。

预制测试和基准解决方案

在之前的章节中,我们已经介绍并利用了许多现代网页浏览器开发者工具集的功能,主要是利用 JavaScript 控制台在开发过程中进行简单的输出和代码调试。许多流行的网页浏览器中预装的开发者工具集还包括许多工具,不仅允许您深入了解自己网站的结构以查找问题,还可以调试互联网上的其他网站,以了解它们的内部运作。

谷歌的 Web 开发工具集

谈到在浏览器中调试和分析 HTML5 内容的工具时,谷歌确实通过创建 Chrome 开发者工具(developers.google.com/chrome-developer-tools/)和谷歌 Web 工具包(developers.google.com/web-toolkit/overview)迈出了重要的一步。所有这些工具结合起来可以用来进行应用程序部署之前可能需要的大部分深度调试。例如,如果您已经成功开发了自己的 HTML5 游戏,并且需要深入了解游戏的结构和流程以确定需要优化的地方,您可以通过编写每个游戏元素的测试来完成这个过程,或者您可以使用谷歌的 Speed Tracer(developers.google.com/web-toolkit/speedtracer/index)这样的工具。

谷歌的 Web 开发工具集

正如您在 Speed Tracer 的截图中所看到的,该应用程序成功地深入到应用程序运行时,显示了有关活动应用程序在特定计算机上实际运行方式的许多方面的统计信息。统计输出中包括加载时间、执行时间、显示时间等等。以传统方式显示内容和视频的简单网站可能不会发现使用这种深度挖掘工具的任何好处。然而,对于任何有兴趣构建强大的交互式 Canvas、WebGL 或任何其他需要调试运行时的重型计算或计算应用程序的人来说,这个工具绝对值得一试。

对于绝大多数 HTML5 开发者来说,Chrome 开发者工具是一个许多网页开发者无法离开的浏览器功能。在每个 Chrome 安装中都包含了整个工具集,您可以轻松地打开并开始使用工具来调试任何网站。从使用元素检查器检查页面元素及其相关样式,如下截图所示,到使用网络检查器显示页面在加载和运行时所做的每个内部和外部请求的加载时间,几乎您需要调试内容的一切都在一个方便的内置窗口中。

谷歌的 Web 开发工具集

由于谷歌在过去几年里一直在极大地推广这款软件,我在这里真的不需要详细介绍它的用法。在项目网站上可以找到多个令人惊叹的视频教程,以及对应用程序功能集中的每个功能的出色文档。

提示

Chrome 开发者工具项目网站包含了大量视频和文本资源,帮助理解扩展能够做到的一切。一定要前往developers.google.com/chrome-developer-tools/docs/videos查看。

随着时间的推移,你可能会发现自己在浏览网页时迅速打开和关闭这个扩展。作为开发者,HTML5 开发的一个伟大方面是当内容是公开的时候,运行它的代码也是公开的。深入研究别人是如何创建让你惊叹的网页内容是一个非常简单的方法,让你对 HTML5 技术栈非常了解。

YSlow 网站评分

尽管我们中的许多人讨厌在学校得到成绩,但让别人给你的网站打分可能是一个非常有用的资源。YSlow(yslow.org)浏览器扩展是一个开源项目,可以添加到 Chrome、Firefox、Opera、Safari 和许多其他形式的基于 Web 的内容中。打开并激活扩展后,你可以在网页浏览器中运行当前打开的网站上的系统。在我们对www.packtpub.com运行测试后,考虑一下 YSlow 扩展窗口。

YSlow 网站评分

在扩展窗口的左上角,你可以看到该网站得到了 B 级的评分。现在没有关于评分构成的详细信息,这些数据实际上是没有用的。因此,为了确认评分结果,让我们深入了解为什么这个网站得到了它的评分。在评分下面的一个颜色编码列表中,你将找到 YSlow 检查的所有领域的单独测试结果。对于绝大多数测试,Packt Publishing 网站得分都非常好。然而,测试得出结论,首先,该网站可以减少 HTTP 请求,这意味着页面所包含的代码和资产过多地引用了各种外部资源。其次,测试结果还显示了过期头的不良结果。

这意味着页面上有许多资产的缓存到期时间非常遥远。这样的配置很可能会导致一些用户在页面加载期间看到实际上已经在服务器上更新的页面内容。由于某些资产的缓存时间设置得非常遥远,如果用户之前访问过你的网站,他们的网页浏览器可能不会重新请求相同的资产,认为由于缓存时间如此遥远,数据不可能发生变化。

提示

如果你对 YSlow 是如何做到它所做的感兴趣,一定要前往项目的 GitHub 页面了解更多(github.com/marcelduran/yslow)。

YSlow 涵盖的远不止你在上一张截图中看到的结果的简短列表。由于测试实际上只需要几秒钟,而浏览器扩展应用程序的设置允许你在几秒钟内安装它,我强烈建议下载这个扩展并测试它;不仅是在你的网页内容上,还有其他的网页内容上。

代码最小化和混淆

在开发完 HTML5 应用程序后,你可能会注意到对外部 JavaScript 和 CSS 文件的引用量可能有点过多。开始争取更快页面加载的最简单方法之一是将你的 JavaScript 和 CSS 源代码最小化到尽可能小的文件大小。这将允许客户端在较短的时间内通过互联网检索内容,从而使页面的其余部分完成加载。

一些 CSS 编译器,如 LESS(lesscss.org)和 SAS(sass-lang.com)具有自动最小化 CSS 的能力,可以节省您在每次更新网站后都要自己记得去做这件事的麻烦。然而,如果你不使用 CSS 编译器,仍然有许多很好的方法来完成相同的任务。我个人最喜欢的在线 CSS 压缩资源之一是www.csscompressor.com。这个易于使用的 CSS 压缩器不仅可以配置执行优化 CSS 的操作,还可以控制压缩的级别。为了更好地了解这种压缩是什么样子,可以考虑以下示例 CSS 语法:

#example {
  width:800px;
  height:600px;
  overflow:hidden;
  margin-right:10px;
  top:20px;
}
#example a {
  color:#FFFFFF;
  text-decoration:none;
}
#example li {
  line-height:20px;
  color:#EFEFEF;
  padding-left:20px;
}
#example .alert {
  display:none;
}

这些 CSS 样式没有什么特别之处,除了我们只对#example标签元素及其内部内容进行样式设置。这个 CSS 示例只是作为一个示例,你可能会在项目中经常看到这样的样式。当然,你的最终应用程序可能包含的 CSS 属性远远多于四个,但无论 CSS 源代码的大小如何,最终结果都将是相同的。为了简化并节省书中的空间,我们将只使用这些样式。

打开一个网络浏览器,然后转到www.csscompressor.com,我们可以将这些样式复制到网站上的大文本区域中。

代码最小化和混淆

在开始 CSS 压缩之前,请注意文本输入区域上方的选项。尽管这个特定的 CSS 最小化网站只是互联网上众多免费可用的网站之一,但一般的配置属性是相同的。正如你在之前的截图中所看到的,你还可以选择排序属性,这将使你的 CSS 样式按照极简配置进行排序,有效地优化你的代码。最重要的是下拉选择器,它允许你选择压缩输出的压缩模式。例如,选择并压缩我们的示例 CSS 将输出以下内容:

#example{width:800px;height:600px;overflow:hidden;margin-right:10px;top:20px}
#example a{color:#FFF;text-decoration:none}
#example li{line-height:20px;color:#EFEFEF;padding-left:20px}
#example .alert{display:none}

由于我们的示例 CSS 本来就很简单,所以在配置中压缩我们的代码所做的工作量非常小。实际上,压缩器只需要删除所提供的 CSS 中的所有额外空格,并将所有元素属性放在单独的行上。这可能看起来是一种最小量的工作和输出,但事实是,压缩后的 CSS 源代码现在比最初提供的源代码小了 14.8%。对于只需几分钟的工作来说,这是一个巨大的优化量。我们甚至可以进一步选择最高选项作为压缩模式。选择此选项后,压缩器将把整个压缩后的 CSS 源代码放在一行上。通过这种设置,我们的文件大小再减少了 2.2%,总共减少了 16%的文件大小。

#example{width:800px;height:600px;overflow:hidden;margin-right:10px;top:20px}#example a{color:#FFF;text-decoration:none}#example li{line-height:20px;color:#EFEFEF;padding-left:20px}#example .alert{display:none}

如前所述,这种文件压缩不仅适用于你的 CSS 源代码。这种技术实际上对 JavaScript 更有用。因为你的 JavaScript 源代码通常负责所有客户端动作,所以你的源代码大小可能会变得非常大。这些外部源代码不仅可能在文件大小上很大,而且可能还包含一些半敏感的代码,你可能希望最终用户不要知道。当然,JavaScript 不适合硬编码用户名和密码,但如果你要在 JavaScript 中编写一个游戏,你可能不希望用户找到进入下一级的方法。你可以使用 JavaScript 压缩来帮助保护你的代码隐私,但当然要记住 JavaScript 是一种客户端语言。无论你如何努力,最终用户始终可以访问你的源代码,所以在开发应用程序时要时刻记住这一点。

JavaScript 压缩在许多流行的 JavaScript 库和框架中很常见,其中许多我们在本书中已经看过。jQuery(jquery.com)库有两种不同的包,开发版和生产版。两者之间的区别在于生产版本已经压缩,准备在网上使用,而开发版本是用户友好的 JavaScript 源码,可以进一步操作。网站,比如www.minifyjavascript.com,是一个很好的在线资源,可以帮助你完成这个任务,包括在你的 HTML5 项目中包含的自定义 JavaScript。

外部依赖

随着你的 HTML5 项目变得越来越丰富,包括 jQuery 或各种 jQuery 插件等外部依赖的需求可能会同样增加。在应用程序开发周期结束时,你可能会从单个 HTML 文件中引用超过十几个外部 JavaScript 文件。尽管从人类的角度来看,将所有应用程序功能分开放在不同的文件中可能是最有利于开发的,但当要在网上发布所有这些文件时,实际上是在让最终用户的连接在尝试加载内容时做更多的工作。由于 JavaScript(或 CSS)功能的每个部分可能在不同的文件中,浏览器需要单独请求并接收文档中引用的每个文件,然后文档才能完成加载。解决这个问题的一个方法是将所有外部第三方 JavaScript 或 CSS 文件合并到一个文件中,以便 HTML 文档请求。这样只会向服务器发出一个请求,减少页面加载时间和带宽消耗。然而,在大多数情况下,你可能只想合并那些未来不打算编辑的 JavaScript 和 CSS 内容。当所有文件合并成一个文件时,当需要调试问题时,难度因素可能会增加。将自定义 JavaScript 和 CSS 保存在它们自己的文件中可以更容易地进行编辑和重新发布。

在你的网站发布一段时间后,无论你是否更新它,你的应用程序使用的依赖可能会过时。你的应用程序使用的项目的开发人员可能会在没有你的知识的情况下发布小的错误修复或整个版本更新到他们的库或框架。尽管你的应用程序应该可以正常运行,前提是之前也是如此,你可能会倾向于更新特定的插件以获得最新的功能集或安全修复。例如,如果更新了一个 jQuery 插件的版本,而你使用的 jQuery 版本要么过时要么不兼容,当尝试运行你的应用程序时,你可能会遇到各种问题。

这个问题可能很容易通过更新您网站所使用的 jQuery 版本来解决;然而,如果您使用的其他插件与最新版本的 jQuery 不兼容,那么这些插件也可能会出现问题。

正是因为这种恶性循环,我强调所有 HTML5 开发人员应该注意两件事。首先,尝试跟上您项目中使用的外部依赖的开发进展。即使您对扩展功能不感兴趣,这些项目的开发人员通常会发布安全更新和代码优化。最后,尝试花时间进行适当的研究和测试,以确定更新项目使用的任何依赖项是否会直接影响与之一起工作的其他依赖项。

使部署变得简单

如果您正在开发一个需要不断更新的 HTML5 网站或应用程序,您可能会有兴趣找到简化手动文件更新的方法,以便发布更新到您的 Web 服务器。手动更新特定文件到您的 Web 服务器上可能位于不同目录的 FTP 或 SFTP 连接可能会变得非常繁琐。寻找自动化任何浪费时间的过程通常是任何开发人员感兴趣的点。因此,为了您未来项目的健康,让我们来看看一些有趣的选项,可以帮助 Web 开发人员自动化从压缩代码到部署网站的许多过程。

使用 Grunt 创建任务

Grunt (gruntjs.com) 是 Web 开发领域的一个相对较新的工具,但自 2011 年推出以来一直在进行非常活跃的开发。该软件背后的概念非常简单——自动化。Grunt 在 HTML5 Web 开发人员社区取得成功的关键在于,您编写的用于执行自定义任务的脚本完全是用 JavaScript 编写的。这使得任何有 JavaScript 编写经验的人都可以创建通常使用 BASH 脚本或其他命令行部署的编程语言创建的任务。

使用 Grunt 创建任务

Grunt 是建立在 Node.js (nodejs.org/) 框架上的,并且需要在使用它的机器上安装 Node.js。由于 Grunt 与 Node.js 的关系,您可以找到插件来开始构建 Grunt 任务的基础,并以与安装 Node.js 包相同的方式安装它们。在自动部署网站源代码到 Web 服务器方面,您可能需要首先弄清楚如何通过 FTP 或 SFTP 从 Grunt 脚本代码与 Web 服务器建立连接。幸运的是,正如前面提到的,Node.js 包管理器或 NPM 系统可以通过在npmjs.org上快速搜索 Grunt SFTP 来为您提供正确的方向。在结果中,您将找到 grunt-sftp-deploy (npmjs.org/package/grunt-sftp-deploy) 插件,它将使您的 Grunt 任务能够轻松连接并将本地网站源代码部署到外部 Web 服务器上的 SFTP 连接。

提示

如果您对了解更多关于 Grunt 或者想在您的计算机上安装它感兴趣,请访问官方 Grunt 文档gruntjs.com/getting-started 并按照入门教程。

实际上,当涉及到使用 Grunt 创建定制任务时,你的想象力真的是无限的。如果你发现自己一遍又一遍地做同样的任务,并希望自动化重复,无论问题是个人的还是全局的,编写一些代码来执行自动化对你来说都是一个非常好的学习经验和时间节省器。前往官方项目网站的 Grunt 插件页面(gruntjs.com/plugins)寻找更多关于你可以创建或利用的出色任务的灵感。

使用 Git 部署内容

如果你还没有使用过的话,另一个你可能会发现自己在使用的软件是 Git(git-scm.com/)。Git 是一个被世界各地的大型和小型项目所使用的免费开源版本控制系统。大多数开发人员无法想象一个没有使用某种版本控制软件的世界,而大多数使用版本控制软件的 Web 开发人员使用的是 Git。Git 在 Web 开发领域的成功主要不仅仅是因为它是一款如此出色和可靠的软件,还因为诸如 Github(github.com)和 BitBucket(bitbucket.org)这样的网站已经被创建出来,允许开发人员将他们的代码存储在私人或公开的仓库中。开发人员可以下载包括其他开发人员所做的所有先前编辑历史的整个代码库,而不是将代码自由分发为存档的 ZIP 文件。这也意味着,如果你对别人的代码进行了更改,他们仍然有权选择将你的编辑集成到他们的发布中。版本控制的好处可以填满一整本书,所以如果你还没有使用 Git 或其他形式的版本控制,一定要深入了解一下。

使用 Git 自动部署你的 Web 内容一开始可能有点棘手,但一旦配置好,这种设置不仅可以让你轻松地发布内容到你的 Web 服务器,还可以让任何其他可能正在开发同一应用程序的开发人员也能够这样做。Git 利用分支的概念允许开发人员在应用程序代码的自己的环境中工作,而不会干扰其他开发人员。当两个开发人员都在各自的分支中完成了对应用程序的更新后,Git 可以自动将两个分支中的编辑合并成一个文件。通过利用分支的概念,开发人员可以同意将一个特定的分支(通常是主分支)作为在应用程序 Web 服务器上部署的工作分支。如果你在 Web 服务器上附加了 Git 仓库,并从基于 Web 的脚本中调用 Git pull 命令,你的开发团队中的任何人都可以轻松地将 Git 仓库中的最新版本推送到活动 Web 服务器上,而无需通过 FTP 或 SFTP 连接并手动复制文件。

提示

如果你对学习更多内容或者想知道如何为你的基于 Web 的 HTML5 项目设置这种类型的解决方案感兴趣,请查看 Github 上的 git-deploy 项目(github.com/git-deploy/git-deploy)。

总结

在本章中,我们花了一些时间讨论了一些为在公共 Web 服务器上部署 HTML5 应用程序或网站做准备的常见方面。尽管没有 Web 项目的常见实践,但有许多方面,如代码和文件结构的优化,如果给予适当的时间,将使您的内容保持稳定,并将服务器请求的交付时间最小化。有时,除非您已经完成了对项目的有效用户测试,否则很难预料到问题。这种用户测试,除非您有一个团队可供使用,通常是在您的应用程序首次向公众发布时进行的。确保您的应用程序设置为报告错误或生成统计数据将减少您定位和纠正可能出现的任何问题所需的时间。甚至像将 Google Analytics(www.google.ca/analytics/)添加到您的网站中这样简单的添加也将使您能够查看用户可能遇到死胡同或错误请求的位置。

学习 HTML5 以及它为 Web 开发社区提供的内容是一项无止境的承诺,这将极大地有利于您作为开发人员的职业生涯。尽管仍处于早期阶段,但这个最新版本的 HTML 规范已被证明是将 Web 推向更类似应用程序基础设施的最重要方面之一。HTML5 开发世界已向来自其他语言的开发人员敞开了大门。作为一名具有 Flash 和 ActionScript 3 经验的开发人员,您不仅在学习曲线方面已经占据优势,而且每个 Flash 开发人员对多媒体及其在代码中的集成的理解将是一项宝贵的技能,您在学习过程中将不断依赖它。

最后,我想对您抽出时间阅读本书的页面表示衷心的感谢。我希望您进入 HTML5 开发的过程与我的一样愉快。过去几年涌现的令人惊叹的项目证明了围绕 HTML5 的兴奋情绪以及规范中新增的新功能。似乎每周都会有一个新的令人兴奋的框架或库出现在社区的聚光灯下。尽管项目的多样性可能很大,但可供在您的项目中利用的大量优秀的第三方内容意味着,为了做出有效的决策,您需要尽可能跟上最新动态。加入您喜欢的社交网络,找到一些与您兴趣相似的其他开发人员,并开始一个新项目,以推动您在 HTML5 中所能实现的极限。谁知道,也许我将在我的下一个项目中使用您编写的库!

posted @   绝不原创的飞龙  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示