一、基础知识
在本章中,我们将介绍
-
HTML 文档的基本结构
-
html
、head
、title
、script
、style
、body
、img
和a
元素 -
级联样式表(CSS)示例
-
一个 JavaScript 代码示例,使用了
Date
和document.write
介绍
超文本标记语言(HTML)是一种在网络上传递内容的语言。HTML 不属于任何人,而是许多国家和许多组织的人们定义语言特性的结果。HTML 文档是可以使用任何文本编辑器生成的文本文档。HTML 文档包含由标签包围的元素——以<
符号开始并以>
符号结束的文本。标签的一个例子是<img src="home.gif"/>
。这个特殊的标签将显示保存在文件home.gif
中的图像。这些标签就是标记。正是通过使用标签,网页中包含了超链接、图像和其他媒体。
基本的 HTML 可以包括以称为层叠样式表(CSS)的语言进行格式化的指令,以及以称为 JavaScript 的语言进行交互的程序。浏览器,如 Firefox 和 Chrome,解释 HTML 以及任何 CSS 和 JavaScript,以产生我们访问网站时的体验。HTML 保存网站的内容,标签提供内容的性质和结构的信息,以及对图像和其他媒体的引用。CSS 指定格式。相同的内容可以用不同的方式格式化。JavaScript 是一种编程语言,用于使网站具有动态性和交互性。在除了最小的工作组之外的所有工作组中,不同的人可能负责 HTML、CSS 和 JavaScript,但是对这些不同的工具如何协同工作有一个基本的了解总是一个好主意。如果你已经熟悉了 HTML 的基础知识,以及 CSS 和 JavaScript 是如何结合在一起的,你可以跳到下一章。尽管如此,在我们开始第一个核心示例之前,浏览一下本章的内容,以确保您对所有内容都了如指掌,可能还是值得的。
HTML(及其相关的 CSS 和 JavaScript)的最新版本是 HTML5。它已经产生了相当大的兴奋,因为像画布这样的功能可以显示图片和动画;支持视频和音频;以及用于定义常见文档元素的标签,例如header
、section
和footer
。你可以用 HTML5 创建一个复杂的、高度互动的网站。在撰写本文时,并非所有的浏览器都接受所有的特性,但是您现在可以开始学习 HTML5、CSS 和 JavaScript。学习 JavaScript 将向您介绍一般的编程概念,如果您尝试学习任何其他编程语言,或者如果您作为团队的一部分与程序员一起工作,这些概念将是有益的。
我将在本书中使用的方法是在具体示例的上下文中解释 HTML5、CSS 和 JavaScript 概念,其中大多数都是熟悉的游戏。在这个过程中,我将使用一些小例子来演示具体的特性。希望这能帮助你们理解自己想做什么,并懂得如何去做。当我解释概念和细节的时候,你会知道我们要去哪里。
本章的任务是建立一个链接到其他网站的网页。这样,通过少量的 CSS 代码和 JavaScript 代码,您将对 HTML 文档的结构有一个基本的了解。对于这个和其他例子,请思考如何让项目对你有意义。该页面可以是您自己的项目、喜爱的网站或特定主题网站的列表。对于每个网站,您将看到文本和超链接。第二个示例包括一些额外的格式,以框的形式围绕文本、图片以及当天的日期和时间。图 1-1 和图 1-2 显示了我创建的不同示例。
图 1-2
带有额外格式的收藏网站
图 1-1
游戏的注释列表
当您重新加载收藏夹网站页面时,日期和时间将根据您的计算机更改为当前日期和时间。
关键要求
对链表应用程序的要求是构建包含文本、链接和图像的网页的最基本要求。对于图 1-1 所示的例子,每个条目都显示为一个段落。相反,在图 1-2 所示的例子中,每个条目周围都有一个方框。第二个例子也包括图像和获取当前日期和时间的方法。后面的应用程序将需要更多的讨论,但是对于这一个,我们将直接讨论如何使用 HTML、CSS 和 JavaScript 来实现它。
HTML5、CSS 和 JavaScript 特性
正如我提到的,HTML 文档是文本,那么我们如何指定链接、图片、格式和编码呢?答案就在标记中,也就是标签。除了定义内容的 HTML,通常还会发现 CSS 样式,它们可以在 HTML 文档内部或外部文档中指定。您还可以包含用于交互性的 JavaScript,这也是在 HTML 文档或外部文档中指定的。我们首先来看看如何构建简单的 HTML 标记,以及如何在同一个文档中添加内联 CSS 和 JavaScript。
基本 HTML 结构和标签
HTML 元素以开始标记开始,后面是元素内容和结束标记。结束标记包括一个符号/
,后面跟着元素类型,例如/head
。元素可以嵌套在元素中。标准的 HTML 文档如下所示:
<html>
<head>
<title>Very simple example
</title>
</head>
<body>
This will appear as is.
</body>
</html>
请注意,我在这里缩进了嵌套标签,使它们更加明显,但是 HTML 本身忽略了这种缩进(或者空白,众所周知),您不需要将它添加到您自己的文件中。事实上,对于本书中的大多数例子,我都没有缩进我的代码。
这个文档由html
元素组成,由开始标签<html>
表示,以结束标签</html>
结束。
HTML 文档通常有一个head
和一个body
元素,就像这个一样。这个head
元素包含一个元素title
。HTML 标题出现在不同浏览器的不同位置。图 1-3 显示了火狐标签上的标题“非常简单的例子”。
图 1-3
Firefox 浏览器中标签页上的 HTML 标题
在大多数情况下,你会在网页正文中创建一些你认为是标题的东西,但它不会是 HTML 标题!图 1-3 也显示了网页的主体:一小段文本。请注意,html、head、title 和 body 这些词没有出现。标签“告诉”浏览器如何显示 HTML 文档。
我们可以对文本做更多的事情,但让我们继续看看如何让图像出现。这需要一个img
元素。与使用开始和结束标签的html
、head
和body
元素不同,img
元素只使用一个标签。它被称为单例标签。它的元素类型是img
(不是 image ),您使用所谓的属性将所有信息与标签本身放在一起。什么信息?最重要的项目是保存图像的文件的名称。标签
<img src="frog.jpg"/>
告诉浏览器查找名为 frog、文件类型为. jpg 的文件。在这种情况下,浏览器会在与 HTML 文件相同的目录或文件夹中查找。你也可以参考其他地方的图像文件,稍后我会展示这一点。src
代表来源。它被称为元素的属性。>
前的斜线表示这是一个单例标签。不同的元素类型有共同的属性,但是大多数元素类型都有附加的属性。img
元素的另一个属性是width
属性。
<img src="frog.jpg" width="200"/>
这指定图像应该以 200 像素的宽度显示。高度将是保持图像原始纵横比所需的任何值。如果您想要特定的宽度和高度,即使这可能会扭曲图像,也要指定width
和height
属性。
小费
您将会看到一些例子(甚至可能是我的一些例子),在这些例子中,右斜杠丢失了,但却工作得很好。将它包括在内被认为是一种良好的做法。类似地,您会看到文件名没有引号的例子。HTML 在语法(标点)方面比大多数其他编程系统更宽容。最后,您将看到以类型为!DOCTYPE
的标记开始的 HTML 文档,并且该 HTML 标记包含其他信息。在这一点上,我们不需要这个,所以我将尽可能保持事情的简单(引用爱因斯坦的话,没有更简单)。
制作超链接类似于制作图像。超链接的元素类型是a
,重要属性是href
。
<a href="http://faculty.purchase.edu/jeanine.meyer">Jeanine Meyer's Academic Activities </a>
正如您所看到的,这个元素有一个开始和结束标记。元素的内容,无论在两个标签之间的是什么——在本例中,是 Jeanine Meyer 的学术活动——都显示为蓝色并带有下划线。起始标签以a
开始。记住这一点的一种方法是将它视为 HTML 中最重要的元素,因此它使用字母表的第一个字母。你也可以想到一个锚,这是a
实际上代表的意思,但对我来说没有什么意义。href
属性(想想超文本链接)指定了当超链接被点击时,浏览器所去的网站。请注意,这是一个完整的 web 地址(简称为统一资源定位器或 URL)。
网址可以是绝对的,也可以是相对的。绝对地址以http://
开始。相对地址是相对于 HTML 文件的位置。使用相对地址可以更容易地将你的项目移动到不同的网站,并且你可以通过使用../
来指示上一级的文件夹。在我的例子中,frog.gif
文件、frogface.gif
文件和其他图像文件与我的 HTML 文件位于同一个文件夹中。他们在那里是因为我把他们放在那里!对于大型项目,许多人将所有图像放在一个名为 images 的子文件夹中,并将地址写为img/postcard.gif
。文件管理是创建网页的一个重要部分。
我们可以将一个超链接元素和一个img
元素结合起来,在屏幕上生成一张用户可以点击的图片。请记住,元素可以嵌套在其他元素中。不是将文本放在起始的<a>
标签之后,而是放一个<img>
标签:
<a href="http://faculty.purchase.edu/jeanine.meyer">
<img src="jhome.gif" width="100" />
</a>
现在让我们把这些例子放在一起:
<html>
<head>
<title>Second example </title>
</head>
<body>
This will appear as is.
<img src="frog.gif"/>
<img src="frog.gif" width="200"/>
<a href=http://faculty.purchase.edu/jeanine.meyer>Jeanine Meyer's Academic
Activities </a>
<a href=http://faculty.purchase.edu/jeanine.meyer><img src="jhome.gif"/></a>
</body>
</html>
我创建了 HTML 文件,保存为second.html
,然后在 Chrome 浏览器中打开。图 1-4 显示了显示的内容。
图 1-4
图像和超链接示例
这产生了文本;图像的原始宽度和高度;宽度固定为 200 像素、高度成比例的图像;一个超链接,将带你到我的网页(我保证);另一个链接使用的图片也将带您到我的网页。然而,这并不是我想要的。我希望这些元素在页面上间隔排列。
这演示了一些你需要记住的事情:HTML 忽略换行符和其他空白。如果你想要换行,你必须指定它。一种方法是使用br
singleton 标签。稍后我会展示其他方法。看看下面修改过的代码。注意<br/>
标签不需要单独在一行上。
<html>
<head>
<title>Second example </title>
</head>
<body>
This will appear as is. <br/>
<img src="frog.gif"/>
<br/>
<img src="frog.gif" width="200"/>
<br/>
<a href=http://faculty.purchase.edu/jeanine.meyer>Jeanine Meyer's Academic
Activities </a>
<br/>
<a href=http://faculty.purchase.edu/jeanine.meyer><img src="jhome.gif"/></a>
</body>
</html>
图 1-5 显示了这段代码产生的结果。
图 1-5
带有换行符的文本、图像和链接
HTML 元素类型有很多:h1
到h6
标题元素产生不同大小的文本;列表和表格有各种元素,表单有其他元素。正如我们马上会看到的,CSS 也用于格式化。您可以为文本选择不同的字体、背景颜色和颜色,并控制文档的布局。将格式放在 CSS 中,将交互性放在 JavaScript 中,并保留内容的 HTML,这被认为是很好的做法。HTML5 提供了新的结构元素——比如article
、section
、footer
和header
——将格式设置放入样式元素中,并利用新元素,称为语义标签,便于与其他人合作。然而,即使您只是自己工作,将内容、格式和行为分开也能让您轻松地更改格式和交互。格式,包括文档布局,是一个很大的话题。在这本书里,我坚持基本原则。
使用级联样式表
CSS 是一种专门用于格式化的特殊语言。样式本质上是一种规则,它指定了特定元素的格式。这意味着您可以将样式信息放在不同的地方:一个单独的文件,一个位于head
元素中的style
元素,或者 HTML 文档中的一个样式,也许是在您想要以特定方式格式化的一个元素中。除非指定了不同的样式,否则样式信息会层叠、向下流动。换句话说,最接近元素的样式就是所使用的样式。例如,您可能使用您公司的官方字体,如在head
元素的 style 部分中给出的,来流过大部分文本,但是在 local 元素中包含规范来样式化一段特定的文本。因为该样式最接近元素,所以它是所使用的样式。
基本格式包括要格式化的内容的指示符,后跟一个或多个指令。在本章的例子中,我将指定类型为section
的元素的格式,即每个项目周围的边框或方框、边距、填充、对齐以及白色背景。清单 1-1 中完整的 HTML 文档是一个混合体(有人会说是一团乱麻!)的特性。元素body
和p
(段落)是 HTML 原始版本的一部分。section
元素是 HTML5 中新增的元素类型之一。section 元素确实需要格式化,不像body
和p
,它们有默认的格式,即主体和每个p
元素将在新的一行开始。CSS 可以修改新旧元素类型的格式。请注意,节中文本的背景色不同于节外文本的背景色。
在清单 1-1 的代码中,我为body
元素(只有一个)和section
元素指定了样式。如果我有不止一个 section 元素,那么样式将适用于每个元素。正文的样式指定背景颜色和文本颜色。最初,浏览器只接受 16 种颜色的名称,包括黑色、白色、红色、蓝色、绿色、青色和粉红色。然而,现在最新的浏览器按名称接受 140 种颜色。 https://www.w3schools.com/colors/colors_names.asp
见。
您还可以使用 RGB(红绿蓝)十六进制代码来指定颜色,但您需要使用图形程序(如 Adobe Photoshop、Corel Paint Shop Pro 或 Adobe Flash Professional)来计算 RGB 值,或者您可以进行实验。我使用 Paint Shop Pro 来确定青蛙头图片中绿色的 RGB 值,并将其用于边框。
指令就像它们听起来的那样:它们指示是将材料居中还是向左对齐。font-size
以像素为单位设置文本的大小。边框很复杂,而且在不同的浏览器中看起来不一致。这里我指定了一个 4 像素的纯绿色边框。section
的width
规范指出浏览器应该使用 85%的窗口,不管那是什么。p
的规格将段落的宽度设置为 250 像素。填充是指文本和部分边框之间的间距。边距是指该部分与其周围环境之间的间距。
<html>
<head>
<title>CSS example </title>
<style>
body {
background-color:tan;
color: #EE015;
text-align:center;
font-size:22px;
}
section {
width:85%;
border:4px #00FF63 solid;
text-align:left;
padding:5px;
margin:10px;
background-color: white;
}
p {
width: 250px;
}
</style>
</head>
<body>
The background here is tan and the text is the totally arbitrary RED GREEN BLUE➥
value #EE1055<br/>
<section>Within the section, the background color is white. There is text with➥
additional HTML markup, followed by a paragraph with text. Then, outside the➥
section there will be text, followed by an image, more text and then a➥
hyperlink. <p>The border color of the section matches the color of the➥
frog image. </p></section>
<br/>
As you may have noticed, I like origami. The next image represents a frog head.<br/>
<img src="frogface.gif"/> <br/>If you want to learn how to fold it, go to
<a href=http://faculty.purchase.edu/jeanine.meyer/origami>the Meyer Family➥
Origami Page <img src="crane.png" width="100"/></a>
</body>
</html>
Listing 1-1A Complete HTML Document with Styles
这将产生如图 1-6 所示的屏幕。
图 1-6
示例 CSS 样式
小费
如果你不能马上理解所有的事情,不要担心。修改这些例子,自己编一个。你会在网上找到很多帮助。特别是,在 http://dev.w3.org/html5/spec/Overview.html
可以看到 HTML 5 的官方来源。
你可以用 CSS 做很多事情。您可以使用它来指定元素类型的格式,如下所示;您可以指定元素是类的一部分;您还可以使用id
属性来识别单个元素。在第六章中,我们创建了一个测验,我使用 CSS 来定位窗口中的特定元素,然后使用 JavaScript 来移动它们。
JavaScript 编程
JavaScript 是一种编程语言,具有访问 HTML 文档各部分的内置特性,包括 CSS 元素中的样式。它被称为脚本语言,以区别于编译语言,如 C++。编译语言在使用前被一次性翻译,而脚本语言由浏览器逐行解释。本文假设读者之前没有编程经验或 JavaScript 知识,但参考其他书籍可能会有所帮助,如特里·麦克纳威奇(2010 年编辑的朋友)的《JavaScript 入门》,或网上资源,如 http://en.wikipedia.org/wiki/JavaScript
。每个浏览器都有自己的 JavaScript 版本。
HTML 文档在位于head
元素中的script
元素中保存 JavaScript。为了显示如图 1-2 所示的时间和日期信息,我在 HTML 文档的head
元素中添加了以下内容:
<script>
document.write(Date());
</script>
与其他编程语言一样,JavaScript 由各种类型的语句组成。在后面的章节中,我将向您展示赋值语句、复合语句,如if
、switch
和for
语句,以及创建所谓的程序员定义函数的语句。函数是在一个块中一起工作的一个或多个语句,并且可以在您需要该功能的任何时候被调用。函数可以避免一遍又一遍地写出相同的代码。JavaScript 提供了许多内置函数。某些函数与对象相关联(稍后将详细介绍),被称为方法。代码
document.write("hello");
是一个 JavaScript 语句,使用参数"hello"
调用文档对象的write
方法。参数是传递给函数或方法的附加信息。语句以分号结束。这段代码将写出字符串 h,e,l,l,o 作为 HTML 文档的一部分。
document.write
方法写出括号内的任何内容。由于我希望写出的信息随着日期和时间的变化而变化,所以我需要一种方法来访问当前的日期和时间,所以我使用了内置的 JavaScript Date
函数。这个函数产生一个带有日期和时间的对象。稍后,您将看到如何使用Date
对象来计算玩家完成一个游戏需要多长时间。现在,我想做的就是显示当前的日期和时间信息,这正是这段代码要做的:
document.write(Date());
使用正式的编程语言:这段代码调用document
对象的write
方法,这是一段内置的代码。句点(.
)表示要调用的write
是与 HTML 文件生成的文档相关联的方法。所以,有些东西是作为 HTML 文档的一部分写出来的。写出来的是什么?左括号和右括号之间的内容。那是什么?它是调用内置函数Date
的结果。Date
函数获取本地计算机维护的信息,并将其交给write
方法。Date
也要求使用括号,这也是你看到这么多的原因。write
方法将日期和时间信息显示为 HTML 文档的一部分,如图 1-2 所示。这些结构的组合方式是典型的编程语言。该语句以分号结束。为什么不是一个时期?句点在 JavaScript 中还有其他用途,例如指示方法和用作数字的小数点。
自然语言,比如英语,和编程语言有很多共同点——不同类型的语句;使用某些符号的标点符号;和语法来正确定位元素。在编程中,我们用术语符号代替标点符号,用句法代替语法。编程语言和自然语言都可以让你用独立的部分构建非常复杂的语句。然而,有一个根本的区别:正如我告诉我的学生,我在课堂上说的很多内容很可能在语法上不正确,但他们仍然会理解我。但是,当你通过编程语言与计算机“交谈”时,你的代码必须在语法规则方面完美无缺,才能得到你想要的东西。好消息是,与人类观众不同,计算机不会表现出不耐烦或任何其他人类情感,所以你可以花时间把事情做好。也有一些坏消息,你可能需要一段时间才能领会。如果您在 HTML、CSS 或 JavaScript 中犯了语法错误(称为句法错误),浏览器仍然会尝试显示某些内容。当你在工作中没有得到你想要的结果时,你要找出问题出在哪里。
使用文本编辑器
您使用文本编辑器构建 HTML 文档,并使用浏览器查看/测试/播放该文档。虽然你可以使用任何文本编辑程序来编写 HTML,但我建议你在 PC 上使用 TextPad,在 MAC 上使用 Sublime。这些是共享软件,这使得它们相对便宜。不要使用文字处理程序,它可能会插入非文本字符。记事本也可以工作,尽管其他工具也有好处,比如我将演示的颜色编码。要使用编辑器,您需要打开它并输入代码。图 1-7 展示了 Sublime 屏幕的样子。
图 1-7
从崇高开始
你会希望经常保存你的工作,最重要的是,保存为 type.html 文件。在开始时这样做,然后你将获得颜色编码的好处。在 Sublime 中,点击文件 ➤ 另存为,然后输入带有文件扩展名的名称。html,如图 1-8 。
图 1-8
将文件保存为 HTML 类型
请注意,我给了文件一个名称和文件扩展名,还指定了文件所在的文件夹。保存文件后,出现如图 1-9 所示的窗口,带有颜色编码。
图 1-9
将文件保存为 HTML 后
颜色编码表示标签和引用字符串,只有在文件保存为 HTML 后才能看到。这对于捕捉许多错误很有价值。Sublime 和其他编辑器确实提供了改变配色方案的选项。假设您正在使用这里显示的方法,如果您看到黄色的长段(引用字符串的颜色),这可能意味着缺少右引号。顺便说一下,你可以使用单引号或双引号,但不能混淆。此外,如果你从 Word 或 PowerPoint 中复制粘贴,并复制所谓的“智能”引号,那些曲线,这将导致问题。
构建应用程序
您可以在查看以下应用程序的源代码。???。HTML 文档的源代码通常包括 HTML 文档和其他文件。
-
verysimple.html
文件本身是完整的,如图 1-3 所示。 -
second.html
应用如图 1-4 所示。引用了两个图像文件:frog.gif
两次,jhome.gif
一次。 -
带有花哨颜色的
third.html
引用了两个图像文件:frogface.gif
和crane.gif
。 -
games.html
文件本身是完整的,因为它没有引用任何图像文件。如果在a
标签的href
属性中提到的文件不存在,那么当点击超链接时将会出现错误消息。 -
ch01FavoriteSites.html
文件引用了两个图像文件:avivasmugmug.jpeg
和apressshot.jpeg
。
跟踪文件是构建 HTML 应用程序的关键部分。
现在让我们深入研究 HTML 编码,首先是描述游戏的带注释的链表,然后是最喜欢的网站。该代码使用了上一节中描述的特性。表 1-1 显示了产生如图 1-1 所示显示的完整代码:链接到不同文件的文本段落,它们都位于同一文件夹中。
表 1-1
“我的游戏”注释链接代码
|密码
|
说明
|
| — | — |
| <html>
| 开始html
标签。 |
| <head>
| 开始head
标签。 |
| <title>Annotated links</title>
| 开始的title
标签、标题文本和结束的title
标签。 |
| </head>
| |
| <body>
| 开始body
标签。 |
| <h1>My games</h1>
| 开始h1
标签,文本,然后结束h1
标签。这将使“我的游戏”以大字体出现。实际字体将是默认字体。 |
| <p>
| 段落标签的开始p
。 |
| The <a href="craps.html">Dice game</a> presents the game called craps.
| 带有a
元素的文本。开始标签a
的属性href
被设置为值craps.html
。大概这是一个和这个 HTML 文件在同一个文件夹中的文件。a
元素的内容——无论是在<a>
和</a>
之间的内容——都将被显示出来,首先显示为蓝色,单击后显示为淡紫色,并带有下划线。 |
| </p>
| 关闭p
标签。 |
| <p>
| 开始p
标签。 |
| The <a href="cannonball.html">Cannonball</a> is a ballistics simulation. A ball appears to move on the screen in an arc. The program determines when the ball hits the ground or the target. The player can adjust the speed and the angle.
| 参见前面的案例。这里的a
元素指的是cannonball.html
文件,显示的文本是Cannonball
。 |
| </p>
| 关闭p
标签。 |
| <p>
| 开始p
标签。 |
| The <a href="slingshot.html">Slingshot</a> simulates shooting a slingshot. A ball moves on the screen, with the angle and speed depending on how far the player has pulled back on the slingshot using the mouse.
| 参见上一篇。这一段包含了到slingshot.html
的超链接。 |
| </p>
| 关闭p
标签。 |
| <p>
| 开始p
标签。 |
| The <a href="memory.html">Concentration/memory game</a> presents a set of plain rectangles you can think of as the backs of cards. The player clicks on first one and then another and pictures are revealed. If the two pictures represent a match, the two cards are removed. Otherwise, the backs are displayed. The game continues until all matches are made. The time elapsed is calculated and displayed
.
| 参见上一篇。这一段包含了到memory.html
的超链接。 |
| </p>
| 关闭p
标签。 |
| <p>
| 开始p
标签。 |
| The <a href="quiz1.html">Quiz game</a> presents the player with 4 boxes holding names of countries and 4 boxes holding names of capital cities. These are selected randomly from a larger list. The player clicks to indicate matches and the boxes are moved to put the guessed boxes together. The program displays whether or not the player is correct
.
| 参见上一篇。这一段包含了到quiz1.html
的超链接。 |
| </p>
| 关闭p
标签。 |
| <p>
| 开始p
标签。 |
| The <a href="maze.html">Maze</a> program is a multi-stage game. The player builds a maze by using the mouse to build walls. The player then can move a token through the maze. The player can also save the maze on the local computer using a name chosen by the player and retrieve it later, even after closing the browser or turning off the computer
.
| 参见上一篇。这一段包含了到maze.html
的超链接。 |
| </p>
| 关闭p
标签。 |
| </body>
| 关闭body
标签。 |
| </
html>
| 关闭html
标签。 |
一旦您创建了几个自己的 HTML 应用程序,您就可以构建一个像这样的文档,作为您自己的注释列表。如果您使用文件夹,href
链接将需要反映 HTML 文档的位置。
Favorite Sites 代码具有带注释的列表的功能,并增加了格式:每个项目周围有一个绿色框,三个项目中有两个项目有一张图片。参见表 1-2 。
表 1-2
收藏夹站点代码
|密码
|
说明
|
| — | — |
| <html>
| 开始html
标签。 |
| <head>
| 开始head
标签。 |
| <title>Annotated links</title>
| 完成title
元素:开始和结束标签以及它们之间的注释链接。 |
| <style>
| 开始style
标签。这意味着我们现在要使用 CSS。 |
| article {
| 样式的开始。对正在被样式化的内容的引用是所有的article
元素。然后样式有了一个大括号- {
。开始和结束括号包围了我们正在创建的样式规则,很像 HTML 中的开始和结束标记。 |
| width:60%;
| width
设置为包含元素的 60%。注意,每个指令都以一个;
(分号)结束。 |
| text-align:left;
| 文本靠左对齐。 |
| margin:10px;
| 边距为 10 像素。 |
| border:2px green double;
| 边框是 2 像素的绿色双线。 |
| padding:2px;
| 文本和边框之间的间距为 2 像素。 |
| display:block;
| 文章是块,意思是前后有换行符。 |
| }
| 关闭article
的样式。 |
| img {display:block;}
| Style img
元素到 block style:前后换行。 |
| </style>
| 关闭style
标签。 |
| <script>
| 开始script
标签。我们现在正在编写 JavaScript 代码。 |
| document.write(Date());
| 一条代码语句:写出Date()
调用产生的内容。 |
| </script>
| 关闭script
标签。 |
| </head>
| |
| <body>
| 开始body
标签。 |
| <h3>Favorite Sites</h3>
| 由h3
和/h3
标签包围的文本。这使得文本看起来比正常的要大一些。 |
| <article>
| 开始article
标签。 |
| The <a href="
http://faculty.purchase.edu/jeanine.meyer"Jeanine Meyer's Academic Activities</a> site displays information on my current and past courses, along with publications and other activities.
| 该文本将以指定的样式为准。它包括一个a
元素。 |
| </article>
| 关闭article
标签。 |
| <article>
| 开始article
标签。 |
| The <a href="
https://avivameyer . smugmug . com/">Aviva Meyer's photographs</a> site is a collection of Aviva's photographs stored on a site called smugmug. The categories are Music, Adventures and Family (which requires a password)``.
| 这篇文章与上一篇相似,有一个a
元素和一些文本。 |
| <img src="avivasmugmug.jpeg" width="300"/>
| 一个img
标签。图像的来源是文件avivasmugmug.jpeg
。如果文件的扩展名为. jpg,这将不起作用。宽度设置为 300 像素。由于 style 部分中的 style 指令,前后都有换行符。 |
| </article>
| 关闭article
标签。 |
| <article>
| 开始article
标签。 |
| <a href="
http://apress.com ">Apress publishers</a> is the site for the publishers of this book. <br/>
| 这类似于上一篇文章:一个 a 元素和一些文本。 |
| <img src="apressshot.jpeg" width="300"/>
| 一个img
元素。来源是apressshot.jpeg
。宽度设置为 300 像素。 |
| </article>
| 关闭article
标签。 |
| </body>
| 关闭body
标签。 |
| </
html>
| 关闭html
标签。 |
如何让这个应用程序成为你自己的非常简单:使用你自己喜欢的网站!在大多数浏览器中,如果您想为超链接使用网站徽标,可以下载并保存图像文件,也可以包含其他图片。我的理解是,制作一个带有评论并包含图像(如徽标)的网站列表属于“合理使用”的范畴,但我不是律师。大多数情况下,人们喜欢链接到他们的网站。这不影响法律问题,但是如果您不想将特定的图像文件下载到您的计算机,然后再上传到您的网站,您也可以选择将img
标签中的src
设置为图像所在网站的网址。
您也可以通过更改格式使该应用程序成为您自己的应用程序。样式可用于指定字体,包括特定的字体、字体系列和大小。这使您可以选择一种喜爱的字体,并指定在用户计算机上没有该字体时使用的字体。您可以指定边距和填充,或者独立地改变上边距、左边距、上边距等。
测试和上传应用程序
除非您使用完整的网址,否则您需要将所有文件(在本例中是单个 HTML 文件和所有图像文件)放在同一个文件夹中。为了使链接工作,您需要拥有所有href
属性的正确地址。我的例子展示了如何对同一文件夹中的 HTML 文件或 Web 上其他地方的 HTML 文件执行此操作。
你可以开始测试你的工作,即使它还没有完全完成。例如,您可以放入一个单独的img
元素或一个单独的a
元素。打开浏览器,如 Firefox、Chrome 或 Safari。在 Firefox 中,点击文件,然后打开文件,浏览到你的 HTML 文件。在 Chrome 中,在 PC 上按 Ctrl(MAC 上按 CMD)和 o,然后浏览到文件,点击确定打开。你应该看到类似我的例子。
点击超链接进入其他网站。使用浏览器的重新加载图标重新加载页面,并观察不同的时间。如果你没有看到你所期望的——就像我的例子一样——你需要检查你的代码。常见的错误有:
-
开始和结束标记缺失或不匹配。
-
图像文件或 HTML 文件的名称错误,或者图像文件的文件扩展名错误。您可以使用 JPG、GIF 或 PNG 类型的图像文件,但是标签中指定的文件扩展名必须与图像的实际文件类型相匹配。
-
缺少引号。编辑器中可用的颜色编码可以帮助您识别这一点。
摘要
在这一章中,你学习了如何用文本、图像和超链接组成 HTML 文档。这包括:
-
基本标签,包括
html
、head
、title
、style
、script
、body
-
用于显示图像的
img
元素 -
超链接的
a
元素 -
使用按照级联样式表(CSS)规则编写的样式元素进行简单格式化
-
提供日期和时间信息的一行 JavaScript 代码
这一章仅仅是个开始,尽管使用基本的 HTML,不管有没有级联样式表,都有可能制作出漂亮的信息丰富的网页。在下一章中,您将学习如何在应用程序中包含随机性和交互性,以及如何使用 Canvas 元素,这是 HTML5 的关键特性。
二、骰子游戏
在本章中,我们将介绍
-
在画布上绘画
-
随机处理
-
游戏逻辑
-
表单输出
介绍
HTML5 中最重要的新特性之一是canvas
元素。这个元素为开发人员提供了一种以完全自由的方式绘制线条画、包含图像和定位文本的方法,这是对旧 HTML 的一个重大改进。尽管在早期版本中你可以做一些花哨的格式化,但布局往往是四四方方的,页面缺乏动态性。你如何在画布上画画?你使用一种脚本语言,通常是 JavaScript。我将向您展示如何在画布上绘图,并解释 JavaScript 的重要特性,我们将需要这些特性来构建一个名为 craps 的骰子游戏的实现:如何定义一个函数,如何调用所谓的伪随机行为,如何实现这个特定游戏的逻辑,以及如何向玩家显示信息。不过,在我们进一步深入之前,您需要了解这个游戏的基础知识。
掷骰子游戏有以下规则:
玩家掷出一对骰子。两个顶面之和才是最重要的,所以 1 和 3 与 2 和 2 是一样的。两个 6 面骰子的和可以是 2 到 12 之间的任何数字。如果玩家第一次掷出 7 或 11,玩家获胜。如果玩家掷出 2、3 或 12,该玩家输了。对于任何其他结果(4,5,6,8,9,10),这个结果被记录为所谓的球员得分,并要求进行后续投掷。在后续的投掷中,一次投掷 7 分失败,一次投掷该球员的点数获胜。对于其他任何事情,游戏继续遵循后续投掷规则。
让我们看看我们的游戏会是什么样子。图 2-1 显示游戏开始时投掷两个一的结果。
图 2-1
第一次投掷,导致玩家失败
这里并不明显,但是我们的骰子游戏应用程序每次都使用canvas
标签来绘制骰子面。这意味着没有必要下载单个芯片表面的图像。
掷两个 1 意味着玩家输了,因为规则规定第一次掷 2、3 或 12 是输。下一个例子显示玩家赢了,第一次掷出 7,如图 2-2 所示。
图 2-2
第一次掷出 7 意味着玩家赢了
图 2-3 显示了下一次投掷——8 分。这既不是赢也不是输,而是意味着必须有后续的投掷。
图 2-3
8 分意味着运动员的 8 分被结转的后续投掷
假设玩家最终再次掷出 8,如图 2-4 所示。
图 2-4
又是一次 8 分,玩家赢了
正如前面的序列所示,唯一有价值的是骰子表面值的总和。积分值是用两个 4 定的,但是比赛是用一个 2 和一个 6 赢的。
规则表明,一场游戏不会总是掷出相同数量的骰子。玩家可以在第一次投掷中获胜或失败,也可以有任何次数的后续投掷。游戏制作者的工作是制作一个可以运行的游戏——运行意味着遵守规则,即使这意味着游戏会持续下去。我的学生有时表现得好像他们的游戏只有赢了才有效。在一个正确执行的游戏中,玩家会有赢有输。
关键要求
构建骰子游戏的要求从模拟随机掷骰子开始。起初,这似乎是不可能的,因为编程意味着精确地指定计算机将做什么。幸运的是,与大多数其他编程语言一样,JavaScript 有一个内置的工具,可以产生看似随机的结果。有时语言使用一个很长的位串的中间位(1 和 0)来表示以毫秒为单位的时间。确切的方法对我们并不重要。我们将假设浏览器提供的 JavaScript 在这方面做得很好,这被称为伪随机处理。
现在假设我们可以从 1 到 6 中随机抽取任意一个数字,并对两个骰子面进行两次,我们需要实现游戏规则。这意味着我们需要一种方法来跟踪我们是处于第一次投掷还是后续投掷。它的正式名称是应用程序状态,意思是事情现在的样子,在游戏和其他类型的应用程序中都很重要。然后,我们需要使用基于条件做出决策的结构。像if
和switch
这样的条件结构是编程语言的标准组成部分,你很快就会明白为什么像我这样的计算机科学教师——他们从来没有去过赌场或后巷——真的喜欢掷骰子的游戏。
我们需要给玩家一个掷骰子的方法,所以我们将在屏幕上实现一个按钮来点击它。然后我们需要向玩家提供发生了什么的信息。对于这个应用程序,我通过在屏幕上绘制骰子面来产生图形反馈,并且以文本形式显示信息,以指示游戏的阶段、分值和结果。与用户交互的早期术语是输入输出(I/O),那时交互主要涉及文本。术语*图形用户界面**【GUI】*现在通常用来表示用户与计算机系统交互的各种方式。其中包括使用鼠标点击屏幕上的特定点或者将点击与拖动相结合来模拟移动物体的效果(参见第四章中的弹弓游戏)。在屏幕上绘图需要使用坐标系来指定点。在大多数编程语言中,计算机屏幕的坐标系统都是以类似的方式实现的,我将很快解释这一点。
HTML5、CSS 和 JavaScript 特性
现在让我们来看看 HTML5、CSS 和 JavaScript 的具体特性,它们提供了我们实现骰子游戏所需的东西。
伪随机处理和数学表达式
JavaScript 中的伪随机处理是使用一个名为Math.random
的内置方法来执行的。从形式上来说,random
是Math
类的一个方法。调用Math.random()
生成一个从 0 到 1 的数,但不包括 1,结果是一个十进制数,例如 0.253012。这对我们来说似乎不是立即有用的,但实际上这是一个非常简单的过程,将那个数字转换成我们可以使用的数字。我们将这个数乘以 6,得到一个从 0 到 6 的数,但不包括 6。例如,如果我们将. 253012 乘以 6,我们得到 1.518072。这几乎是我们所需要的,但还不完全是。下一步是去掉分数,保留整数。为此,我们使用另一种Math
方法Math.floor
。此方法在移除任何小数部分后生成一个整数。顾名思义,floor
方法向下舍入。在我们的特殊例子中,我们从. 253012 开始,然后到达 1.518072,因此,进行调用Math.floor(1.58072)
,结果是整数 1。一般来说,当我们将随机数乘以 6 并对其取底时,我们会得到一个从 0 到 5 的数。最后一步是加一个 1,因为我们的目标是得到一个从 1 到 6 的数,一遍又一遍,没有特定的模式。
您可以使用类似的方法获得任何范围内的整数。例如,如果你想要数字 1 到 13,你可以将随机数乘以 13,然后加 1。这对纸牌游戏很有用。你会在本书中看到类似的例子。
我们可以将所有这些步骤组合成一个所谓的表达式。表达式是常量、方法和函数调用的组合,有些东西我们将在后面探讨。我们使用运算符将这些项放在一起,例如+表示加法,*表示乘法。
还记得第一章中的标签如何组合——将一个标签嵌套在另一个标签中——以及我们在 Favorite Sites 应用程序中使用的一行 JavaScript 代码吗:
document.write(Date());
我们可以在这里使用类似的过程。我们可以将random
调用作为floor
方法的一个参数来传递,而不必分别编写random
调用和floor
方法。看一下这段代码:
1+Math.floor(Math.random()*6)
这个表达式将产生一个从 1 到 6 的数字。我称它为代码片段,因为它不是一个完整的语句。运算符+
和*
指的是算术运算,与普通数学中使用的相同。操作顺序从内到外依次进行。
-
调用
Math.random()
获得一个从 0 到 1 的十进制数,但不完全是 1。 -
将结果乘以 6。
-
用
Math.floor
去掉分数,留下整数。 -
加 1。
在我们的最终代码中,你会看到一个带有这个表达式的语句,但是我们需要先介绍一些其他的东西。
变量和赋值语句
像其他编程语言一样,JavaScript 有一个叫做变量 *,*的结构,它本质上是一个放值的地方,比如一个数字。这是一种将名称与值相关联的方式。您可以在以后通过引用名称来使用该值。一个类比是办公室人员。在美国,我们称之为“总统”。2010 年,当我创作这本书的第一版时,总统是巴拉克·奥巴马。现在(2018 年 7 月),总统是唐纳德·特朗普。“总统”一词的价值发生了变化。在编程中,变量值也可以变化,因此得名。
术语var
用于声明为变量。
下一节中描述的变量和函数的名字由程序员决定。有规则,包括内部不能有空格,不能用句号,名字必须以字母字符开头。名字的长度是有限制的,但是我们倾向于使名字简短以避免打字。然而,我建议你不要把它们写得太短,以至于忘了它们是什么。你确实需要保持一致,但你不需要遵守英语拼写规则。例如,如果您想设置一个变量来保存值的总和,并且您认为 sum 的拼写是 som,那就没问题。只要确保你一直使用 som。但是如果你想引用 JavaScript 的一部分,比如function
或document
或random
,你需要使用 JavaScript 期望的拼写。
您应该避免在 JavaScript 中为变量使用内置结构的名称(比如random
或floor
)。尽量使名字独特,但仍然容易理解。编写变量名的一种常见方法是使用所谓的骆驼格。这包括用小写字母开始你的变量名,然后用大写字母表示一个新单词的开始,例如,numberOfTurns
或userFirstThrow
。你可以看到为什么它被称为骆驼案——大写字母形成了单词中的“驼峰”。您不必使用这种命名方法,但这是许多程序员遵循的惯例。
保存前面部分解释的伪随机表达式的代码行是一种特殊类型的语句,称为赋值语句。举个例子,
var ch = 1+Math.floor(Math.random()*6);
将名为ch
的变量设置为等号右侧表达式的结果值。当在var
语句中使用时,它也被称为初始化语句。=
符号用于设置变量的初始值,如在这种情况下以及在下面将要描述的赋值语句中。我选择用ch
这个名字作为选择的简写。这对我很有意义。一般来说,如果你需要在一个短名字和一个你能记住的长名字之间做出选择,选择一个长的!请注意,该语句以分号结束。你可能会问,为什么不是句号呢?答案是句点用于另外两种情况:作为小数点和用于访问对象的方法和属性,如在document.write
中。
赋值语句是编程中最常见的语句类型。下面是一个已经定义的变量的赋值语句的例子:
bookname = "The Essential Guide to HTML5";
等号的使用可能会引起混淆。想象一下,左边的产量等于右边的产量。在本书中,你会遇到许多其他变量以及运算符和赋值语句的其他用法。
警告
定义变量的var
语句称为声明语句 。 JavaScript 不像其他语言,它允许程序员省略声明语句,直接使用变量。我尽量避免这样做,但是你会在很多网上的例子中看到。
对于掷骰子的游戏,我们需要定义游戏状态的变量,即它是第一次投掷还是后续投掷,以及玩家的点数是什么(记住点数是前一次投掷的值)。在我们的实现中,这些值将由所谓的全局变量保存,这些变量是用任何函数定义之外的var
语句定义的,以便保留它们的值(当函数停止执行时,函数内部声明的变量值消失)。
你不需要总是使用变量。例如,我们在这里创建的第一个应用程序设置变量来保存骰子的水平和垂直位置。我本可以在代码中放入文字数字,因为我不会更改这些数字,但是因为我在几个不同的地方引用这些值,所以将这些值存储在变量中意味着如果我想更改一个或两个数字,我只需要在一个地方进行更改。
程序员定义的函数
JavaScript 有许多内置的函数和方法,但是它没有您可能需要的所有东西。比如,据我所知,它并没有专门模拟掷骰子的功能。所以 JavaScript 让我们定义和使用自己的函数。这些函数可以使用参数,就像Math.floor
方法一样,其中的参数,比如调用Math.floor(rawScore)
中的变量rawScore
,用于计算不大于当前值rawScore
的最大整数。该声明
score = Math.floor(rawScore);
将用于根据rawScore
中的值,用整数设置变量 score,该值可能有小数部分。我在炫耀骆驼肠衣的用途。请记住,这是我的编码,只有我的编码才能建立联系。
参数是可以传递给函数的值。把它们当成额外的信息。
函数定义的格式是术语function
,后面是您要赋予函数的名称,后面是包含任何参数名称的括号,再后面是一个开括号、一些代码,然后是一个闭括号。正如我在前面提到的,程序员选择名字。下面是一个函数定义的例子,它返回两个参数的乘积。顾名思义,你可以用它来计算一个矩形的面积。
function areaOfRectangle(wd,ln) {
return wd * ln;
}
注意关键字return
。这告诉 JavaScript 将函数的结果发送给我们。在我们的例子中,这让我们编写类似于rect1 = areaOfRectangle(5,10)
的代码,将值 50 (5 × 10)赋给我们的rect1
变量。函数定义将被写成script
元素中的代码。在现实生活中定义这个函数可能有意义,也可能没有意义,因为在代码中编写乘法非常容易,但它确实是程序员定义函数的一个有用示例。一旦这个定义被执行,很可能就是在 HTML 文件被加载的时候,其他代码就可以通过调用它的名字来使用这个函数,就像在areaOfRectangle(100,200)
或者areaOfRectangle(x2-x1,y2-y1)
中一样。
第二个表达式假设x1
、x2
、y1
、y2
指的是在别处定义的坐标值。
也可以通过设置某些标签属性来调用函数。例如,body
标签可以包括对onLoad
属性的设置:
<body onLoad="init();">
我的 JavaScript 代码包含一个我称为init
的函数的定义。将这个放入body
元素意味着当浏览器第一次加载 HTML 文档时或者每当玩家点击重载/刷新按钮时,JavaScript 将调用我的init
函数。类似地,使用 HTML5 的一个新特性,我可以包含按钮元素:
<button onClick="throwdice();">Throw dice </button>
这将创建一个保存文本Throw dice
的按钮。当玩家点击它时,JavaScript 调用我在script
元素中定义的throwdice
函数。
稍后描述的form
元素可以以类似的方式调用函数。
条件语句:如果和切换
掷骰子游戏有一套规则。总结规则的一种方法是,如果是第一次掷骰子,我们检查掷骰子的某些值。如果不是第一次掷骰子,我们检查掷骰子的其他值。JavaScript 为此提供了if
和switch
语句。
if
语句基于条件 ,可以是比较或相等检查——例如,名为temp
的变量是否大于 85,或者名为course
的变量是否保存值"Programming Games"
。比较产生两个可能的逻辑值— true
或false
。到目前为止,您已经看到了数字值和字符串值。逻辑值是另一种数据类型。它们也被称为布尔值,以数学家乔治·布尔的名字命名。我提到的条件和检查将用代码写成
temp>85
和
course == "Programming Games"
将第一个表达式读作:变量temp
的当前值是否大于 85?
第二个问题是:变量course
的当前值是否与字符串"Programming Games"
相同?
比较例很好理解;我们使用>
来检查一个值是否大于另一个值,使用<
来检查相反的情况。表达式的值将是两个逻辑值之一,true
或false
。
第二种表达方式可能更令人困惑。你可能想知道两个等号,也可能想知道引号。JavaScript(和其他几种编程语言)中检查相等性的比较运算符是两个等号的组合。我们需要两个等号,因为单个等号用在赋值语句中,它不能双重作用。如果我们写了course = "Programming Games"
,我们会把值"Programming Games"
赋给我们的course
变量,而不是比较这两个项目。引号定义了一个字符串,以 P 开始,包括空格,以 s 结束。
有了这些,我们现在可以看看如何编写代码,只有当条件为真时才执行某些操作。
if (condition) {
code
}
如果我们希望我们的代码在条件为真时做一件事,而在条件不为真时做另一件事,格式是:
if (condition) {
if true code
}
else {
if not true code
}
请注意,我在这里使用斜体是因为这是所谓的*伪代码,*不是我们将包含在 HTML 文档中的真实 JavaScript。
下面是一些真实的代码示例。他们使用alert
,这是一个内置函数,可以在浏览器中弹出一个小窗口,显示括号中参数所指示的消息。用户必须单击“确定”才能继续。
if (temp>85) {
alert("It is hot!");
}
if (age >= 21) {
alert("You are old enough to buy a drink.");
}
else {
alert("You are too young to be served in a bar.");
}
我们可以只使用if
语句来编写 craps 应用程序。然而,JavaScript 提供了另一种更容易理解的结构——switch
语句。一般格式是:
switch(x) {
case a:
codea;
case b:
codeb;
default: codec;
}
JavaScript 评估switch
语句第一行中x
的值,并将其与案例中指示的值进行比较。一旦命中,即确定x
等于a
或b
,则执行case
标签后的代码。如果不匹配,则执行default
之后的代码。没有必要有默认的可能性。如果任其自生自灭,计算机将继续运行switch
语句,即使它找到了匹配的case
语句。如果您希望它在找到匹配时停止,您需要包含一个break
语句来中断切换。
你可能已经看到if
和switch
将如何做我们在骰子游戏中需要的事情。您将在下一节中了解如何操作。首先,让我们看一个例子,它确定由变量mon
表示的一个月中的天数,变量【】包含三个字母的缩写("Jan"
、"Feb"
等)。).
switch(mon) {
case "Sep":
case "Apr":
case "Jun":
case "Nov":
alert("This month has 30 days.");
break;
case "Feb":
alert("This month has 28 or 29 days.");
break;
default:
alert("This month has 31 days.");
}
如果变量mon
的值等于"Sep"
、"Apr"
、"Jun"
或"Nov"
,则控制流向第一个alert
语句,然后由于break
而退出switch
语句。如果变量mon
的值等于"Feb"
,则执行提到 28 或 29 天的alert
语句,然后控制流退出switch
。如果mon
的值是其他任何值,顺便说一下,包括一个无效的三个字母缩写,则执行提到 31 天的alert
。
正如 HTML 忽略换行符和其他空白一样,JavaScript 不要求这些语句有特定的布局。如果你愿意,你可以把所有的东西放在一行。然而,让事情变得简单,使用多行和缩进。
在画布上画画
现在我们来看看 HTML5 中最强大的新特性之一,即canvas
元素。我将解释应用程序中涉及到canvas
的代码片段,然后展示一些简单的例子,最后回到我们在画布上绘制骰子面的目标。回想一下,HTML 文档的大纲是
<html>
<head>
<title>... </title>
<style>...</style>
<script> .... </script>
</head>
<body>
... Here is where the initial static content will go...
</body>
</html>
注意:你不必包含标题、样式或脚本元素,它们可以按任何顺序排列。第一章中的 favorites 示例使用了一个样式元素,但是 dice 示例不会。
为了使用canvas
,我们在 HTML 文档的body
元素中包含了canvas
的标签,在script
元素中包含了 JavaScript。我将首先描述一种编写canvas
元素的标准方法。
<canvas id="canvas" width="400" height="300">
Your browser doesn't support the HTML5 element canvas.
</canvas>
如果带有此编码的 HTML 文件被不识别 canvas 的浏览器打开,屏幕上会出现消息Your browser doesn't support the HTML5 element canvas.
。如果你正在为所有的浏览器准备网页,你可以选择引导访问者到别的地方或者尝试另一种策略。在本书中,我只关注 HTML5。
HTML canvas
标签定义这个元素有一个“canvas”的id
。这可能是任何东西,但使用画布没有坏处。但是,您可以有多个画布,在这种情况下,您需要为每个 ID 使用不同的值。但是,这不是我们为这个应用程序所做的,所以我们不必担心。设置width
和height
的属性来指定这个canvas
元素的维度。
现在我们已经看到了body
中的画布,让我们看看 JavaScript。在画布上绘图的第一步是在 JavaScript 代码中定义适当的对象。为此,我需要一个变量,所以我用下面的代码设置了一个名为ctx
的变量
var ctx;
在任何函数定义之外。这使它成为一个全局变量,可以从任何函数访问或设置。ctx
变量是所有绘图都需要的东西。我选择将我的变量命名为ctx
,这是上下文的缩写,复制了我在网上看到的许多例子。我可以选择任何名字。
在代码的后面(您将看到后面例子中的所有代码,您可以下载源代码),我编写了设置ctx
值的代码。
ctx = document.getElementById('canvas').getContext('2d');
语句设置ctx
在我定义的名为init
的函数中,该函数在body
标签中被引用
<body onload=”init();>
将语句放在init
函数中意味着在下载完主体中的所有内容之后,调用任何其他函数之前,调用该语句。
赋值语句设置ctx
首先获取文档中 ID 为'canvas'
的元素,然后提取所谓的'2d'
上下文。我们都可以预料到,未来可能会带来其他的情境!现在,我们使用2d
的一个。
在 JavaScript 代码中,您可以绘制矩形、包括线段和圆弧的路径,并在画布上定位图像文件。你也可以填充矩形和路径。然而,在我们这样做之前,我们需要处理坐标系和弧度。
正如全球定位系统使用纬度和经度来定义您在地图上的位置一样,我们需要一种方法来指定屏幕上的点。这些点被称为像素,我们在前一章中使用它们来指定图像的宽度和边框的厚度。像素是一个非常小的测量单位,如果你做些实验,你就会发现。但是,大家一致同意线性单位是不够的。我们还需要就测量的起点达成一致,就像 GPS 系统使用格林威治子午线和赤道一样。对于作为画布的二维矩形,它被命名为原点或注册点。原点是canvas
元素的左上角。请注意,在第六章中,当我们通过在 HTML 文档中而不是在canvas
元素中创建和定位元素来描述智力竞赛节目时,坐标系是类似的。原点仍然是窗口的左上角。
这不同于你可能从解析几何或制图中回忆起来的。水平数字的值从左向右增加。垂直数字的值随着屏幕上的下移而增加。写坐标的标准方式是先放水平值,再放垂直值。在某些情况下,水平值称为 x 值,垂直值称为 y 值。在其他情况下,水平值是左边(认为它是从左边),垂直值是顶部(认为它是从顶部)。
图 2-5 显示了一个 900 像素宽 600 像素高的浏览器窗口的布局。数字表示角和中间的坐标值。
图 2-5
浏览器窗口的坐标系
现在我们来看几个画图的语句,然后把它们放在一起画出简单的形状(见图 2-6 到 2-10 )。之后,我们将看到如何绘制点和矩形来代表模具面。
下面是绘制矩形的 HTML5 JavaScript 代码:
ctx.strokeRect(100,50,200,300);
这将绘制一个空心矩形,其左上角距离左侧 100 像素,距离顶部 50 像素。该矩形的宽度为 200,高度为 300。该语句将使用线宽和颜色的任何当前设置。
下一段代码演示了如何将线宽设置为 5,并将笔画的颜色(即轮廓)设置为指示的 RGB 值(即红色)。使用变量x
、y
、w
和h
中的值绘制矩形。
ctx.lineWidth = 5;
ctx.strokeStyle = "rgb(255,0,0)";
ctx.strokeRect(x,y,w,h);
这个片段
ctx.fillStyle = "rgb(0,0,255)";
ctx.fillRect(x,y,w,h);
在指示的位置和尺寸绘制蓝色实心矩形。如果您想要绘制一个带有红色轮廓的蓝色矩形,可以使用两行代码:
ctx.fillRect(x,y,w,h);
ctx.strokeRect(x,y,w,h);
HTML5 让你画出所谓的由弧线和线段组成的路径。使用ctx.moveTo
和ctx.lineTo
的组合绘制线段。我将在几章中介绍它们:第四章的弹弓游戏,第五章的使用多边形的记忆游戏,以及第九章的刽子手游戏。在第四章的炮弹游戏中,我还会演示如何倾斜一个长方形,第九章的刽子手游戏演示如何画椭圆形。在这一章中,我将把重点放在弧线上。
您可以使用以下方式开始一条路径
ctx.beginPath();
结束它,路径被画出,或者
ctx.closePath();
ctx.stroke();
或者
ctx.closePath();
ctx.fill();
还有一些情况下,您可以省略对closePath
的调用。
弧可以是整个圆,也可以是圆的一部分。在骰子应用程序中,我们只画完整的圆来表示每个骰子面上的点数,但是我将解释弧一般是如何工作的,以使代码不那么神秘。绘制圆弧的方法具有以下格式:
ctx.arc(cx, cy, radius, start_angle, end_angle, direction);
其中cx
、cy
、radius
为圆心横坐标、纵坐标和半径。要解释接下来的两个参数,需要讨论测量角度的方法。你对角度的度数单位很熟悉:我们说 180 度转弯,意思是 U 形转弯,90 度角是由两条垂直线形成的。但是大多数计算机编程语言使用另一种系统,称为弧度。这里有一种直观化弧度的方法——想象一下将一个圆的半径放在这个圆上。你可以挖掘你的记忆,意识到这不会是一个整齐的拟合,因为圆周围有 2* PI 弧度,比 6 多一些。因此,如果我们想画一个完整的圆弧,我们指定起始角度为 0,结束角度为 2 *π。幸运的是,Math
类提供了一个常量Math.PI
,它是圆周率的值(根据需要,精确到尽可能多的小数位),所以在代码中,我们写2*Math.PI
。如果我们想要指定一个半圆的圆弧,我们使用Math.PI
,而一个直角(90 度)将是.5*Math.PI
。
arc 方法还需要一个参数,方向。我们如何画出这些弧线?想象一下钟面上指针的运动。在 HTML 5 中,顺时针是假方向,逆时针是真方向。(别问为什么。这就是 HTML5 中指定的方式。)我使用内置的 JavaScript 值true
和false
。当我们需要画非整圆的圆弧时,这将是很重要的。如果你需要画非完整圆的圆弧,这个特殊问题的性质决定了你如何定义角度。
这里有一些例子,带有完整的代码,供你创建(使用 TextPad 或 TextWrangler),然后改变以测试你的理解。第一个画一个弧线,代表微笑。
<html>
<head>
<title>Smile</title>
<script>
function init() {
var ctx =document.getElementById("canvas").getContext('2d');
ctx.beginPath();
ctx.strokeStyle = "rgb(200,0,0)";
ctx.arc(200, 200,50,0,Math.PI, false);
ctx.stroke();
}
</script>
</head>
<body onLoad="init();">
<canvas id="canvas" width="400" height="300">
Your browser doesn't support the HTML5 element canvas.
</canvas>
</body>
</html>
图 2-6 显示了屏幕的一部分,该部分带有由该代码产生的弧线。
图 2-6
表情产生的“微笑”ctx.arc(200,200,50,0,Math.PI, false)
;
你可以向前看图 2-11 、 2-12 和 2-13 ,其中我捕捉了更多的屏幕以查看绘图的位置。请改变你自己例子中的数字,这样你就能理解坐标系是如何工作的,以及一个像素实际上有多大。
在看到皱眉之前,试着让弧线变宽或变高,或者改变颜色。然后试着上下左右移动整个弧线。提示:你需要改变路线
ctx.arc(200, 200,50,0,Math.PI, false);
改变200,200
重置圆心,改变50
改变半径。
现在,让我们继续其他的变化。一定要把每一个都拿来做实验。将arc
方法的最后一个参数更改为true
:
ctx.arc(200,200,50,0,Math.PI,true);
使弧线逆时针旋转。完整的代码是:
<html>
<head>
<title>Frown</title>
<script type="text/javascript">
function init() {
var ctx =document.getElementById("canvas").getContext('2d');
ctx.beginPath();
ctx.strokeStyle = "rgb(200,0,0)";
ctx.arc(200, 200,50,0,Math.PI, true);
ctx.stroke();
}
</script>
</head>
<body onLoad="init();">
<canvas id="canvas" width="400" height="300">
Your browser doesn't support the HTML5 element canvas.
</canvas>
</body>
</html>
请注意,我还更改了标题。标题出现在浏览器的选项卡上。你的用户/观众会注意到标题。我发现我在调试中使用标题来跟踪不同的版本。该代码产生如图 2-7 所示的屏幕。
图 2-7
表达式 ctx.arc(200,200,50,0,数学)产生的“皱眉”。PI,真);
输入语句以关闭笔划前的路径:
ctx.closePath();
ctx.stroke();
在皱眉示例中,将“结束”圆弧。完整的代码是
<html>
<head>
<title>Frown</title>
<script type="text/javascript">
function init() {
var ctx =document.getElementById("canvas").getContext('2d');
ctx.beginPath();
ctx.strokeStyle = "rgb(200,0,0)";
ctx.arc(200, 200,50,0,Math.PI, true);
ctx.closePath();
ctx.stroke();
}
</script>
</head>
<body>
<body onLoad="init();">
<canvas id="canvas" width="400" height="300">
Your browser doesn't support the HTML5 element canvas.
</canvas>
</body>
</html>
这将产生如图 2-8 所示的屏幕。
图 2-8
通过在ctx.stroke();
前添加ctx.closePath();
,眉头皱成了一个半圆
closePath
命令并不总是必需的,但是包含它是一个好习惯。您会注意到我等待调用closePath
并填充多个点的语句。在这里进行实验,并期待第五章中的弹弓图和第九章中的刽子手图。如果你想填充路径,你可以用ctx.fill()
代替ctx.stroke()
,这样会产生一个黑色的填充形状,如图 2-9 所示。完整的代码是
<html>
<head>
<title>Smile</title>
<script type="text/javascript">
function init() {
var ctx =document.getElementById("canvas").getContext('2d');
ctx.beginPath();
ctx.strokeStyle = "rgb(200,0,0)";
ctx.arc(200, 200,50,0,Math.PI, false);
ctx.closePath();
ctx.fill();
}
</script>
</head>
<body onLoad="init();">
<canvas id="canvas" width="400" height="300">
Your browser doesn't support the HTML5 element canvas.
</canvas>
</body>
</html>
黑色是默认颜色。
图 2-9
使用ctx.fill()
填充半圆
如果你希望一个形状被填充并有一个清晰的轮廓,你可以使用fill
和stroke
命令,并使用fillStyle
和strokeStyle
属性指定不同的颜色。配色方案基于第一章中介绍的相同红/绿/蓝代码。你可以尝试或使用 Photoshop 或在线图片编辑器pixlr.com
等工具来获得你想要的颜色。以下是完整的代码:
<html>
<head>
<title>Smile</title>
<script type="text/javascript">
function init() {
var ctx =document.getElementById("canvas").getContext('2d');
ctx.beginPath();
ctx.strokeStyle = "rgb(200,0,0)";
ctx.arc(200, 200,50,0,Math.PI, false);
ctx.fillStyle = "rgb(200,0,200)";
ctx.closePath();
ctx.fill();
ctx.strokeStyle="rgb(255,0,0)";
ctx.lineWidth=5;
ctx.stroke();
}
</script>
</head>
<body onLoad="init();">
<canvas id="canvas" width="400" height="300">
Your browser doesn't support the HTML5 element canvas.
</canvas>
</body>
</html>
这段代码产生一个填充了紫色(红蓝混合)的半圆,用笔画出来,也就是一个纯红的轮廓,如图 2-10 所示。编码指定一个路径,然后将该路径绘制为填充,然后将该路径绘制为笔画。
图 2-10
使用不同颜色的填充和描边
许多不同的命令都会生成一个完整的圆,包括:
ctx.arc(200,200,50,0, 2*Math.PI, true);
ctx.arc(200,200,50, 0, 2*Math.PI, false);
ctx.arc(200,200,50, .5*Math.PI, 2.5*Math.PI, false);
你不妨坚持第一个,它和其他的一样好。注意,我仍然使用closePath
命令。从几何角度来看,圆可能是一个封闭的图形,但从 JavaScript 角度来看,这无关紧要。
如果你把canvas
元素想象成一块画布,你在上面放一些墨水或颜料,你会意识到你需要擦除画布或它的适当部分来绘制新的东西。为此,HTML5 提供了命令
ctx.clearRect(x,y,width,height);
后面的例子展示了如何绘制弹弓(第四章)、记忆/注意力游戏的多边形(第五章)、迷宫的墙壁(第七章)以及《刽子手》中的简笔画(第九章)。现在让我们回到掷骰子游戏需要什么。
使用表单显示文本输出
在画布上写文本是可能的(参见第五章,但是对于 craps 应用程序,我选择使用一个form
,一个在 HTML 的旧版本和当前版本中都存在的元素。我不使用玩家输入的表单。我确实用它来输出掷骰子结果的信息。HTML5 规范指出了建立表单的新方法,包括检查或验证输入的类型和范围。下一章的应用程序演示了验证。
我使用下面的 HTML 来生成骰子游戏的表单:
<form name="f">
Stage: <input name="stage" value="First Throw"/>
Point: <input name="pv" value=" "/>
Outcome: <input name="outcome" value=" "/>
</form>
表单以一个name
属性开始。文本Stage:
、Point:
和Outcome:
出现在输入字段旁边。输入标签——注意这些是单独标签——既有名称字段,也有值字段。JavaScript 代码将使用这些名称。您可以将任何 HTML 放入一个表单中,也可以将一个表单放入任何 HTML 中。
因为骰子游戏使用了新的button
元素,所以我只是添加了带有用于向玩家显示信息的字段的form
元素,而没有包括类型为submit
的输入元素。或者,我可以使用带有submit
输入字段的标准表单(不需要新的按钮元素),代码如下:
<form name="f" onSubmit="throwdice();">
Stage: <input type="text" name="stage" value="First Throw"/>
Point: <input type="text" name="pv" value=" "/>
Outcome: <input type="text" name="outcome" value=" "/>
<input type="submit" value="THROW DICE"/>
</form>
类型submit
的输入元素在屏幕上产生一个按钮。这些都是我们构建 craps 应用程序所需的概念。我们现在可以开始编码了。
构建应用程序并使之成为您自己的应用程序
您可能已经在小例子中尝试过使用本章中描述的 HTML5、CSS 和 JavaScript 结构。提示:请做。唯一的学习方法就是自己树立榜样。作为构建 craps 应用程序的一种方式,我们现在来看三个应用程序:
-
扔一次骰子,然后重新装弹再扔一次
-
用一个按钮扔两个骰子
-
掷骰子的完整游戏
图 2-11 显示了第一个应用程序的可能打开屏幕。我说可能是因为它不会总是 4。我故意捕捉这个截图来显示几乎所有的窗口,这样你就可以看到绘图在屏幕上的位置。
图 2-11
单模应用
图 2-12 显示了掷骰子应用程序的打开屏幕。出现的只是按钮。
图 2-12
双骰子应用程序的开始屏幕
最后,图 2-13 显示玩家点击按钮后的屏幕。
图 2-13
点击按钮掷出一对骰子
逐步构建您的应用程序是一种很好的技术。这些应用程序是使用文本编辑器构建的,如 TextPad 或 TextWrangler。记得将文件保存为类型。html——尽早并经常这样做。您不必在保存前完成。当您完成第一个应用程序并保存和测试它时,您可以使用新的名称再次保存它,然后对这个新副本进行修改以成为第二个应用程序。对第三个应用程序进行同样的操作。
扔一个骰子
第一个应用程序的目的是在画布上显示一个随机的骰子面,以标准方式显示圆形。
对于任何应用程序,通常有许多可行的方法。我意识到,我可以从一些编码中获得双重职责,因为 3 模面的图案可以通过组合 2 和 1 图案来制作。类似地,5 的模式是 4 和 1 的组合。6 的图案是 4 的图案和一些独特图案的组合。我可以把所有的代码都放在init
函数中,或者使用一个单独的drawface
函数。无论如何,这对我来说是有意义的,我很快就编程并调试了它。表 2-1 列出了所有的功能并指出什么调用什么。表 2-2 显示了完整的代码,解释了每一行的作用。
表 2-2
投掷单个骰子应用程序的完整代码
|密码
|
说明
|
| — | — |
| <html>
| 开始html
标签。 |
| <head>
| 开始head
标签。 |
| <title>Throwing 1 die</title>
| 完整的title
元素。 |
| <script>
| 开始script
标签。 |
| var cwidth = 400;
| 保存画布宽度的变量;也用于擦除画布,为重绘做准备。 |
| var cheight = 300;
| 保存画布高度的变量;也用于擦除画布,为重绘做准备。 |
| var dicex = 50;
| 保持单个模具水平位置的变量。 |
| var dicey = 50;
| 保持单个模具垂直位置的变量。 |
| var dicewidth = 100;
| 保存模具面宽度的变量。 |
| var diceheight = 100;
| 保存模具面高度的变量。 |
| var dotrad = 6;
| 保存点的半径的变量。 |
| var ctx;
| 保存画布上下文的变量,在所有绘制命令中使用。 |
| function init() {
| 开始为init
函数定义函数,该函数在文档的onLoad
中被调用。 |
| var ch = 1+Math.``floor(Math.random()*6);
| 声明并将ch
变量的值随机设置为数字 1、2、3、4、5 或 6。 |
| drawface(ch);
| 用参数ch
调用drawface
函数。 |
| }
| 结束函数定义。 |
| function drawface(n) {
| drawface
函数的函数定义的开始,其参数是点数。 |
| ctx = document.getElementBy``Id('canvas').getContext('2d');
| 获取用于在画布上绘制的对象。 |
| ctx.lineWidth = 5;
| 将线宽设置为 5。 |
| ctx.clearRect(dicex,dicey,``dicewidth,diceheight);
| 清除可能已绘制模具面的空间。这在第一次没有效果。 |
| ctx.strokeRect(dicex,dicey,``dicewidth,diceheight);
| 画出模具面的轮廓。 |
| ctx.fillStyle = "#009966";
| 设置圆的颜色。我用一个图形程序来确定这个值。你可以这样做,或者实验。 |
| switch(n) {
| 使用点数开始switch
。 |
| case 1:
| 如果是 1。 |
| draw1();
| 调用draw1
函数。 |
| break;
| 打开开关。 |
| case 2:
| 如果是 2。 |
| draw2();
| 调用draw2
函数。 |
| break;
| 打开开关。 |
| case 3:
| 如果是 3。 |
| draw2();
| 先打draw2
然后。 |
| draw1();
| 调用draw1
。 |
| break;
| 打开开关。 |
| case 4:
| 如果是 4。 |
| draw4();
| 调用draw4
函数。 |
| break;
| 打开开关。 |
| case 5:
| 如果是 5。 |
| draw4();
| 调用draw4
函数,然后。 |
| draw1();
| 调用draw1
函数。 |
| break;
| 打开开关。 |
| case 6:
| 如果是 6。 |
| draw4();
| 调用draw4
函数,然后。 |
| draw2mid();
| 调用draw2mid
函数。 |
| break;
| 断开开关(并非绝对必要)。 |
| }
| 关闭switch
语句。 |
| }
| 关闭drawface
功能。 |
| function draw1() {
| 开始定义draw1
。 |
| var dotx;
| 用于绘制单点的水平位置的变量。 |
| var doty;
| 用于绘制单点的垂直位置的变量。 |
| ctx.beginPath();
| 开创一条道路。 |
| dotx = dicex + .5*dicewidth;
| 将该点的中心水平设置在模具面的中心 |
| doty = dicey + .5*diceheight;
| …垂直地。 |
| ctx.arc(dotx,doty,dotrad,0,Math.PI*2,true);
| 构造一个圆(用 fill 命令绘制)。 |
| ctx.closePath();
| 关闭路径。 |
| ctx.fill();
| 画出路径,也就是填满圆。 |
| }
| 关闭draw1
。 |
| function draw2() {
| 开始draw2
功能。 |
| var dotx;
| 用于绘制两点的水平位置的变量。 |
| var doty;
| 用于绘制两点的垂直位置的变量。 |
| ctx.beginPath();
| 开创一条道路。 |
| dotx = dicex + 3*dotrad;
| 将该点的中心设置为距离模具面上角三个半径长度的水平距离 |
| doty = dicey + 3*dotrad;
| …垂直地。 |
| ctx.arc(dotx,doty,dotrad,0,Math.PI*2,true);
| 构造第一个点。 |
| dotx = dicex+dicewidth-3*dotrad;
| 将该点的中心设置为距离模具面下角三个半径长度的水平距离 |
| doty = dicey+diceheight-3*dotrad;
| …垂直地。 |
| ctx.arc(dotx,doty,dotrad,0,Math.PI*2,true);
| 构造第二个点。 |
| ctx.closePath();
| 关闭路径。 |
| ctx.fill();
| 把两个点都画出来。 |
| }
| 关闭draw2
。 |
| function draw4() {
| 开始draw4
功能。 |
| var dotx;
| 用于绘制点的水平位置的变量。 |
| var doty;
| 用于绘制点的垂直位置的变量。 |
| ctx.beginPath();
| 开始路径。 |
| dotx = dicex + 3*dotrad;
| 将第一个点水平放置在左上角内 |
| doty = dicey + 3*dotrad;
| …垂直地。 |
| ctx.arc(dotx,doty,dotrad,0,Math.PI*2,true);
| 构建圆圈。 |
| dotx = dicex+dicewidth-3*dotrad;
| 将第二个点水平放置在右下角 |
| doty = dicey+diceheight-3*dotrad;
| …垂直地。 |
| ctx.arc(dotx,doty,dotrad,``0,Math.PI*2,true);
| 构建点。 |
| ctx.closePath();
| 关闭路径。 |
| ctx.fill();
| 画两个点。 |
| ctx.beginPath();
| 开始路径。 |
| dotx = dicex + 3*dotrad;
| 将此点水平放置在左下角 |
| doty = dicey + diceheight-3*dotrad;
| …垂直(注意,这是刚刚使用的相同 y 值)。 |
| ctx.arc(dotx,doty,dotrad,``0,Math.PI*2,true);
| 构建圆形。 |
| dotx = dicex+dicewidth-3*dotrad;
| 将该点水平放置在左上角内侧 |
| doty = dicey+ 3*dotrad;
| …垂直地。 |
| ctx.arc(dotx,doty,dotrad,``0,Math.PI*2,true);
| 构建圆形。 |
| ctx.closePath();
| 关闭路径。 |
| ctx.fill();
| 画两个点。 |
| }
| 关闭draw4
功能。 |
| function draw2mid() {
| 启动draw2mid
功能。 |
| var dotx;
| 用于绘制两点的水平位置的变量。 |
| var doty;
| 用于绘制两点的垂直位置的变量。 |
| ctx.beginPath();
| 开始路径。 |
| dotx = dicex + 3*dotrad;
| 将这些点水平地放在里面 |
| doty = dicey + .5*diceheight;
| 中间垂直。 |
| ctx.arc(dotx,doty,dotrad,``0,Math.PI*2,true);
| 构建圆形。 |
| dotx = dicex+dicewidth-3*dotrad;
| 将这个点放在右边框内。 |
| doty = dicey + .5*diceheight; //no change
| 中间位置 y。 |
| ctx.arc(dotx,doty,dotrad,``0,Math.PI*2,true);
| 构建圆形。 |
| ctx.closePath();
| 关闭路径。 |
| ctx.fill();
| 画点。 |
| }
| 关闭draw2mid
功能。 |
| </script>
| 关闭script
元素。 |
| </head>
| 关闭head
元素。 |
| <body onLoad="init();">
| 开始body
标签,设置onLoad
属性来调用init()
函数。 |
| <canvas id="canvas" width="400" height="300">``Your browser doesn't support``the HTML5 element canvas.``</canvas>
| 如果浏览器不接受canvas
元素,设置画布并提供通知。 |
| </body>``</html>
| 关闭body
和关闭html
元件。 |
表 2-1
单骰子投掷应用中的函数
|功能
|
调用方/被调用方
|
打电话
|
| — | — | — |
| init
| 由标签<body>
中的onLoad
动作调用 | drawface
|
| drawface
| 由init
调用 | draw1, draw2, draw4, draw2mid
|
| draw1
| 由drawface
在三个地方调用 1、3、5 | |
| draw2
| 由drawface
在 2 和 3 两个面调用 | |
| draw4
| 被drawface
在三个地方调用为 4、5、6 | |
| draw2mid
| 在一个地方被drawface
呼叫了 6 | |
您可以并且应该在代码中添加注释。注释是被浏览器忽略的文本片段,但它可以提醒你,也许还有其他稍后会看到这个程序的人,正在发生什么。一种形式的注释以一行上的两个斜线开始。斜线右侧的所有内容都将被忽略。对于较大的注释,可以用斜杠和星号开始注释,用星号和斜杠结束注释。
/*
This is a comment.
*/
这是一个照我说的做,而不是照我做的情况。由于我使用表格在每一行放置注释,并且您可以将整个章节视为注释,所以我没有在代码中包含很多注释。我再说一遍:你应该!
提示:当我开发这段代码(以及任何涉及随机效果的代码)时,我不想用随机编码进行初始测试。所以,就在这条线后面
var ch = 1+Math.floor(Math.random()*6);
我放了线
ch = 1;
测试了一下,然后我把它改成了
ch = 2;
诸如此类。当我完成这个阶段的测试时,我删除了这一行(或者用//
注释掉它)。这属于一般的建议:在开发游戏时,尽量避免玩复杂的游戏。
扔两个骰子
下一个应用程序使用一个按钮让玩家做一些事情,而不仅仅是重新加载网页,它还模拟了一对骰子的投掷。在看代码之前,想想你能从第一个应用程序中继承什么。总的回答是:大部分。“延续”是编写代码和测试代码的一种节约。
第二个应用程序需要对两个模具面的定位做一些事情,为此使用另外两个变量,dx
和dy
。它还需要使用Math.random
并调用drawface
两次来重复代码,以生成每个模具面。需要改变引发投掷的原因。表 2-3 描述了调用和被调用的函数,本质上与表 2-1 相同,除了现在有一个名为throwdice
的函数,它由按钮标签的onClick
属性设置的动作调用。表 2-4 包含投掷两个骰子应用程序的完整 HTML 文档。
表 2-4
完整的双骰子应用
|密码
|
说明
|
| — | — |
| <html>
| 开始html
标签。 |
| <head>
| 开始head
标签。 |
| <title>Throwing dice</title>
| 完整的title
元素。 |
| <script>
| 开始script
标签。 |
| var cwidth = 400;
| 保存画布宽度的变量。 |
| var cheight = 300;
| 保存画布高度的变量;也用于擦除画布,为重绘做准备。 |
| var dicex = 50;
| 可变地保持单个模具的水平位置;也用于擦除画布,为重绘做准备。 |
| var dicey = 50;
| 保持单个模具垂直位置的变量。 |
| var dicewidth = 100;
| 保存模具面宽度的变量。 |
| var diceheight = 100;
| 保存模具面高度的变量。 |
| var dotrad = 6;
| 保存点的半径的变量。 |
| var ctx;
| 保存画布上下文的变量,在所有绘制命令中使用。 |
| var dx;
| 用于水平定位的变量,并根据两个模面的不同而变化。 |
| var dy;
| 用于垂直定位的变量。这对于两个模具面是相同的。 |
| function throwdice() {
| 启动throwdice
功能。 |
| var ch = 1+Math.floor(Math.random()*6);
| 声明变量ch
,然后用一个随机值设置它。 |
| dx = dicex;
| 为第一个模具面设置dx
。 |
| dy = dicey;
| 为第一个和第二个模具面设置dy
。 |
| drawface(ch);
| 以ch
为点数调用drawface
。 |
| dx = dicex + 150;
| 调整第二个模具面的dx
。 |
| ch=1 + Math.floor(Math.random()*6);
| 用随机值重置ch
。 |
| drawface(ch);
| 以ch
为点数调用drawface
。 |
| }
| 关闭throwdice
功能。 |
| function drawface(n) {
| drawface
函数的函数定义的开始,其参数是点数。 |
| ctx = document.getElementById``('canvas').getContext('2d');
| 获取用于在画布上绘制的对象。 |
| ctx.lineWidth = 5;
| 将线宽设置为 5。 |
| ctx.clearRect(dx,dy,dicewidth,diceheight);
| 清除可能已绘制模具面的空间。这第一次没有效果。 |
| ctx.strokeRect(dx,dy,dicewidth,diceheight);
| 画出模具面的轮廓。 |
| var dotx;
| 保持水平位置的变量。 |
| var doty;
| 保持垂直位置的变量。 |
| ctx.fillStyle = "#009966";
| 设置颜色。 |
| switch(n) {
| 使用点数开始switch
。 |
| case 1:
| 如果是 1。 |
| draw1();
| 调用draw1
函数。 |
| break;
| 打开开关。 |
| case 2:
| 如果是 2。 |
| draw2();
| 调用draw2
函数。 |
| break;
| 打开开关。 |
| case 3:
| 如果是 3。 |
| draw2();
| 先打draw2
然后。 |
| draw1();
| 调用draw1
。 |
| break;
| 打开开关。 |
| case 4:
| 如果是 4。 |
| draw4();
| 调用draw4
函数。 |
| break;
| 打开开关。 |
| case 5:
| 如果是 5。 |
| draw4();
| 调用draw4
函数,然后 |
| draw1();
| 调用draw1
函数。 |
| break;
| 打开开关。 |
| case 6:
| 如果是 6。 |
| draw4();
| 调用draw4
函数,然后 |
| draw2mid();
| 调用draw2mid
函数。 |
| break;
| 断开开关(并非绝对必要) |
| }
| 关闭switch
语句。 |
| }
| 关闭drawface
功能。 |
| function draw1() {
| 开始定义draw1
。 |
| var dotx;
| 用于绘制单点的水平位置的变量。 |
| var doty;
| 用于绘制单点的垂直位置的变量 |
| ctx.beginPath();
| 开创一条道路。 |
| dotx = dx + .5*dicewidth;
| 将该点的中心水平设置在模具面的中心(使用dx
) |
| doty = dy + .5*diceheight;
| …(使用dy
)垂直。 |
| ctx.arc(dotx,doty,dotrad,``0,Math.PI*2,true);
| 构造一个圆(用 fill 命令绘制)。 |
| ctx.closePath();
| 关闭路径。 |
| ctx.fill();
| 画出路径,也就是圆。 |
| }
| 关闭draw1
。 |
| function draw2() {
| 开始draw2
功能。 |
| var dotx;
| 用于绘制两点的水平位置的变量。 |
| var doty;
| 用于绘制两点的垂直位置的变量。 |
| ctx.beginPath();
| 开创一条道路。 |
| dotx = dx + 3*dotrad;
| 将该点的中心设置为距离模具面上角三个半径长度的水平距离 |
| doty = dy + 3*dotrad;
| …垂直地。 |
| ctx.arc(dotx,doty,dotrad,0,``Math.PI*2,true);
| 构造第一个点。 |
| dotx = dx+dicewidth-3*dotrad;
| 将该点的中心设置为距离模具面下角 3 个半径长度的水平距离 |
| doty = dy+diceheight-3*dotrad;
| …垂直地。 |
| ctx.arc(dotx,doty,dotrad,0,``Math.PI*2,true);
| 构造第二个点。 |
| ctx.closePath();
| 关闭路径。 |
| ctx.fill();
| 把两个点都画出来。 |
| }
| 关闭draw2
。 |
| function draw4() {
| 开始draw4
功能。 |
| var dotx;
| 用于绘制点的水平位置的变量。 |
| var doty;
| 用于绘制点的垂直位置的变量。 |
| ctx.beginPath();
| 开始路径。 |
| dotx = dx + 3*dotrad;
| 将第一个点水平放置在左上角内 |
| doty = dy + 3*dotrad;
| …垂直地。 |
| ctx.arc(dotx,doty,dotrad,0,``Math.PI*2,true);
| 构建圆圈。 |
| dotx = dx+dicewidth-3*dotrad;
| 将第二个点水平放置在右下角 |
| doty = dy+diceheight-3*dotrad;
| …垂直地。 |
| ctx.arc(dotx,doty,dotrad,0,``Math.PI*2,true);
| 构建点。 |
| ctx.closePath();
| 关闭路径。 |
| ctx.fill();
| 画两个点。 |
| ctx.beginPath();
| 开始路径。 |
| dotx = dx + 3*dotrad;
| 将此点水平放置在左下角 |
| doty = dy + diceheight-3*dotrad;
| …垂直(注意,这与刚刚使用的y
值相同)。 |
| ctx.arc(dotx,doty,dotrad,0,``Math.PI*2,true);
| 构建圆形。 |
| dotx = dx+dicewidth-3*dotrad;
| 将该点水平放置在左上角内侧 |
| doty = dy+ 3*dotrad;
| …垂直地。 |
| ctx.arc(dotx,doty,dotrad,0,``Math.PI*2,true);
| 构建圆形。 |
| ctx.closePath();
| 关闭路径。 |
| ctx.fill();
| 画两个点。 |
| }
| 关闭draw4
功能。 |
| function draw2mid() {
| 启动draw2mid
功能。 |
| var dotx;
| 用于绘制两点的水平位置的变量。 |
| var doty;
| 用于绘制两点的垂直位置的变量。 |
| ctx.beginPath();
| 开始路径。 |
| dotx = dx + 3*dotrad;
| 将这些点水平地放在里面 |
| doty = dy + .5*diceheight;
| 中间垂直。 |
| ctx.arc(dotx,doty,dotrad,0,``Math.PI*2,true);
| 构建圆形。 |
| dotx = dx+dicewidth-3*dotrad;
| 将这个点放在右边框内。 |
| doty = dy + .5*diceheight;
| 位置y
中途(无变化)。 |
| ctx.arc(dotx,doty,dotrad,0,``Math.PI*2,true);
| 构建圆形。 |
| ctx.closePath();
| 关闭路径。 |
| ctx.fill();
| 画点。 |
| }
| 关闭draw2mid
功能。 |
| </script>
| 关闭script
元素。 |
| </head>
| 关闭head
元素。 |
| <body>
| 开始body
标签。 |
| <canvas id="canvas" width="400" height="300">
| 画布tag
开始。 |
| Your browser doesn't support the``HTML5 element canvas.
| 如果浏览器不接受canvas
元素,设置画布并提供通知。 |
| </canvas>
| 关闭canvas
标签。 |
| <br/>
| 换行。 |
| <button onClick="throwdice();">``Throw dice </button>
| 按钮元素(注意属性onClick
设置调用throwdice
)。 |
| </body>
| 关闭body
标签。 |
| </html>
| 关闭html
标签。 |
表 2-3
双骰子应用中的函数
|功能
|
调用方/被调用方
|
打电话
|
| — | — | — |
| throwdice
| 由标签<button>
中的onClick
动作调用 | drawface
|
| drawface
| 由throwdice
调用 | draw1, draw2, draw4, draw2mid
|
| draw1
| 由drawface
在三个地方调用 1、3、5 | |
| draw2
| 被drawface
在两个地方调用为 2 和 3 | |
| draw4
| 被drawface
在三个地方调用为 4、5、6 | |
| draw2mid
| 在一个地方被drawface
呼叫了 6 | |
掷骰子的完整游戏
第三个应用程序是完整的双骰子游戏。同样,许多内容可以从以前的应用程序中继承过来。然而,现在我们需要加入游戏规则。其中,这将意味着使用条件语句if
和switch,
以及全局变量,即在任何函数定义之外定义的变量,来跟踪是否是第一回合(firstturn
)以及玩家的要点是什么(point
)。这两个变量保存了掷骰子游戏的应用程序状态。正是这种相对简单的应用程序状态的存在、全局和局部变量的使用、条件语句和随机处理使得 craps 成为编程教师最喜欢的话题。
功能表与第二个应用给出的功能表相同(见表 2-3 ),所以我不再重复。表 2-5 保存了该应用程序的代码。新动作都在throwdice
函数里。我会评论新的台词。
表 2-5
完整的 Craps 应用程序
|密码
|
说明
|
| — | — |
| <html>
| |
| <head>
| |
| <title>Craps game</title>
| |
| <script>
| |
| var cwidth = 400;
| |
| var cheight = 300;
| |
| var dicex = 50;
| |
| var dicey = 50;
| |
| var dicewidth = 100;
| |
| var diceheight = 100;
| |
| var dotrad = 6;
| |
| var ctx;
| |
| var dx;
| |
| var dy;
| |
| var firstturn = true;
| 全局变量,初始化为值true
。 |
| var point;
| 全局变量不需要初始化,因为它将在使用前设置。 |
| function throwdice() {
| 开始throwdice
功能。 |
| var sum;
| 变量来保存两个骰子的值的总和。 |
| var ch = 1+Math.floor(Math.random()*6);
| 用第一个随机值设置ch
。 |
| sum = ch;
| 将此分配给sum
。 |
| dx = dicex;
| 设置dx
。 |
| dy = dicey;
| 设置dy
。 |
| drawface(ch);
| 绘制第一个模具面。 |
| dx = dicex + 150;
| 调整水平位置。 |
| ch=1 + Math.floor(Math.random()*6);
| 用随机值设置ch
。这是给第二个骰子的。 |
| sum += ch;
| 将ch
添加到已经在sum
中的内容中。 |
| drawface(ch);
| 画第二个骰子。 |
| if (firstturn) {
| 现在开始实施规则。这是第一次转弯吗? |
| switch(sum) {
| 如果是,以sum
为条件启动一个switch
。 |
| case 7:
| 为了 7 |
| case 11:
| …或者 11。 |
| document.f.outcome.value="You win!";
| 显示You win!
|
| break;
| 退出交换机。 |
| case 2:
| 对两个人来说, |
| case 3:
| …或者 3 |
| case 12:
| …或者 12 个 |
| document.f.outcome.value="You lose!";
| 显示You lose!
|
| break;
| 退出交换机。 |
| default:
| 还有别的吗 |
| point = sum;
| 将总和保存在变量点中。 |
| document.f.pv.value=point;
| 显示点值。 |
| firstturn = false;
| 将firstturn
设置为false
。 |
| document.f.stage.value="Need follow-up throw.";
| 显示Need follow-up throw
。 |
| document.f.outcome.value=" ";
| 擦除(清除)结果字段。 |
| }
| 结束切换。 |
| }
| 结束if-true
子句。 |
| else {
| 否则(不是第一轮)。 |
| switch(sum) {
| 再次使用sum
启动开关。 |
| case point:
| 如果sum
等于point
中的值。 |
| document.f.outcome.value="You win!";
| 显示You win!
。 |
| document.f.stage.value="Back to first throw.";
| 显示Back to first throw
。 |
| document.f.pv.value=" ";
| 清除点值。 |
| firstturn = true;
| 重置firstturn
使其再次为真。 |
| break;
| 退出交换机。 |
| case 7:
| 如果总和等于 7。 |
| document.f.outcome.value="You lose!";
| 显示You lose!
。 |
| document.f.stage.value="Back to first throw.";
| 显示Back to first throw
。 |
| document.f.pv.value=" ";
| 清除点值。 |
| firstturn = true;
| 重置firstturn
,使其再次变为true
。 |
| }
| 合上开关。 |
| }
| 关闭else
子句。 |
| }
| 关闭throwdice
功能。 |
| function drawface(n) {
| |
| ctx = document.getElementById('canvas').getContext('2d');
| |
| ctx.lineWidth = 5;
| |
| ctx.clearRect(dx,dy,dicewidth,diceheight);
| |
| ctx.strokeRect(dx,dy,dicewidth,diceheight)
;
| |
| ctx.fillStyle = "#009966";
| |
| switch(n) {
| |
| case 1:
| |
| draw1();
| |
| break;
| |
| case 2:
| |
| draw2();
| |
| break;
| |
| case 3
:
| |
| draw2();
| |
| draw1();
| |
| break;
| |
| case 4:
| |
| draw4();
| |
| break;
| |
| case 5:
| |
| draw4();
| |
| draw1();
| |
| break;
| |
| case 6:
| |
| draw4();
| |
| draw2mid();
| |
| break
;
| |
| }
| |
| }
| |
| function draw1() {
| |
| var dotx;
| |
| var doty;
| |
| ctx.beginPath();
| |
| dotx = dx + .5*dicewidth;
| |
| doty = dy + .5*diceheight;
| |
| ctx.arc(dotx,doty,dotrad,0, Math.PI*2,true);
| |
| ctx.closePath();
| |
| ctx.fill();
| |
| }
| |
| function draw2() {
| |
| var dotx
;
| |
| var doty;
| |
| ctx.beginPath();
| |
| dotx = dx + 3*dotrad;
| |
| doty = dy + 3*dotrad;
| |
| ctx.arc(dotx,doty,dotrad,0,Math.PI*2,true);
| |
| dotx = dx+dicewidth-3*dotrad;
| |
| doty = dy+diceheight-3*dotrad;
| |
| ctx.arc(dotx,doty,dotrad,0,Math.PI*2,true);
| |
| ctx.closePath();
| |
| ctx.fill();
| |
| }
| |
| function draw4() {
| |
| var dotx;
| |
| var doty;
| |
| ctx.beginPath();
| |
| dotx = dx + 3*dotrad;
| |
| doty = dy + 3*dotrad
;
| |
| ctx.arc(dotx,doty,dotrad,0,Math.PI*2,true);
| |
| dotx = dx+dicewidth-3*dotrad;
| |
| doty = dy+diceheight-3*dotrad;
| |
| ctx.arc(dotx,doty,dotrad,0,Math.PI*2,true);
| |
| ctx.closePath();
| |
| ctx.fill();
| |
| ctx.beginPath();
| |
| dotx = dx + 3*dotrad;
| |
| doty = dy + diceheight-3*dotrad; //no change
| |
| ctx.arc(dotx,doty,dotrad,0,Math.PI*2,true);
| |
| dotx = dx+dicewidth-3*dotrad;
| |
| doty = dy+ 3*dotrad;
| |
| ctx.arc(dotx,doty,dotrad,0,Math.PI*2,true);
| |
| ctx.closePath();
| |
| ctx.fill()
;
| |
| }
| |
| function draw2mid() {
| |
| var dotx;
| |
| var doty
;
| |
| ctx.beginPath();
| |
| dotx = dx + 3*dotrad;
| |
| doty = dy + .5*diceheight;
| |
| ctx.arc(dotx,doty,dotrad,0,Math.PI*2,true);
| |
| dotx = dx+dicewidth-3*dotrad;
| |
| doty = dy + .5*diceheight; //no change
| |
| ctx.arc(dotx,doty,dotrad,0,Math.PI*2,true);
| |
| ctx.closePath();
| |
| ctx.fill();
| |
| }
| |
| </script>
| |
| </head>
| |
| <body>
| |
| <canvas id="canvas" width="400" height="300">
| |
| Your browser doesn't support the HTML5 element canvas
.
| |
| </canvas>
| |
| <br/>
| |
| <button onClick="throwdice();">Throw dice </button>
| |
| <form name="f">
| 启动一个名为f
的表单。 |
| Stage: <input name="stage" value="First Throw"/>
| 在文本Stage:
之前,设置一个名为stage
的输入字段。 |
| Point: <input name="pv" value=" "/>
| 在文本Point:
之前,设置一个名为pv
的输入字段。 |
| Outcome: <input name="outcome" value=" "/>
| 在文本Outcome:
之前,设置一个名为outcome
的输入字段。 |
| </form>
| 关闭form
。 |
| </body>
| 关闭body
。 |
| </html>
| 关闭html
。 |
让应用程序成为你自己的
让这个应用程序成为你自己的并不像 favorite sites 应用程序那样简单,因为骰子的规则就是骰子的规则。然而,你可以做很多事情。使用fillRect
并将fillStyle
设置为不同的颜色,改变骰子面的大小和颜色。改变整个画布的颜色和大小。将结果的文本更改为更丰富多彩的内容。你也可以使用标准或特制的骰子来实现其他游戏。
你可以看到下一章,学习在画布上绘制图像,而不是用圆弧和矩形来绘制每个模具面。HTML5 提供了一种引入外部图像文件的方法。这种方法的缺点是,您必须跟踪这些单独的文件。
你可以开发保持分数的编码。对于一个赌博游戏,你可以给玩家一个固定的金额,比如说 100,无论货币单位是什么,然后扣除一些金额,比如说 10,然后加上一些金额,比如说 20,如果且仅如果玩家赢了。您可以将这些资金信息作为form
元素的一部分添加到正文中:
<form name="f" id="f">
Stage: <input name="stage" value="First Throw"/>
Point: <input name="pv" value=" "/>
Outcome: <input name="outcome" value=" "/>
Bank roll: <input name="bank" value="100"/>
</form>
JavaScript(和其他编程语言)区分数字和代表数字的字符串。即值"100"
是一串字符串,“1”、“0”、“0”。值 100 是一个数字。然而,在这两种情况下,变量的值都存储为 1 和 0 的序列。对于数字,这将是以二进制数表示的数字。对于字符串,每个字符将使用标准编码系统表示,如 ASCII 或 UNICODE。在某些情况下,JavaScript 会进行从一种数据类型到另一种数据类型的转换,但是不要依赖于它。我建议的代码使用内置函数String
和Number
来完成这些转换。
在throwdice
函数中,在if(firstturn)
语句之前,添加表 2-6 中的代码(或者类似的东西)。
表 2-6
为玩家添加银行
|密码
|
说明
|
| — | — |
| var bank = Number(document.f.bank.value);
| 将新变量bank
设置为银行输入字段中的值所代表的数字。 |
| if (bank<10) {
| 比较bank
和 10。 |
| alert("You ran out of money! Add some more and try again.");
| 如果bank
小于 10,则发出警报。 |
| Return;
| 不做任何事情就退出该功能。 |
| }
| 关闭if true
子句。 |
| bank = bank – 10;
| 将bank
减少 10。只有当坡度大于 10 时,才达到这条线。 |
| document.f.bank.value = String(bank);
| 将该值的字符串表示形式放入 bank 字段。 |
然后,在玩家获胜的每个地方(在 7 和 11 情况后的第一回合的switch
语句中,或者在点情况后的后续回合的switch
语句中),添加表 2-7 中的代码。
表 2-7
增加银行的价值
|密码
|
说明
|
| — | — |
| bank = Number(document.f.bank.value);
| 将bank
设置为银行输入字段中的值所代表的数字。再次设置bank
允许玩家在游戏中途重新设置银行金额。 |
| bank +=20;
| 使用+=
运算符将 bank 的值增加 20。 |
| document.f.bank.value = String(bank);
| 将银行金额的字符串表示放入银行字段。 |
当玩家输了,或者是后续回合的时候,你不加任何代码。每次新游戏开始前,银行价值都会下降。
测试和上传应用程序
这些应用程序在 HTML 文件中是完整的。不使用其他文件,如图像文件。相反,骰子面是画在画布上的。(供您参考,我用旧 HTML 编写的骰子游戏版本使用了一两个img
元素。为了让这些固定的img
元素显示不同的图像,我编写了代码,将src
属性更改为不同的外部图像文件。当我上传应用程序时,我必须上传所有的图像文件。)
在浏览器中打开 HTML 文件。需要重新加载第一个应用程序以获得新的(单个)芯片。第二个和第三个应用程序(第三个是骰子游戏)使用一个按钮来掷骰子。
我重复我之前写的。为了测试这个程序,你需要检查许多案例。当你作为玩家赢了的时候,你还没有结束。典型问题包括:
-
开始和结束标记缺失或不匹配。
-
不匹配的开始和结束括号、
{
和}
包围函数、switch
语句和if
子句。 -
缺少引号。使用 TextPad 和一些其他编辑器时可用的颜色编码在这里会有所帮助,因为它会高亮显示它识别的关键词。
-
变量和函数的命名和使用不一致。这些名称可以是您选择的任何名称,但是您需要保持一致。功能
draw2mid
不会被drawmid2()
调用。
除了最后一个,这些都是语法错误,类似于语法和标点符号的错误。语义(即意义)的错误可能更难发现。如果您编写第二个switch
语句在 7 上赢,在点值上输,您可能编写了正确的 JavaScript 代码,但这不会是掷骰子的游戏。
这不应该发生在这里,因为你可以复制我的代码,但一个常见的错误是混淆坐标系,认为垂直值在屏幕上向上而不是向下增加。
摘要
在本章中,您学习了如何
-
声明变量并使用全局变量来表示应用程序状态
-
编写代码来执行算术运算
-
定义和使用程序员定义的函数
-
使用 JavaScript 的几个内置特性,包括
Math.random
和Math.floor
方法 -
使用
if
和switch
语句 -
使用 HTML 元素创建画布
-
画矩形和圆形
本章介绍了 HTML5 的一个关键特性,画布,以及随机性和交互性的概念。它还展示了许多编程特性,您将在本书剩余部分的示例中使用这些特性。尤其是,分阶段构建应用程序的技术非常有用。下一章的特色是一个球在盒子里弹跳的动画——为第四章的真实游戏做准备——被称为炮弹和弹弓的弹道模拟。
三、弹跳球
在本章中,我们将介绍
-
创建程序员定义的对象
-
使用
setInterval
制作动画 -
绘制图像
-
接受和验证表单输入
-
使用按钮
-
使用
for
循环 -
带渐变的绘图
-
预加载图像
介绍
动画,无论是在电影中,使用动画书,还是由计算机生成,都包括以足够快的速度显示一系列静止图像,以便我们将所看到的理解为运动,理解为生活。在这一章中,我将向你展示如何通过模拟一个在二维盒子中弹跳的球来制作动画场景,水平和垂直速度可以由玩家来改变。我们程序的第一次迭代以固定的时间间隔计算球的新位置并显示结果,它还确定球和墙何时会发生虚拟碰撞以及球如何从墙上反弹。之后,我们将看到如何用图像替换球,以及如何使用渐变绘制矩形。我们将研究用于验证表单输入的 HTML5 特性。最后,我将向您展示一个交互式示例,它为玩家提供了一种停止和重新开始弹跳的方法。这四个例子是:
-
在 2D 盒子中弹跳的球(见图 3-1
-
用一个图像替换球,并对盒子壁使用渐变(见图 3-2
-
验证输入(参见图 3-3
-
使图像在背景图像上反弹,并提供一种停止和恢复动作的方法(参见图 3-4
注意
我们要制作的这种动画叫做计算动画,在这种动画中,一个物体的位置被计算机程序重新计算,然后这个物体被重新显示。这与 cel(或逐帧)动画形成对比,后者使用预先绘制的单独静态图片。动画 gif 是 cel 动画的例子,可以在许多图形程序中制作。
你必须想象这些静态图片所代表的动画。在图 3-1 中,注意带有设置水平和垂直速度字段的表格。
图 3-1
弹跳球
在图 3-2 中,球已经被一个图像取代,墙壁也使用渐变填充。
图 3-2
球现在是来自外部文件的图像
HTML5 让您指定输入应该是什么。在这个例子中,我已经指定输入应该是一个数字,并指出最小和最大值。我使用 CSS 来指定如果用户输入了一个无效的条目,字段的颜色会变成红色。如图 3-3 所示。
图 3-3
显示错误输入的表单
这组应用程序演示了大量的编程,但它并不是真正的游戏,尽管人们喜欢看到头部或其他图像在盒子中跳动。受到最近的一张家庭照片的启发,我决定制作一个程序,它有一张跳动的照片,并带有停止和恢复动画的附加功能。我还包括显示背景图片的功能。图 3-4 为一张截图。游戏的目标是让移动的物体,一张棉花糖的照片,停下来靠近孩子,安妮卡,脸上涂着代表熊猫的颜料。见图 3-4 。这为我提供了一个例子来展示所谓的事件驱动编程的优势。
图 3-4
弹跳棉花糖游戏截图
关键要求
在开始编写任何代码之前,定义需求对于这个应用程序,实际上对于所有的编程都是非常重要的。该应用程序需要我在前面章节中演示过的东西:在画布元素上绘制形状和使用表单画布元素。在这个例子中,我们实际上将使用表单字段进行输入。在第二章描述的骰子游戏中,它们被严格用于输出。
在第一章中,HTML 文档使用了外部图像文件。在第二章中,我们完全用编码绘制了骰子的正面。在这一章中,我将演示两者:一个用代码绘制的弹跳圆圈和一个来自图像文件的弹跳图像。
为了实现这一点,我们需要一些代码能够在固定的时间间隔内做一些事情——现在,做什么并不重要。间隔需要足够短,以使结果看起来像运动。
在这种情况下,要做的事情是重新定位球,或者说球的位置。此外,代码需要确定球是否会碰到墙。现在,没有一个球,也没有任何墙壁。都是虚拟的,所以都是编码。我们将编写代码来计算球的虚拟位置和每面墙的虚拟位置。如果出现虚拟击球,代码会调整水平或垂直位移值,使球从墙上弹回。为了更准确,冒着变得迂腐的风险,代码设置了某些值,以便在下一次迭代中,球对象朝着不同的方向前进。
为了计算重新定位,我们使用初始值或者在表单的输入字段中输入的任何新值。然而,目标是产生一个健壮的系统,它不会对玩家的错误输入采取行动。错误的输入可能不是数字或超出指定范围的数字。我们可以不对错误的输入采取行动。然而,我们想给玩家反馈输入错误,所以我们会让输入框改变颜色,如图 3-3 所示。
为了给用户提供一种方式,现在称之为“播放器”,一种与应用程序交互的方式,我添加了代码来呈现播放器所看到的停止按钮和继续按钮。响应点击停止按钮的功能停止时间间隔事件。响应点击恢复按钮的函数启动时间间隔事件。
HTML5、CSS、JavaScript 特性
让我们看看实现弹跳球应用程序所需的 HTML5、CSS 和 JavaScript 的具体特性。我们将建立在前几章的基础上,特别是 HTML 文档的一般结构,使用一个canvas
元素,程序员定义的和内置的函数,以及一个form
元素。
画一个球或一幅或多幅图像
如第二章所述,在画布上绘制任何东西,比如一个代表球的圆,需要在HTML
文档的body
部分包含canvas
元素。接下来,我们需要定义一个变量ctx
,并添加设置这个变量的值的代码,这样我们就可以使用 JavaScript 了。下面是实现这一点的语句:
ctx = document.getElementById('canvas').getContext('2d');
正如我们在第二章中看到的,一个圆是通过绘制一个圆弧作为路径的一部分来创建的。下面几行代码启动路径,设置填充颜色,指定弧线,然后使用fill
方法绘制一个封闭的填充路径。注意,arc
方法使用变量来指定圆心坐标和半径。参数0
和Math.PI*2
代表角度,在本例中为0
到Math.PI*2
,形成一个完整的圆。true
参数表示逆时针方向,尽管在这种特殊情况下,false
会产生相同的效果。
ctx.beginPath();
ctx.fillStyle ="rgb(200,0,50)";
ctx.arc(ballx, bally, ballrad,0,Math.PI*2,true);
ctx.fill();
对于弹跳球的第一个版本,该框被绘制为矩形轮廓。轮廓的宽度,称为笔画,使用
ctx.lineWidth = ballrad;
你可以试试线宽。请记住,如果您将宽度设置得很小,并将球设置为快速移动,球可以一步弹过墙。
绘制矩形的语句是
ctx.strokeRect(boxx,boxy,boxwidth,boxheight);
我把球的代码放在矩形的代码之前,这样矩形就会在上面。我觉得这样跳起来更好看。
该程序的第二个版本显示了球的图像。这需要代码通过调用Image()
使用new
操作符建立一个img
对象,将它赋给一个变量,并给src
属性一个值。在应用程序中,我们在一条语句中完成了所有这些工作,但是让我们来看看各个部分。
你会在第二章中读到var
语句。这样的语句定义,或者说声明,一个变量。这里我们的var
可以用 img 这个名字;与 HTML img
元素没有冲突。new
操作符名副其实:它创建了一个新对象,在这里是内置类型Image
。Image 函数称为构造函数:它构造一个 Image 类型的对象。Image
函数没有任何参数,所以只有左括号和右括号。
图像对象有属性,就像 HTML 元素如img
一样。使用的特定图像由src
属性的值指示。这里,pearl.jpg
是与 HTML 文档位于同一文件夹中的图像文件的名称。下面两条语句设置了img
变量,并将其src
(源)设置为图像文件的地址,即 URL。
var img = new Image();
img.src="pearl.jpg";
对于您的应用程序,请使用您选择的图像文件的名称。它可以是 JPG、PNG 或 GIF 类型,请确保将它放在与 HTML 文档相同的文件夹中,或者包含适当的路径。注意匹配名称和扩展名的大小写。
要在画布上绘制这个图像,我们需要一行代码来指定图像对象、图像左上角的位置以及图像显示中使用的宽度和长度。与矩形的情况一样,这段代码是对一个上下文对象的方法的调用,所以我使用 init 函数中定义的变量ctx
。我需要调整用于圆心的ballx
和bally
值来表示上角。我用两倍的球半径来表示宽度和长度。声明是
ctx.drawImage(img,ballx-ballrad,bally-ballrad,2*ballrad,2*ballrad);
现在让我们休息一下。亲爱的读者,轮到你做些工作了。考虑下面的 HTML 文档:
<html>
<head>
<title>The Origami Frog</title>
<script>
var img = new Image();
img.src = "frogface.gif";
var ctx;
function init() {
ctx =document.getElementById("canvas").getContext('2d');
ctx.drawImage(img,10,20,100,100);
}
</script>
</head>
<body onLoad="init();">
<canvas id="canvas" width="400" height="300">
Your browser doesn't support the HTML5 element canvas.
</canvas>
</body>
</html>
找到你自己的图像文件,用它的名字代替frogface.gif
。把标题改成合适的。用这条线做实验
ctx.drawImage(img,10,20,100,100);
也就是说,更改 10,20 以重新定位图像,更改 100,100 以更改宽度和高度。进行更改,看看程序是否如您所愿做出响应。请记住,当您指定宽度和高度时,您可能会改变图片的形状,即纵横比。
这里要注意的重要一点是,由于代码是在画布上绘制或绘画,为了产生移动球的效果,我们还需要代码来擦除所有内容,然后在新的位置用球重新绘制所有内容。抹去一切的声明是:
ctx.clearRect(boxx,boxy,boxwidth,boxheight);
也许可以只擦除(清除)画布的一部分,但我选择擦除然后重新绘制所有内容。在每种情况下,你需要决定什么是有意义的。
想象一下在画布上画两个图像。你需要用两个不同的变量来代替img
。对于这个任务,给变量起一个独特的名字。如果是效仿苏斯博士,可以用thing1
和thing2
;不然就选对自己有意义的吧!
为了绘制背景图像,然后是移动的棉花糖,我的代码简单地首先绘制背景图像,总是在相同的位置,然后在计算的位置绘制棉花糖。完整的代码如下。你将在后面的章节中读到关于moveandcheck
的内容。
function moveball(){
ctx.clearRect(boxx,boxy,boxwidth,boxheight);
moveandcheck();
ctx.drawImage(bkg,0,0,4000,3000,0,0,400,300);
ctx.drawImage(ball,0,0,388,435,ballx-ballrad,bally-ballrad,388/10,435/10);
ctx.strokeRect(0,0,400,300);
}
你可能会问为什么要重画背景。答案是,一旦在画布上绘制了一些东西,就有相当于颜料的点——术语是像素,图片元素——被设置为特定的颜色。每次迭代都会有一些变化(请等待下一节关于计时间隔的内容),虽然画布的大部分保持不变,但生成新图片的最佳方式是清空画布,绘制背景,然后绘制球。
梯度,附带解释数组
让我们看看如何使用渐变,一种彩虹般的颜色组合,为弹跳程序。您可以使用渐变来设置fillStyle
属性。我不想把球放在一个填充的矩形上面,所以我需要弄清楚如何分别画出四面墙。
渐变是 HTML5 中的一种对象类型。有线性梯度和径向梯度。在这个应用中,我们使用线性梯度。代码使用我们之前用变量ctx
定义的 canvas 上下文的方法,将变量定义为渐变对象。渐变的代码如下所示:
var grad;
grad=ctx.createLinearGradient(boxx,boxy,boxx+boxwidth,boxy+boxheight);
渐变在矩形上延伸。
渐变包含多组颜色。一个典型的做法是编写代码来设置所谓的颜色停止点,例如使渐变成为彩虹。为此,我在一个名为hue
的变量中设置了一个数组的数组。
您可以将数组视为值集合的容器。一个变量只能保存一个值,而一个数组可以保存多个值。在下一章,你将读到一个名为everything
的数组,它将保存所有要在屏幕上绘制的对象。在描述刽子手游戏的第九章中,单词列表是一个单词数组。在这本书里,你会读到数组的许多应用。这里有一个具体的例子。下面的var
语句将一个变量设置为一个特定的数组:
var family = ["Daniel","Aviva", "Annika"];
变量family
是一个数组。它的数据类型是数组。它包括一份我家的名单。要访问或设置这个数组的第一个元素,可以使用family[0]
。指定数组特定成员的值称为索引值或索引。数组索引从零开始。family[0]
这个表达会产生Daniel"
。表达式family[1]
会产生"Aviva"
。表达式family[2]
会产生"Annika"
。如果变量relative
的值为 1,那么family[relative]
将产生Aviva
。要确定数组中元素的数量,可以使用family.length
。在本例中,长度为 3。注:长度为 3;指数从 0 到 2。
数组中的各个项可以是任何类型,包括数组。例如,我可以修改族数组以提供更多信息:
var family = [["Daniel","son"],
["Aviva", "daughter"],
["Annika","granddaughter"]
];
带有换行符和缩进的格式不是必需的,但这是一个好习惯。它不由 JavaScript 解释。我们必须把括号和逗号弄正确!
family[2][1]
这个表达产生了“孙女”。请记住:数组索引从 0 开始,因此数组的索引值 2(在这种类型的示例中有时称为外部数组)产生["Annika “,“孙女”],对于该数组,索引 1 产生"孙女”。这些内部数组不必长度相同。考虑这个例子:
var family = [["Daniel","teacher"],
["Aviva", "government staff"],
["Annika"]
];
代码将检查数组的长度,如果它是 2 而不是 1,第二项将是个人的职业。如果内部数组的长度为 1,则可以认为这个人没有职业。
数组的数组对于产品名称和成本非常有用。以下语句指定了商店非常有限的库存:
var inventory = [
["toaster",25.99],
["blender",74.99],
["dish",10.50],
["rug",599.99]
];
这家商店有四种商品,最便宜的是盘子,在索引 2 的位置表示,最贵的是地毯,在索引 3。
现在,让我们看看如何使用这些概念来定义梯度。我们将使用一个数组,它的单个元素也是数组。
每个内部数组保存一种颜色的 RGB 值,即红色、黄色、绿色、青色、蓝色和品红色。
var hue = [
[255, 0, 0 ],
[255, 255, 0 ],
[ 0, 255, 0 ],
[ 0, 255, 255 ],
[ 0, 0, 255 ],
[255, 0, 255 ]
] ;
这些值代表从红色(RGB 255,0,0)到洋红色(RGB 255,0,255)的颜色,其间指定了四种颜色。JavaScript 中的渐变功能填充颜色以产生彩虹图案,如图 3-2 所示。通过沿从 0 到 1 的间隔指定点来定义渐变。您可以指定彩虹以外的渐变。例如,您可以使用图形程序选择一组 RGB 值作为所谓的停止点,JavaScript 将填充值以从一个值混合到下一个值。
数组数值并不是我们所需要的,所以我们必须处理它们来产生 JavaScript 所需要的东西。
数组的操作通常需要对数组的每个成员做一些事情。许多编程语言中都有这样的一个构造,那就是for
循环,它使用一个叫做索引变量的变量。for
回路的结构是
for (initial value for indexing variable; condition for continuing; change for➥
indexing variable) {
code to be done every time. The code usually references the indexing variable
}
这里说:从这个初始值开始;只要这个条件成立,就继续做这个循环;并以这种指定的方式更改索引值。一个典型的变化表达式将使用运算符,如++
。++
操作员将指示变量增加 1。典型的for
标题语句是
for (n=0;n<10;n++)
这个for
循环使用一个名为n
的变量,其中n
被初始化为0
。如果n
的值小于 10,则执行循环内部的语句。每次迭代后,n
的值增加 1。在这种情况下,循环代码将被执行 10 次,n 保存值 0、1、2,一直到 9。
这里还有一个例子,一个演示数组的常见例子。让grades
变量被设置为保存一组学生的成绩:
var grades = [4.0, 3.7, 3, 2.3, 3];
根据学校的不同,这可能表示 A、A-、B、C+和 B 级。下面的代码片段计算平均绩点,并将其存储在名为gpa
的变量中。注意,我们需要初始化名为sum
的变量,以 0 值开始。+=
运算符将索引值g
处的grades
数组中的值加到sum
中保存的值上。
var sum = 0;
for (g=0;g<grades.length;g++) {
sum += grades[g];
}
var gpa;
gpa = sum/grades.length;
为了生成构建渐变所需的内容,代码从hue
数组中提取值,并使用它们生成表示 RGB 值的字符串。我们使用hue
数组和一个名为color
的变量来设置色标以定义渐变。使用一个for
循环将color
设置为所需格式的字符串,即从rgb(
开始,包括三个值,将色标设置在 0 和 1 之间的任意点。
for (h=0;h<hue.length;h++) {
color = 'rgb('+hue[h][0]+','+hue[h][1]+','+hue[h][2]+')';
grad.addColorStop(h*1/hue.length,color);
}
赋值语句设置color
可能对你来说很奇怪:有很多事情正在进行——那些加号在做什么?记住,我们的任务是生成表示特定 RGB 值的字符串。加号不是在这里表示数字的相加,而是字符串的连接。这意味着这些值粘在一起,而不是数学相加,所以当5+5
产生 10,'5'+'5'
将给出 55。因为第二个例子中的 5 是用引号括起来的,所以它们是字符串而不是数字。方括号表示数组的成员。JavaScript 将数字转换成等价的字符串,然后组合它们。请记住,它查看数组中的数组,因此方括号中的第一个数字(在本例中,由我们的变量h
提供)给出了第一个数组,方括号中的第二个数字给出了我们在该数组中的数字。让我们看一个简单的例子。我们的循环第一次运行时,h
的值将是 0,这给了我们hue
数组中的第一个条目。然后,我们查找该条目的各个部分,以构建我们的最终颜色。
在这之后,我们的代码已经设置了变量grad
用于指示填充模式。代码没有将fillStyle
设置为一种颜色,而是将其设置为变量grad
。
ctx.fillStyle = grad;
画矩形和以前一样,但是现在用指定的填充。这是位于原始矩形的左、右、上、下的四面窄墙。我把墙做得和球的半径一样厚。该厚度在垂直壁的情况下是宽度,在水平壁的情况下是高度。
ctx.fillRect(boxx,boxy,ballrad,boxheight);
ctx.fillRect(boxx+boxwidth-ballrad,boxy,ballrad,boxheight);
ctx.fillRect(boxx,boxy,boxwidth,ballrad);
ctx.fillRect(boxx,boxy+boxheight-ballrad,boxwidth,ballrad);
设置定时事件
在 HTML5 中设置定时事件实际上类似于在旧版本的 HTML 中的做法。内置函数有两个:setInterval
和setTimeout
。我们将在第五章的记忆游戏中,在这里和setTimeout
处查看setInterval
。每个函数都有两个参数。请记住,参数是函数或方法调用中包含的额外信息。回到第一章,我们看到document.write
将屏幕上显示的内容作为它的唯一参数。
我先描述第二个论点。第二个参数以毫秒为单位指定时间量。一秒有 1000 毫秒。这似乎是一个非常短的工作单元,但它正是我们想要的游戏。对于电脑游戏来说,一秒(1000 毫秒)相当长。
第一个参数指定在第二个参数指定的时间间隔内要做什么。第一个参数可以是函数的名称。对于该应用程序,init
函数定义包含以下代码行:
setInterval(moveball,100);
这告诉JavaScript engine
每 100 毫秒调用函数moveball
(每秒 10 次)。moveball
是将在此 HTML 文档中定义的函数的名称;是定时间隔事件的事件处理程序。在编写代码定义函数之前,不要担心您是否编写了这行代码。重要的是应用程序运行时存在的内容。
JavaScript 还为事件处理程序提供了一种不同于函数名的方式。你可以写作
setInterval("moveball();",100);
为了同样的效果。换句话说,对于动作是不带参数的函数调用的情况,函数名就可以了。对于更复杂的情况,您可以编写一个字符串来指定代码。假设我有一个名为slide
的函数,它本身有一个参数,我希望这个参数是变量d
值的 10 倍,我希望每 1 . 5 秒发生一次,我会编写代码
setInterval("slide(10*d);",1500);
我注意到moveball
不需要参数的原因是因为位置和位移使用了全局变量。
通常情况下,您希望在屏幕上显示时间的流逝。下面的例子将显示 0,1,…等等。数字每秒都在变化。
<html>
<head>
<title>elapsed</title>
<script>
function init() {
setInterval(increase,1000);
}
function increase() {
document.f.secs.value = String(1+Number(document.f.secs.value));
}
</script>
</head>
<body onLoad="init();">
<form name="f">
<input type="text" name="secs" value="0"/>
</form>
</body>
</html>
这是一个花时间编写和运行的好例子,因为它展示了计时事件,也因为它让你知道一秒钟持续了多长时间。代码以名为f
的形式从secs
输入字段中取出值,将该值转换成一个数字,将1
加到该数字上,然后将其转换回一个字符串,作为secs
元素的value
。尝试用语句替换increase
函数中的单个语句
document.f.secs.value = 1+document.f.secs.value;
看看会发生什么。这是数字和字符串区别的一课。请随便举个小例子。如果您想让数字以较小的增量上升,请将 1000 更改为 250,将 1 更改为 0.25。这使得脚本显示四分之一秒的变化。
如果你想让你的代码停止一个特定的事件,你可以设置一个全局变量(在任何函数之外的变量)。我使用一个名为tev
的变量,这是我对定时事件的简写。
var tev;
然后,您可以将setInterval
调用修改为:
tev = setInterval(moveball,100);
当您想要停止此事件时,您应该包括以下代码:
clearInterval(tev);
顺便说一下,如果我的代码在没有发出clearInterval
的情况下再次调用带有setInterval
函数的语句,就相当于设置了一个额外的闹钟。效果将是提高速度。当我描述棉花糖游戏时,你会注意到我的代码包含了多个clearInterval
语句。
重申一下,setInterval
函数设置一个定时事件,该事件会一直发生,直到被清除。如果您知道您希望一个事件只发生一次,那么setTimeout
方法正好设置一个事件。您可以使用任何一种方法来产生相同的结果,但是 JavaScript 同时提供了这两种方法来使事情变得更简单。
对于弹跳球应用程序,moveball
函数计算球的新位置,进行计算以检查碰撞,当碰撞发生时,重定向球并绘制新的显示。这是一次又一次的重复——对moveball
的调用不断发生,因为我们使用了setInterval
。
计算新位置和碰撞检测
既然我们知道了如何绘制,如何清除和重绘,以及如何在固定的时间间隔做一些事情,那么挑战就是如何计算新的位置以及如何进行碰撞检测。我们将通过声明变量ballx
和bally
来保存球中心的x
和y
坐标;ballvx
和ballvy
保持球位要改变的量;以及boxboundx
、inboxboundx
、boxboundy
和inboxboundy
来表示比用于碰撞计算的实际盒子稍小的盒子。球位置的变化量被初始化为 4 和 8(完全任意),当球员做出有效的改变(见下一节)并点击改变按钮时,球位置的变化量被改变。这些量被称为位移或增量,以及不太正式的速度或速率。
在这种情况下,方向的改变非常简单。如果球“击中”一面垂直的墙,水平位移必须改变符号;也就是说,如果球向右移动了四个单位,我们撞到了一堵墙,我们要开始给它的位置加-4,这将使它向左移动。垂直位移保持不变。通过将下一个水平值与边界进行比较来确定命中。类似地,如果通过将垂直位置与适当的边界进行比较来确定球“击中”了水平墙,则垂直位移改变符号,而水平位移保持不变。变化是为了下一次迭代。碰撞检查进行了四次,即针对四面墙中的每一面。计算包括将建议的新 x 或 y 值(如果适用)与特定墙的边界条件进行比较。如果球的中心经过四面墙中的一面,正好在边界上,那么暂定的新位置将被调整。这样做的效果是使球稍稍移动到每面墙的后面,或者看起来被每面墙挤压。边界值设置在方框内,上角为boxx
、boxy
,宽度为boxwidth
,高度为boxheight
。我可以用更复杂的计算来比较圆上的任何一点和墙上的任何一点。然而,这里涉及到一个更基本的原则。没有墙,也没有球。这是基于计算的模拟。计算每隔一段时间进行一次。如果球移动得足够快,并且壁足够薄,比这里指定的ballrad
更薄,球可以逃出盒子。这就是为什么我用下一步棋和一个稍微小一点的盒子来计算。
var boxboundx = boxwidth+boxx-ballrad;
var boxboundy = boxheight+boxy-ballrad;
var inboxboundx = boxx+ballrad;
var inboxboundy = boxy+ballrad;
下面是moveandcheck
函数的代码,该函数检查碰撞并重新定位球:
function moveandcheck() {
var nballx = ballx + ballvx;
var nbally = bally +ballvy;
if (nballx > boxboundx) {
ballvx =-ballvx;
nballx = boxboundx;
}
if (nballx < inboxboundx) {
nballx = inboxboundx
ballvx = -ballvx;
}
if (nbally > boxboundy) {
nbally = boxboundy;
ballvy =-ballvy;
}
if (nbally < inboxboundy) {
nbally = inboxboundy;
ballvy = -ballvy;
}
ballx = nballx;
bally = nbally;
}
你可能会说这里实际发生的事情不多,你是对的。变量ballx
和bally
被修改,以便稍后在画布上绘制时使用。
从这段代码中并不明显,但是请记住,垂直值(y 值)随着屏幕的向下移动而增加,水平值(x 值)随着从左向右移动而增加。
确认
表单,从用户/玩家/客户端获取输入的方式,是原始 HTML 的一部分。表单元素以一个<form>
标签开始,它提供了一种指定提交表单时的动作的方法,并包含输入元素。HTML5 为验证表单输入提供了新的工具。表单的创建者可以指定输入域的类型是number
而不是text
,HTML5 会立即检查用户/玩家输入的数字。类似地,我们可以指定max
和min
值。该表单的代码是
<form name="f" id="f" onSubmit="return change();">
Horizontal velocity <input name="hv" id="hv" value="4" type="number" min="-10" max="10" />
<br>
Vertical velocity <input name="vv" id="vv" value="8" type="number" min="-10" max="10"/>
<input type="submit" value="CHANGE"/>
</form>
输入仍然是文本,即一个字符串,但值是可以解释为指定范围内的数字的文本。
其他类型的输入包括"email"
和"URL"
,让 HTML5 检查这些输入非常方便。当然,您可以使用isNumber
和更复杂的编码来检查任何字符串,看看它是否是一个数字,包括正则表达式(可以匹配的字符模式),以检查有效的电子邮件地址和 URL。检查电子邮件地址的一个常用策略是让用户输入两次,这样你就可以比较两次,确保用户没有犯任何错误。
我们希望利用 HTML5 将为我们做的工作,但我们也希望让用户/玩家知道是否有问题。通过为有效和无效输入指定样式,您可以使用 HTML5 和 CSS 来做到这一点。
input:valid {background:green;}
input:invalid {background:red;}
HTML5 验证在最新版本的浏览器中是有效的,至少在电脑上是有效的,但是你需要决定你想为老版本的浏览器和设备做什么。如果您使用兼容的浏览器,比如 Chrome,您可以测试下一节给出的例子。请注意,即使在指定了数字的地方输入了无效的值,例如“abc ”,球也会一直反弹,因为程序会继续使用当前的设置。
小费
在任何应用程序中,验证输入并向用户提供适当的反馈都是非常重要的。HTML5 提供的新特性之一是 input 元素中的 pattern 属性,其中一种称为正则表达式的特殊语言可以用来指定有效的输入。将“HTML5 正则表达式”放入搜索字段,以查找最新信息。
停止和恢复由按钮触发的动画
当我决定添加停止和恢复时,我认为一个重要的教训是这可能只是一个添加,对程序的其余部分没有改变。这里发生的事情有一个术语叫做事件驱动编程。我们,建筑者,或多或少清楚地思考不同的事件。我还决定使用按钮元素,这是 HTML5 中引入的一个特性。button 元素提供了一种方式来指定事件,在本例中是onClick
,以及将处理该事件的函数。标签<button>
和标签</button>
之间的文本显示在菱形按钮中。旧的方法是使用表单,对于我的例子来说,这意味着多个表单。
下面的代码生成了这两个按钮。下一节描述了return
语句的重要性。
是所谓的实体,产生一个空格,但不强制换行。
<button onClick="return stopcc();">STOP </button>
<button onClick="return resume();">RESUME </button>
我现在欠你stopcc
函数和resume
函数的定义。
stopcc
功能的任务是停止背景上棉花糖图像的移动。您知道如何做:调用clearInterval
。我的代码确实需要做更多的事情。因为我想要恢复弹跳,所以我编写代码来保存ballvx
和ballvy
值。这可能是不必要的,但某些情况下似乎需要这样做。代码还调用moveBall
生成另一张图片。下一节将解释return
的使用。代码如下。
function stopcc() {
clearInterval(tid);
stoppedx = ballvx;
stoppedy = ballvy;
moveBall();
return false;
}
resume
函数确实包含了对setInterval
的调用,但是我需要做一些别的事情来保护玩家免受他们自己的伤害。如果玩家没有停止动画就点击了恢复按钮,或者只是想看看会发生什么,那么调用多个setInterval
会产生多个计时事件。这反过来会使弹跳越来越快。为了展示这一点,我插入了对clearInterval
的调用。如果没有合适的定时事件,什么都不会发生。我的代码使用之前保存的值重置ballvx
和ballvy
。这可能没有必要,但这是一种预防措施。
function resume(){
clearInterval(tid);
ballvx = stoppedx;
ballvy = stoppedy;
tid = setInterval(moveball,100);
return false;
}
HTML 重新载入页面
在继续之前,我想提及一些可能会导致意外问题的问题。浏览器带有重新加载/刷新按钮。当单击按钮时,文档被重新加载。我们在第二章中的简单掷骰应用中使用了这个。然而,有时您可能想要阻止重新加载,在这种情况下,您可以在没有任何内容可返回的函数中放置一个return (false);
来阻止页面重新加载。
当文档有表单时,重新加载并不总是重新初始化表单输入。您可能需要离开该页面,然后使用完整的 URL 重新加载它。
最后,浏览器试图使用先前下载到客户端(用户)计算机的文件,而不是基于日期和时间的检查从服务器请求文件。客户端计算机上的文件存储在所谓的缓存中。如果您认为自己做了更改,但浏览器没有显示最新版本,您可能需要采取清除缓存等措施。
预加载图像
计算机是如此之快,而一般来说,我们的感知是足够慢的,以至于我们做任何事情都不会有延迟。然而,网站上的图像必须从服务器下载到我们的本地计算机,大图像显然是大文件。实际上,我应该提出另一个观点。我们的现代相机产生由数千个像素组成的图像,这被称为高分辨率。这会使文件变大。为了确保图像可以使用,一个技巧是创建在body
元素中保存图像的img
元素。对于这个例子,这包括背景照片和棉花糖照片。在通过body
标签中的onLoad
属性的动作调用init
功能之前,文件将被加载。在棉花糖图像被绘制在其上之前,完全加载的背景图像将可被绘制。挑战在于如何防止这两幅图像被显示出来。答案是在style
元素中包含以下指令。
img {visibility: hidden;}
CSS 指令阻止任何img
文件被显示。在我的例子中,img
元素从不显示。显示的是由代码创建和操作的Image
元素。
构建应用程序并使之成为您自己的应用程序
我现在将解释基本弹跳球应用程序的代码;使用球的图像和墙壁的渐变的应用程序;验证输入和弹跳棉花糖的应用程序。表 3-1 显示了所有的函数调用和被调用的内容。该表包括所有四个应用程序的函数。stopcc
和resume
功能只出现在第四个应用程序中。
表 3-1
弹跳球应用中的函数
|功能
|
调用方/被调用方
|
打电话
|
| — | — | — |
| init
| body
标签中onLoad
的动作 | moveball
|
| moveball
| 由init
和setInterval
的动作直接调用 | moveandcheck
|
| moveandcheck
| 由moveball
调用 | |
| change
| 由form
标签中的onSubmit
动作调用 | |
| stopcc
| 由button
标签中的onClick
动作调用 | moveBall
|
| resume
| 由button
标签中的onClick
动作调用 | |
moveandcheck
代码可以是moveball
函数的一部分。我选择将它分开,因为定义执行特定操作的函数是一个很好的实践。通常,在开发应用程序时,更多更小的功能比更少更大的功能更好。顺便说一下,当你自己编程时,不要忘记在代码中加入注释,如第二章所述。并添加空行以使代码可读性更好。表 3-2 显示了基本弹跳球应用程序的代码,并解释了每一行的作用。
表 3-2
弹跳球应用程序
|密码
|
说明
|
| — | — |
| <html>
| 开始html
。 |
| <head>
| 开始head
。 |
| <title>Bouncing Ball``with inputs</title>
| 完成title
元素。 |
| <style>
| 开始style
。 |
| form {
| 开始表单样式。 |
| width:330px;
| 设置width
。 |
| margin:20px;
| 设置margin
。 |
| background-color:brown;
| 设置背景color
。 |
| padding:20px;
| 设置内部padding
。 |
| }
| 关闭此样式。 |
| </style>
| 关闭样式元素。 |
| <script type="text/javascript">
| 开始script
元素。(类型不是必需的。我在这里展示它只是为了让你知道你会在网上的许多例子中看到什么。) |
| var boxx = 20;
| 盒子上角的 x 位置。 |
| var boxy = 30;
| 盒子上角的 y 位置。 |
| var boxwidth = 350;
| 方框宽度。 |
| var boxheight = 250;
| 框高。 |
| var ballrad = 10;
| 球的半径。 |
| var boxboundx =``boxwidth+boxx-ballrad;
| 右边界。 |
| var boxboundy =``boxheight+boxy-ballrad;
| 底部边界。 |
| var inboxboundx =``boxx+ballrad;
| 左边界。 |
| var inboxboundy =``boxy+ballrad;
| 顶部边界。 |
| var ballx = 50;
| 球的初始 x 位置。 |
| var bally = 60;
| 球的初始 y 位置。 |
| var ctx;
| 保存画布上下文的变量。 |
| var ballvx = 4;
| 初始水平位移。 |
| var ballvy = 8;
| 初始垂直位移。 |
| function init() {
| 开始init
功能。 |
| ctx = document.getElementById``('canvas').getContext('2d');
| 设置ctx
变量。 |
| ctx.linewidth = ballrad;
| 设置线条宽度 |
| ctx.fillStyle ="rgb(200,0,50)";
| 设置填充样式 |
| moveball();
| 第一次调用moveball
功能,移动、检查并显示球。 |
| setInterval(moveball,100);
| 设置定时事件。 |
| }
| 关闭init
功能。 |
| function moveball(){
| 开始moveball
功能。 |
| ctx.clearRect(boxx,boxy,``boxwidth,boxheight);
| 清除(擦除)框(包括球上的任何油漆)。 |
| moveandcheck();
| 做检查并移动球。 |
| ctx.beginPath();
| 开始路径。 |
| ctx.arc(ballx, bally, ballrad,0,Math.PI*2,true);
| 设置在球的当前位置画圆。 |
| ctx.fill();
| 填写路径;也就是画一个实心圆。 |
| ctx.strokeRect(boxx,boxy,``boxwidth,boxheight);
| 绘制矩形轮廓。 |
| }
| 关闭moveball
。 |
| function moveandcheck() {
| 开始moveandcheck
。 |
| var nballx = ballx + ballvx;
| 设置暂定的下一个 x 位置。 |
| var nbally = bally +ballvy;
| 设置暂定的下一个 y 位置。 |
| if (nballx > boxboundx) {
| 这个 x 值超出右墙了吗? |
| ballvx =-ballvx;
| 如果是,改变水平位移。 |
| nballx = boxboundx;
| 将下一个 x 精确地设置在这个边界上。 |
| }
| 关闭子句。 |
| if (nballx < inboxboundx) {
| 这个 x 值小于左边界吗? |
| nballx = inboxboundx;
| 如果是这样,将 x 值设置为正好在边界处。 |
| ballvx = -ballvx;
| 改变水平位移。 |
| }
| 关闭子句。 |
| if (nbally > boxboundy) {
| y 值是否超出了底部边界? |
| nbally = boxboundy;
| 如果是这样,将 y 值设置为正好在边界处。 |
| ballvy =-ballvy;
| 改变垂直位移。 |
| }
| 关闭子句。 |
| if (nbally < inboxboundy) {
| y 值是否小于上边界? |
| nbally = inboxboundy;
| 如果是这样,将 y 值设置为边界。 |
| ballvy = -ballvy;
| 改变垂直位移。 |
| }
| 关闭子句。 |
| ballx = nballx;
| 将 x 位置设置为nballx
。 |
| bally = nbally;
| 将 y 位置设置为nbally
。 |
| }
| 关闭moveandcheck
功能。 |
| function change() {
| 开始change
功能。 |
| ballvx = Number(document.f.hv.value);
| 将输入转换成数字并分配给ballvx
。 |
| ballvy = Number(document.f.vv.value);
| 将输入转换成数字并分配给ballvy
。 |
| return false;
| 返回false
以确保没有页面重载。 |
| }
| 关闭功能。 |
| </script>
| 关闭脚本。 |
| </head>
| 关闭头部。 |
| <body onLoad="init();">
| 开始body
元素。设置对init
功能的调用。 |
| <canvas id="canvas" width=``"400" height="300">
| canvas
元素的开始。 |
| Your browser doesn't support the``HTML5 element canvas.
| 针对不兼容浏览器的消息。 |
| </canvas>
| 关闭canvas
元素。 |
| <br/>
| 换行。 |
| <form name="f" id="f" onSubmit=``"return change();">
| 表单开始。给出名称和 id(某些浏览器可能需要)。在提交按钮上设置操作。 |
| Horizontal velocity <input name="hv"``id="hv" value="4" type="number"``min="-10" max="10" />
| 标记水平速度的输入字段。 |
| <br>
| 换行。 |
| Vertical velocity <input name=``"vv" id="vv" value="8" type="number"``min="-10" max="10"/>
| 标记垂直速度的输入字段。 |
| <input type="submit" value="CHANGE"/>
| 提交按钮。 |
| </form>
| 关闭form
。 |
| </body>
| 关闭body
。 |
| </html>
| 关闭html
。 |
使用图像作为球和渐变填充墙的应用程序非常相似。表 3-3 显示了所有的代码——但是我只是注释了不同的代码。我没有偷懒;这个想法是让你看到每个应用程序是如何建立在前一个之上的。
表 3-3
第二个应用程序,使用图像作为球和渐变填充的墙
|密码
|
说明
|
| — | — |
| <html>
| |
| <head>
| |
| <title>Bouncing Ball with inputs</title>
| |
| <style>
| |
| form {
| |
| width:330px;
| |
| margin:20px;
| |
| background-color:#b10515;
| |
| padding:20px;
| |
| }
| |
| </style>
| |
| <script type="text/javascript">
| |
| var boxx = 20;
| |
| var boxy = 30;
| |
| var boxwidth = 350;
| |
| var boxheight = 250;
| |
| var ballrad = 20;
| 这不是一个实质性的变化,但图片需要一个更大的半径。 |
| var boxboundx = boxwidth+boxx-ballrad;
| |
| var boxboundy = boxheight+boxy-ballrad;
| |
| var inboxboundx = boxx+ballrad;
| |
| var inboxboundy = boxy+ballrad;
| |
| var ballx = 50;
| |
| var bally = 60;
| |
| var ballvx = 4;
| |
| var ballvy = 8;
| |
| var img = new Image();
| 将img
变量定义为一个Image
对象。这就是new
操作符和对Image
函数的调用所做的事情。 |
| img.src="pearl.jpg";
| 将该图像的src
设置为"pearl.jpg"
文件。 |
| var ctx;
| |
| var grad;
| 将grad
设置为变量。它将在init
函数中被赋值。 |
| var color;
| 用于设置梯度grad
。 |
| var hue = [
| 用于设置梯度grad
。这是一个数组的数组,每个内部数组提供 RGB 值。 |
| [255, 0, 0 ],
| 红色。 |
| [255, 255, 0 ],
| 黄色。 |
| [ 0, 255, 0 ],
| 绿色。 |
| [ 0, 255, 255 ],
| 青色。 |
| [ 0, 0, 255 ],
| 蓝色。 |
| [255, 0, 255 ]
| 紫色(洋红色)。 |
| ];
| 关闭数组。 |
| function init(){
| 用于设置渐变。 |
| var h;
| |
| ctx = document.getElementById('canvas').``getContext('2d');
| |
| grad = ctx.createLinearGradient(boxx,boxy,``boxx+boxwidth,boxy+boxheight);
| 创建并分配一个渐变值。 |
| for (h=0;h<hue.length;h++) {
| 开始for
循环。 |
| color = 'rgb('+hue[h][0]+','``+hue[h][1]+','+hue[h][2]+')';
| 将color
设置为表示 RGB 值的字符串。 |
| grad.addColorStop(h*1/hue.length,color);
| 设置色标来定义渐变。 |
| }
| 关闭for
回路。 |
| ctx.fillStyle = grad;
| 将填充设置为grad
。 |
| ctx.lineWidth = ballrad;
| |
| moveball();
| |
| setInterval(moveball,100);
| |
| }
| |
| function moveball(){
| |
| ctx.clearRect(boxx,boxy,boxwidth,boxheight);
| |
| moveandcheck();
| |
| ctx.drawImage(img,ballx-ballrad,``bally-ballrad,2*ballrad,2*ballrad);
| 画个像。 |
| ctx.fillRect(boxx,boxy,ballrad,boxheight);
| 画左边的墙。 |
| ctx.fillRect(boxx+boxwidth-ballrad,boxy,ballrad,boxheight);
| 画右边的墙。 |
| ctx.fillRect(boxx,boxy,boxwidth,ballrad);
| 画顶墙。 |
| ctx.fillRect(boxx,boxy+boxheight-ballrad,boxwidth,ballrad);
| 画底墙。 |
| }
| |
| function moveandcheck() {
| |
| var nballx = ballx + ballvx;
| |
| var nbally = bally +ballvy;
| |
| if (nballx > boxboundx) {
| |
| ballvx =-ballvx;
| |
| nballx = boxboundx;
| |
| }
| |
| if (nballx < inboxboundx) {
| |
| nballx = inboxboundx;
| |
| ballvx = -ballvx;
| |
| }
| |
| if (nbally > boxboundy) {
| |
| nbally = boxboundy;
| |
| ballvy =-ballvy;
| |
| }
| |
| if (nbally < inboxboundy) {
| |
| nbally = inboxboundy;
| |
| ballvy = -ballvy;
| |
| }
| |
| ballx = nballx;
| |
| bally = nbally;
| |
| }
| |
| function change() {
| |
| ballvx = Number(document.f.hv.value);
| |
| ballvy = Number(document.f.vv.value);
| |
| return false;
| |
| }
| |
| </script>
| |
| </head>
| |
| <body onLoad="init();">
| |
| <canvas id="canvas" width=``"400" height="300">
| |
| This browser doesn't support``the HTML5 canvas element.
| |
| </canvas>
| |
| <br/>
| |
| <form name="f" id="f" onSubmit=``"return change();">
| |
| Horizontal velocity <input name=``"hv" id="hv" value="4" type=``"number" min="-10" max="10" />
| |
| <br>
| |
| Vertical velocity <input name=``"vv" id="vv" value="8" type=``"number" min="-10" max="10"/>
| |
| <input type="submit" value="CHANGE"/>
| |
| </form>
| |
| </body>
| |
| </html>
| |
我选择在第一个应用程序中对风格信息构建进行适度的更改。表 3-4 显示了第三个弹跳球应用,带有表单验证。同样,我只对新代码进行了注释,但为了完整起见,我包含了所有代码。
表 3-4
第三个弹跳球应用程序,带有表单验证
|密码
|
说明
|
| — | — |
| <html>
| |
| <head>
| |
| <title>Bouncing Ball with inputs</title>
| |
| <style>
| |
| form {
| |
| width:330px;
| |
| margin:20px;
| |
| background-color:brown;
| |
| padding:20px;
| |
| }
| |
| input:valid {background:green;}
| 为有效输入设置反馈。 |
| input:invalid {background:red;}
| 为无效输入设置反馈。 |
| </style>
| |
| <script type="text/javascript">
| |
| var cwidth = 400;
| |
| var cheight = 300;
| |
| var ballrad = 10;
| |
| var boxx = 20;
| |
| var boxy = 30;
| |
| var boxwidth = 350;
| |
| var boxheight = 250;
| |
| var boxboundx = boxwidth+boxx-ballrad;
| |
| var boxboundy = boxheight+boxy-ballrad;
| |
| var inboxboundx = boxx+ballrad;
| |
| var inboxboundy = boxy+ballrad;
| |
| var ballx = 50
;
| |
| var bally = 60;
| |
| var ctx;
| |
| var ballvx = 4;
| |
| var ballvy = 8;
| |
| function init(){
| |
| ctx = document.getElementById('canvas').``getContext('2d');
| |
| ctx.lineWidth = ballrad;
| |
| moveball();
| |
| setInterval(moveball,100);
| |
| }
| |
| function moveball(){
| |
| ctx.clearRect(boxx,boxy,boxwidth,boxheight);
| |
| moveandcheck();
| |
| ctx.beginPath();
| |
| ctx.fillStyle ="rgb(200,0,50)";
| |
| ctx.arc(ballx, bally, ballrad,0,Math.PI*2,true)
;
| |
| ctx.fill();
| |
| ctx.strokeRect(boxx,boxy,boxwidth,boxheight);
| |
| }
| |
| function moveandcheck() {
| |
| var nballx = ballx + ballvx;
| |
| var nbally = bally +ballvy;
| |
| if (nballx > boxboundx) {
| |
| ballvx =-ballvx;
| |
| nballx = boxboundx;
| |
| }
| |
| if (nballx < inboxboundx) {
| |
| nballx = inboxboundx;
| |
| ballvx = -ballvx;
| |
| }
| |
| if (nbally > boxboundy) {
| |
| nbally = boxboundy;
| |
| ballvy =-ballvy;
| |
| }
| |
| if (nbally < inboxboundy) {
| |
| nbally = inboxboundy;
| |
| ballvy = -ballvy;
| |
| }
| |
| ballx = nballx;
| |
| bally = nbally;
| |
| }
| |
| function change() {
| |
| ballvx = Number(document.f.hv.value);
| |
| ballvy = Number(document.f.vv.value);
| |
| return false;
| |
| }
| |
| </script>
| |
| </head>
| |
| <body onLoad="init();">
| |
| <canvas id="canvas" width="400" height="300">
| |
| Your browser doesn't support the HTML5 element canvas.
| |
| </canvas>
| |
| <br/>
| |
| <form name="f" id="f" onSubmit="return change();">
| |
| Horizontal velocity <input name="hv" id=``"hv" value="4" type="number" min="-10" max="10" />
| |
| <br>
| |
| Vertical velocity <input name="vv" id=``"vv" value="8" type="number" min="-10" max="10"/>
| |
| <input type="submit" value="CHANGE"/>
| |
| </form>
| |
| </body>
| |
| </html>
| |
第四个应用程序是带有弹跳棉花糖的游戏。我做的第一件事超出了 HTML/JavaScript/CSS 编程的范围。我使用 pixlr 提取了棉花糖原始图片的一部分,并使用另一张照片来填充缺失的空间。
我不打算包括棉花糖游戏的完整代码,而只是指出增加的部分。
| `` | | | `` | | | `` | | | `` | 身体标签。注意,`init`函数在所有东西都被加载时被调用,包括在` `标签中提到的图像文件。 | | `...` | | | `` | | | `...` | | | `STOP ` | 调用`stopcc`的按钮。注意使用` `定位下一个按钮。 | | `RESUME ` | 按钮来调用简历。 | | `` | | | `

有许多方法可以让你自己制作这样的应用程序。你可以选择你自己的球的图像,尝试墙壁的颜色,有或者没有渐变。您可以更改每面墙的位置和尺寸。您可以向页面添加文本和 HTML 标记。您可以更改表单的外观。
您可以包含多个球,跟踪每个球的位置。如果你决定使用两个球,你需要两组变量和两行代码,每一行都是你以前用过的。一种系统化的方法是使用编辑器中的搜索功能来查找所有的ball
实例,并且对于每一行,用两行来代替ballx
,这样就有了ball1x
和ball2x
,并且代替了var ballx = 50;
var ball1x = 50;
var ball2x = 250;
这将第二个球放在画布上 200 像素。
你还需要第二套所有墙壁的对比。
如果你想使用两个以上的球,你可以考虑使用数组。后续章节将向您展示如何处理对象集。
你也可以试着写一些代码,让球每次碰到墙的时候都慢下来。这是一个很好的效果,并且模拟了真实的物理结果。在代码中通过改变适当变量的符号来改变方向的每个地方,添加一个因子来减小绝对值。例如,如果我选择减少 10%的值,我会写
if (nballx > boxboundx) {
ballvx =-ballvx *.9;
nballx = boxboundx;
}
这意味着垂直方向的增量变化将下降到原来的 90%。
您可以使用自己的照片,使游戏更像游戏,从而在棉花糖游戏的基础上发展和/或从中获得灵感。考虑一个测试休息的地方是否足够好。限制停止和恢复动作的次数。研究本文剩余部分中的例子(并继续 HTML5 和 JavaScript 项目)来学习其他动作,比如使用鼠标或触摸。
测试和上传应用程序
第一个和第三个应用程序在 HTML 文档中完成。第二个和第四个应用程序要求图像文件存在于同一文件夹中。您可以在网上的任何地方访问文件,但您需要确保包含正确的地址。例如,如果您将 HTML 文档上传到名为mygames
的文件夹中,并将pearl.jpg
上传到名为images
的mygames
的子文件夹中,指示这一点的行必须是
img.src = "img/pearl.jpg";
您还必须使用准确的文件扩展名,如 JPG,来指示正确的文件类型。一些浏览器是宽容的,但许多不是。您可以尝试提交错误数据,并使用不同的浏览器查看响应。
摘要
在这一章中,你学习了如何创建一个基于用户输入而改变动画的应用程序。我们讨论了许多编程和 HTML5 特性,包括:
-
使用
setInterval
为动画设置一个定时事件,并使用 clearInterval 来结束该事件 -
验证表单输入
-
使用程序员定义的函数来水平和垂直地重新定位一个圆或图像,以模拟一个弹跳的球
-
虚拟碰撞测试
-
绘制矩形、图像和圆形,包括颜色渐变
-
使用按钮元素
-
确保下载图像文件
下一章描述了玩家试图击中目标的炮弹和弹弓游戏。这些应用程序使用了与我们制作动画时相同的编程和 HTML5 特性,但是更进了一步。在第八章中,你还可以看到一个石头剪子布的动画示例。