HTML5 解决方案:面向 HTML5 开发者的基本技巧(全)
原文:HTML5 Solutions Essential Techniques for HTML5 Developers
协议:CC BY-NC-SA 4.0
零、简介
超文本标记语言的发展停止于 1999 年,其最终版本 n.4 由万维网联盟(W3C)制定。然而,技术并没有停滞不前:W3C 还致力于一些有趣的项目,如 XML 的通用标准通用标记语言(SGML ),以及新的标记语言,如可伸缩矢量图形(SVG)、XForms 和 MathML。
另一方面,Web 浏览器供应商更喜欢专注于他们程序的新功能,而 web 开发人员开始学习 CSS 和 JavaScript 语言,在使用异步 JavaScript + XML (AJAX)的框架上构建他们的应用。
然而,事情已经发生了变化,最近,由于苹果、谷歌、Opera 软件和 Mozilla 基金会等公司的工作,HTML 已经起死回生,这些公司(以 WhatWG,Web 超文本应用技术工作组的名义)合作开发了旧 HTML 的更新和增强版本。
出于这种兴趣,W3C 开始开发新版本的 HTML,称为 HTML5,采用了 Web 应用 1.0 的官方名称,并向 HTML 中引入了前所未见的新结构元素。
HTML5 引入的新元素倾向于弥合由标记定义的结构之间的差距;由样式指令定义的渲染特征;和由文本本身定义的网页内容。此外,HTML5 引入了本地开放标准来交付多媒体内容,如音频和视频、协作 API、本地存储、地理位置 API 等等。
在这本以实践为导向的书中,我们想为接触新语言的人所面临的常见问题提供一系列解决方案。因此,您会发现许多现成的代码,您可以在 web 应用中构建这些代码。
这本书是给谁的?
不管你是设计师还是开发人员,这本书的目标读者是任何想马上开始使用 HTML5 的人。
HTML5 解决方案 实际上是为那些希望通过常见问题的快速解决方案和最佳实践技术来进一步提高其 HTML5 技能的读者设计的。这本书充满了真实世界的例子和代码,以支持你进入 HTML5 开发的世界。
本书使用的约定俗成
这本书使用了几个值得注意的惯例。本书中使用了以下术语:
HTML 既指 HTML,也指 XHTML 语言。
除非另有说明, CSS 涉及 CSS 2.1 规范。
现代浏览器 被认为是 Firefox、Safari、Chrome、Opera 的最新版本,还有 IE 7 及以上版本。
假设本书中的所有 HTML 示例都嵌套在有效文档的中,而 CSS 包含在外部样式表中。为了简洁起见,HTML 和 CSS 偶尔会放在同一个代码示例中。
有时代码在书中的一行是写不下的。在发生这种情况的地方,我用一个箭头来断开这条线。
这些手续办完了,让我们开始吧。
你需要什么
为了理解和创建本书中的例子,你需要一个简单的文本编辑器。TextMate、UltraEdit 和 Notepad++只是具有代码支持的强大文本编辑器的一些例子。
我的建议是使用下面的工具之一,它将允许你提高你的编码活动的生产率:
Google Web Toolkit 孵化器项目通过 GWTCanvas 这样的类支持 HTML5 的一些特性。它是完全免费的,可以从这个u[
code.google.com/p/google-web-toolkit-incubator/](http://code.google.com/p/google-web-toolkit-incubator/)
下载
Dreamweaver CS 5 的 HTML5 包扩展。它增强了 Dreamweaver CS 5,增加了对 HTML5 的完全支持。您可以从 Adobe 网站下载免费试用版[
www.adobe.com/support/dreamweaver/](http://www.adobe.com/support/dreamweaver/)
问题和联系人
请将任何关于本书的技术问题或评论发送至m.casario@comtaste.com
。
关于其他 HTML5 和 CSS 书籍的更多信息,请访问我们的网站:[www.friendsofed.com](http://www.friendsofed.com).
一、HTML5 页面结构
2004 年,一群来自苹果、Opera 和 Mozilla 的开发人员对 HTML 和 XHTML 的发展方向感到不满。作为回应,他们成立了一个名为网络超文本应用技术工作组(WHATWG)的组织。他们在 2005 年以 Web 应用 1.0 的名义发布了他们的第一个提议。2006 年,万维网联盟(W3C)决定正式支持 WHATWG,而不是继续开发 XHTML。2007 年,W3C 以 HTML5 的名称重新发布了新规范。
虽然人们认为最终规格要到 2022 年才会公布,但现在正在重新考虑这个时间表。在 2009-2010 年,人们对 HTML5 的兴趣激增,因此,越来越多的浏览器和设备支持它。
第一章将介绍 HTML5 规范中的许多新结构。此外,它将检查那些将支持新 HTML5 结构的设备。
解决方案 1-1:在 HTML5 中创建 DOCTYPE
因为 HTML 有几个版本,所以浏览器需要一个 DOCTYPE 类型来告诉它使用的是哪个版本,以及如何正确地呈现它。
在这个解决方案中,您将学习如何正确地为 HTML5 创建 DOCTYPE。
涉及到什么
在传统的 HTML 或 XHTML 文档中,DOCTYPE 标记可能如下所示:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
DOCTYPE 有许多变体。
HTML5 将 DOCTYPE 简化为:
<!DOCTYPE html>
如何建造它
打开您选择的 HTML 或文本编辑器。对于本章中显示的例子,我们使用 Dreamweaver CS5。不要使用文字处理器,因为这可能会嵌入 HTML 无法识别的额外字符。
If necessary, start a new HTML document and give it the name and location of your choice.
如果您使用像 Dreamweaver 这样的 HTML 编辑器,您可能会得到如下所示的代码:
`
Untitled Document
`
如果您的代码看起来与上面的有些不同,现在不要担心。
按如下方式更改 DOCTYPE 标签:<!DOCTYPE html>
专家提示
不要在 DOCTYPE 标记前留下任何空格。空格可能会导致浏览器呈现 HTML5 代码时出错。
解决方案 1-2:在 HTML5 中创建字符编码声明
不同的语言使用不同的字符集。这个标签声明使用哪个字符集。大多数语言最常用的字符集是 UTF-8。
在这个解决方案中,您将学习如何正确格式化 HTML5 中的字符集。
涉及到什么
在大多数 HTML 文档中,您会在开头看到以下标记:
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
HTML5 现在已经将这个标签简化为:
<meta charset="UTF-8" />
如何建造它
在解决方案 1-1 所示的<!DOCTYPE html>
标签下,键入以下内容:
<meta charset = "UTF-8" />
专家提示
虽然 UTF-8 在大多数情况下都可以工作,但是许多开发人员发现使用 ISO-8859-1 作为字符集提供了更多的灵活性。另一种字符集 UTF-16 有时会导致错误的字符,在某些情况下,会导致应用无法正常运行。
解决方案 1-3:将文档分成多个部分
在 HTML 中,将文档细分为不同部分的唯一真正方法是使用<div>
标签。HTML5 提供了一些新的选项。
在这个解决方案中,您将学习如何使用新的 HTML5 标记来创建不同的文档节。在后续的解决方案中,我们将讨论其他结构分割元素。
涉及到什么
HTML <div>
标签成功地将文档分成几个部分。但是单词<div>
在标识文档的各个部分时没有什么意义。HTML5 提供了几个新的结构元素,将文档分成有意义的部分。
这些元素中的第一个是<section></section>
标签。该元素表示文档的任何逻辑部分。这可能意味着产品描述、章节、讨论等等。虽然它的功能类似于<div>
标签,但它提供了一种更具描述性和内容敏感性的方式来划分文档。
当在 HTML5 中创建一个部分时,就像在 HTML 中使用<div>
标签一样,可以使用 id 或 class 属性。因为这两个属性可以应用于任何 HTML5 元素,所以它们被称为全局属性。每个 id 必须是唯一的,就像在 HTML 中一样,并且可以多次使用类来调用预定义的脚本或格式。
所有 HTML5 元素都有三种类型的属性:global,是所有元素共有的;特定于元素(仅适用于该元素)和事件处理程序内容属性(将根据文档中的内容触发)。随着你在本书中的进展,将会讨论其中的许多问题。
如何建造它
假设您正在创建一个关于制作芝士蛋糕的文档。以下是<section></section>
元素的典型用法。
`
The proper way to mix ingredients
When using a stand-mixer, it is important that you do not over-mix the ingredients<.../p>
Proper baking techniques
It is important that you bake your cheesecake using a lot of moisture in the oven…
`
专家提示
本章中显示的<section></section>
元素和后续结构元素的目的不是替换 HTML <div>
标签。如果你把你的文档分成逻辑文档部分,使用<section></section>
元素或者一个结构元素。然而,如果你只是为了格式化而分割文档,那么使用< div >标签是合适的。
解决方案 1-4:使部分文档可分发
越来越重要的是使页面的全部或部分内容可分发。例如,论坛讨论、博客、读者评论等等都可能成为分发或联合的对象。
在这个解决方案中,我们将讨论新的 HTML5 元素,<article></article>
,它比传统的 HTML 更容易实现。
涉及到什么
这个结构标签的目的不是作为将文档分成几个部分的另一种方式。相反,它用于标识文档中您希望独立于文档其余部分并可从文档其余部分分发的部分。
由于<article></article>
元素是独立的,它可以有自己的部分和子部分。
您可以通过用<article></article>
元素包围任何元素来使其可分发。
如何建造它
使用解决方案 1-3 中显示的示例,您可以按如下方式分发芝士蛋糕说明。
`
The proper way to mix ingredients
When using a stand-mixer, it is important that you do not over mix the ingredients…
Proper baking techniques
It is important that you bake your cheesecake using a lot of moisture in the oven…
`
专家提示
将<article></article>
元素视为独立的文档,而不是更大文档的一部分。这样,当它被分发时,它将是完全可读和可理解的。
解决方案 1-5:创建旁白
如果想在传统的 HTML 中创建一个边讨论,你使用<div>
标签并正确使用层叠样式表(CSS)进行适当定位。HTML5 通过提供一个新的结构元素<aside></aside>
使这个过程变得更加容易。像<section>
元素一样,它提供了一种更具描述性的文档分段方式。
在这个解决方案中,您将学习如何使用<aside></aside>
元素。
涉及到什么
通常,您可能想要创建通常称为边栏讨论的内容。图 1-1 显示了一个用<aside></aside>
标签完成的例子。
图 1-1。 使用<aside></aside>
元素
当然,这也可以通过使用<div>
元素来完成。然而,<aside>
< /aside >元素的使用提供了更有意义的结构描述。
如何建造它
上一节中显示的示例是用以下代码构建完成的:
`
To create a water bath, use a pan that will allow you to fill it with boiling water that goes halfway up the springform pan in which the cake is placed.
When baking a cheesecake, it is important not to over bake it. You only want to bake it until the middle has a slight wiggle, not until it is rock solid.
It is important that you use a water bath, discussed at the right, to ensure even baking of your cheesecake.
`
专家提示
<aside></aside>
元素的放置至关重要。在上面的例子中,它作为主文档的一部分。但是,如果您希望侧边栏特定于某个部分,那么您必须将它放在该部分中。如果您使用的是<article></article>
元素,这一点尤其重要,这样侧边栏就可以发布其余的相关资料。
解决方案 1-6:创建标题
使用结构化的<header></header>
元素创建一个文档或章节标题。也可以包含<h1>
到<h6>
;虽然,正如你将在本章后面看到的,它们由<hgroup></hgroup>
元素更好地服务。您还可以使用它来帮助放置徽标和章节目录。
在这个解决方案中,您将看到一个使用<header></header>
元素的例子。
涉及到什么
<header></header>
元素是创建文档和章节介绍的一种简单方法。图 1-2 显示了添加到我们例子中的<header></header>
元素。
图 1-2。 使用<header></header>
元素
注意,在这个例子中,<hr>
元素用于绘制水平线。这不是任何形式的要求。
如何建造它
以下代码用于创建图 1-2 中所示的文档。
`
To create a water bath, use a pan that will allow you to fill it with boiling water that goes halfway up the spring form pan in which the cake is placed.
When baking a cheesecake, it is important not to over bake it. You only want to bake it until the middle has a slight wiggle, not until it is rock solid.
It is important that you use a water bath, discussed at the right, to ensure even baking of your cheesecake.
`
您可以使用<header></header>
结构元素作为对文档中大多数其他结构元素的介绍。
专家提示
不能在<footer>
、<address>
或其他<header>
元素中使用<header></header>
元素。如果这样做,结果将是不正确的渲染。
方案 1-7:分组< h1 >到< h6 >元素
在某些情况下,您可能希望将<h1>
到<h6>
元素组合在一起。例如,您可能想要创建一个使用<h1>
元素的部分标题。然后,在它的正下方,放置一个使用了<h2>
元素的副标题。在 HTML5 中,您可以将<h1>
和<h2>
元素组合到一个名为<hgroup>
的新结构元素中。
在这个解决方案中,您将看到一个使用<hgroup></hgroup>
元素的例子。
涉及到什么
结构上的<hgroup></hgroup>
让你能够显示你正在将标题(<h1>
到<h6>
)组合在一起,以满足替换标题和副标题的需要。
添加到解决方案 1-6 的示例中,会出现一个<hgroup>
,如图 1-3 中的所示。
图 1-3。 使用<h 组></h 组>元素
在这种情况下,<h1>
和<h2>
标题在<hgroup>
中使用。
如何建造它
在图 1-3 所示的例子中,使用了以下代码。:
`
Cheesecake Tips and Techniques
Professional Tips
To create a water bath, use a pan that will allow you to fill it with boiling water that goes halfway up the spring form pan in which the cake is placed.
When baking a cheesecake, it is important not to over bake it. You only want to bake it until the middle has a slight wiggle, not until it is rock solid.
It is important that you use a water bath, discussed at the right, to ensure even baking of your cheesecake.
`
注意,我们将<hgroup>
元素放在了<header></header>
元素的下面。虽然这不是必需的,但是这是一个很好的实践。你可以在 HTML5 文档的任何部分使用<hgroup>
元素。
专家提示
在一个构建良好的 HTML5 文档中,<hgroup></hgroup>
元素是将各种标题和副标题联系在一起的好方法。如果您正在使用<article></article>
元素,这一点尤其正确。这样你就可以确信,任何标题及其相关的子标题都将作为一个整体移动。
解决方案 1-8:创建页脚
顾名思义,<footer></footer>
元素将为 HTML5 文档创建一个页脚——该文档的一个结构部分。页脚可以包含版权信息、作者信息、引用、隐私政策等等。
在这个解决方案中,您将检查<footer></footer>
元素是如何工作的。
涉及到什么
您可以使用结构化的<footer></footer>
元素为 HTML5 文档或该文档中的任何部分创建页脚。
基于解决方案 1-3,<footer>
元素的结果显示在图 1-4 中。
图 1-4。 使用<footer></footer>
元素
版权符号“©”以及任何关于权利和所有权的文本都放在页脚中。
如何建造它
在图 1-4 中,使用了以下代码:
`
Cheesecake Tips and Techniques
Professional Tips
To create a water bath, use a pan that will allow you to fill it with boiling water that goes halfway up the spring form pan in which the cake is placed.
When baking a cheesecake, it is important not to over bake it. You only want to bake it until the middle has a slight wiggle, not until it is rock solid.
It is important that you use a water bath, discussed at the right, to ensure even baking of your cheesecake.
© 2011 - Better Made Cheesecakes - All rights reserved `
<footer></footer>
元素既可以用于整个 HTML5 文档,就像这里一样,也可以用于文档内的结构划分。
专家提示
在<标题>元素或另一个<footer>
元素中不能使用<footer>
元素。这将导致不正确的渲染。
解决方案 1-9:在 HTML5 文档中创建导航
大多数网站都有导航链接。链接,不管是超链接还是某种按钮,通常都通过使用一个<div>
元素与文档的其余部分分开。同样,除了它是一个分区之外,它并没有将该部分标识为专门用于导航。在 HTML5 中,您现在可以在标记中标识用于导航帮助的部分。
在本节中,您将了解新的结构化 HTML5 元素:<nav></nav>
。
涉及到什么
结构元素<nav></nav>
可以用来创建一个容器来保存整个 HTML5 文档或文档中任何部分的导航元素。
结果如图 1-5 所示。
图 1-5。 使用<nav></nav>
元素
这为导航部分提供了自己的标记,使其更容易识别。
如何建造它
以下代码用于图 1-5 。所示的导航链接仅用于说明目的,没有任何功能。
`
Baking |
Ingredients |
Mixing |
Toppings
Cheesecake Tips and Techniques
Professional Tips
To create a water bath, use a pan that will allow you to fill it with boiling water that goes halfway up the spring form pan in which the cake is placed.
When baking a cheesecake, it is important not to over bake it. You only want to bake it until the middle has a slight wiggle, not until it is rock solid.
It is important that you use a water bath, discussed at the right, to ensure even baking of your cheesecake.
© 2011 - Better Made Cheesecakes - All rights reserved `
如您所见,文档中的导航元素现在有了自己易于识别的标记。它们可以放在整个 HTML5 文档中,如此处所示,也可以放在文档的任何部分中。
专家提示
你并不局限于图 1-5 中所示的超链接。在<nav></nav>
容器元素中,您可以放置任何您想要的导航工具。
第二个技巧是不要在<footer>
元素中使用<nav></nav>
容器。虽然没有严格禁止,但这可能会导致不正确的呈现。
解决方案 1-10:插入图形
在网页中插入照片、插图、图表等是相当常见的。到目前为止,开发者可以在任何需要的地方插入一个<img>
元素。现在,您可以使用 HTML5 中新的<figure></figure>
元素,使用标记来指定图片应该放在哪里。
在这个解决方案中,您将看到一个使用<figure></figure>
元素的例子。
涉及到什么
结构元素<figure></figure>
可以用来创建一个容器来保存 HTML5 文档或文档中任何部分的说明性元素。
结果如图 1-6 所示。
图 1-6。 使用<figure></figure>
元素
文档中的说明性元素现在有了自己的易于识别的标记。它可以用于文档的任何子部分。
如何建造它
以下代码用于图 1-6 。所示的导航链接仅用于说明目的,没有任何功能。
`
Baking |
Ingredients |
Mixing |
Toppings
Cheesecake Tips and Techniques
Professional Tips
To create a water bath, use a pan that will allow you to fill it with boiling water that goes halfway up the spring form pan in which the cake is placed.
When baking a cheesecake, it is important not to over bake it. You only want to bake it until the middle has a slight wiggle, not until it is rock solid.
It is important that you use a water bath, discussed at the right, to ensure even baking of your cheesecake.
© 2011 - Better Made Cheesecakes - All rights reserved `
很容易识别照片的位置,因为它现在使用<figure></figure>
元素拥有自己的容器标记。
专家提示
随着新的<figure>
元素而来的是另一个新的 HTML5 元素,叫做<figcaption> </figcaption>
。将它放在<figure>
元素中,如下所示:
<figure> <img src="cheescake.jpg" width="170" height="128" /> <figcaption>One of our many cheesecakes</figcaption> </figure>
如果你把<figcaption>
元素放在图片下面,如上面的例子所示,它会出现在右边。如果你把它放在上面,它会出现在左边。
解决方案 1-11:浏览器兼容性
本章中显示的数字来自 Safari 版本 5.0.3 和 Mozilla Firefox 版本 3.6.13。虽然它可以在所有流行的浏览器上运行,但 HTML5 的兼容性既不一致也不通用。
在本解决方案中,我们讨论如何测试浏览器兼容性,如果不兼容,如何纠正不兼容性。
涉及到什么
虽然大多数流行浏览器的当前版本能够很好地处理最新的 HTML5 规范,但对于较旧的浏览器来说却不是这样。特别值得关注的是版本 9 之前的 Internet Explorer 版本。
如何建造它
最喜欢的一个网站是:[www.findmebyip.com](http://www.findmebyip.com)
。
如图 1-7 所示,该网站将测试你的浏览器对 HTML5 的兼容性。
图 1-7。 一个[www.findmebyip.com](http://www.findmebyip.com)
的例子
如你所见,HTML5 的大部分功能在这款浏览器上都运行良好。根据您的浏览器或浏览器版本,您可能会得到不同的结果。
另一个网站[www.caniuse.com](http://www.caniuse.com)
,展示了一组全面的表格,讨论了各种浏览器与 HTML5 的兼容性。该位置如图 1-8 所示。
图 1-8。 HTML5 兼容性列于[www.caniuse.com](http://www.caniuse.com)
[www.caniuse.com](http://www.caniuse.com)
的一大特色是能够过滤信息并聚焦于你所关注的元素。还有许多与这个网站相关的讨论和教程。
使用最广泛的 HTML5/CSS 资源站点之一是[www.modernizr.com](http://www.modernizr.com)
。该位置如图 1-9 所示。
图 1-9。 首页[www.modernizr.com](http://www.modernizr.com)
该网站提供了一个强大的可下载 JavaScript 库,可以调整 HTML5 和 CSS3 代码以适应特定的浏览器。这个项目是开源的,因此完全免费。大多数使用 HTML5 的开发人员使用这个库作为检查和调整代码的手段。
专家提示
因为 HTML5 标准正在发展,我们强烈建议您不要完全修改现有的网站。相反,随着更新变得必要,开始逐渐采用 HTML5 中的元素。
总结
在本章中,我们研究了一种更精确的创建 HTML 标记的方法,而不是大量的新功能。例如,现在可以使用诸如<section>
、<nav>
、<figure>
等元素,而不是简单地使用通用的<div>
元素。早期清晰的标记有助于以后更好的控制和功能。
后续章节将研究这一新功能。从第二章的开始,你将开始看到这种增加的功能的可能性。
二、HTML5 标记
在第一章中,我们探索了许多与 HTML5 相关的新结构标签。通过使用附加的结构标签,您可以更详细、更准确地描述文档的各个部分。您还了解了许多与标记相关的属性。这些属性中有些是特定于特定标签的,有些是所有标签的全局属性。
本章回顾了你可能在 HTML 早期版本中使用过的许多标签。但是,你会看到这些熟悉的标签在 HTML5 中得到了极大的增强。在本章中,你还将学习 HTML5 如何帮助你把你的应用与外界联系起来,更重要的是,与多媒体联系起来。这里我们试着解决几个问题。
解决方案 2-1:在 HTML5 中使用< hr >标签
在以前的 HTML 版本中,<hr>
标签被严格用于在页面上创建水平线。在 HTML5 中,它在语义上发生了变化。
乍一看,<hr>
标签看起来在 HTML5 中做的事情和它在以前的 HTML 版本中做的完全一样。然而,HTML 的目的是描述文档的各个部分。之前,<hr>
标签画了一条水平线。虽然这一行很方便,但除了一条水平线之外,它实际上并不能描述文档的一部分。
W3C 在语义上改变了<hr>
标签的功能。官方说法是,它的目的现在是定义“一个部分的结束和另一个部分的开始。”这里是混乱开始的地方:正如在第一章的中所讨论的,HTML5 有一个叫做<section>
的新标签,它被设计用来分隔部分。这是开发者之间持续争论的主题。在撰写本文时,大家一致认为或许可以使用<hr>
标签来分隔一个部分中的主题。由于 HTML5 仍然是一项正在进行中的工作,而且很可能会持续很长时间,也许会有一个更最终的定义。
涉及到什么
让我们假设你有几个段落,你想用一条水平线分开。你可能希望它看起来像图 2-1 中的简单例子。
图 2-1。 使用<hr>
标签
如果你正在挠头,想知道有什么不同,你并不孤单。很多开发者都在做完全一样的事情。虽然图 2-1 看起来和以前的 HTML 版本有相同的功能,但它实际上在语义上将湿摩擦和干摩擦的主题分开了。在图 2-1 中,我们也使用它将描述从标题“摩擦”中分离出来
如何建造它
图 2-1 中所示的例子由以下代码完成:
<!DOCTYPE html> <html> <head> <title>Using the <hr> tag</title> </head> <body> <h1>How to Barbecue and Smoke Meat</h1> This page will discuss the many ways to marinate, smoke, and grill your favorite meats. <h2>Rubs</h2> <hr> Moist rubs give your meats a nice spicy and moist taste. But be ready, they are messy to eat. <hr> Dry rubs give the meat a nice smokey taste and are much easier to eat at dinner parties. </body> </html>
根据 W3C,应该用<hr>
标签而不是<p>
标签来分隔各个部分。在这个例子中,为了符合 HTML5 标准,没有使用<p>
标签。当然,<p>
标签仍然可以用来分隔一个节中的段落。
专家提示
为了适应<hr>
标签的新语义定义,您可以使用 CSS 来改变<hr>
标签产生的内容。请注意以下示例:
hr { height: 15px; background: url('decorative.gif'); no-repeat 50% 50%; }
但是,您需要对此保持谨慎。截至本文撰写之时,Safari 和 Firefox 将用上述代码中显示的图形替换水平线;但是 Internet Explorer 会用水平线包围图形。
解决方案 2-2:使用< iFrame >标签
您可以使用<iframe>
标签在浏览器中创建一个浏览器,并从其他来源打开文档。这可以在图 2-2 中的所示的例子中看到。
图 2-2。 使用<iFrame>
标签打开两个外部网站
在图 2-2 中,使用两个<iFrame>
标签打开了两个独立的网站。这个标签的重要性增加了,因为 HTML5 不支持 scrolling、frameborder 和 marginheight 属性,而所有这些属性在以前的 HTML 版本中都是受支持的。
涉及到什么
HTML5 中<iFrame>
标签的一个建议规范是seamless
属性。这允许外部源代码被合并到宿主文档中,没有边框或滚动条。这意味着外部源看起来就像是宿主文档的一部分。不幸的是,在撰写本文时,还没有浏览器支持seamless
属性。
如何建造它
以下代码用于创建图 2-2 中所示的示例:
<!DOCTYPE html > <html> <head> <title>Using the <iFrame> tag</title> </head> <body> <strong>Using the iFrame tag</strong><hr> <iframe src="http://www.apress.com/" width="600" height="250" seamless></iframe><br> <iframe src="http://www.friendsofed.com/" width="600" height="250" seamless ></iframe><br> </body> </html>
请注意,上面的代码中使用了seamless
属性,但是,您仍然可以看到滚动条和边框。这意味着浏览器不支持使用seamless
属性。
专家提示
当从外部来源引入任何类型的内容时,安全性可能是一个因素。在 HTML5 中,<iFrame>
标签周围有一个沙箱来帮助提高安全性。沙箱的属性包括以下内容:
allow-scripts :阻止或允许执行某些脚本内容,包括 JavaScript。
允许-表单 :允许或阻止表单提交。
allow-same-origin :强制外部文档的内容源自同一原点。因此,页面不能从一个原点接收数据,然后将数据写入不同的原点。
允许-顶层导航 :允许或阻止导航到顶层浏览内容。
解决方案 2-3:在页面中嵌入媒体
与上一节中的<iFrame>
标签一样,<embed>
标签用于将外部内容嵌入到主机网页中。然而,这里的重点是媒体,如照片,视频,声音和,尽管所有的谣言相反,Flash 内容。例如,在图 2-3 中,你会得到以下结果(假设一张照片是从外部来源带入的):
图 2-3。 使用<embed>
标签
涉及到什么
虽然您可能认为可以用<img>
标签做同样的事情,但是您可以使用<embed>
标签将各种媒体引入网页。例如,您可以引入视频、声音文件等等。然而,在处理视频时,事情在这个时候有点转变。
在撰写本文时,反对使用 Adobe Flash Player 等浏览器插件的趋势似乎越来越明显。许多移动技术目前不支持这些插件。例如,最常用的 Flash 视频格式之一是 FLV。这需要使用前面提到的 Adobe Flash Player。标签似乎指向一个可能的解决方案;然而,有一个警告。视频有多种风格——FLV、MOV、H.264 等等。每种 MIME 类型(文件扩展名)都需要有人获取原始视频并对其进行编码。这意味着将其包装在一个称为编解码器(压缩解压缩)的特殊容器中。此外,观看视频的人需要在他们的计算机上安装视频编解码器的副本。对于 Flash 视频,编解码器包含在 Flash 播放器中。
在撰写本文时,有两种编解码器支持 HTML5 视频:H.264 和 Theora。记住这一点,在撰写本文时,还有以下浏览器支持:
Firefox 3.5 及以上版本只支持 Theora 的 HTML5,不支持 H.264。
Internet Explorer 9 仅支持 H.264 的 HTML5 视频,不支持 Theora。
Safari 3 及以上版本只支持 H.264 而不支持 Theora 的 HTML5 视频。
Chrome 3 及以上版本同时支持 H.264 和 Theora 的 HTML5 视频。
Opera 10.5 只支持 Theora 的 HTML5 视频,不支持 H.264。
如何建造它
以下代码用于创建图 2-3 中所示的示例:
`
Using the embed tag
Using the embed tag
`
区分这个标签的是type
属性。您可能会将type
属性与像<input>
这样的标签联系起来。然而,在 HTML5 中,属性的使用比标签更加一致。在大多数情况下,媒体的 MIME 类型(文件扩展名)用作类型。但是,您很快就会看到,还有一种识别视频类型的替代方法。
另一个独特的属性是src
。这可以是连接到源所需的任何 URL。
专家提示
如上图所示,不同的浏览器支持不同的 HTML5 视频编码。因此,可能有必要对任何给定的视频进行两种编码:一种用于 H.264,一种用于 Theora。在<embed>
标签中,您使用type
属性来标识要打开的编码视频。
当对视频使用type
属性时,视频的类型可以用语法video/type
来标识。例如,对于 QuickTime 视频,将类型标识为type = "video/Quicktime"
。
解决方案 2-4:使用<区域>标签
虽然您可能在以前的 HTML 版本中使用过<area>
标记,但是在 HTML5 中实现的版本提供了一些新的有趣的可能性。让我们先来看看在 HTML5 中使用<area>
标签的一些基础知识。
涉及到什么
<area>
标签用于使用图像映射创建超链接。换句话说,它将一个现有的图形分成几个部分,每个部分都有自己独特的超链接。然而,HTML5 看待链接的方式与以前的 HTML 版本略有不同。它将链接分为三类:
超链接 :允许用户导航到给定的资源。
外部资源 :链接到被自动处理以扩充当前文档的资源。
注释 :对外部资源中使用的自动资源的修改;他们还可以修改超链接的工作方式。
在以前的 HTML 版本中,rel
属性用于显示当前资源和它所链接的资源之间的关系。例如,请注意以下内容:
rel = "stylesheet"
这表明资源链接到的文档是一个样式表。虽然这听起来不多,但它可能对搜索引擎理解网站内各种文档的关系有巨大的影响。
虽然rel
属性不是新的,但是 HTML5 扩展了关系定义。
表 2.1 显示了撰写本文时<area>
标签与外部文档的关系。由于本规范处于过渡阶段,表中提供的信息可能会发生变化。然而,你会惊讶地发现,现在的参考值代表了如此多样的关系,如家庭,职业,甚至浪漫。
表 2-1 。与<area>
标签引用文件的关系
| **相对值** | **与<区>标签**一起使用 | **描述** |
| :-- | :-- | :-- |
| 相识 | 是 | 这表明在参考文档中表示的人是在当前文档中表示的人的熟人。 |
| 交替的 | 是 | 用于指定引用的文档将与指定的介质一起使用。在 HTML5 中,媒体属性现在可以和`
`标签一起使用。这在以前的 HTML 版本中是不正确的。 |
| 档案 | 是 | 一份有历史意义的文件的链接。 |
| 作者 | 是 | 这表明引用的文档提供了关于作者的附加信息。 |
| 书签 | 是 | 这表明引用的文档是宿主文档的祖先。 |
| 儿童 | 是 | 这表明引用文档中表示的人员是当前文档中表示的人员的子代。 |
| 同事 | 是 | 这表明引用文档中所代表的人员与当前文档中所代表的人员具有职业关系。 |
| 接触 | 是 | 联系方式。 |
| 同居者 | 是 | 这表示引用文档中表示的人与当前文档中表示的人共享一个地址。 |
| 同事 | 是 | 这表明引用文档中代表的人员是当前文档中代表的人员的同事。 |
| 迷恋 | 是 | 这表明在参考文献中描述的人对在当前文献中描述的人有浪漫的兴趣。 |
| 日期 | 是 | 这表明引用文档中表示的人员正在与当前文档中表示的人员约会。 |
| 外部 | 是 | 这表明引用的文档不是宿主文档所在站点的一部分。 |
| 第一 | 是 | 这表示引用的文档是一系列文档中的第一个文档。 |
| 朋友 | 是 | 这表明引用文档中所代表的人是当前文档中所代表的人的朋友。 |
| 帮助 | 是 | 这表明引用的文档是上下文相关的帮助。 |
| 图标 | 不 | 这将导入一个图标来表示引用的文档。 |
| 指数 | 是 | 指向索引或目录的链接。 |
| 家族 | 是 | 这表明参考文档中所代表的人是当前文档中所代表的人的家庭成员或大家庭成员。 |
| 最后的 | 是 | 这表示引用的文档是一系列文档中的最后一个文档。 |
| 许可证 | 是 | 这是指向当前文档许可信息的链接。 |
| 我 | 是 | 这表示当前文档和链接的文档都代表您。 |
| 遇见 | 是 | 这表明当前文档中的人和引用文档中的人已经实际相遇。 |
| 沉思 | 是 | 这表明引用文档中所代表的人给了当前文档中所代表的人灵感。 |
| 然后 | 是 | 这表示引用的文档是引用的一系列文档中的下一个文档。 |
| 邻居 | 是 | 这表明引用文档中表示的人是当前文档中表示的人的邻居。 |
| nofollow(无跟踪) | 是 | 这意味着引用的文档没有得到宿主文档作者的认可,而是被用作引用或关系。 |
| 诺弗罗 | 是 | 这意味着 HTTP 头不使用超链接中的 referrer 属性。 |
| 父母 | 是 | 这表明引用文档中表示的人员是当前文档中表示的人员的父代。 |
| pingback | 不 | 允许引用的服务器 ping 回当前文档。 |
| 预取 | 是 | 允许预取引用的文档。 |
| 上一个 | 是 | 与下一个引用一样,这表示这是对一系列文档的引用,并且这是前一个文档。 |
| 搜索 | 是 | 此链接指向一个工具,该工具可以对当前或引用的文档执行搜索。 |
| 补充报道 | 是 | 这将在浏览器中将引用的文档显示为侧栏。 |
| 兄弟 | 是 | 这表明引用文档中所代表的人是当前文档中所代表的人的兄弟或姐妹。 |
| 配偶 | 是 | 这表明引用文档中代表的人员是当前文档中代表的人员的配偶。 |
| 爱人 | 是 | 这表明参考文献中所描述的人是当前文献中所描述的人的亲密爱人。 |
| 样式表 | 不 | 这表明引用的文档是样式表。 |
| 标签 | 是 | 这表示引用文档中的标签适用于当前文档。 |
在表 2-1 中列出的所有rel
环节中:
超链接是用熟人、候补、档案、作者、书签、孩子、同事、联系人、共同居民、同事、迷恋、日期、外部、第一、朋友、帮助、索引、亲属、许可证、我、met、muse、邻居、下一个、父母、上一个、搜索、侧边栏、兄弟姐妹、配偶、爱人和标签值创建的。
通过预取引用外部资源。
注释由 nofollow 和 noreferrer 使用。
同样,从 HTML5 开始,<area>
标签可以使用 media 属性来指示链接的目标媒体。在以前的 HTML 版本中,您不能在<area>
标签中使用 media 属性。为了保持一致,HTML5 现在允许使用它。
如何建造它
有许多不同的方式来使用<area>
标签。下面的例子就是这样一种方式。请注意,此示例仅用于演示,并不代表工作现场。
`
Example For Area Tag href attribute
`
<area>
标签与<map>
标签一起使用。注意<img>
标签中的usemap
属性。在<map>
标签的name
属性中使用了相同的标识符。这将图像与图像映射相关联。
媒体属性用于显示链接与哪个媒体相关联。如前所述,在<area>
标签中使用该属性是 HTML5 的新功能。
专家提示
当使用<area>
标签时,结合使用hreflang
属性和href
属性是一个很好的实践。这将指示目标文档或资源的语言。
总结
在这一章中,我们看了 HTML5 标记如何赋予传统的 HTML 标签新的功能。我们还研究了如何使用标签如<iFrame>
和<area>
将 HTML5 文档链接到外部世界。
在下一章,你将看到 HTML5 如何使用 DOM API 模型。在这个过程中,我们会重温您在本章和第一章中学到的一些概念。但是,您将在更复杂的环境中看到它们。
三、HTML5 结构和语义元素
“语义网”这个术语绝对不是新的。万维网的创始人蒂姆·伯纳斯·李第一次使用了这个词,他谈到了万维网向一种环境的转变,在这种环境中,发布的文档与以可解释的格式指定其语义上下文的信息和数据相关联。
使用 HTML5,在万维网的这个方面已经做了很多工作,并且已经引入了大量的新元素来为网页提供更好的定义和结构。然而,还有更多因素需要解决。
在这一章中,我们将重点介绍使用微数据、图形容器和 side 元素为 web 页面创建语义结构的技术和元素。
了解微数据
HTML5 引入了使用微数据在网页中定义自定义语义的可能性。
微数据使您能够通过使用由名称-值对和现有内容组成的语法,在 web 页面中指定机器可读的定制元素。
代表主题的自定义元素称为项目。分配给这些项目的值是它们的属性。为了使这些定制元素有效,您需要定义一个定制词汇表,其中包括表示主题的命名属性列表。
例如,如果您想要创建表示用户主题的微数据,您必须在微数据词汇表中定义它的属性。这个词汇表将包括属性声明,比如名字、姓氏、地址和社会保险号。
若要创建项,请使用 itemscope 属性。若要定义项目的属性,请使用 itemprop 属性。
特别是,itemscope 属性是与 HTML 元素关联的元素,它允许您定义元素的范围。itemscope 属性可以与任何有效的 HTML 元素相关联:
`
Name:Marco .
Last Name: Casario .
Photo:
Address: Via Lazzaro Spallanzani .
Social Security Number: 000-0000-000 .
`
在本例中,我们定义了一个具有五个属性的项目:
通过使用这种结构,可以创建您想要的所有项目。您还可以将多个属性与具有相同名称和不同值的项目相关联:
`
Name:Marco .
Last Name: Casario .
Photo:
Address: Via Lazzaro Spallanzani .
Address: Via Ludovico di Breme .
Social Security Number: 000-0000-000 .
`
但是,在创建项目之前,您需要创建自定义词汇表,以便能够定义元素的有效属性。因此,词汇表应是一个带有描述的属性列表,如表 3.1 所示。
表 3.1。 用户微数据词汇
| **属性** | **描述** |
| :-- | :-- |
| 名字 | 用户的名称。必选。 |
| 姓 | 用户的姓氏。必选。 |
| 地址 | 用户的地址。 |
| 照片 | 用户的 PNG、GIF 或 JPG 格式的图像。 |
| 社会保险号 | 用户的有效社会保险号。必选。 |
自定义词汇表与 itemtype 属性的元素相关联:
`
Name:Marco .
Last Name: Casario .
Photo:
Address: Via Lazzaro Spallanzani .
Social Security Number: 000-0000-000 .
`
分配给 itemtype 属性的值被标识为统一资源定位器(URL)。事实上,在上面的例子中,itemtype 属性指向 URL: [
www.my-vocabulary.org/user](http://www.my-vocabulary.org/user)
。
词汇表还允许您为打算使用相同结构描述相同主题的开发人员提供指导。
此外,微观数据的另一个重要方面与搜索引擎有关。事实上,谷歌等搜索引擎旨在向用户呈现最有用、最丰富的搜索结果。这些信息不会影响页面的内容,但可以让搜索引擎理解来自网页的信息,并更好地呈现出来。因此,微数据也被用作一种方法,使网页更便于搜索引擎搜索。
微数据是以结构化方式执行 HTML 内容标记的三种方法之一。(另外两种方法是微格式和 RDFs。)
解决方案 3-1:使用 itemprop 和 itemscope 属性
HTML5 引入的微数据是一种为内容分配标签的方式,以便描述特定类型的信息。
在这个解决方案中,您将创建一个包含带有 HTML5 微数据的食谱的网页。
涉及到什么
要使用微数据语法创建一个元素,基本上需要向标准 HTML 标记声明三个属性:
项目范围: 用于创建项目的布尔属性
itemprop: 用于向一个项目或该项目的一个后代添加属性
itemtype: 用于定义自定义词汇
在这个解决方案中,我们将使用 Google 为我们想要描述的项目定义的自定义词汇表。在 URL: [www.data-vocabulary.org/](http://www.data-vocabulary.org/)
:有一些自定义词汇已经被知名的搜索引擎定义和认可。
事件
组织
人
秘诀
产品
回顾
审核-汇总
面包屑
提供
报价汇总
因此,您所要做的就是将 itemtype 属性指向 URL: [www.data-vocabulary.org/](http://www.data-vocabulary.org/)
并添加元素类型。在这个例子中,您将使用定义一个食谱的微数据,您可以在这个地址:[www.data-vocabulary.org/Recipe/](http://www.data-vocabulary.org/Recipe/)
找到它。
有效属性在表 3-2 中指定。
表 3-2。 菜谱微数据属性
| **属性** | **描述** |
| :-- | :-- |
| 名字 | 包含菜肴的名称。必选。 |
| 配方类型 | 菜的种类。例如,开胃菜、主菜或甜点。 |
| 照片 | 正在准备的菜肴的图像。 |
| 出版者 | 配方以 ISO 8601 `([`www.iso.org/iso/date_and_time_format`](http://www.iso.org/iso/date_and_time_format)`日期格式发布的日期。 |
| 摘要 | 描述菜肴的简短摘要。 |
| 回顾 | 对这道菜的评论。可以包含嵌套的审阅信息。 |
| 准备时间 | 以 ISO 8601 时间长度格式准备菜肴食谱所需的时间长度。可以使用 min 和 max 作为子元素来指定时间范围。 |
| 烹饪时间 | 以 ISO 8601 持续时间格式烹饪这道菜所需的时间。可以使用 min 和 max 作为子元素来指定时间范围。 |
| totalTime(总时间) | 以 ISO 8601 时间格式准备和烹饪一道菜所花费的总时间。可以使用 min 和 max 作为子元素来指定时间范围。 |
| 营养 | 关于食谱的营养信息。可以包含以下子元素:食用量、卡路里、脂肪、饱和脂肪、不饱和脂肪、碳水化合物、糖、纤维、蛋白质和胆固醇。这些元素不是食谱微格式的明确组成部分,但是 Google 会识别它们。 |
| 说明 | 做这道菜的步骤。可以包含子元素指令,可用于注释每个步骤。 |
| 产量 | 食谱生产的数量(例如,服务的人数、份数等等。 |
| 佐料 | 食谱中使用的一种配料。可以包含子项的名称(配料名称)和数量。用这个来识别单独的成分。 |
| 作者 | 食谱的创造者。可以包含嵌套的人员信息。 |
如何建造它
当您想要使用微数据创建新元素时,首先要做的是指定它将引用的自定义词汇表。在这个解决方案中,我们将使用 Google 在 URL: [www.data-vocabulary.org/Recipe/](http://www.data-vocabulary.org/Recipe/)
定义的词汇表,其有效属性是在表 3-2 中指定的属性。
使用 itemtype 属性指定词汇:
<section itemtype="http://www.data-vocabulary.org/Recipe">
要创建一个新元素,您所要做的就是使用 Boolean itemscope 属性:
<section itemscope itemtype="http://www.data-vocabulary.org/Recipe">
现在,您可以为 Recipe type 元素声明由自定义微数据词汇表标识的属性。
首先声明菜谱的名称、成品菜肴的照片以及菜谱的作者:
`
Ciabatta Bread
Author's original URL .
`
还可以通过指定评论属性来插入对菜肴的评论:
<span itemprop="review"> Ciabatta is an Italian white bread made with wheat flour and yeast. The loaf is somewhat elongated, broad, and flattish. Its name is the Italian word for slipper. There are many variations of Ciabatta. Ciabatta in its modern form was developed in 1982\. Since the late 1990s, it has been popular across Europe and the United States. It is widely used as a sandwich bread. From Wikipedia http://en.wikipedia.org/wiki/Ciabatta </span>
每个食谱都需要配料,所以使用配料属性来指定它们:
`
1 1/2 cups water
15 grams of salt
1 teaspoon sugar
1 tablespoon olive oil
3 1/4 cups all-purpose flour
20 grams of fresh yeast
`
为了插入 Recipe 对象的元素,我们使用了一个无序列表,并为 H3 元素声明了 itemprop 属性。事实上,可以为任何 HTML 元素声明 itemprop 属性。
现在添加说明,这次使用编号列表:
`
Instructions
In Kitchen Aid style mixer, mix all ingredients roughly until combined with paddle. Let it rest for 10 minutes.
With the paddle (I prefer the hook to prevent the dough from crawling into the guts of the mixer), beat the living hell out of the batter. It will start out like pancake batter but, in anywhere from 10 to 30 minutes, it will set up and work like a very sticky dough. If it starts climbing too soon, then switch to the hook.
You'll know it's done when it separates from the side of the bowl and starts to climb up your hook/paddle, just coming off the bottom of the bowl. (I mean this literally about the climbing—I once didn't pay attention, and it climbed up my paddle into the greasy inner workings of the mixer. It was not pretty!) Anyway, it will definitely pass the windowpane test.
Place into a well-oiled container, and let it triple! It must triple! For me, this takes about 2.5 hours.
Empty onto a floured counter (scrape if you must, however you got to get the gloop out), and cut into 3 or 4 pieces.
Spray with oil and dust with lots of flour. Let it proof for about 45 minutes, which gives you enough time to crank that oven up to 500°F.
After 45 minutes or so, the loaves should be puffy and wobbly; now it's iron fist, velvet glove time.
Pick up and stretch into your final Ciabatta shapes (~10" oblong rectangles), and flip them upside down. This redistributes the bubbles, so that you get even bubbles throughout. Then place the loaves on parchment or a heavily-floured peel.
Try to do it in one motion and be gentle. It might look like you've ruined them completely, but the oven spring is immense on these things.
Bake at 500°F until they are 205°F in the center (about 15-20 minutes), rotating 180-degrees half way through. Some people like to turn the oven down to 450°F after 10 minutes—whatever floats your boat. I usually bake in 2 batches.
`
您还可以指定估计的烹饪时间:
`
Cook Time: 50 minutes approximately
`
通过在浏览器中打开该文件,您将获得如图 3-1 所示的结果。
图 3-1。 使用微数据的配方网页
除非用户打开页面的源代码,否则他或她不会注意到由于微数据而产生的任何差异。然而,你所做的是为你的网页创建一个健壮的语义结构,搜索引擎肯定会欣赏它。
以下是该示例的完整源代码:
`
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Solution 3-1: Using the itemprop and itemscope</title>
</head>
<body>
Ciabatta Bread
Author's original URL .
Ciabatta is an Italian white bread made with wheat flour and yeast. The loaf is somewhat elongated, broad, and flattish. Its name is the Italian word for slipper. There are many variations of Ciabatta.
Ciabatta in its modern form was developed in 1982\. Since the late 1990s, it has been popular across Europe and the United States. It is widely used as a sandwich bread.
From Wikipedia http://en.wikipedia.org/wiki/Ciabatta
1 1/2 cups water
15 grams of salt
1 teaspoon sugar
1 tablespoon olive oil
3 1/4 cups all-purpose flour
20 grams of fresh yeast
Cook Time: 50 minutes approximately
Instructions
In Kitchen Aid style mixer, mix all ingredients roughly until combined with paddle. Let it rest for 10 minutes.
With the paddle (I prefer the hook to prevent the dough from crawling into the guts of the mixer), beat the living hell out of the batter. It will start out like pancake batter but in anywhere from 10 to 30 minutes it will set up and work like a very sticky dough. If it starts climbing too soon, then switch to the hook.
You'll know it's done when it separates from the side of the bowl and starts to climb up your hook/paddle, just coming off the bottom of the bowl. (I mean this literally about the climbing—I once didn't pay attention, and it climbed up my paddle into the greasy inner workings of the mixer. It was not pretty!) Anyway, it will definitely pass the windowpane test.
Place into a well-oiled container and let it triple! It must triple! For me, this takes about 2.5 hours.
Empty onto a floured counter (scrape if you must, however you got to get the gloop out), and cut into 3 or 4 pieces.
Spray with oil and dust with lots of flour. Let it proof for about 45 minutes, which gives you enough time to crank that oven up to 500°F.
After 45 minutes or so, the loaves should be puffy and wobbly; now it's iron fist, velvet glove time.
Pick up and stretch into your final Ciabatta shapes (~10" oblong rectangles), and flip them upside down. This redistributes the bubbles, so that you get even bubbles throughout. Then place the loaves on parchment or a heavily-floured peel.
Try to do it in one motion and be gentle. It might look like you've ruined them completely, but the oven spring is immense on these things.
Bake at 500°F until they are 205°F in the center (about 15-20 minutes), rotating 180-degrees half way through. Some people like to turn the oven down to 450°F after 10 minutes—whatever floats your boat. I usually bake in 2 batches.
`
专家提示
包括谷歌在内的一些搜索引擎通常不会显示不可见的用户元素,比如元标签。然而,这并不意味着搜索引擎完全忽略这些信息。
在某些情况下,向搜索引擎提供更详细的信息是有用的,即使我们不想让访问者看到这些信息。例如,如果我们允许用户在网站上为我们的食谱打分,平均分数是 8 分,那么用户(而不是搜索引擎)会认为这个分数是基于 1 到 10 分的范围。在这种情况下,您可以使用 meta 元素来指定这个方面,如下所示:
`
`
在这段代码中,meta 标记指定了在页面中看不到的附加信息,但是这些信息有助于搜索引擎理解分级系统是基于 1 到 10 的范围。属性的值是使用 content 属性指定的。
解决方案 3-2:创建自定义词汇表
使用微数据的优势在于,它允许您在 web 页面中创建 HTML 标准元素中没有的自定义元素。
您需要创建一个自定义词汇表,以便您将创建的结构能够定义名称-值对组,供其他开发人员重用,并被搜索引擎识别。
自定义词汇表只是自定义属性的列表,您创建它来定义某些元素,这些元素将作为其他开发人员和搜索引擎设计人员的技术文档。
即使这不是一个强制性的练习,也强烈建议你创建一个自定义词汇表。
涉及到什么
正如您在前面的解决方案中已经看到的,Google 为一些定制元素提供了一些定制词汇表。已经被定义并被搜索引擎识别的定制词汇可以在 URL: [www.data-vocabulary.org/](http://www.data-vocabulary.org/)
获得。
要创建自定义词汇表,您所要做的就是记录定义元素的属性,发布 web 页面,并使用 itemtype 属性指向它。
如何建造它
创建自定义词汇表意味着创建一个 HTML 页面,记录定义自定义元素结构的属性。在本解决方案中,您将创建一个自定义词汇表来描述培训课程。
因此,创建一个新网页,然后发布到远程服务器:
`
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Solution 3-2: Creating a custom vocabulary</title>
</head>
<body>
<h1>
Course
</h1>
<p>
When course information is marked up in web pages, use the following information in a course.</p>
<p>To see an example, go to the <a href="http://www.comtaste.com/en/html-5-training.htm">Comtaste Training page</a>: http://www.comtaste.com/en/html-5-training.htm</p>
<h2>
Course Properties
</h2>
<table width="100%" border="1" cellspacing="2" cellpadding="2">
<tr>
<th scope="col">Properties</th>
<th scope="col">Description</th>
</tr>
<tr>
<td>name</td>
<td>The title of the course. Required.</td>
</tr>
<tr>
<td>subtitle</td>
<td>A short summary describing the course.</td>
</tr>
<tr>
<td>summary</td>
<td>A short description of the course.</td>
</tr>
<tr>
<td>description</td>
<td>A detailed description of the course and subjects.</td>
</tr>
<tr>
<td>date</td>
<td>The published dates.</td>
</tr>
<tr>
<td>hour</td>
<td>The total duration of the course. </td>
</tr>
<tr>
<td>location</td>
<td>The location of the course.</td>
</tr>
<tr>
<td>students</td>
<td>The maximum number of students per course.</td>
</tr>
<tr>
<td>prerequisites</td>
<td> The skill required to participate to the course (if needed).</td>
</tr>
<tr>
<td>objectives</td>
<td>A short description of what you'll learn.</td>
</tr>
<tr>
<td>HDrequirements</td>
<td>Hardware and software requested for this course.</td>
</tr>
<tr>
<td>book</td>
<td>A list of course assets.</td>
</tr>
<tr>
<td>outline</td>
<td>The complete outline of the course.</td>
</tr>
<tr>
<td>price</td>
<td>The price of the course.</td>
</tr>
<tr>
<td>instructor</td>
<td>The name of the instructor that will lead the course.</td>
</tr>
</table>
</body>
</html>`
因为是简单的 HTML 文件,所以可以用常用的 web 浏览器打开,如图图 3-2 所示。
图 3-2。 记录课程元素属性的自定义词汇表页面
为了使用词汇表,在将 HTML 页面发布到远程 URL 后,必须使用 itemtype 属性指定地址:
<div itemscope itemtype="http://www.comtaste.com/microdata/course">
要查看使用课程元素微数据的 HTML 页面示例,您可以访问这个 URL: [www.comtaste.com/en/html-5-training.htm](http://www.comtaste.com/en/html-5-training.htm)
,如图 3-3 所示。
图 3-3。 课程微数据页面
专家提示
Google 为开发者提供了一个有趣的工具,丰富的代码片段测试工具:[www.google.com/webmasters/tools/richsnippets](http://www.google.com/webmasters/tools/richsnippets)
。
你可以插入一个有效的 URL 来检查 Google 是否能正确解析你的结构化数据标记并显示在搜索结果中,如图图 3-4 所示。
图 3-4。 丰富的代码片段测试工具正在运行
解决方案 3-3:了解链接类型和关系
从 HTML 的第一个版本开始,链接就使得简单的文本页面易于导航。然而,HTML5 引入了一个新概念:链接关系。链接关系让你不仅能告诉浏览器你所指向的页面或资源,还能告诉你为什么要指向它。
链接关系可以分为两类:
外部资源链接: 指向浏览器处理的资源的所有链接,如样式表、快捷图标、pingback 服务器等
超链接: 链接到其他文档的简单链接
涉及到什么
本质上,在 HTML5 中创建链接关系需要三个标签:
在页面的<head>
中使用<link
>元素
使用<a>
元素
使用一个<area>
元素
要确定哪些链接类型适用于链接或区域元素,必须在空格上拆分元素的rel
属性:
<a href="myTag/html5" rel="tag">html5</a>
在这个代码示例中,我们创建了一个标记链接。
<a>
和<area>
元素上的rel
属性控制元素创建什么类型的链接。属性的值必须是一组空格分隔的标记。
rel
属性没有默认值。如果省略该属性,或者用户代理无法识别该属性中的任何值,则该文档与目标资源没有特定的关系,只是两者之间有一个超链接。
如何建造它
使用链接关系非常简单。这里不提供代码示例,而是用 HTML5 提供的新的rel
值列表来查看一个表,这样更有用。
表 3-3 总结了可用于rel
属性的链接类型。
表 3-3 。新的 HTML5 链接类型
| **属性** | **描述** |
| :-- | :-- |
| 档案馆 | 提供记录、文档或其他历史资料的链接。
一个例子就是:
<a href="/archive" rel="archives">My Blog's Archive</a>
|
| 作者 | 定义一个页面的超链接,该页面提供关于最近的 article 元素的作者、定义超链接的元素的祖先(如果有)或整个页面的附加信息。
例如:
This article has been written by <a href="http://www.linkedin.com/in/marcocasario" rel="author">Marco Casario</a>
|
| 外部 | 指示超链接指向网站外部的资源。 |
| 第一个、最后一个、上一个、下一个和上一个 | 指示超链接指向当前页面出现的序列中的第一个、最后一个、上一个或下一个资源。
up 值表示与当前文档相比的“父”文档(在层次结构中)。
例如:
<a href="/firstArticle.htm" rel="first">First Part</a> <a href="/nextArticle.htm" rel="next">Read the next article</a> <a href="/lastArticle" rel="last">Last part of this series</a>
|
| 图标 | 定义表示页面或站点的图标,当在用户界面中表示页面时,用户代理应该使用该图标。
尺寸属性给出了可视媒体图标的尺寸。
比如:
`
` |
| 许可证 | 表示引用的文档提供版权许可条款,根据这些条款提供当前文档的主要内容。 |
| nofollow(无跟踪) | 表示该链接未经该页面的原作者或出版商认可,或者包含指向引用文档的链接主要是因为与这两个页面有关联的人员之间的商业关系。
该关键字不创建超链接。相反,它注释由元素创建的任何其他超链接。 |
| 诺弗罗 | 指示在跟随链接时不会泄露推荐人信息。
该关键字不创建超链接,但是注释该元素创建的任何其他超链接。 |
| 广播 | pingback 系统是一种当其他网站链接到博客时自动通知该博客的方式。
它允许你在有人链接到他们的文档时得到通知。
例如:
<link rel="pingback" href="pingback server url">.
更多信息参见 Pingback 1.0 规范
[
hixie.ch/specs/pingback/pingback-1.0](http://hixie.ch/specs/pingback/pingback-1.0)
|
| 预取 | 指示抢先获取和缓存指定的资源可能是有益的,因为用户很可能需要该资源。 |
| 搜索 | 表示引用的文档提供了一个专门用于搜索文档及其相关资源的界面。
查看此链接了解更多信息:[www.opensearch.org/Specifications/OpenSearch/1.1#Autodiscovery_in_HTML.2FXHTML](http://www.opensearch.org/Specifications/OpenSearch/1.1#Autodiscovery_in_HTML.2FXHTML)
|
| 补充报道 | 指示引用的文档如果被检索,将在二级浏览上下文中显示(如果可能),而不是在当前浏览上下文中显示。 |
| 标签 | 指示引用文档表示的标签应用于当前文档。 |
| 帮助 | 链接到上下文相关的帮助页面。 |
专家提示
当考虑网页的语义时,你不能避免考虑你的选择对搜索引擎优化(SEO)的影响。
事实上,拥有一个网页链接之间关系的语义表也有助于它们在传统搜索引擎上的定位。
谷歌没有隐藏它——恰恰相反:它鼓励在你的网页中使用语义和结构。
使用上一页、下一页、最后一页和第一页的关系,同时改进搜索和存档,意味着你可以通过将搜索引擎流量导向更连贯的内容,帮助搜索引擎正确地索引你网站的页面。
标签关系在对页面进行分类时非常有用,它通过给文档分配一个更广泛接受的类别,因此比旧的元关键字系统更有用,也更受重视。
外部关系允许你指定一个链接为“外部”,这实际上意味着告诉搜索引擎分配一个不同的权重(可能是一个较低的值)给那个特定的链接,而不是指向你的网站页面的链接。
解决方案 3-4:标题和 h 组元素
在印刷中,标题是以清晰和直接的方式传达文章和章节标题信息的基本元素。从 HTML 的早期版本开始,这也是 Web 上的一个明显需求。标题总是代表网页的一个重要的语义元素。搜索引擎也让这个标签变得重要起来。
图 3-5 显示了一个典型的网页示例,它使用标题来传达课程的标题和副标题:
图 3-5 。HTML 标题的经典用法
然而,当我们看这一页时,我们注意到除了标题之外还有一个副标题:
主标题: 培训 html 5–2011 日历
标题: 使用 HTML5 的下一代富 Web 应用
字幕: 使用 HTML5 WebSocket 和通信开发实时协作 web 应用
对于以前版本的 HTML,我们只能使用<h1>
到<h6>
标签来区分标题和定义页面结构。如果你在图 3-5 中查看网页的源代码,你会看到下面的 HTML 代码:
`
TRAINING HTML5 - 2011 Calendar
Next-Generation Rich Web Application with HTML5
Developing real-time and collaborative web applications using HTML5 WebSocket and Communication `
您可以看到通过<h1>
、<h2>
和<h4>
标签指定的三个头,但是从语义的角度来看,这段代码并不完全正确。实际上,副标题和主标题并不是页面的章节;相反,它们只是副标题和额外的信息。
HTML5 引入了新的标签来避免这个问题:<header>
和<hgroup>
。
涉及到什么
header 元素通常用于包含部分的标题,即 h1–h6 元素或 hgroup 元素。
元素包含了一节的标题。当标题有多个级别时,如副标题、可选标题或标语,它作为包装器对一组 h1-h6 元素进行分组:
`
`
利用这个结构,我们创建了一个带有标题(<h1>
)和副标题(<h2>
)的部分标题的语义表示。
还可以创建使用标题内容的嵌套部分内容元素:
`
Course Title
Course Subtitles
Course Features
Prerequisites
This HTML5 course is designed for software developers interested in designing, creating, and deploying HTML5 web applications.
Overview
HTML5 is the next major milestone in HTML, and it is not just another incremental enhancement; it represents an enormous advance for modern web applications.
`
如何建造它
最初,<header>
和<hgroup>
标签的使用可能会令人困惑,但是你所要做的就是记住:
<hgroup>
只能包含一组<h1>
到<h6>
元素
<header>
可以包含一个<h1>
到<h6>
元素或者一个<hgroup>
在本解决方案中,您将使用标题创建一个描述培训课程的页面,提供有关标题、副标题、简短描述的信息,以及有关日期、地点、参与者人数和持续时间的详细信息。
在下面的代码中,sectioning 内容元素中的第一个 heading 内容元素成为该节的标题。
以下是解决方案的完整代码:
`
Solution 3-4: The header and hgroup elements
</head>
<body>
TRAINING HTML5 - 2011 Calendar
Next-Generation Rich Web Application with HTML5
Developing real-time and collaborative web applications using HTML5 WebSocket and Communication
Rome TBC
Milan TBC
HOUR:
4 consecutive lessons. Each lesson has a duration of 8 hours, and it takes place during the hours between 9:00 a.m. and 6:00 p.m. (with a 1-hour break for lunch)
LOCATIONS:
Rome: via Famiano Nardini 1/c (Metro linea B, fermata Bologna)
Rome: via Lazzaro Spallanzani 36/A
Milan: via Imperia, 2
NUMBER OF PARTICIPANTS: Up to 12
`
您可能会注意到这个 HTML 文件与一个外部 CSS 文件相关联,该文件应用了带有<link>
标签的简单样式格式。你会在有本章源代码的文件夹里找到Solution_3_9781430233862.css
文件。
上述文件在浏览器中渲染的最终结果如图图 3-6 所示。
图 3-6。 页面结构使用了<头>和<组>元素。
解决方案 3-5:将图像与其标题连接起来
传统上,每个图像都与一个标题相关联,标题可以是该图像的简短文本描述或图例。
在印刷出版行业,图像/标题对不仅仅是一种最佳实践:例如,这本书就使用了这种方法。
HTML 引入了两个新的语义元素,允许您通过将文本与充当图例的图像相关联来获得这个结果。
涉及到什么
允许您将标题与图像相关联的元素是<figure>
和<figcaption>
。
<figure>
标签是图像的容器:
`
<img src="http://media02.linkedin.com/mpr/mpr/shrink_80_80/p/3/000/000/3d2/0f362fd.jpg "
alt="A photo of mine used in my LinkedIn profile">
The
is the text associated with the image that acts as a caption:
Marco Casario as seen on TV !
`
也可以在一个标题中嵌套多个图像:
`
<img src="http://media02.linkedin.com/mpr/mpr/shrink_80_80/p/3/000/000/3d2/0f362fd.jpg "
alt="A photo of mine used in my LinkedIn profile">
Marco Casario as seen on TV !
`
使用<figure>
和<figcaption>
标签的一个好处是,你可以通过外部或内部样式表对它们应用 CSS,就像你对任何其他容器标签所做的一样。
如何建造它
使用 HTML5,仔细规划网页的结构是非常重要的。有了提供给您的所有新标签,您可以为每个元素赋予明确的语义值。
这个规划阶段对图像也很重要。
在这个解决方案中,您将通过将文本指定为图像的标题来创建一个包含图片、姓名和角色的简单名片。然后你会应用一些简单的样式表,只是为了提高最终的效果,如图图 3-7 所示。
图 3-7。 页面结构使用了<header>
和<hgroup>
元素
让我们从声明容器图形开始,在这里您将声明图像:
`
<img src="http://media02.linkedin.com/mpr/mpr/shrink_80_80/p/3/000/000/3d2/0f362fd.jpg " alt=
"A photo of mine used in my LinkedIn profile">
`
这张图片是从 LinkedIn 服务器加载的。使用<figcaption>
标签添加标题:
`
<img src="http://media02.linkedin.com/mpr/mpr/shrink_80_80/p/3/000/000/3d2/0f362fd.jpg " alt=
"A photo of mine used in my LinkedIn profile">
`
我们使用了一个<br>
来发送标题的文本作为一个新的段落;否则它会出现在图像旁边。
现在,您可以创建样式表来重新创建边框效果,定义边距,并设置图像的背景色:
img { padding:2px; border:1px solid #e6e6e6; background-color:#fff; }
最后,为容器和标题文本添加样式:
`figure, figcaption {
display: block;
background-color:#ddf0f8;
border:1px solid #666;
text-align: center;
}
figcaption {
font-face: Arial;
font-size: 12px;
font-style: italic;
background-color:#ddf0f8;
padding:2px;
min-height:10px;
margin:0 0 3px;
border:1px solid #FFF
}`
以下是该解决方案的完整代码:
`
Solution 3-4: Connecting images with their captions
</head>
<body>
`
解决方案 3-6:添加切线内容
HTML5 引入了一系列新的标签,这些标签有助于更精确地定义网页的语义结构。根据 W3C,在这些标签中,有一个标签表示与构成文档主要文本流的内容无关的内容。这个新元素,<aside>
标签,允许您将附加文本(切线)关联到一篇文章或整个页面。
涉及到什么
从<aside>
标签的定义开始,我们可以推断出它有两个特征:它与主要内容相关,但只是间接相关。也可以脱离主要内容单独考虑。
为了更好地理解标签的用法,让我们回到印刷出版行业。图 3-8 显示了一个例子,在这个例子中,侧边栏元素被定义在两条水平规则中,为主文章的运行文本提供额外的信息。
图 3-8。 右边的元素给文章增加了切题的内容。这是一个使用 side 元素的完美环境。来源:[www.bbc.co.uk/news/uk-12614995](http://www.bbc.co.uk/news/uk-12614995)
如何建造它
在这个解决方案中,您将创建一个 article 元素,它将包含来自 LinkedIn 的 Marco Casario 的个人资料中的信息,并且您将添加两个 aside 元素——一个与文章相关联,一个与页面相关联。
首先插入文章的语义元素:
`
Marco Casario Profile
Marco has been passionate about information technology since he was little more than a child and used to program games in BASIC for the Commodore 64 before dedicating himself, while still very young, to innovative projects for the web using Flash and Director.
In 2001, he began to collaborate with Macromedia Italy. Since then, he has produced and headed a long series of presentations, conferences, and articles, which you can find listed in detail in his blog (casario.blogs.com), which currently receives several thousand unique visits every day.
In 2005, Marco founded Comtaste (www.comtaste.com), a company dedicated to exploring new frontiers in RIAs and the convergence between the web and the world of mobile devices. MobyMobile (www.mobymobile.com) and YouThruBiz (www.youthrubiz.com) are representative of their recent work.
Another example of Marco's achievements is that he is founder of the biggest worldwide Yahoo Flash Lite UG and of www.augitaly.com, a reference point for the Italian community of Adobe users in which he carries out the role of Channel Manager for the section dedicated to Flex (www.augitaly.com/flexgala.)
Speaking Engagements:
FATC New York
TAC Singapore
Adobe MAX (Europe and US)
FlashOnTheBeach Brighton
FlexCamp London
MultiMania Belgium
FITC Amsterdam
360Flex
AJAXWorld Conference NY city
O'Reilly Web 2.0 Summit Berlin
Adobe MAX 2007
... and many others
`
你需要在这个内容的侧框中添加文本。这个横向框的位置和样式将由 CSS 语句定义。首先,在文章中插入一个旁白元素:
`
Marco Casario Profile
Marco has been passionate about information technology since he was little more than a child and used to program games in BASIC for Commodore 64 before dedicating himself, while still very young, to innovative projects for the web using Flash and Director.
In 2001, he began to collaborate with Macromedia Italy. Since then, he has produced and headed a long series of presentations, conferences, and articles, which you can find listed in detail in his blog (casario.blogs.com), which currently receives several thousand unique visits every day.
In 2005, Marco founded Comtaste (www.comtaste.com) a company dedicated to exploring new frontiers in RIAs and the convergence between the web and the world of mobile devices. MobyMobile (www.mobymobile.com) and YouThruBiz (www.youthrubiz.com) are representative of their recent work.
Another example of Marco's achievements is that he is founder of the biggest worldwide Yahoo Flash Lite UG and of www.augitaly.com, a reference point for the Italian community of Adobe users in which he carries out the role of Channel Manager for the section dedicated to Flex (www.augitaly.com/flexgala.)
Speaking Engagements:
FATC New York
TAC Singapore
Adobe MAX (Europe and US)
FlashOnTheBeach Brighton
FlexCamp London
MultiMania Belgium
FITC Amsterdam
360Flex
AJAXWorld Conference NY city
O'Reilly Web 2.0 Summit Berlin
Adobe MAX 2007
... and many others
`
插入另一个旁白元素,但这次是在文章之外。这样,内容将与整个页面相关联:
`
He is author of the following books:
HTML5 Solutions: Essential Techniques for HTML5 Developers (Apress)
Flex 4 Cookbook (O'Reilly)
Professional Flash Catalyst (Wrox)
AIR Cookbook (O'Reilly)
Advanced AIR Applications (FOED)
The Essential Guide to AIR with Flash CS4 (FOED)
Flex Solutions: Essential Techniques for Flex 3 Developers (FOED)
`
外部 side 元素包含元素列表;也就是作者所有的书。
现在您所要做的就是创建 CSS 语句来添加一些格式和定位。
创建一个样式块,并将 aside 元素的样式插入到文章中:
``
保存文件并在浏览器中打开它。带有两个侧面元素的最终结果显示在图 3-9 中。
图 3-9。右边的框为文章添加了更多的内容,而底部的列表引用了整个页面。
side 元素在 web 页面的语义方面非常有用。此外,搜索引擎能够更好地索引使用这些新 HTML5 标签的页面。
总结
尽管自从 HTML 诞生以来,关于使用语义 HTML 的讨论就一直存在,但只有 HTML5 在这一领域实现了重大改进。
现在可以使用特定的 HTML5 标签来表达隐含的含义,而不是像<div>
和<span>
这样的 HTML 标签。事实上,HTML5 中引入了大量新元素,为网页提供了更好的定义和结构。
在本章中,您已经学习了如何使用这些技术和元素,通过微数据和词汇表为 web 页面创建语义结构,这允许您通过使用由名称-值对和现有内容组成的语法,在 web 页面中指定机器可读的自定义元素。自定义词汇表使您创建的结构能够定义名称-值对组,该组可以被其他开发人员重用,并且可以被搜索引擎识别。在本章中,您还学习了链接类型和关系。这些不仅可以让你向浏览器显示你所指向的页面或资源,还可以显示你为什么要指向它。接下来,我们介绍了 figure 容器,它将文本与作为图例的图像相关联。最后,您学习了aside
元素。这个元素表示与内容无关的内容,它构成了文档的主要文本流。
在第四章中,你将学习所有 web 应用中使用的表单。
四、HTML5 表单
表单是任何需要用户输入的 web 应用的重要组成部分。
自从最初用 HTML 2.0 规范创建以来,表单已经允许我们使网页具有交互性。事实上,通过表单控件,用户可以与各种类型的信息交互并插入这些信息,然后这些信息可以被发送到服务器进行处理。
HTML 4 提供了非常基本的表单控件,通常是文本字段,大部分交互发生在服务器端。对插入到表单控件中的数据类型进行验证是使用 HTML 4 在服务器端执行操作的一个经典例子,除非使用 JavaScript 或 AJAX 框架。
有了今天的 HTML5 表单,可以更快、更有效地开发具有高级终端用户体验的应用。不幸的是,浏览器的兼容性差别很大,并不是所有新的表单元素都被支持。图 4-1 说明了在不同的浏览器和操作系统中对一些新的 HTML5 表单元素的支持:
图 4-1 。由[www.findmebyip.com/litmus/](http://www.findmebyip.com/litmus/)
发布的网页设计师 HTML5 & CSS3 清单。清单显示了浏览器对一些新的 HTML5 表单元素的支持。
久而久之,浏览器支持将会越来越符合 HTML 规范,所以在我们让每个人都能看到我们的表单之前,我们需要的只是耐心。
了解新的输入类型
HTML5 表单引入了一组新的工具,使得表单开发更加容易和丰富。
以下是 HTML5 引入的一些新表单元素:
keygen 元素
输出元件
进度元素
仪表元件
电子邮件输入类型
url 输入类型
日期选择器元素
时间、日期时间、月和周元素
数字输入类型
搜索输入类型
范围输入类型
电话输入类型
颜色输入类型
除了输入类型之外,HTML5 还引入了几个可以在表单中使用的新属性,比如 list、autofocus、placeholder、required、multiple、pattern、autocomplete、min 和 max 以及 step。
甚至表单元素声明的语法也随着 HTML5 而改变。事实上,现在您可以在页面的任何位置声明一个表单元素,并通过使用元素的 form 属性与表单对象相关联:
<form id="myForm" /> <input type="text" form="myForm" />
在这个例子中,文本输入是在表单标记(称为表单所有者)之外声明的,但是它仍然与具有表单属性的myForm
表单相关联。
在下面的解决方案中,我们展示了新 HTML5 表单的潜力。
解决方案 4-1:使用电子邮件输入类型
<input type="email">
标签创建了一个表单元素,它期望从用户那里接收一个有效的电子邮件地址。表单控件显然不会验证电子邮件地址是否实际存在,只有当用户插入到字段中的文本使用了有效的语法时才会验证。
从用户的角度来看,在 HTML 表单中使用这种输入类型并不会改变外观。事实上,该元素在浏览器中呈现为普通的文本输入(除了 Opera,它在文本字段旁边使用一个电子邮件图标,如图 4-2 所示)。
图 4-2。 Opera 浏览器在邮件输入类型中增加了一个小图标。
发生变化的是验证操作改为在浏览器的后台执行,如果电子邮件地址未通过验证,将返回一个错误。这种验证因浏览器而异。以 Opera 为例,在输入中包含@ 就足以让它被接受;而在 Safari、Chrome 和 Firefox 中,你至少需要输入*@-。-(符号@前后的一个字符和一个句点后跟一个字符)。
涉及到什么
要使用新的电子邮件输入类型,使用以下标记就足够了:
<input type="email" />
表 4-1 显示了该输入类型的有效属性列表。
表 4-1。 电子邮件输入类型所接受的有效属性
| **属性** | **描述** |
| :-- | :-- |
| 名字 | 包含与此元素关联的名称/值对的名称,用于提交表单。 |
| 有缺陷的 | 设置禁用的控件。 |
| 类型 | 指定其 input 元素是一个控件,用于编辑元素值中给定的电子邮件地址或电子邮件地址列表。 |
| 形式 | 所有表单元素的容器。 |
| 自动完成 | 存储用户输入的值。 |
| 自(动)调焦装置 | 一旦元素被加载,就将焦点放在元素上。 |
| 最大长度 | 元素的最大允许值长度。 |
| 目录 | 指定该元素表示禁用的控件。 |
| 模式 | 指定用于检查值的正则表达式。 |
| 只读的 | 表示其值不应被编辑的控件。 |
| 需要 | 指定元素是否是必需的。 |
| 大小 | 元素所表示的控件要显示的选项数。 |
| 占位符 | input 元素中显示的文本(旨在帮助用户向控件中输入数据)。 |
| 多个的 | 允许您为一个输入元素指定多个电子邮件或文件值。 |
| 价值 | 包含电子邮件地址或电子邮件地址列表。 |
如何建造它
下面的代码示例演示如何使用新的电子邮件输入类型:
`
Solution 4-1: Using the email input type
`
`
`
在创建标签表单之后,已经声明了一个fieldset
标签。这个元素指定了一组表单控件,可以根据第一个legend
元素给出的公共名称进行分组。
在代码示例中,fieldset
的名称是:
<legend>Solution 4-1: Using the email input type</legend>
然后,为用户名和电子邮件地址声明一个输入文本。对于这两种输入类型,label
元素通过for
属性关联,引用输入的 id:
<label for="name">Name</label> <input id="name" name="name" type="text"/><br/> <label for="email">Email</label> <input id="email" name="email" type="email"/><br/>
表单以一个提交数据的按钮结束。
如果您在 Opera 12 中打开该文件,并且为电子邮件地址插入了一个无效值,那么当单击提交按钮时,将会返回一条错误消息并且表单不会被提交,如图图 4-3 所示。
图 4-3。 如果电子邮件地址无效,Opera 会显示一条错误消息。
如果用户插入有效的电子邮件地址,将不会出现任何消息,表单将被提交。
在 Firefox、Internet Explorer 和 Chrome 中,不会向用户显示任何错误信息。
为了得到错误消息,我们必须等待这些浏览器的未来版本(或使用 JavaScript)才能使用这个有趣的特性,它最终将允许我们在客户端而不是服务器端验证客户端电子邮件地址。
专家提示
触摸屏移动设备(如 iPhone、iPad 和 Android OS 设备)的用户在使用电子邮件输入类型浏览网页时会有一个惊喜。
对于其中一些设备,浏览器能够识别新的 HTML5 输入类型,设备会改变屏幕键盘来帮助用户进行这种输入。这意味着更好的用户体验!
事实上,这些设备在输入文本时会在主屏幕上显示带有@和句点符号的虚拟键盘,如图图 4-4 所示。
图 4-4。 iPhone 和 iPad 浏览器支持新的电子邮件输入类型,它显示了一个支持电子邮件的虚拟键盘。
解决方案 4-2:使用 URL 输入类型
如今,用户越来越频繁地在表单中插入网址。无论是他或她的个人网站、博客或 LinkedIn 帐户的 URL,到目前为止,这个元素通常是通过简单的文本输入来管理的。HTML5 现在引入了一种新的输入类型,通过遵守互联网地址标准来管理这种类型的文本。
目前,对 URL 输入元素的支持相当糟糕。例如,在 Opera 浏览器中,它似乎是唯一能够识别新标签的 web 浏览器,但这个元素只得到部分支持。下面是它的作用:
Opera 会自动添加http://
后缀,即使你只是插入一个短格式的地址,比如[www.comtaste.com](http://www.comtaste.com)
。
它显示了最近访问过的网站列表(从浏览历史)。
与电子邮件输入类型不同,这种类型的输入不进行验证。
iPhone 上的 Safari 会动态显示屏幕键盘上的“.com
”按钮。
不支持这种输入类型的浏览器会将该元素视为普通的文本输入。
涉及到什么
要创建一个 URL 输入元素,您所要做的就是指定标签输入的属性type
:
<input type="url" />
这样,URL 类型将用于包含 URL 地址的输入字段。
控件接受的数据类型是绝对 URI(统一资源标识符)。
注意:人们经常互换使用术语 URI 和 URL(统一资源定位器)。你需要知道的是,URL 是 URI 协议的子集,比如http://
、ftp://
和mailto:
。因此,所有的网址都是 URIs。
如何建造它
从上一个解决方案中提供的示例开始,向 HTML 页面添加一个 URL 类型的输入控件。
以下是完整的代码:
`
<html>
<head>
<title>
Solution 4-2: Using the URL input type
</title>
</head>
` 
图 4-5。 Safari 不支持 URL 输入类型,它呈现为简单的文本输入。
当你用 Chrome、Internet Explorer 或 Safari 打开该文件时,与简单的文本字段相比,你不会注意到任何区别,如图图 4-5 所示。
解决方案 4-3:使用数字微调控件
与数字打交道总是比你想象的要复杂。例如,用户可以很容易地在期望接收数字的文本字段中插入不需要的值。
一个经典的例子是电子商务网站中的数量字段。当您挑选要购买的商品并点击“添加到购物车”按钮时,系统还会要求您输入要购买的商品数量。有些网站有一个简单的数量文本字段,如图图 4-6 所示。
图 4-6。 数量字段需要一个简单的文本输入。
用户可以在这个字段中插入任何值,那么如果用户错误地插入了一个字母或一个无效的符号会发生什么呢?
应用必须在客户端或服务器端对其进行验证,以避免传递电子商务系统拒绝的无效信息(或者发送和开具不正确数量的发票)。
有些网站,比如 Amazon,通过使用组合框控件来解决无效数据类型输入的问题,如图图 4-7 所示。
图 4-7。 Amazon 允许用户从组合框中插入数量。
HTML5 通过向工具库中添加微调控件来帮助开发人员。
一个微调控件 是一个带有上下箭头的文本输入控件。结果是一个单行的文本输入,可以旋转显示文本字段中的每个数字,如图图 4-8 所示:
图 4-8。 微调控件,由 Google Chrome 呈现
用户可以单击箭头来更改他或她想要放入表单的数值。
通过使用这个表单元素,用户在填写表单时不会出错,因为提供的数字都是有效的。
不支持这种新标签的浏览器将组件呈现为一个简单的文本字段。
涉及到什么
尽管您看到的控件具有内置的导航机制,允许用户更改文本字段中的数值(不能直接编辑),但开发人员不必做任何事情。
您需要做的就是使用 number 值作为这个输入的类型属性,以便继承所有这些函数:
<input type="number" />
数值的增减机制会自动起作用。
有些属性允许您自定义控件。这些属性允许您设置最大值和最小值以及用于增加和减少机制的单位。这些具体特性在表 4-2 中列出。
表 4-2。 数字输入类型接受的有效属性
| **属性** | **描述** |
| :-- | :-- |
| 最大 | 一个浮点数,包含要在输入标记中使用和显示的最大数值。 |
| 部 | 一个浮点数,包含要在输入标记中使用和显示的最小数值。 |
| 步骤 | 包含元素值的增量单位。 |
| 价值 | 表示数字的字符串。 |
注意:使用这些属性,开发人员可以决定数字输入元素的行为方式,以及哪些值将从控件传递到服务器。
如何建造它
创建 spinner 控件所涉及的大部分工作都是由浏览器及其对该标签的支持级别来完成的。
开发人员所要做的就是插入一个数字类型的输入。
这里有一个例子:
`
Solution 4-3: Using a spinner control for numbers
`
`
`
我们创建了一个具有以下属性的数字类型输入控件:
min="0" max="100" step="1"
这意味着当用户单击向上或向下箭头时,值将增加或减少一个单位(步长=1),但他或她不能插入任何小于零或大于 100 的值。
该控件的呈现方式取决于浏览器。在图 4-9 中,我们展示了 Safari(左)和 Opera(右)如何显示这个对象。
图 4-9。 数字输入类型,由 Safari(左)和 Opera(右)呈现
然而,一些平板电脑的浏览器表现不同。图 4-10 显示了基于 Android 2.2 的 7 英寸三星 Galaxy Tab 使用的浏览器如何将输入控件呈现为简单的文本输入。但是,它会动态显示数字屏幕键盘:
图 4-10。 数字输入类型,由三星 Galaxy Tab 平板电脑呈现
专家提示
对于数字输入类型,有几个有趣的方法可以和 JavaScript 一起使用。它们是:
stepUp(n)
:将字段的值增加 n
stepDown(n)
:将字段值减少 n
valueAsNumber
:返回元素的值,解释为数字
解决方案 4-4:使用范围输入类型向表单添加滑块
HTML5 提供的另一个用来处理数字和表单的有趣组件是滑块。
一个滑块组件 是一个表单控件,允许用户沿着一个或两个轴在有限的范围内调整值。用户通过在对应于一系列值的轨道端点之间滑动图形滑块来选择值。
与微调器一样,该控件也有助于避免错误。
滑块在 web 应用中非常受欢迎,尤其是在配置器中,如图 4-11 所示的 Kayak.com 网站页面。
图 4-11。 滑块控件用于帮助用户在一系列值中进行选择。
用户与 thumb 对象交互来更改输入中的数值。以 Kayak.com 为例,用户更改航空旅行的出发时间。
HTML5 之前的 Slider 创建包括为元素的用户界面设计图形元素,以及用 JavaScript 编写控件的行为逻辑。有了 HTML5,这个控件现在是浏览器的原生控件,包括与键盘用户可访问性相关的所有方面(见第十二章)。
涉及到什么
在表单中创建滑块控件的标记非常简单。您只需要将范围指定为输入类型及其属性:
<input type="range" min="1" max="10" step="1" value="6"/>
与数字输入类型一样,这些属性允许您设置最小和最大值以及用于增加和减少函数的单位。
表 4-3 列出了这些具体的属性。
表 4-3。 范围输入类型接受的有效属性
| **属性** | **描述** |
| :-- | :-- |
| 最大 | 它是一个浮点数,包含要在输入标记中使用和显示的最大数值。 |
| 部 | 它是一个浮点数,包含要在输入标记中使用和显示的最小数值。 |
| 步骤 | 包含用作元素值的增量或减量的单位。 |
| 价值 | 表示数字的字符串。 |
许多浏览器的最新版本都支持这种新的输入类型:Opera、Safari、Chrome 和 Internet Explorer。
如何建造它
开发人员也不需要为这个解决方案做太多工作。或者,您可以使用<input>
标记并将range
值赋给type
属性。浏览器会完成剩下的工作。
下面是一个完整的例子:
`
<html>
<head>
<title>
Solution 4-4: Adding a slider to your form with the range input type
</title>
</head>
`
当你在浏览器中打开这个例子时,会出现如图图 4-12 所示。
图 4-12。 范围输入类型,由 Chrome 浏览器呈现
控件输入的交互性由浏览器自动应用,用户可以使用 thumb 元素来更改值。
专家提示
HTML5 中有一个新元素可以让你显示计算结果:<output>
。例如,该元素可用于显示用户在滑块中选择的数值。
在下面的示例中,我们已经更改了解决方案的代码,因此每当用户移动滑块时,选定的值都会显示在<output>
元素中:
`
`
在<input type="range">
标签中,我们在onchange
事件中添加了以下语句,每次用户移动滑块中的 thumb 元素时都会触发该事件:
<input type="range" min="1" max="10" step="1" value="6" onchange="myOutput.value=this.value"/>
myOutput
是在下面一行中声明的输出元素的名称:
<output name="myOutput"> 6 </output>
奇怪的是,iPhone、iPad 和 Android 等移动设备中的浏览器目前不支持这种表单控件;他们将其呈现为一个简单的文本框。
键盘不会为了数字输入而动态改变。
解决方案 4-5:发送多个文件
通过使用表单,特别是使用<input type = file>
,已经可以将任何类型的文件从你的计算机发送到一个使用旧版本 HTML 的远程服务器。
然而,这个表单控件有一个限制,一次只能发送一个文件。如果用户想要上传一个相册并发送几张照片,开发人员必须使用其他技术,如 Flash 或 JavaScript 来提供这一功能。
现在,有了 HTML5 和附加的属性,不使用任何外部语言就可以管理一切。
涉及到什么
HTML5 为文件输入类型引入了一个新属性multiple
,以提高文件上传的可用性。Multiple 是一个布尔属性,指示是否允许用户指定多个值。它是在标记输入标签内联指定的:
<input type="file" multiple />
Safari、Chrome、Firefox、Internet Explorer 和 Opera 的最新版本都支持该属性。
输入控件将根据浏览器呈现,具有简单的文本输入,在侧边具有用于选择文件的按钮(例如 Opera),或者只有一个按钮(例如 Chrome 和 Safari)。
如果多重属性是用“添加文件”文本声明的,Opera 也会改变按钮的标签,正如你在图 4-13 和图 4-14 中看到的。
图 4-13。 按钮的标签,在 Opera 中显示,当设置了 multiple 属性时改变
图 4-14。 Opera 中文件输入类型的按钮标签,如果未指定 multiple 属性
其他浏览器,如 Chrome,使用与简单文件输入类型相同的按钮标签。但是,它们为用户指定了所选文件的数量(而不是它们的文件名,就像 Opera 和 Firefox 一样),如图 4-15 所示。
图 4-15。 Chrome 渲染多属性的文件输入类型,具有相同的按钮标签,但指定了所选文件的数量
要进行多重选择,用户在点击选择文件或添加文件按钮后,使用 SHIFT 或 CTRL 或 CMD 键。
如何建造它
从技术角度来看,允许用户上传多个文件的唯一需要注意的事情是在标记文件输入类型的声明中添加 multiple 属性。
下面是一个完整的例子:
`
<html>
<head>
<title>
Solution 4-5: Sending multiple files
</title>
</head>
`
专家提示
用户选择的文件必须发送到服务器,并使用服务器端语言进行处理。一些编程语言,比如 PHP,需要在标签的 name 属性中添加括号来发送多个文件:
<input name="filesUploaded[]" type="file" multiple />
通过这样做,PHP 将构造一个数组数据类型,它将包含服务器上上传的文件。如果不指定括号,编程语言将按顺序处理文件,并且只提供脚本中的最后一个文件。
解决方案 4-6:使用数据列表组件创建类似建议的自动完成功能
在这个时代,任何用户都喜欢自动完成系统来帮助简化他们的 web 体验。
在过去,创建这样一个系统并不是一件简单的事情。这可能需要几个小时的工作。您可以通过搜索 Web 找到各种免费或付费的解决方案和库。
HTML5 引入了新的<datalist>
标记。此标记表示一组选项元素,这些元素代表其他控件的预定义选项。因此,它可以与表单控件(如文本输入)相关联。当控件获得焦点时,它会在填充数据时向用户提供一个预定义选项的列表,并允许用户自己键入内容。
旧的浏览器,或者那些不支持数据列表的浏览器,将呈现一个简单的文本字段。
涉及到什么
要将一个<datalist>
标签与一个<input>
控件相关联,可以使用一个列表属性:
`
`
在上面的代码片段中,list 属性通过<datalist>
标签的 id 将<input>
元素链接到<datalist>
标签。
一旦将 datalist 元素与输入控件相关联,就可以指定当用户选择该输入时将建议的值。要指定这些值,可以在<datalist>
标签中使用<option>
标签:
`
`
没有其他需要键入的内容。浏览器会完成剩下的工作。
如何建造它
下面是如何使用 datalist 元素的完整示例:
`
<html>
<head>
<title>`
` Solution 4-6: Creating a suggest-like autocomplete with the data list component
</title>
</head>
`
数据列表与文本输入相关联:
<input type="text" name="movies" list="movies"/> <datalist id="movies"> <option value="Star Wars"> <option value="The Godfather"> <option value="Goodfellas"> </datalist>
当用户在文本输入控件中插入一个值时,支持这个新标记的浏览器会在输入控件的正下方弹出一个菜单,其中包含数据列表中建议的值。图 4-16 显示了在 Opera 中实现的这一功能。
图 4-16。 建议选项显示在输入文本框的正下方。
此外,当用户从菜单中选择一个值时,该值将显示在与onforminput
事件的以下语句相关联的输出元素中:
<output name="myOutput" onforminput="this.value=movies.value" />
当表单接收到用户输入时,执行onforminput
事件。最终结果如图 4-17 所示。
图 4-17。 当用户选择其中一个选项时,该值被写入输出元素。
专家提示
为了使你的代码兼容旧的浏览器和那些不支持 datalist 的浏览器,你可以使用<select>
标签。
这样,该标记将只在不支持 datalist 元素的浏览器中显示:
` Enter your favorite movies:
or select one from the list:
`
解决方案 4-7:验证表单控件
在本章的第一个解决方案中,我们向您展示了如何使用内置机制来验证电子邮件输入类型。这个函数是 HTML5 的新功能。
当您处理表单时,数据验证无疑是一个重要的方面,可能需要付出相当多的努力。我们经常求助于混合验证系统。使用 JavaScript 或 AJAX 框架(如 JQuery、Dojo 和 MooTools),您可以进行客户端验证,并且可以使用服务器端语言(PHP、Python、Java 等)开发服务器端验证过程。
由于 HTML5 中插入了新的属性,您可以将一些数据验证功能委托给浏览器,并减少这类操作所需的工作量(至少从客户端来说)。
涉及到什么
一些表单控件继承了验证系统,而无需编写任何代码。在解决方案 4-1 中,我们展示了电子邮件类型文本输入的验证机制如何通过仅声明标记来自动工作:
<input type="email" />
这同样适用于 URL 和数字标记、<input type="url">
和<input type="number">
。
但是,有一个属性可以用来指定表单中是否存在必填字段,不能留空。
要请求这种验证,您需要在输入控件中使用required
:
<input type="text" required />
该属性是布尔型的。指定时,元素是必需的。
支持此属性的浏览器会显示一条错误消息或在产生错误的字段上插入一个红色边框,并且不会提交表单。
如何建造它
对于这个解决方案,通过为三个字段中的两个添加required
属性来修改解决方案 4-2 中的代码。
以下是完整的代码:
`
<code> </code> Solution 4-7: Validating form controls<br>
`
如果您打开文件并尝试发送两个必填字段中没有任何数据的表单,如果必填字段为空,浏览器将不允许您提交表单,您将获得如图图 4-18 所示的结果。
图 4-18。 浏览器显示必填字段(Opera)的错误消息。
表单可用性的一个重要方面是,必填字段必须为用户提供可视或文本提示,告知他们所需的数据。一种惯例是在必填字段旁边插入一个星号(*)。
通过使用伪类:valid
、:invalid
、:optional
和:required
,你可以用 CSS 添加一些文本和图形线索。
如果表单元素的值在提交它所属的表单之前分别是必需的或可选的,那么它就是:required
或:optional
。
你可以通过阅读这篇文章进一步了解这个话题:[www.w3.org/TR/css3-ui/#pseudo-validity](http://www.w3.org/TR/css3-ui/#pseudo-validity)
。你所要做的就是使用这些伪类使字段更加稳定。通过添加一个<style>
块来更改之前的代码,如下所示:
`
<br>
Solution 4-7: Validating form controls<br>
`
在新的样式块中,我们声明了两个 CSS 语句。第一个作用于所需的类,并用伪类:after
在选择器后添加文本;在我们的例子中,标签元素旁边的一个*:
#myForm .required:after { content: " * "; color:red;}
另一方面,第二条语句在输入标记上使用所需的伪类,将背景色改为红色:
#myForm input:required { background:red; }
代码中唯一需要更改的是标记标签,您需要使用属性类将:required
类与该标签相关联,因此它将插入*:
<label for="name" class="required">Name</label> <input id="name" name="name" type="text" placeholder="Insert your first name" required /><br/> <label for="email" class="required">Email</label> <input id="email" name="email" type="email" placeholder="Insert your email" required /><br/>
一旦你保存了文件并在浏览器中执行它,你会看到 CSS 伪类被应用到如图图 4-19 所示的字段中。
图 4-19。 格式样式显示在输入字段中(显示在 Opera 中)。
专家提示
默认情况下,将自动应用 HTML5 中字段的内置验证机制。但是,在某些情况下,有必要使用 JavaScript 来创建更复杂、更健壮的验证例程。
在这种情况下,有必要覆盖浏览器的默认验证系统。
novalidate
属性指定表单在提交时不应被验证:
`
`
您可以在表单级别指定该属性,如上例所示,或者在下面的<input>
类型中指定:文本、搜索、URL、电话、电子邮件、密码、日期选择器、范围和颜色。
解决方案 4-8:使用正则表达式创建自定义输入类型
正则表达式提供了一种强大、简洁和灵活的方法来匹配文本字符串,例如特定的字符、单词或字符模式。正则表达式是用正规语言编写的,可以被正则表达式处理器解释,正则表达式处理器是一个程序,它或者作为解析器生成器,或者检查文本并识别与提供的规范相匹配的部分。详见维基百科:[
en.wikipedia.org/wiki/Regular_expression](http://en.wikipedia.org/wiki/Regular_expression)
。
HTML5 允许您检查用户的输入,并将输入值与正则表达式进行匹配。
涉及到什么
在以前版本的 HTML 中使用正则表达式需要编写的代码如下:
<input type="text" name="ssn" onblur="if (!^\d{3}-\d{2}-\d{4}$this.value) alert(this.title+'\nAn error occurred. Please verify your data.');" title="The Social Security Number"/>
在 input 元素的onblur
事件上,执行一个 JavaScript 语句。它控制应用于字段中数据的模式,如果验证不成功,它会提供一条错误消息。
HTML5 提供了一个新的属性,允许您通过正则表达式将字符模式与文本输入相关联,以验证字段中插入的数据。这个标记非常简单:
<input type="text" name="ssn" pattern="(!^\d{3}-\d{2}-\d{4}$"
pattern 属性中指定的值必须与本文档中描述的 JavaScript 模式产品相匹配:[www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf)
。
注意:匹配 JavaScript 模式意味着用于该属性的正则表达式语言与 JavaScript 中使用的相同,只是模式属性必须匹配整个值——而不仅仅是任何子集。(这似乎暗示了^(?:在模式的开头,a )$在结尾。)
为了向用户提供模式的描述,或者在输入无效值时在字段上提供错误报告,您可以使用属性title
:
<input type="text" name="ssn" pattern="(!^\d{3}-\d{2}-\d{4}$" title="The Social Security Number" />
如何建造它
在下面的解决方案中,我们使用正则表达式来验证美国邮政编码:
(\d{5}([\-]\d{4})?)
这个表达式被插入到文本输入的pattern
属性中。以下是完整的代码:
`
<html>
<head>
<title>
Solution 4-8: Creating custom input types using regular expressions
</title>
<body>
`
当您在支持模式属性的浏览器(如 Opera)中执行该文件时,单击表单的提交按钮,您将获得如图图 4-20 所示的结果。
图 4-20。 使用正则表达式模式的文本输入中显示的错误消息。
浏览器提供了对与属性模式中指定的正则表达式相匹配的数据的有效性的控制。如果失败,它将返回一条错误消息。
专家提示
还不是所有的浏览器都支持这个强大的属性。幸运的是,有一个库填补了这个空白:Google 的 Web Forms 2,你可以在下面的地址找到它:[
github.com/westonruter/webforms2](https://github.com/westonruter/webforms2)
。
正如网站上所描述的,该项目是 WHATWG Web Forms 2.0 规范的跨浏览器实现。如果库在加载时意识到浏览器与一些新的 HTML5 函数不兼容,比如 pattern 属性,它会应用自己的方法。
您需要使用 Script 标记导入 JavaScript wbforms2_src.js 库来使用该库:
<script type="text/javascript" src="YOUR_FOLDER/webforms2_src.js"></script>
同样重要的是,webforms9781430233862.css 和 webforms2-msie.js 与 webforms2.js 或 webforms2-p.js(无论您决定使用哪个)位于同一目录中。
该实现已经过测试,应该可以在以下浏览器中工作:
Mozilla Firefox 1.0.8
Mozilla Firefox 1.5.0.9
Mozilla Firefox 2
Internet Explorer 6
Internet Explorer 7
野生动物园 2.0.4
Safari 3 (Windows)
Opera 9(本地实验实现)
解决方案 4-9:在输入字段中设置占位符文本
可用性是 web 应用,尤其是表单的一个微妙且非常重要的方面。当用户输入数据时,开发人员试图通过最小化错误的可能性来使用户对表单的体验尽可能平滑。一个简单但有效的技巧是在输入字段中放置文本,为用户提供提示。
图 4-21 显示了 Opera 搜索栏中的“使用 Google 搜索”提示文本。
图 4-21。 Opera 搜索栏使用“用谷歌搜索”文本作为对用户的提示。
即使在过去创建这种功能很简单,您仍然必须使用 JavaScript。
HTML5 引入了一个与输入控件相关联的新的智能属性,它可以在输入字段中插入文本,但是如果字段获得焦点,文本就会消失;如果字段失去焦点,文本就会重新出现。所有这些都是在没有 JavaScript 代码的情况下完成的。
涉及到什么
允许您实现该功能的属性是placeholder
。在 web 表单中使用占位符文本非常容易:
<input type="text" placeholder="Insert your first name"/>
不支持该属性的浏览器将会忽略它,并且在字段中不显示任何内容。
该属性仅允许您管理简单文本—不允许换行(LF)或回车(CR)字符,因此您不能使用 HTML 标记或图像。
如何建造它
在这个解决方案中,我们对解决方案 4-2 的代码做了一些小小的修改,在输入字段中添加了placeholder
属性:
`
<html>
<head>
<title>
Solution 4-9: Setting placeholder text in an input field
</title>
</head>
`
`
`
专家提示
如果您阅读了关于这个属性的规范(在这个 URL: [www.w3.org/TR/html5/common-input-element-attributes.html#the-placeholder-attribute](http://www.w3.org/TR/html5/common-input-element-attributes.html#the-placeholder-attribute)
),您将会了解到一个可用性建议是将title
属性用于更长的提示或其他建议文本。
解决方案 4-10:创建日期和时间控件
日期和时间经常在表单中使用。如果你想一想我们使用这些数据的频率,用不了多久就会意识到,它们的准确有效使用可以帮助决定整个网站的成败。你觉得我夸张了?
考虑预订航班。您在该查询的典型表单中插入的信息是:出发机场、到达机场、出发日期、返回日期以及出发和返回航班的首选时间。
所以想象一下,拥有一个可以帮助用户准确插入日期和时间信息的表单,减少甚至排除错误的可能性,是多么重要。
我最喜欢使用的预订航班的网站之一是 Kayak.com,它有一个非常高效的航班搜索系统(被大多数旅游网站使用)。从图 4-22 中,您可以看到一旦选择了出发或返回字段,一个日期选择器就会出现在您的表单顶部,并允许您选择日期。
图 4-22。 日期选择器允许用户从日历中插入日期,以避免键入错误。
此外,还必须考虑到日期格式会因国家而异。美国和英国等盎格鲁-撒克逊国家使用月-日-年格式,而大多数欧洲国家使用日-月-年格式。
Web 开发人员在使用 JavaScript 控件开发日期选择器函数时会考虑到这些问题,这些控件可以在最常见的 AJAX 框架中找到(或者从 Web 上的各种 JavaScript 库中找到)。
HTML5 引入了一系列标记来处理日期,将管理日期和时间的艰巨任务留给了浏览器。
涉及到什么
HTML5 引入了许多不同的输入类型来处理日期/时间选择器:
<input type="date" /> <input type="time" />
日期类型创建一个带有内置机制的日期选择器,通过浏览日历来选择数据,如图图 4-23 所示。
图 4-23。 一个日期控件,由浏览器呈现(显示在 Mac OSX Safari 中)
包含小时、分钟和秒分隔符的输入类型时间文本输入如图 4-24 所示。
图 4-24。 一个时间控件,由浏览器呈现(显示在 Mac OSX Safari 中)
还有其他处理日期和时间的控件。
timedate
类型表示将元素的值设置为表示全球日期和时间(带时区信息)的字符串的控件。
此外,您可以允许用户选择一个值,即一个month
或一个week
,而不是一个完整的日期。事实上,有周和月输入类型:
<input type="month" /> <input type="week" />
如何建造它
下面是一个完整的例子,它使用了 HTML5 提供的所有日期和时间输入类型:
`
<html>
<head>
<title>
Solution 4-10: Creating Date and Time Controls
</title>
`
`
`
总结
所有的 web 应用都使用表单。事实上,表单是任何需要用户输入的 web 应用的重要组成部分。
HTML 版本 4 提供了相当基本的表单控件,大部分是文本字段。此外,大多数交互都发生在服务器端。插入到表单控件中的数据类型的验证是 HTML 4 中在服务器端执行的操作的一个经典例子,除非它们使用 JavaScript 或 AJAX 框架。
在这一章中,你已经了解了使用 HTML5 表单,现在可以更快更有效地开发具有高级终端用户体验的应用。在第五章中,你将学习如何使用新的 HTML5 音频和视频标签在网页中嵌入视频。
五、HTML5 媒体元素:音频和视频
今天,网络真正走向了多媒体。音频和视频已经成为我们每天在网上浏览的内容不可或缺的一部分。由于多媒体内容的带宽和压缩技术的不断发展,现在在移动设备上观看电视节目或舒适地坐在电视机前观看电影已经很常见,这要归功于支持苹果或谷歌电视技术的设备。
此外,大多数电视网络在其网站上提供免费或收费的内容,更不用说 YouTube 和类似的视频分享网站的流行了。
尽管多媒体内容的可用性很高,但在 HTML5 之前,并没有在 Web 上传送多媒体的开放标准。事实上,多媒体内容在 Web 上的交付曾经(现在仍然)委托给第三方插件,如 QuickTime、Windows Media Player、Flash Player 和 Real Player。
一开始是一片混乱。插件的不均衡可用性导致那些想要在网站上发布视频的人以不同的格式提供视频,这些格式可以在最常见的播放器上使用。
多种格式意味着在网站上发布时使用不同的视频编解码器对同一视频进行编码。视频编解码器是能够对数字视频进行视频压缩和/或解压缩的软件。压缩通常采用有损数据压缩方法。“有损”是指软件通过丢失原始源的一些信息来压缩数据。在维基百科上阅读更多关于视频编解码器的信息:[
en.wikipedia.org/wiki/Video_codec](http://en.wikipedia.org/wiki/Video_codec)
。
这是插件之间的一场真正的战争,在 2005 年到 2006 年间,一个插件脱颖而出:Adobe Flash Player。
Adobe Flash Player 是一种插件,在早年非常流行,90%的联网机器都安装了这种插件。在被 Adobe 收购之前,Macromedia 在 Flash Player 7 中插入了 Flash 视频格式,没过多久,业内的大公司就采用了这种格式,这样几乎任何人都可以播放基于 Flash 的视频。
Flash Video 是一种容器文件格式,用于使用 Adobe Flash Player 版本 6–10 通过互联网传送视频。Flash 视频内容也可以嵌入到 SWF 文件中。
有两种不同的视频文件格式称为 Flash 视频:FLV 和 F4V。FLV 文件中的音频和视频数据的编码方式与 SWF 文件中的相同。Adobe Flash Player 支持这两种格式,它们目前由 Adobe Systems 开发。FLV 最初是由 Macromedia 开发的。
这种格式已经迅速成为网络上嵌入式视频的首选格式。Flash 视频格式的著名用户包括 YouTube、Hulu、Google Video、Yahoo!视频、Metacafe、Reuters.com 和许多其他新闻提供商。
注意:虽然 Flash Player 和 Flash Video 格式非常受欢迎和支持,但 Apple 决定不支持 iOS 设备(iPhone、iPad 和 iPod Touch)使用它。相反,这些设备支持 HTML5。
如果你想进一步研究这个话题,可以从苹果公司首席执行官在[www.apple.com/hotnews/thoughts-on-flash/](http://www.apple.com/hotnews/thoughts-on-flash/)
发表的“关于 Flash 的想法”和 Adobe 首席执行官在[blogs.wsj.com](http://blogs.wsj.com)/digits/2010/04/29/live-blogging-the-journals-interview-with-adobe-ceo/
的回复开始。
有关 Flash 视频的更多信息,请访问维基百科:[
en.wikipedia.org/wiki/Flash_Video](http://en.wikipedia.org/wiki/Flash_Video)
。
在这个非凡的发展时期,HTML 提供了两个标签来允许人们在网页中插入外部插件可以使用的内容:标签<object>
和标签<embed>
。
HTML5 引入了一种用于传送多媒体内容的本地开放标准。因为语言规范仍在定义中,所以只在表 5-1 中列出的浏览器版本中支持这些新标签。
当前的问题不是采用 HTML5 引入的新标记,因为 HTML5 是编解码器使用的标准定义(见图 5-1 )。相反,问题是 Ogg Theora、H.264 和 VP8/WebM 是目前视频元素支持的唯一格式。其中哪些会在未来得到浏览器的普遍支持?这个问题我们还没有答案。
目前在这个问题上有很大的争论和不同的观点。讨论最多的是谷歌的立场,谷歌收购了 On2 Technologies,并开源了 VP8/WebM 视频编解码器。2011 年 1 月,谷歌也宣布将从 Chrome 上移除对 H.264 编解码器的支持,但苹果和微软仍将继续支持这一编解码器。
注:On2 Technologies 生产以下视频编解码器:VP3、VP4、VP5、VP6、VP7 和 VP8。Ogg Theora 源自 On2 Technologies 向公众发布的专有 VP3 编解码器。
本质上,一场建立下一个视频编解码器的新战役已经开始。和往常一样,我们只能拭目以待。
图 5-1 网络上视频编解码器的现状及其浏览器支持。(来源: 维基百科,[
en.wikipedia.org/wiki/HTML5_video](http://en.wikipedia.org/wiki/HTML5_video)
)
在本章中,我们将介绍如何使用新的 HTML5 标记来处理音频和视频多媒体内容的解决方案。
解决方案 5-1:在网页中嵌入视频
有了 HTML5,在网页上发布视频变成了真正简单的操作。我们借助第三方插件让视频在 HTML 页面中可访问的时代即将结束。
像这样的代码可能会成为遥远的记忆:
``
最终用户将不再需要为了观看视频而下载额外的插件或者更新到他们已经安装的插件的正确版本。也不会因为某些第三方插件的不稳定性而导致浏览器崩溃。
让我们看看 HTML5 的变化。
涉及到什么
随着新的<video>
标签的引入,我们所要做的就是在网页中声明这个标记,指定要加载的视频,浏览器将完成剩下的工作(假设它支持视频元素):
<video src="your_video.ogg" />
src
属性包含要在页面中显示的媒体资源(视频或音频)的地址。在上面的代码示例中,我们要求它加载 Ogg Theora 格式的视频。
即使该代码本身足以使视频可用,也最好使用宽度和高度属性来指定视频容器的尺寸:
<video width="640" height="360" src="your_video.mp4" />
如果不设置这些值,浏览器将使用原始视频资源的尺寸。
视频标签支持的其他属性包括:
preload: 告诉浏览器在页面加载时预加载视频内容。这样,用户在播放视频时就不必等待视频加载。
自动播放: 告诉浏览器一旦视频可用就自动播放。您需要小心使用这个属性,因为您并不总是确定用户是否想看视频。如果用户通过移动设备连接,这一点尤其正确,因为带宽更贵。
循环: 视频一结束就重新执行。
控件: 如果指定,它告诉浏览器显示一组内置控件,如播放、停止、暂停和音量。
poster: 指定用户代理(浏览器)在没有视频数据可用时可以显示的图像文件。
如何建造它
下面的代码示例演示如何在网页中导入和显示视频:
`
<html>
<head>
<title>
Solution 5-1: Embedding a video in a web page
</title>
Comtaste's Showreel
`
如果在浏览器中打开这个例子,视频会立即执行,占据 640 像素宽,800 像素高的空间,如图图 5-2 所示。
图 5-2。 视频会在网页中自动播放。
注意:如果您想尝试这个加载远程视频的解决方案,您可以使用下面的代码:
*<video width="640" height="360" src="http://www.youtube.com/demo/ google_main.mp4" autoplay />*
如果用户在不支持视频标签的浏览器中打开 HTML 文件,将显示一个空白页面。因此,在发生这种情况时,最好在页面中提供替代内容。使用 poster 属性显示可能是视频帧的图像(作为图像捕获)。它可以是本地的,也可以来自网络上的其他地方。
让我们将该属性添加到视频标签中:
<video width="640" height="360" src="comtaste_showreel.mp4" autoplay poster="../img/Figure_5_3.png"> Video is not supported in this browser! </video>
如果视频没有加载,浏览器会显示海报属性中指定的图像,如图图 5-3 所示。
图 5-3。 浏览器显示海报属性中指定的图像。
如果未指定 poster 属性,并且浏览器无法加载视频,则默认情况下将显示电影的第一帧。
专家提示
对于移动 Apple iOS 设备(iPhones、iPod Touches 和 ipad)和 Android 设备,HTML5 视频支持存在一些问题:
如果使用 poster 属性,iOS 将忽略视频元素。苹果已经声明在 iOS 4.0 中修复了这个 bug。
iOS 只支持 H.264 格式。如果使用<source>
标签(见下一个解决方案),它只会识别第一种视频格式。
另一方面,Android 设备不支持浏览器的本地控件,因此会忽略它们。此外,操作系统会对用于指定视频容器的 type 属性感到有点困惑。
解决方案 5-2:检测跨浏览器的视频支持
在这一章的前面,我们已经讨论了网络上可用的各种视频格式。
HTML5 的第一个规范规定浏览器对视频元素的支持必须基于两种格式:Ogg Vorbis(用于音频)和 Ogg Theora(用于视频)。这一声明在苹果和诺基亚等巨头中引起了不小的轰动,以至于 WSC 从规范中删除了对音频和视频格式的任何引用。
这个选择显然导致了网络上的格式混乱。MPEG4 (H.264)、Ogg Theora、AVI 和 VP8/WebM 都准备发动战争,希望成为未来的标准。就连浏览器也开始站队了。图 5-4 总结了各种浏览器对视频容器的支持:
Opera 支持 Ogg Theora,未来还会支持 WebM。
Chrome 支持 Ogg Theora 和 WebM。(它最近宣布不再支持 H.264。)
Firefox 支持 Ogg Theora,未来还会支持 WebM。
Safari 支持 H.264。
Internet Explorer 支持 H.264 和 WebM。
图 5-4。 浏览器对视频容器的支持
在当前情况下,很容易理解为什么有必要进行检查来验证加载页面的浏览器,并选择要加载的视频格式的正确版本。
涉及到什么
有多种技术可以验证加载页面的浏览器支持哪种视频格式。这个解决方案使用了来自[www.modernizr.com](http://www.modernizr.com)
的 Modernizr JavaScript 库,我们在第一章中介绍过。
我们还可以使用这个视频库的本地函数来验证它对视频标签和编解码器的支持。这里有一个例子:
if (Modernizr.video) { if (Modernizr.video.webm) { // Support the WebM } else if (Modernizr.video.ogg) { // Support the Ogg Theora + Vorbis } else if (Modernizr.video.h264){ // Support the H.264 video + AAC audio } }
基本上,该库通过测试视频元素的canPlayType
属性来执行视频支持验证。
然后,浏览器返回三个值之一:
空字符串: 不支持。
可能是字符串: 不确定不支持。
大概字符串: 支持容器和编解码器的组合。
如何建造它
要创建一个验证视频标签支持并加载正确代码的 HTML 页面,请使用一种与前面的解决方案类似的机制,但有一些小的不同。
以下是使用 Modernizr 库检测跨浏览器视频支持的完整代码:
`
<html>
<head>
<title>
Solution 5-2: Detecting Video support across browser
</title>
<body>
Comtaste's Showreel
This is an HTML5 Video Element
`
注意:在撰写本文时,Modernizr 库的最新版本是 1.7,但是这个解决方案也适用于旧版本。
我们在代码中创建了两个 div:一个包含支持<video>
标签的浏览器的代码(id 等于#video),另一个包含其他浏览器的代码(id 等于#no-video)。我们还插入了一个带有 CSS 语句的样式块,以隐藏带有命令 display: none
的必要 DIV。
在第一个 DIV(id # video)块中,我们插入了<video>
标签,而在第二个块中,我们将使用 Kroc Camen 编写的技术,它不使用 JavaScript,只需要两种视频编码:一个 Ogg 文件和一个 MP4 文件。
这种技术基于一种假设,即如果不支持 HTML5 视频,将使用 Adobe Flash 来代替。
这种方法与 HTML4、HTML5(有效标记)和 XHTML 1 兼容,当用作 application/XHTML+XML 时也能工作。
在 Kroc Camen 的“人人视频”中,可以了解更多关于这种使用 HTML5 视频的无 JavaScript 方法的信息。你可以在[
camendesign.com/code/video_for_everybody](http://camendesign.com/code/video_for_everybody)
找到这篇文章。
在第二个 DIV(如果浏览器不支持 video 标记,将加载该 DIV)中,插入以下代码:
<!-- fallback to Flash: --> <object width="640" height="360" type="application/x-shockwave-flash" data="__FLASH__.SWF"> <!-- Firefox uses the
data attribute above, IE/Safari uses the param below --> <param name="movie" value="comtaste_showreel.SWF" /> <param name="flashvars" value="controlbar=over&image= comtaste_showreel.JPG&file=comtaste_showreel.mp4" /> <!-- fallback image. note the title field below, put the title of the video there --> <img src="comtaste_showreel.JPG" width="640" height="360" title="No video playback capabilities, please download the video below" /> </object>
专家提示
对于如何处理网络上的视频,还有其他的解决方案。在[
github.com/etianen/html5media](https://github.com/etianen/html5media)
有一个非常成功的项目是 HTML5media 项目。
这个项目通过使用 HTML5 多媒体播放器来检测对视频标签和视频格式的双重支持。它支持 H.264 (MP4)和 Ogg Theora 格式。
如果浏览器不支持 HTML5 视频标签,它会使用 Adobe Flash Player 来提供与原始视频相同的功能。这也是我们使用 Flowplayer JavaScript 库([
flowplayer.org](http://flowplayer.org))
)的原因。
要在所有主流浏览器中启用 HTML5 视频标签,您只需调用 jQuery 库和文档头中的脚本:
`
`
然后,您可以使用以下代码将视频插入到 HTML 页面中:
<video src="video.mp4" autoplay autobuffer></video>
其他相同主题的库有:[
mediaelementjs.com/](http://mediaelementjs.com/)
和[
videojs.com/](http://videojs.com/)
。
解决方案 5-3:创建自定义视频控制器
任何多媒体元素,无论是音频还是视频,都必须为用户提供使用传统的播放、停止、暂停和音量控制按钮与内容进行交互的选项。HTML5 有一个在屏幕视频控制器上本地呈现上述按钮的属性。
但是,创建自定义控件按钮是可能的(通常也是更可取的),以便它们与发布视频的网站的图形相匹配。
音频和视频元素是 HTML5 DOM 媒体元素的一部分,它提供了一个强大且非常易于使用的 API 来控制电影回放。在本解决方案中,我们将了解如何向视频添加自定义控件。
涉及到什么
在视频内容中插入一个视频控件很简单。事实上,<video>
标签有一个 controls 属性,它利用了浏览器的内置控件。您需要做的只是在标签中指定属性,以在视频上显示控制器:
<video width="640" height="360" src="comtaste_showreel.mp4" controls />
每个浏览器都将使用自己的图形来设计视频控件。例如,在图 5-5 中,您可以看到 Safari 中用于视频控制的图形。
图 5-5。 Safari 渲染的视频控件
这种差异会让许多设计师感到惊讶,因为他们想要控制视频控件的外观和感觉,使它们与网站的图形相匹配。
在图 5-6 中,你可以看到所有其他主流浏览器中用于视频控件的图形。
图 5-6。 主流浏览器如何渲染视频控件
但是,您可以使用自己的图形创建自定义视频控件,并决定提供哪些功能。例如,您可以编写一个功能来同步视频中的字幕并用视频控制器激活它们,或者编写一个功能来允许用户从一个书签跳到另一个书签,以查看他或她最感兴趣的片段。
因为你是用 JavaScript 写视频控制器功能的人,所以真的可以随心所欲。
为此,您必须使用 HTML5 媒体属性和您可以监听的 DOM 事件,例如加载进度、媒体播放、媒体暂停和媒体播放完成。例如,对于播放视频的功能,有以下属性:
media.paused: 如果回放暂停,则返回 true 否则为假。
media.ended: 如果回放已经到达媒体资源的结尾,则返回 true。
media . defaultplaybackrate[= value]: 返回当用户不在媒体资源中快进或倒退时的默认回放速率。
媒体。playbackRate [ = value ]: 返回当前播放速率,其中 1.0 为正常速度。
Media.played: 返回一个TimeRanges
对象,代表浏览器已经播放的媒体资源的范围。
Media.play(): 将 media.paused 属性设置为 false,加载媒体资源并在必要时开始播放。如果播放已经结束,它将从头开始播放。
Media.pause(): 将 media.paused 属性设置为 true,在必要时加载媒体资源。
Media.volume: 获取或设置视频音轨的音量。它采用范围从 0.0(无声)到 1.0(最大声)的浮点值。
Media.muted: 使视频静音。
Media.currentTime: 以秒为单位返回当前播放位置,用浮点数表示。
有关媒体属性和事件的完整列表,请参阅下页:[
www.whatwg.org/specs/web-apps/current-work/multipage/video.html](http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html)
。
通过使用这些方法和几行 JavaScript,您可以创建一个简单的视频控制器:
if (video.paused) { video.play(); }
这个条件控制视频是否暂停,如果暂停,它用play()
方法执行视频。另一方面,如果视频已经结束,我们必须通过操作currentTime
属性回到开始,然后再次播放它:
if (video.paused) { video.play(); } else if (video.ended) { video.currentTime=0; video.play(); }
基本上,契约是用几行代码完成的。让我们看看如何创建一个完整的示例。
如何建造它
首先创建两个 DIV 块。第一个将包含视频元素,第二个将包含视频控件。
然后创建一个新文件,并在一个 DIV 块中添加一个<video>
标记,并为其分配一个等于video_container
的 id。id 很重要,因为稍后您将使用 JavaScript 和 CSS 访问它。
以下是代码片段:
`
`
它加载一个 MPEG 格式的视频,大小为 320×176 像素。
插入第二个 DIV 块,它将包含用于播放、暂停、音量、静音和计时的视频控件。为此,请为播放、暂停和静音按钮使用<button>
标签,然后使用滑块来控制音量。关于视频播放时间的信息包含在一个标签中:
`
`
在这种情况下,注意标记的 id 属性的名称也很重要,因为稍后您将需要它们来引用 JavaScript 中的对象。
网页用户界面的最终结果如图图 5-7 所示。
图 5-7。 视频元素及其自定义视频控制器
现在你要写视频控制器的逻辑。插入一个脚本块,并在网页加载后立即启动事件处理程序。在事件处理程序中,定义包含视频元素的全局变量和包含视频控制器引用的局部变量:
`
</head>
<body>
`
专家提示
video 元素公开了一个事件,该事件允许您跟踪视频执行的剩余时间。
当当前回放位置作为正常回放的一部分改变时,执行timeupdate
事件。
让我们添加一个标记标签,它将包含视频播放时的计时。
在 DIV 块中插入以下代码:
` Play
Pause
Mute
<label id="time">-:--:--
`
我们将使用 JavaScript 在标签中插入视频执行小时、分钟和秒的值。
为视频元素的timeupdate
事件添加一个事件处理程序。在 window 对象的 onload 事件的函数中插入以下代码行:
video.addEventListener('timeupdate', updateTime, false);
然后声明updateTime
方法,该方法将小时/分钟/秒值写入我们在网页视图中创建的<label>
中:
`function updateTime()
{
var sec= video.currentTime;
var h = Math.floor(sec/3600);
sec=sec%3600;
var min =Math.floor(sec/60);
sec = Math.floor(sec%60);
if (sec.toString().length < 2) sec="0"+sec; if (min.toString().length < 2) min="0"+min;
document.getElementById('time').innerHTML = h+":"+min+":"+sec;
}`
该函数的唯一任务是将currentTime
(以秒为单位)分割成单独的小时/分钟/秒字符串,然后通过innerHTML
方法将其写入 id 等于时间的标签中:
document.getElementById('time').innerHTML = h+":"+min+":"+sec;
然后添加一点 CSS 来使标签字段更好看:
``
最终结果如图图 5-8 所示。
图 5-8。 视频元素及其工作视频控制器
解决方案 5-4:预加载视频
通过互联网传送媒体有两种主要方法:
对这两种方法之间差异的讨论超出了本解决方案的范围。我们需要知道的是,第一种方法,即流式传输,使用一个服务器和一个协议来允许用户在整个视频长度内查看视频的任何部分,而不必等待视频加载。
另一方面,第二种方法使用 HTTP 标准协议来传输文件。在这种情况下,用户不能跳到尚未加载的视频部分。
因此,对于以渐进式下载方式交付的视频,管理预加载操作非常重要。这允许你在后台加载视频,即使用户还没有执行电影。
涉及到什么
video 元素有一个 preload 属性,允许您在后台加载视频。您只需在 video 标记中声明它,并指定它可以具有的三个值之一:
auto(默认值): 页面一加载就开始下载视频文件。
无: 不开始下载视频文件。
元数据 :建议浏览器预取资源元数据,如大小、持续时间等。
然而,你必须非常注意如何使用这个属性。事实上,这个属性告诉浏览器在页面加载时就开始下载视频文件。即使用户从不执行视频,在后台页面也会要求加载,浪费了宝贵的带宽。
下面是如何使用属性的示例:
<video width="640" height="360" src="comtaste_showreel.mp4" autoplay preload poster="../img/Figure_5_3.png" />
注意,iOS 上的 Safari 浏览器会忽略 preload 属性,从不预加载视频。
如何建造它
为此解决方案创建一个新文件,从插入要预加载的视频开始:
`
<html>
<head>
<title>
Solution 5-4: Preloading a video
</title>
</head>
<body>
<video width="320" height="176" preload
src="comtaste_showreel.mp4" />
`
因为 auto 值是默认值,所以不必写 preload="auto "。
然而,在本例中,我们指定浏览器根本不应该预加载视频:
<video width="640" height="360" src="comtaste_showreel.mp4" autoplay preload="none" poster="../img/Figure_5_3.png" />
专家提示
有些浏览器支持autobuffer
属性,在不使用autoplay
时使用,它会强制在后台下载视频。iOS 上的 Safari 浏览器忽略预加载属性,从不预加载视频。如果autoplay
和autobuffer
都被使用,那么autobuffer
被忽略。
由于autobuffer
属性在 Firefox 4 中不再存在,并且preload
属性在 Firefox 3.5 和 3.6 中也不存在,如果您想要完整下载一个视频,您需要在 video 元素中同时使用preload attribute set to "auto"
和autobuffer
:
<video autobuffer preload="auto" width="640" height="360" src="comtaste_showreel.mp4" />
请注意这种方法,因为即使用户永远看不到视频,也会下载视频,从而耗尽带宽。如果您希望视频仅在用户实际播放时才被下载,您必须省略preload
或autobuffer
属性。
解决方案 5-5:为视频创建自定义搜索栏
每个像样的视频控制器还提供了通过搜索栏跳到电影中特定点的选项,以及管理音量、播放和暂停视频的功能。
YouTube 允许用户通过在视频控制器的播放和暂停按钮上拖动拇指,使用搜索栏快速跳过视频,如图图 5-9 所示。
图 5-9。 YouTube 使用的搜索栏
通过使用 video 元素的一些属性并编写几行 JavaScript,也可以用 HTML5 创建这种类型的函数。
涉及到什么
我们将使用两个属性和一个事件来创建一个搜索栏:currentTime
和duration
属性,以及timeupdate
事件。
我们已经在之前的解决方案中讨论过这些属性,但这里有一个快速提醒:
属性返回当前时间,以秒为单位,作为一个浮点值。
duration
属性以浮点值的形式返回以秒为单位的实际持续时间。如果持续时间未知,则返回 NaN 如果视频是流式的,则返回 Infinite
无论当前位置如何变化,都会执行timeupdate
事件。
通过将这些属性与滑块控件相关联,您可以为用户提供从视频的一部分跳到另一部分的选项。
对于滑块控件,使用 HTML5 的新输入类型range
,它使用浏览器的内置特性:
<input type="range" step="any" id="seekbar">
当timeupdate
事件被执行时,这个输入元素将更新它的值,因为它将被绑定到视频的时间值。
让我们看看它是如何工作的。
如何建造它
对于这个解决方案,让我们以我们在前一个解决方案中创建的示例为例,您将向其中添加一个滑块控件,该控件将充当视频元素的搜索栏。
首先添加本例中唯一的新用户界面元素:slider。
在 id 等于video_controller
的 DIV 块中,在静音按钮和包含视频时序的标签之间添加一个range
输入类型:
`
`
我们还没有处理max
属性,该属性允许我们为这个 input 元素的 slider 控件指定最高值。我们将使用 JavaScript 为这个属性赋值。
但是首先,让我们处理代码的脚本块。
首先,将下面几行 JavaScript 代码添加到对象窗口的 onload 事件中。
使用 document 对象的getElementById
方法创建一个包含滑块引用的局部变量:
var seekbar = document.getElementById('seekbar');
在 slider 控件的 change 事件和 video 元素的timeupdate
和durationchange
上声明三个事件处理程序:
video.addEventListener('timeupdate', updateTime, false); video.addEventListener('durationchange', initSeekBar, false); seekbar.addEventListener('change', changeTime, false);
因此,onload 事件上的函数如下所示:
`window.onload = function(){
video = document.getElementsByTagName("video")[0];
var btn_play = document.getElementById("btn_play");
var btn_pause = document.getElementById("btn_pause");
var btn_mute = document.getElementById("btn_mute");
var seekbar = document.getElementById('seekbar');
btn_play.addEventListener('click', doPlay, false);
btn_pause.addEventListener('click', doPause, false);
btn_mute.addEventListener('click', doMute, false);
video.addEventListener('timeupdate', updateTime, false);
video.addEventListener('durationchange', initSeekBar, false);
seekbar.addEventListener('change', changeTime, false);`
现在让我们通过将滑块的min
和max
属性与视频元素的startTime
和duration
属性相关联来设置它们。特别是,将min
属性设置为 0,将max
属性设置为duration
。我们将此语句写在initSeekBar()
事件处理程序上,当执行视频的durationchange
事件时会调用该处理程序:
function initSeekBar() { seekbar.min = 0; seekbar.max = video.duration; }
为了确保当用户移动滑块的拇指时视频改变其定时,并且搜索栏与视频的定时同步,我们必须在事件处理程序上编写代码,以便它们分别在视频的updateTime
事件和滑块控件的change
事件上执行。我们用addEventListener
方法注册了具有以下功能的事件处理程序:
seekbar.addEventListener('change', changeTime, false); video.addEventListener('timeupdate', updateTime, false);
在第一个视频的名为updateTime()
的事件处理程序中,我们将滑块控件的值与视频的currentTime
相关联:
function updateTime() { seekbar.value = video.currentTime; }
而在第二个事件处理程序changeTime
上,我们做了相反的事情,这意味着我们将视频的currentTime
与滑块的值相关联:
function changeTime() { video.currentTime = seekbar.value; }
保存文件并在浏览器中打开。你会看到滑块控件与视频完全同步,如果你拖动滑块,你会看到视频的当前时间相应改变,如图 5-10 所示。
图 5-10。 搜索栏与视频元素的当前时间完美同步。
为了清楚起见,下面是这个例子的完整代码:
`
<html>
<head>
<title>
Solution 5-5: Browsing the video with a seek bar
</title>
</head>
<body>
`
专家提示
有一种情况,上面写的例子不能正确工作。事实上,如果视频是流式的,duration
属性将变成无穷大,视频将从等于 0 的startTime
开始执行。因此,我们必须更改代码,使其在这种情况下也能正常工作。
视频的startTime
属性来拯救。它通常返回 0,但对于流式视频,它可能会返回不同的值。
因此,我们必须在代码中做两处修改。第一个是在initSeekBar()
事件处理程序中,与min
和max
属性有关:
function initSeekBar() { seekbar.min = video.startTime;
seekbar.max = video.startTime + video.duration; }
基本上,min
属性的值不再为零;相反,它继承包含在startTime
属性中的值,而max
值等于startTime
加上包含在duration
属性中的值。
第二个变化是在事件处理程序updateTime()
中,我们将使用另一个属性:buffered
属性。它返回一个包含一个length
属性、start()
和end()
方法的TimeRanges
对象。
我们通过访问buffered
属性中最后一个范围的结束时间来获取缓冲数据的位置。
如下更改updateTime()
处理程序中的代码:
`function updateTime()
{
var bufferPosition = video.buffered.end(video.buffered.length-1);
seekbar.min = video.startTime;
seekbar.max = bufferPosition;
seekbar.value = video.currentTime;
}`
解决方案 5-6:使用多源视频元素
当前浏览器对各种视频容器和格式的支持如下:
Safari 可以读取任何兼容 H.264/AAC/MP4 格式的视频。
微软还没有说明对 WebM 的支持是原生的,还是在 Internet Explorer 中可单独安装的组件中提供。在任何情况下,它都支持 H.264/AAC/MP4 格式。
Google 支持 WebM 编解码器,它已经宣布未来版本的 Android 将支持 WebM 格式。它最近还表示,截至 2011 年 3 月,Chrome 将不再支持 H.264 编解码器。
火狐、Opera、Chrome 都支持 Ogg/Theora/Vorbis 格式。
在这种丰富多彩的场景中,有必要以不同的格式发布视频,以便能够保证尽可能多的用户使用视频。
然后将由浏览器来理解它能够再现这些格式中的哪一种。
涉及到什么
这些步骤将帮助您提高视频被用户执行的可能性:
以下列格式编码您的视频:在 MP4 容器中编码 WebM (VP8 + Vorbis)、H.264 视频和 AAC 音频,在 Ogg 容器中编码 Theora 视频和 Vorbis 音频。
从一个<video>
元素声明这三个视频文件,并退回到一个基于 Flash 的视频播放器(参见解决方案 5-1 )。
为了使浏览器能够理解它需要加载哪种视频格式,可以使用 source 元素,如下所示:
<video controls autoplay> <source src="comtaste_showreel.ogv"> <source src="comtaste_showreel .mp4"> Sorry, your browser does not support the Video element </video>
浏览器将读取第一个视频源,并尝试加载和播放comtaste_showreel.ogv
。如果它不能播放,它将尝试下一个源元素。
默认情况下,浏览器还会试图以一种它无法再现的格式加载视频。这将是对带宽的巨大浪费。为了节省宝贵的带宽,您可以指定每个视频的 MIME 类型。MIME 类型传达正在使用哪个视频容器,但是它不提供关于正在使用的编解码器的任何信息。因此,在源的type
属性中,还需要指定编解码器。
通过这样做,浏览器不需要下载视频,因为它知道是否支持该 MIME 类型:
<video controls> <source src="comtaste_showreel.ogv" type="video/ogg;codecs='theora, vorbis'"> <source src="comtaste_showreel.mp4" type="video/mp4; codecs="avc1.42E01E, mp4a.40.2”'> Sorry, your browser does not support the Video element </video>
如何建造它
下面是一个完整的代码示例,它使用 source 标记向浏览器公开不同的视频格式:
`
<html>
<head>
<title>
Solution 5-7: Using multiple source video elements
</title>
` ` Your browser does not support the new HTML5 video element
`
注意使用双引号的编解码器参数。这意味着属性值必须使用单引号:
<source src="comtaste_showreel.webm" type='video/webm; codecs="vorbis,vp8"' />
专家提示
使用移动设备进行导航正变得越来越流行。越来越多的 web 开发人员使用 CSS 优化他们的设计,这样他们的网站就可以在比标准笔记本电脑屏幕分辨率更低的设备上使用。因此,视频提供商也有必要考虑谁将在手持设备上使用他们的内容,因为更高的质量可能是不可取的功能-就加载视频所消耗的带宽而言。
HTML5 提供了media
属性,该属性在 video 元素的source
属性中声明。该属性向浏览器询问媒体将被再现的屏幕尺寸。根据响应,视频标签能够发送针对不同屏幕分辨率优化的不同文件:
`
`
这样,浏览器会被询问最小设备宽度是否为 480px。如果是,那么它会加载在编码阶段针对移动设备优化的comtaste_showreel_mobile.ogv
视频。
注意:你可以在[
www.whatwg.org/specs/web-apps/current-work/multipage/video.html](http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html)
阅读更多关于media
属性的内容。
解决方案 5-7:全屏打开视频
近年来,网络上的视频质量有了显著提高。由于高性能带宽的可用性不断增加,今天多媒体内容正在互联网上传播,质量与电视相当。
YouTube 已经列出了大量高清质量的视频,如图 5-11 所示。
图 5-11。 YouTube 允许你选择想要加载的视频质量。
有了这种质量,就可以全屏观看视频,就像我们在笔记本电脑上观看视频一样,只是现在我们是在网上观看这些视频。
Flash Player 的几个版本已经提供了以全屏模式再现视频的可能性。HTML5 和它的 video 元素也提供了获得相同效果的方法。
涉及到什么
一些浏览器已经在视频控制器上提供了一个按钮来原生切换到全屏,例如 Safari 5.0 和 iOS 3.2 及更高版本。另一方面,其他浏览器通过右键菜单来支持全屏模式,如 Firefox 3.6。
您可以使用webkitSupportsFullscreen
布尔属性验证当前视频是否能够以全屏模式播放,并且您可以使用webkitEnterFullscreen()
方法以全屏模式启动视频。您要做的第一件事是检查浏览器是否支持全屏模式:
if (video.webkitSupportsFullscreen) { }
然后执行视频元素的webkitEnterFullscreen()
方法:
video.webkitEnterFullscreen();
若要退出视频的全屏模式,请按 Esc 键返回到普通视图,就像使用 Flash Player 一样。
如何建造它
创建全屏视频功能非常容易。
首先创建用户界面元素,在我们的示例中,它只是一个视频元素和一个简单的按钮:
`
Go Fullscreen
`
加载页面时,该按钮将不可见。事实上,指定一个 CSS 元素使按钮不可见:
style="visibility:hidden".
只有在检查了浏览器支持该功能后,您才能使该按钮可见。
现在,您可以插入一个 JavaScript 块,将事件处理程序与以全屏模式启动视频的按钮的单击相关联,但前提是检查浏览器是否支持该功能。当执行视频的loadedmetadata
事件时,或者当浏览器知道媒体资源的持续时间和尺寸时,执行该检查。事实上,webkitSupportsFullscreen
属性在电影元数据完全加载之前是无效的。
在 window 对象的 onload 事件中编写以下语句:
`
Go Fullscreen
`
图 5-12 显示了点击全屏按钮后的全屏视频。
图 5-12。 全屏模式下的视频。
专家提示
如果你想知道哪些视频播放器支持全屏模式,可以访问这个网页:[
praegnanz.de/html5video/](http://praegnanz.de/html5video/)
。
解决方案 5-8:对视频应用遮罩
你经常会看到视频出现在网络上非矩形或不连续的区域。沃达丰出版了这种情况的最早的例子之一,后来变得很有名;视频展示了在未来手环中播放的电影(见图 5-13 )。
图 5-13。 沃达丰视觉手环中播放的蒙面视频
视觉效果引人注目,并利用了新的 Flash Player 功能,该功能支持视频的 alpha 通道。alpha 通道只不过是充当视频蒙版的图像。这个特殊的图像定义了视频出现的唯一活动区域。
通过使用基于 WebKit 引擎的浏览器的特定属性并应用图像作为遮罩,也可以用 HTML5 重新创建类似的效果。
WebKit ( [
webkit.org/](http://webkit.org/)
)是一个开源的网页浏览器引擎。WebKit 也是 Safari、Dashboard、Mail 和许多其他 OS X 应用使用的 Mac OS X 系统框架版本的名称。Chrome 也是基于 WebKit 的。
目前,只有 Safari 和 Chrome 的最新版本支持这种解决方案。
涉及到什么
允许我们指定使用哪个图像作为遮罩的属性是:-webkit-mask-box-image
。
WebKit 的所有特定属性都以-webkit-
为前缀。还有 CSS 的 WebKit 属性。目前,这些属性仍然不是 WSC 所描述的 HTML5 标准的一部分,但在不久的将来,它们可能会被添加进来。
-webkit-mask-box-image
属性直接在视频标签中指定:
<video src = "comtaste_showreel.mp4" autoplay -webkit-mask-box-image: url(yourMaskImage.png) ;">
url()
参数在指定图像的属性中指定。
遮罩图像通常是与视频尺寸相同的 PNG 文件。显示视频的图像区域必须是透明的;要隐藏视频的区域需要是不透明的。
如何建造它
要创建遮罩并将其应用于视频,只需使用特定的标签和遮罩图像。
创建遮罩图像(也称为视频的 alpha 通道)是一项非常简单的任务。可以使用任何图形工具,如 Photoshop、Illustrator、Fireworks 等。有许多在线教程提供了如何执行此操作的分步指导。在这个例子中,我们使用了之前创建的图像,您可以在本章提供的示例的源代码中找到该图像。
一个简单且制作精良的视频教程发布在 Adobe TV 上:[
tv.adobe.com/watch/photoshop-for-video/straight-alpha-channels/](http://tv.adobe.com/watch/photoshop-for-video/straight-alpha-channels/)
。
以下是完整的解决方案:
`
Solution 5-9: Masking a video
`
我们已经使用一个图像为视频创建了一个框架(digitalScreenImage.jpg)。为了确保将作为帧的视频元素准确地放置在图像的正确高度,我们通过 CSS 使用了相对定位。事实上,我们在 video 标记中声明了 style 属性,方法是将 video 元素放在距离浏览器上边缘 325 像素的位置,这样它就可以留在图像中。最后,我们应用了带有-webkit-mask-box-image
属性的遮罩图像:
<video src = "comtaste_showreel.mp4" autoplay style = "position:relative ; top: -325px ; -webkit-mask-box-image: url(maskImage.png) ;" />
你现在所要做的就是保存文件,并在基于 WebKit 的浏览器中执行它,如谷歌 Chrome 或 Safari,以查看应用于视频的遮罩。
专家提示
除了-webkit-mask-box-image
属性,基于 WebKit 的浏览器还有另一个固有属性,允许您将图形效果应用于视频。事实上,可以通过使用-webkit-box-reflect
属性来添加视频的反射。
倒影是视频元素的副本,可以出现在视频的左侧、右侧、上方或下方。可以自定义视频和倒影之间的偏移空间。该属性被声明为 style 属性的一个属性,如下面的代码片段所示:
<video src = "comtaste_showreel.mp4" autoplay style = "position:relative ; top: -325px ; "-webkit-box-reflect: below 0px;" / >
这段代码在视频下面添加了一个反射效果。
解决方案 5-9:使用音频元素
音频是一种多媒体元素,也常用于网络。就像视频内容一样,音频在过去也因缺乏开放的网络传输标准而遭受损失。这就是为什么在网络上使用音频内容过去是、现在仍然是委托给第三方插件的原因。
QuickTime、Windows Media Player、Flash Player 和 Real Player 是开发人员用来播放音频内容的最常用插件。最后,Flash Player 再次被宣布为获胜者。随着市场渗透率接近 99%的计算机连接到互联网,Flash 保证任何网站都可以发布与此格式兼容的音频内容。
通过使用 Flash Player,可以播放以下格式的音频:AIFF、WAV 和 MP3。
现在 HTML5 引入了一个<audio>
标记,允许你插入音频元素,而不必分发任何第三方插件。
就格式而言,HTML5 规范对音频编解码器或容器格式没有任何限制。
MP3、AAC 或 OGG 音频文件都可以。变化的是各种浏览器对这些格式的支持。为了保证尽可能广泛的兼容性,通过source
属性提供相同音频元素的不同格式是一个很好的实践。然后,将由浏览器选择它可以播放的第一个列出的源。
涉及到什么
<audio>
标签继承了<video>
标签的大部分属性和事件。事实上,要插入一个音频元素,您需要做的就是声明标签并指定src
属性:
<audio controls autoplay src="audio.mp3">
可用于该标记的属性有:
src:包含源代码的本地或远程 URL。
autoplay:指定文件加载后是否播放。
loop:指定文件是否应该重复播放。
controls:指定浏览器是否应显示其默认媒体控件。
preload:指定音频是否必须由页面预加载。它接受以下值:none
、metadata
和auto
。
play():播放音频。
pause():暂停音频。
canPlayType():告知给定的 MIME 类型是否可以播放。
buffered():指定音频缓冲部分的开始和结束时间。
因为并非所有设备和浏览器都支持所有类型的音频,所以最好列出尽可能多的<source>
元素:
`
`
指定向浏览器声明 MIME 类型的 type 属性也很重要。
通过这样做,浏览器可以识别它是否能够在不加载文件的情况下播放该格式。
如何建造它
以下是如何使用音频元素的示例:
`
<html>
<head>
<title>
Solution 5-10: Using the Audio element
</title>
Your browser does not support the new HTML5 audio element
`
我们声明了一个音频元素,为此我们使用了浏览器的本地音频控制器,一旦用户加载网页,它就会自动播放(见图 5-14 )。
图 5-14。 原生音频控制器,由 Opera 浏览器渲染
相同的音频文件以不同的格式编码,以允许所有浏览器播放。为此,我们使用 source 标记来声明不同的格式:
`
`
浏览器从第一个声明的格式开始识别它能够播放的格式。一旦它知道它可以执行哪个文件,它将停止加载其他格式。
总结
如今,媒体元素在网络上非常流行。这就是为什么全新的 HTML5 音频和视频标签备受关注。虽然在网页中嵌入视频非常容易,但在本章中,您已经了解到作为开发人员您面临两个不同的问题:视频元素使用的编解码器的标准定义和浏览器对视频编解码器的采用。Ogg Theora、H.264 和 VP8/WebM 是目前视频元素支持的唯一格式。此外,每个浏览器只支持其中一种视频编解码器,这使得开发人员的日子不好过。
在本章中,您已经学习了如何在网页上嵌入和播放视频、自定义视频控制器以及以不同格式发布视频,以保证视频可供尽可能多的用户使用。您还学习了如何以全屏幕格式打开视频,以及如何对视频应用遮罩。
在下一章中,你将学习 HTML5 canvas 绘图 API,它为你直接在网页和应用中绘图提供了许多可能性。
六、HTML5 绘图 API
HTML5 的一个很酷的特性是,你可以选择动态渲染 2D 形状和位图图像,因为它现在有自己的本地绘图 API。对于 HTML 来说,这是一个巨大的进步,因为 HTML 从创建之初就一直保持着相当的静态和局限。现在那些日子已经过去了,一个新的可能性领域出现了,来处理在 HTML 页面本身中创建的图形。现在可以构建形状、图形、动画甚至游戏,而无需依赖任何外部插件(如 Flash)。这在开发网页和应用时带来了巨大的价值,但在与移动设备兼容时更是如此。
绘图 API 是新的 HTML5 canvas 元素的一部分,它为 2D 绘图提供了一个 API。最近,通过 HTML5 的 WebGL 支持进行 3D 绘图的可能性似乎即将实现。在撰写本文时,很少有浏览器与 3D 绘图兼容,但在不久的将来,它很可能会成为 HTML5 选项的一部分。
所有支持 HTML5 的主流浏览器都与 canvas 元素兼容,因此也与 2D 绘图 API 兼容。然而,当涉及到 Internet Explorer 时,HTML5 兼容性要到 9.0 版本才能实现。画布元素和 2D API 的浏览器和移动设备兼容性如表 6-1 所示。
注意:Internet Explorer 7 和 Internet Explorer 8 与 HTML canvas 元素不兼容。然而,可以通过使用“ExplorerCanvas”扩展来克服这个问题并在 IE 中提供画布支持。你可以在 Google Code 上下载这个扩展,地址:[
excanvas.sourceforge.net/](http://excanvas.sourceforge.net/)
。你只需要包含下面的 JavaScript 代码:
*<!--[if IE]><script src="excanvas.js"></script><![endif]-->*
解决方案 6-1:如何使用 canvas 元素的绘图 API 在 HTML5 中绘图
canvas 元素是一个可以添加到 HTML5 页面的矩形区域,它提供了各种各样的图形可能性,因为您可以通过其 2D 绘图 API 控制每个像素。canvas 元素本身没有绘图功能;您将在其中创建的所有内容都将使用 JavaScript 语言以编程方式绘制。
在这个解决方案中,我们将定义一个 canvas 元素并准备好使用它,以便您可以利用它的绘图 API。
涉及到什么
设置能够在 HTML5 中绘图的基础和背景非常简单。你只需要定义一个画布元素,<canvas></canvas>
。canvas 标记非常简单,只有三个属性:宽度、高度(以像素为单位)和一个标识您正在哪个画布上绘图的 ID。
<canvas id="canvasID" width="300" height="200"></canvas>
您可以在 HTML5 页面中放置画布,并像处理任何其他标签一样对其应用 CSS。
您的<canvas>
最初是空的——一个普通的区域——除非您通过 CSS 给它加上边框或背景色。然而,它不会出现在页面上,直到你在里面画了什么东西。画布只是一个普通的矩形,它将构成一个环境,您可以在其中绘制图形、制作动画等等。
要使用画布并在其中绘图,您需要使用 JavaScript。如果你已经用 ActionScript 3 或 Java 之类的语言编程绘图,你会发现这里有很多相似之处。
一旦创建了画布,您需要做的第一件事就是访问它。您可以像对文档对象模型(DOM)的任何其他元素那样做:
var myCanvas=document.getElementById("canvasID");
然后,您可以访问画布呈现上下文,它提供了对绘图 API 及其方法的访问。为了检索上下文对象,canvas 元素使用 DOM 方法getContext()
,该方法只有一个参数:上下文的类型。在撰写本文时,2D 的上下文是唯一可用的。我们预计在未来几年,3D 环境将在最流行的浏览器中可用,但是,现在我们必须使用 2D 版本。
var context=myCanvas.getContext('2d');
现在您已经有了画布并可以访问它的 context 对象,您可以开始使用绘图 API 及其方法在 HTML 页面中创建一些好的艺术品了。
如何建造它
首先用您最喜欢的代码编辑器创建一个新的 HTML5 页面:
创建一个空白 HTML5 页面。`
solution 6-1/title>
</head>
<body>
</body>
</html>`
</li>
<li>
<p>通过将 canvas 标签插入页面来创建画布,并以像素为单位设置其宽度和高度。(如果不设置任何尺寸,在任何浏览器中都会默认为 300×150。)<code><canvas id="myCanvas" width="300" height="200"></canvas></code></p>
</li>
<li>
<p>Give it a one-pixel grey border through CSS, as shown in Figure 6-1. <code><canvas id="myCanvas" width="300" height="200" style="border:solid 1px #ccc;"></canvas></code> <img src="https://gitee.com/OpenDocCN/vkdoc-html-css-zh/raw/master/docs/h5-solu-essen-tech-h5-dev/img/0601.jpg" alt="images" loading="lazy"></p>
<p><strong>图 6-1。</strong>带有单像素边框的普通画布</p>
<p>在你画之前,画布是一个普通的区域,但是你可以像在任何其他 HTML 元素上一样使用 CSS 属性,它会应用背景颜色、图像、笔触等等。</p>
</li>
<li>
<p>Now you can add fallback content that you might want to show for non-supported browsers. An example of fallback content where canvas is not browser-supported is shown in Figure 6-2. <code><canvas id="myCanvas" width="300" height="200" stye="border:solid 1px #ccc;"> Your browser doesn't support the HTML5 canvas element. <img src="fallbackPicture.jpg" alt="fallback picture"/> </canvas></code> <img src="https://gitee.com/OpenDocCN/vkdoc-html-css-zh/raw/master/docs/h5-solu-essen-tech-h5-dev/img/0602.jpg" alt="images" loading="lazy"></p>
<p><strong>图 6-2</strong> 来自非 canvas 兼容浏览器的回退内容</p>
<p>注意:Canvas 没有辅助功能。您可以在某种程度上使用回退内容来掩盖这个问题,但它不会完全做到这一点。</p>
</li>
<li>
<p>用 JavaScript 通过 DOM 访问画布。`<script type="text/JavaScript" language="JavaScript"></p>
<p>var canvas=document.getElementById('myCanvas');</p>
<p></script>`</p>
</li>
<li>
<p>Retrieve your canvas context object. `<script type="text/JavaScript" language="JavaScript"></p>
<p>var canvas=document.getElementById('myCanvas');</p>
<p>var context = canvas.getContext("2d");<br>
</script>`</p>
<p>您还可以优化代码,并在一行中访问画布上下文,如下所示:</p>
<p><code>var context= document.getElementById('myCanvas').getContext('2d');</code></p>
<p>现在你的画布已经可以在 2D 的背景下绘制了。以下是该示例的完整代码:</p>
<p>`<script type="text/JavaScript" language="JavaScript"><br>
var canvas=document.getElementById('myCanvas');<br>
var context=canvas.getContext("2d");<br>
</script></p>
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>solution 6-1
Your browser doesn't support support the HTML5 canvas element.
`
专家提示
在同一个页面上可以有几个<canvas>
元素,但是每个画布上不能有多个上下文。因此,如果您试图从画布中检索另一个上下文对象,实际上您将重置它。
``
第一个上下文变量现在将返回 null,您可以通过另一个上下文变量访问画布上下文。
解决方案 6-2:使用路径和坐标
现在您已经设置了画布和绘图上下文,让我们看看如何在其中实际绘图。canvas drawing API 为您提供了使用几乎所有图形程序通用的工具进行绘制的选项:笔画、填充、渐变、线条、图元、弧线或贝塞尔曲线。画布绘制是基于矢量图形的;实际绘图是用矢量完成的。然而,一旦绘制完成,它就变成了位图格式,您可以在像素级别与它进行交互。当要在里面画画时,你会处理路径。
这是一个非常简单的 API,它提供了几种方法来允许你绘制自定义的形状。
涉及到什么
当使用路径绘图时,该过程分为两个主要步骤:
定义路径,即路径在画布中的起点、终点及其样式(颜色、线宽、strokestyle、fillstyle 等)。这涉及到使用画布的坐标系。
Render it by adding its stroke and/or fill.
我们先来看看 canvas 元素的坐标系,了解如何在其中绘制路径。
画布坐标系
画布是一个 2D 网格,其坐标基于常规的 2D 坐标系:垂直 Y 轴、水平 X 轴和位于 x=0,y=0 的原点。
画布的坐标总是由像素决定的。原点“0,0”位于左上角,像素值随着沿 x 轴和 y 轴的移动而增加。回想一下解决方案 6-1,当你定义一个画布时,你给了它一个高度和一个宽度(如果没有,这个值默认为 300×150 像素)。绘图网格的尺寸由画布标签大小值决定。
每当您想要在上下文中放置一个元素时,您首先必须给出它在画布上的位置。你可以通过传递 x 和 y 值来实现,如图 6-3 中的所示。正的 x 值将向右移动,正的 y 值将向下移动。
图 6-3 一个画布的坐标系和一个点(7,6)的坐标和位置。
绘制路径
在 HTML5 中使用路径绘图包括绘制形状的大致轮廓(可以是任何形状)。当它结束时,您将通过定义它的笔画和/或填充颜色使它出现在上下文中。一旦你的路径完成,你的形状被渲染,如果你想画另一个形状,只要用一个新的路径重新开始。值得注意的是,每个画布只能有一个路径,因此创建一个路径将自动关闭前一个路径(如果有的话)。
创建路径时,可以连接多个子路径并绘制复杂的形状。您可以使用不同的方法来构造每个子路径:lineTo()
、quadraticCurveTo()
、bezierCurveTo()
和arc()
。每个连续子路径的终点成为上下文网格上的新起点,这将一直持续到您最终关闭路径。
路径绘制过程始终遵循以下顺序:
通过调用beginPath()
方法启动路径。
使用MoveTo(x,y)
方法定义您想要在画布网格中开始路径的坐标,并传递您想要开始路径的 x 和 y 坐标。(使用arc,
,您将把这些坐标作为方法本身的参数传递,并且您不需要使用MoveTo())
。
使用 API 的方法来绘制您的形状,并根据您的需要添加尽可能多的子路径。每次添加子路径时,必须使用MoveTo()
方法定义前一个子路径的最后一个点的坐标。
使用closePath ()
方法结束路径。这将完成形状。(如果您在上述步骤中使用的方法完成了路径,那么closePath ()
方法什么也不做。)
最后,调用stroke ()
或fill()
方法给你的形状添加一个笔画或填充,或者两者都添加。注意,如果你使用的是fill()
方法,它会自动关闭路径,然后填充形状,所以在这种情况下没有必要调用closePath()
方法。
提示:尽管 canvas 上下文默认为一个新子路径,但是在创建每个新路径之前使用beginPath()
方法是一个很好的实践。
2D API 提供了以下方法在画布上绘制路径。同样,记住实际的绘制是在画布上下文中完成的。
Drawing a line path. You will define the starting point of your line with the moveTo()
method and pass the coordinate of the ending point of your line by using the lineTo()
method (see Figure 6.4).
方法 : context.lineTo(x,y);
图 6-4 绘制一条直线路径
Drawing a Bezier curve. A Bezier curve is normally defined by a starting point, an end point, and two control points. You set the starting point with the moveTo()
method. You then have to add the coordinates of the two control points and of the ending point as parameters of the bezierCurveTo()
method (see Figure 6.5).
方法 :context . beziercurveto(control point 1x,controlPoint1Y,controlPoint2X,controlPoint2Y,endPointX,endPointY);
图 6-5 绘制一条贝塞尔曲线
Drawing a quadratic curve. Here you only have one control point, which will define the required curve by creating two invisible tangents connected to the starting point and the ending point, as shown in Figure 6.6. Thus, the parameters of the quadraticCurveTo()
method will be the coordinates of the control point and the ending point. The starting point is, like the other paths, defined through the MoveTo()
method.
方法 :context . quadratic curveto(control pointx,controlPointY,endingPointX,ending pointy);
图 6-6 绘制二次曲线
Drawing an arc. You need to provide first the x and y coordinates of the center of your arc on the canvas context, then the starting angle and ending angle of your arc (they are set in radians), and finally whether you want to draw it counter-clockwise (true) or not (false), as shown in Figure 6-7.
方法 :圆弧(中心,圆心,半径,起始角,终止角,逆时针;
图 6-7 画圆弧
为你的形状设置样式
有几个画布上下文属性可让您对形状、线条和笔画应用自定义样式。
线条属性和样式
Line join style. If you want to draw a shape with several subpaths, there is a line property that addresses the rendering of the line connections: lineJoin
. Paths can have three possible line joins: miter
, round
, or bevel
, as shown in Figure 6-8. By default, it's set to miter
.
图 6-8 线连接
Line width style. You can also set the width of your line with the lineWidth
property. The size should be given in pixels. By default, it's set to 1, and shown in Figure 6-9.
图 6-9 线条宽度样式
线条结束样式: 你也可以通过lineCap
属性在任何画布线条的末端添加一个线帽。线条可以有三种不同的帽样式:butt
、round
或square,
,如图图 6-10 所示。默认情况下,它被设置为butt
线帽样式。
你可能会注意到,这两种发型看起来很相似。这是因为butt
属性添加的是垂直于线条方向的平边,而square
值添加的是一个长度为线条宽度、宽度为线条一半的矩形。
图 6-10 线条末端样式
如何建造它
为了了解如何在画布 API 中使用路径和坐标,我们将向您展示如何绘制 friendsofED 徽标(参见图 6-11 ),因为它需要使用不同种类的路径来呈现您在解决方案 6-1 中已经创建的画布。
图 6-11 的朋友们摆出了 的标志
创建一个空白的 HTML5 页面,并添加一个宽和高为 500 像素、ID 为“myCanvas
”的画布。您应该为没有支持 HTML5 canvas 元素的浏览器的用户添加一些后备内容。`
solution 6-2/title>
</head>
<body onload="drawLogo()">
<canvas id="myCanvas" width="500" height="200">Your browser does not have support for HTML 5
Canvas.</canvas>
</body></html>`
</li>
<li>
<p>从现在开始,一切都用 JavaScript 来完成。您需要编写一个包含绘图过程代码的<code>drawLogo()</code>函数。(在这个解决方案中,我们将脚本包含在<code><head></code>标签页面中,但是您也可以选择将您的脚本具体化,如下所示:<code><script scr="yourcode.js"></script>)</code>。您要做的第一件事是创建对画布及其上下文的访问。`<script type="text/JavaScript" language="JavaScript"><br>
function drawLogo() {</p>
<p>var canvas = document.getElementById("myCanvas");<br>
var context = canvas.getContext("2d");`</p>
</li>
<li>
<p>Now that you have your context object, you can start drawing the designed mouse of the logo. When decomposing it, you have half a line crossing it at its center, and opposite to it a closed half circle. `context.lineWidth=4;</p>
<p>context.beginPath();<br>
context.moveTo(190, 100);<br>
context.arc(190,125,25,Math.PI<em>1.5,Math.PI</em>.5,true);<br>
context.stroke();</p>
<p>context.beginPath();<br>
context.moveTo(165,125);<br>
context.lineTo(190,125);<br>
context.stroke();</p>
<p>context.beginPath();<br>
context.moveTo(198, 100);<br>
context.arc(198,125,25,Math.PI<em>1.5,Math.PI</em>.5,false);<br>
context.closePath();<br>
context.stroke();`</p>
<p>在该绘图中,您希望绘制的任何形状都具有相同的线宽。在定义上下文之后,您只需将<code>lineWidth</code>属性定义为 4 个像素。因此,所有线条都将具有此宽度,您不需要为您绘制的每个形状指定此宽度。如果你想要每个形状有不同的线宽,你只需要为你创建的每个路径定义一个不同的<code>lineWidth</code>。</p>
<p>在上面的代码中,您打开了一个新的路径来绘制每个形状。你也可以看到为什么自己关闭路径是重要的。在第一个形状的情况下,您绘制了一个半弧,但您希望笔画只出现在圆周上,而不是半径上。所以,你在定义了你的圆弧周长之后,调用了<code>stroke()</code>方法。这将封闭路径,并仅在圆周上绘制一个笔划。在第二个弧的情况下,通过闭合连接起点和终点的路径,在它们之间添加子路径。因此,您的笔画也包括起点和终点之间的线。</p>
</li>
<li>
<p>Now draw the mouse wire shape, which is composed of several Bezier curves. <code>context.beginPath(); context.moveTo(73.3, 121.0); context.bezierCurveTo(73.3, 121.0, 76.7, 123.7, 81.3, 123.3); context.bezierCurveTo(81.3, 123.3, 100.3, 116.5, 104.0, 117.0); context.bezierCurveTo(112.0, 118.0, 120.0, 131.3, 125.3, 131.0); context.bezierCurveTo(129.0, 130.8, 136.7, 127.0, 136.7, 127.0); context.bezierCurveTo(136.7, 127.0, 144.7, 120.3, 148.3, 126.3); context.bezierCurveTo(153.5, 134.8, 158.0, 128.0, 158.0, 128.0); context.stroke(); }</code> <code> </script> </head></code></p>
<p>这里你又开始了一个新的路径,然后你定义了制作这个多曲线形状所需的子路径,然后你调用了<code>stroke()</code>方法。现在您已经有了所有的形状,您可以关闭您的函数和脚本。</p>
</li>
<li>
<p>To have your shape rendered when the page loads, add a call to the ‘<code>drawLogo()</code>' function on the page load in the body tag. <code><body onload="drawLogo()"></code></p>
<p>这是该示例的完整代码:</p>
<p>`<!DOCTYPE html></p>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>solution 6-2
`
专家提示
当你想制作更复杂的形状时,用贝塞尔曲线绘图会变得非常乏味,因为这涉及到你所画内容的计算。避免这种情况的一种方法是用任何矢量图形程序绘制图形,然后以 SVG 格式导出文件。然后,您可以用任何代码编辑器打开它,查看曲线的坐标。然后,您可以在 JavaScript 代码中轻松使用它们。
此外,如果您正在使用 Adobe Illustrator,有一个很棒的插件可以帮您省去所有的辛苦工作。它被称为 AI2Canvas,它允许您将文件直接导出为 HTML5 文件,其中包含 Canvas 元素和相应的 JavaScript 代码。它还会转换颜色和渐变。当然,您可能想要重新编写代码来优化它,但是这是一个与设计人员一起工作并避免繁重计算的很好的插件。你可以在这里下载插件:[
visitmix.com/labs/ai2canvas/](http://visitmix.com/labs/ai2canvas/)
。
解决方案 6-3:绘制形状:矩形和圆形
在这个解决方案中,您将看到如何使用 HTML5 绘图 API 绘制圆形和矩形。我们已经在解决方案 6-2 中介绍了自定义形状路径的使用,现在您将看到 API 为特定形状提供的方法。
涉及到什么
圆,或者我们应该说是弧,和矩形的特定方法也使用画布路径。这意味着它遵循您在解决方案 6-2 中看到的相同程序:
设置上下文画布网格上的坐标
设置填充和描边的样式
Applying the fill and/or stroke to complete the process and render the drawing.
我们已经在解决方案 6-2 中提到,每个画布一次只有一条路径,所以你只需为每个圆形或矩形创建一条新路径,就像你为每个自定义形状所做的那样。这些将通过 canvas 元素的 context 对象呈现,就像在画布中绘制的任何其他东西一样。先说长方形。
绘制矩形
与 SVG 不同,HTML5 canvas 只知道如何绘制一种基本形状——矩形。所有其他形状必须通过组合一个或多个路径来创建。有四种不同的方法允许你在画布上画一个矩形;你选择的方法取决于你的目标。
您可以使用rect()
方法,它接受这些参数:矩形左上角的画布上下文网格上的 x 和 y 位置,以及矩形的期望宽度和高度。语法如下:
rect(x, y, rectangle width, rectangle height);
rect()
方法实际上给绘图上下文路径添加了一个矩形路径。您不需要在这里调用beginPath()
方法,因为rect()
方法本身就可以做到这一点。然后stroke()
或fill()
方法将在上下文中呈现它。
您可以通过调用fillRect()
方法直接绘制填充矩形,该方法采用与rect()
方法相同的参数。这就像是将rect()
和fill()
方法合二为一。fillRect(x, y, rectangle width, rectangle height);
调用StrokeRect()
方法会直接画一个描边矩形。它采用与前两种方法相同的参数。strokeRect(x,y,rectangle width, rectangle height);
最后一种方法是clearRect()
。首先,此方法不绘制矩形,而是从另一个形状中减去一个矩形。(如果你熟悉 Illustrator,就像使用‘探路者’工具的减法选项一样)。clearRect()
采用与rect()
方法相同的参数。clearRect(x,y, rectangle width, rectangle height);
画圆
对于圆,您需要使用我们在解决方案 6-2 中使用的圆弧方法,然后绘制一个起始角度为 0,结束角度为 2×π弧度的圆弧,绘制一个完整的圆弧。
画圆弧的方法是arc()
,它采用以下参数:
画布网格上圆心的 x,y 坐标
以弧度指定的开始角度和结束角度值。因为要画一个圆,所以从 0 开始,到 2×pi(相当于 360 度的弧度)结束
A Boolean parameter indicating whether you want to draw your arc counter-clockwise (true) or not (false). In the case of a circle, it will work either way of course, so you can set it to either of these arc(x center coordinate, y center coordinate, 0,Math.pi*2,true)
当它创建一个路径时,arc()
方法将只设置你想要画的圆形。要渲染它,你必须调用stroke()
或fill()
方法,这取决于你的需要。(由于你在这里画了一个完整的圆弧,所以closePath()
的方法就没必要了)。
如何建造它
为了演示这个解决方案,您将通过编程绘制一个圆形和两个矩形,如图 6-12 所示。内部矩形将是一个正方形,填充和描边。这是一个非常基本的绘图,但它涵盖了与这两个形状相关的方法。请记住,绘图 API 本身很简单,但这并不意味着你不能用它来绘制复杂的东西——远非如此。这完全取决于你如何通过将几个简单的形状相加来构建复杂的形状。以下代码的结果将如下所示:
图 6-12 绘制复杂形状
打开一个基本的空 HTML5 页面,如解决方案 6-1,有一个画布和访问其绘图上下文。`
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>solution 6-3</title>
</head>
<body>
<canvas id="shapes" width="300" height="300" style="border:solid 1px #ccc"></canvas>
</body>
</html>`
Create a drawShapes()
function that will be called on the load page event. The first thing to do is to access your canvas and its context object. <script type="text/JavaScript" language="JavaScript"> function drawShapes(){ var canvas=document.getElementById("shapes"); var context=canvas.getContext("2d");} context.beginPath(); context.lineWidth="2"; context.strokeRect(10,10,200,280);
这里有一个矩形,宽 200 像素,高 280 像素,左上角在画布上的坐标(10,10)处。矩形的笔画宽度为 2 个像素。
Draw your circle. context.beginPath(); context.lineWidth="5"; context.arc(110,150,90,0,Math.PI*2,true); context.stroke();
您的圆的中心将位于点 110,150,这是您的矩形的中心(矩形本身位于画布的 10,10)。它的半径为 90 像素,笔画为 5 像素。
To finish, draw a filled square at the center of your circle. context.beginPath(); context.fillRect(80,120,60,60);
你开始了一条新的路径,用fillRect()
方法,你画了一个位于圆心的黑色小方块。(除非另有定义,否则填充的形状将始终为黑色。)
To have your shape rendered when the page loads, add a call to your drawShapes()
function on the page load in the body tag. <body onload="drawShapes()">
以下是该示例的完整代码:
`
solution 6-3
`
专家提示
如果您不熟悉 JavaScript 或用其他语言(如 ActionScript 或 Java)以编程方式绘图,一开始可能会有点困惑。Canvas Console 是一个很棒的在线工具,可以帮助你尝试 Canvas 绘图 API 的所有方法并熟悉它们的使用(见图 6-13 )。这个工具是由领先的 HTML5 和 CSS 专家约翰·奥尔索普编写的,它可以让你在上下文中绘制任何东西,并立即看到结果。它还有一个包含所有方法和属性的小备忘单。你可以在这个地址试试:[
westciv.com/tools/canvasConsole/](http://westciv.com/tools/canvasConsole/)
图 6-13 帆布控制台
解决方案 6-4:用纯色填充形状
在前两个解决方案中,我们讨论了绘制自定义和基本形状。您只使用了默认的线条和填充样式,默认的填充颜色是黑色。我们现在将探索绘图 API 的选项来填充您的绘图,您将看到如何添加一些纯色。
涉及到什么
首先让我们看看如何简单地用基本颜色填充你的形状。
设置 fillStyle 属性
要填充 HTML5 画布形状,需要使用画布上下文的fillStyle
属性。有了它,你可以定义特定的颜色来填充你的形状。然后fill()
方法,或者在矩形情况下的fillRect()
,将应用这个属性,并使用您定义的样式呈现您的形状。
颜色属性可以通过参考其 RGB 颜色值来定义:
canvasContext.fillStyle = "rgb(255,0,255)";
或其十六进制值:
canvasContext.fillStyle = "0xcccccc";
如果在没有定义任何fillStyle
的情况下调用fill()
方法,那么将应用默认颜色,黑色。
注意:正如你在解决方案 6-3 和 6-4 中看到的,形状,无论是否自定义,都是由画布上下文路径的子路径组成的。由于每个画布只有一个路径,调用fill()
方法会自动关闭路径。这意味着,如果您的子路径组合没有连接自己,该路径将关闭,并将最后一个端点与起始路径点连接起来,并填充该区域。因此,您应该小心您希望这发生在哪里,但它也可以节省您一些绘图代码。例如,要画一个填充的三角形,你只需要画两条线,在渲染的时候,API 会创建最后一条线来封闭路径。
混合颜色
当您用纯色填充形状时,绘图 API 还会将 alpha 混合值添加到可以使用的混合中。代替我们上面讨论的rgb()
属性,您必须使用属性rgba()
,其中‘a’代表 alpha 值,是最后一个参数。alpha 值从 0(无透明度;即纯色)到 1(100%或完全透明)。这在图 6-14 中进行了说明。
图 6-14 两个矩形重叠,每个矩形的 alpha 值为 0.5
填充笔画
您还可以使用strokeStyle
属性,然后调用stroke()
方法,为在画布上下文中绘制的形状和文本的笔画设置纯色填充,这将根据您定义的样式(大小、颜色等)呈现形状的笔画。
与fillStyle
一样,fillStroke
颜色属性可以参照其 RGB 颜色值来定义:
canvasContext.fillStroke="rgb(255,0,255)";
或其十六进制值:
canvasContext.fillStroke="0xcccccc";
注意:如果你为一个形状设置了填充和描边,在stroke()
之前调用fill()
。否则,填充将覆盖一半的笔画。
如何建造它
在上一个解决方案中,您在画布中绘制了几个形状。你现在将填充一些纯色。
打开前面的解决方案代码。您将拥有一个 300 x 300 像素的 HTML5 页面。加载时,您已经在其中绘制了几个形状:矩形内的一个圆。`
` `
solution 6-4/title>
</head>
<body on load=drawShapes()>
<canvas id="myCanvas" width="300" height="300"></canvas>
</body>
</html>`
</li>
<li>
<p>从访问画布的上下文对象开始,添加 JavaScript 代码并编写绘制形状的函数。<code><script type="text/JavaScript" language="JavaScript"> function drawShapes(){ var canvas=document.getElementById("myCanvas"); var context=canvas.getContext("2d");</code></p>
</li>
<li>
<p>Inside the function <code>drawShapes</code> that you created, now draw your rectangle by first defining the <code>fillStyle</code> property. In this example, use the red color to fill it. You can either use its hexadecimal value (#ED1C24), or rgb (237,28,36). <code>context.fillStyle="#ED1C24"; context.fillRect(0, 0,200,200);</code></p>
<p>对于这一个,你没有使用任何笔画颜色,所以不会出现笔画。这里有一个 200 乘 200 的正方形,它的左上角将被放置在画布的左上角,因为坐标被设置为 0,0。</p>
<p>使用以下代码会得到相同的结果:</p>
<p><code>context.beginPath(); context.fillStyle="#ED1C24"; context.rect(0, 0,200,200); context.fill();</code></p>
</li>
<li>
<p>Now you can start a new path and draw your circle. <code>context.beginPath(); context.lineWidth="4"; context.strokeStyle="#1B1464"; context.fillStyle="#29ABE2"; context.arc(100,100,50,0,Math.PI*2,true); context.stroke(); context.fill(); }</code></p>
<p>在这里,您定义了您的圆的样式属性(笔画宽度、笔画颜色和填充颜色),然后定义了它的位置和大小,最后您通过调用<code>fill()</code>和<code>stroke()</code>方法像往常一样渲染它。</p>
</li>
<li>
<p>You can now close your script tag. <code></script></code></p>
<p>和前面的解决方案一样,在加载页面时调用<code>drawShapes()</code>函数来加载画布上下文。(或者,您可以在脚本末尾调用它,结果也是一样的。).结果见图 6-15 。</p>
<p><code><body onload=drawShapes()></code> <img src="https://gitee.com/OpenDocCN/vkdoc-html-css-zh/raw/master/docs/h5-solu-essen-tech-h5-dev/img/0615.jpg" alt="images" loading="lazy"></p>
<p><strong>图 6-15</strong> 填充素色的形状</p>
<p>这是该示例的完整代码:</p>
<p>`<!DOCTYPE HTML></p>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>solution 6-4
</head>
`
专家提示
到目前为止,我们一直在处理非常简单的形状组合,除了绘图之外没有任何真正的目的。然而,当您开始构建更复杂的画布并以交互方式使用它们时,您将不得不非常小心代码的性能。绘图可能相当耗费处理器资源,一旦你在画布上画了某样东西,你就不能真正把它从其他被画的对象中分离出来;也就是说,您正在创建单个位图图形。因此,任何改变都意味着重新绘制。因此,在某些情况下,将 CSS 和绘图 API 结合起来是有益的。
如果你已经画了一个背景,比如说,你知道你不想在每次交互时改变,通过 CSS 把它作为画布的背景图像是一个好主意。因此,您的上下文将只重绘必须更改的内容,并为您的应用节省一些渲染开销。
解决方案 6-5:使用渐变填充形状
现在我们知道了如何绘制填充纯色的形状,让我们看看如何用渐变填充它们。
涉及到什么
与任何标准绘图程序一样,API 提供了两种类型的渐变:线性和径向。
使用线性渐变
要用 HTML5 Canvas 创建线性渐变,需要调用createLinearGradient()
方法,这需要四个参数通过创建一条不可见的线来定义渐变的方向(参见图 6-16 )。
var myGradient=context.createLinearGradient(lineStartingPointX, lineStartingPointY, lineEndingPointX, lineEndingPointY);
图 6-16 一个线性渐变
一旦你创建了你的渐变方向并设置了它的坐标,你可以使用addColorStop
属性插入它的颜色。您可以根据需要向渐变中添加任意数量的颜色,并使用以下方法使用两个参数“偏移”和“颜色值”来创建微妙的渐变填充形状和笔画:
myGradient.addColorStop(offset value, color1); myGradient.addColorStop(offset value, color2);
使用径向渐变
要用 HTML5 画布创建径向渐变,您将使用createRadialGradient()
方法。
它有以下六个参数,如表 6-2 所列:
var gradient=context.createRadialGradient(startCircleX, startCircleY, startCirlceRadius, endCircleX, endCircleY, endCirleRadius);
表 6-2。 createaradialgradient()方法的参数和值
| **参数** | **值** |
| :-- | :-- |
| startCircleX | 第一个渐变圆的 x 坐标 |
| startCircleY | 第一个渐变圆的 y 坐标 |
| startcirclrceradius | 第一个渐变圆的半径,以弧度为单位 |
| endCircleX | 第二个渐变圆的 x 坐标 |
| endCircleY | 第二个渐变圆的 y 坐标 |
| endCirleRadius | 第二个渐变圆的半径,以弧度为单位 |
这些参数是定义渐变位置和渐变的两个假想圆的坐标和半径。就像线性渐变一样,你可以用addColorStop
方法添加任意多的颜色来满足你的需求。
图 6-17 径向梯度
你可以在图 6-17 中看到一个半圆形状的两种颜色的径向渐变的例子,以及渐变参数中定义的两个不可见的圆。渐变从半径为 40 像素的第一个圆开始,向半径为 300 像素的第二个圆径向移动,根据颜色的偏移值(0 和 1)部署颜色。定义该径向梯度的代码如下:
var gradient=context.createRadialGradient(350,310,40,350,310,300); gradient.addColorStop(0,"#6699cc"); //light blue color gradient.addColorStop(1,"#cee7fa"); //darker blue color
用渐变填充笔画
以同样的方式,你可以用渐变填充你的形状的笔画,方法是用渐变定义Fillstyle
,然后使用strokeFill()
方法。以下示例将使用渐变填充方形笔画。
gradient=context.createLinearGradient(20, 120, 160, 120); gradient.addColorStop(0, "rgb(0, 0, 0)"); // black color gradient.addColorStop(1, "rgb(255, 255, 255)"); // white color context.strokeStyle=gradient; context.rect(10, 10,200,200); context.stroke();
如何建造它
和其他解决方案一样,从在 HTML5 页面中设置画布开始。加载后,调用函数drawGradientShapes()
,该函数将包含用于绘制两个填充了渐变的形状的 JavaScript。`
solution 6-5
`
现在来看 JavaScript 部分和drawGradientShapes()
函数。首先,访问您的画布及其上下文对象。然后继续绘制自定义形状。`
Your Browser doesn't support HTML5 Canvas !
`
解决方案 6-6:在画布上绘制文本
canvas drawing API 允许您在 canvas 元素中操作和绘制文本。在本解决方案中,我们将向您展示如何做到这一点。
涉及到什么
绘制文本与绘制形状和路径没有太大区别。它遵循类似的过程,除了不是首先描述路径或形状,而是通过属性 font 指定想要使用的字体和字体大小,然后用 stroke 和/或 fill 方法呈现文本。
我们之前看到的所有渲染样式,颜色、渐变等等,可以像应用于任何其他形状一样应用于文本。CSS 样式也可以用于通过画布 id 绘制的文本。
管理画布中的文本不像使用 CSS 管理常规文本那样可定制。但是,让我们看看您可以使用的几个属性。
字体属性
在画布中绘制的文本继承了<canvas>
元素本身的字体大小和样式(如果有的话),但是您可以通过在绘图上下文中设置 font 属性来覆盖它们。
您可以在那里定义的字体属性与您在 CSS 中设置的属性相同——字体样式、字体粗细、字体大小和字体系列。语法如下:
context.font="bold 14px Arial" ;
默认字体是 10 像素的无衬线字体。
对齐文本属性
您可以设置textAlign
属性来对齐画布上下文中的文本。对齐是相对于您为文本建立的 x 和 y 位置设置的。它可以有以下值:left、right、start(默认值)、 end 和 center。开始和结束属性基于画布标记的文本方向,从左到右或从右到左。
文本基线属性
无需深入解释,假设文本对齐基于包含文本的行框,并且每个行框都有基线。(通过打开下面的 HTML5 规范链接,您可以看到表示这一点的图形。)基线决定了文本在行框中的位置,其值可以是:顶部、悬挂、中间、字母(默认)、表意和底部。
对于基于拉丁文的字母,最有可能只使用顶部、底部、中间和字母。如果您必须处理其他字母,您可以参考 HTML5 规范网页上的完整基线描述:
www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d -textbaseline
理解画布上的文本呈现被视为与任何其他路径相同的方式是很重要的。因此,可以描边和填充文本,并且所有呈现样式都可以应用于文本,就像它们应用于任何其他形状一样。也就是说,正如您可能期望的那样,文本绘制例程遵循与任何路径相同的过程:
定义您的文本样式。或者,与其他形状一样,您可以指定fillStyle
、strokeStyle
、lineStyle
和lineWidth
属性,它们的应用方式与其他形状相同。
Call the strokeText()
, or fillText()
method to render the text on the canvas.
这两个方法都有三个参数:画布上文本的 x 和 y 坐标,以及要绘制的字符串。正如您所猜测的,strokeText()
将只绘制字体的笔画,fillText()
将绘制填充的文本。
context.fillText(x,y,"string"); context.strokeText(x,y,"string");
注意:你可能知道,CSS3 现在提供了@font-face
属性,允许你从远程位置加载特定的字体。通过 CSS 设置画布字体属性,您可能会尝试将它用于您的画布文本。然而,当您调用fillText()
或strokeFill()
方法时,画布上的绘制必须立即发生,浏览器通常不会在上下文呈现时从其位置加载字体。所以最有可能的是,它将会回到默认字体。
每当你想在画布上使用文字时,有几件事必须要考虑。首先,画布中呈现的任何内容都是不可访问的,文本也是如此。实际上,在其中绘制的文本也是不可选择的。您可能会尝试使用 canvas fallback 内容来克服这个问题,但它并不真正适合这种需求。
如何建造它
创建一个带有 canvas 元素的基本 HTML5 页面。`
solution 6-6/title>
<script>
</script>
</head>
<body>
<canvas width="500" height="300" id="myCanvas" style="border:1px solid #ccc"></canvas>`
</li>
<li>
<p>在页面的顶部,添加一个 JavaScript 标签并编写一个<code>drawText()</code>函数,该函数将包含用于在画布中呈现文本的代码。与前面的解决方案一样,首先要访问画布及其渲染上下文。`<script type="text/JavaScript" language="JavaScript"><br>
function drawText() {</p>
<p>var canvas=document.getElementById("myCanvas");<br>
var context=canvas.getContext("2d");</p>
<p>}</p>
<p></script>`</p>
</li>
<li>
<p>You can start to draw your text inside your function. First, define your text styles. <code>context.strokeStyle = "#993300"; context.fillStyle=" #ffff09"; context.font="bold 80px arial"; context.textAlign="center"; context.lineWidth="5"; context.lineJoin="round";</code></p>
<p>您已经设置了字体、字体大小、文本笔划和填充颜色。这里您将基线保留为默认值。</p>
</li>
<li>
<p>Call the methods to draw the actual stroked and filled text and close your function. <code>context.strokeText("TML5",220,90); context.fillText("HTML5",220,90); }</code></p>
<p>在绘制时,必须同时绘制描边和文本填充,才能获得描边文本。</p>
</li>
<li>
<p>在页面的 body 标签中加载页面时调用函数。<code><body onLoad="drawText()"></code></p>
</li>
<li>
<p>结果将如图 6-19 所示:</p>
</li>
</ol>
<p><img src="https://gitee.com/OpenDocCN/vkdoc-html-css-zh/raw/master/docs/h5-solu-essen-tech-h5-dev/img/0619.jpg" alt="images" loading="lazy"></p>
<p><strong>图 6-19</strong> 。背景填充文本</p>
<p>这是该示例的完整代码:</p>
<p>`<!DOCTYPE html></p>
<html>
<head>
<meta charset="utf-8">
<title>solution 6-6
`
专家提示
您可能会发现 canvas 元素中的文本可能性有点有限。优秀的 JavaScript 开发人员 Jim Studt 创建了一个有趣的 JavaScript 插件,名为“canvas text”,它扩展了 canvas 上下文并添加了一些额外的方法来处理文本。它将以下方法添加到画布中:
context.drawText / context.drawTextRight / context.drawTextCenter
:这将在该位置(基线)以指定的字体和大小绘制文本。
context.measureText
:返回文本的宽度。
context.fontAscent / ctx.fontDescent
:返回从基线的上升/下降。
拥有这些额外的方法增加了操纵文本大小和位置的可能性。例如,它们在应用的动态渲染中非常有用。您可以了解关于这些方法的更多信息,并阅读文档,网址:[
jim.studt.net/canvastext/](http://jim.studt.net/canvastext/)
解决方案 6-7:使用相对字体大小在画布上绘制文本
每当你添加文本,你必须设置它的大小。它可以以像素为单位固定,但也可以像处理 CSS 一样相对地设置它。
涉及到什么
就像页面上的其他元素一样,<canvas>
有一个基于你在 CSS 中定义的字体大小。这和你在处理你的 HTML4 页面时所习惯的没有什么不同。让我们简单回顾一下相对字体大小的概念,以理解它在 canvas 中是如何工作的。
字体大小、em 和相关文本内容
有两种方法可以设置 HTML 页面中的字体大小,一种是以像素(px)或磅(pt)为单位的绝对值,另一种是相对值,即“em”值..
Em 为你提供了相对于父元素设置字体大小的可能性。在 CSS 中,这对于根据呈现媒体调整样式非常有用。
您可以为画布文本使用 em 值。这是什么意思?给字体指定一个 em 大小就是根据其父元素的字体大小按比例指定其大小。因此,如果您的画布字体设置为 10 px,然后您绘制一个大小为 2 em 的文本,该文本将是画布上没有定义特定字体大小的任何其他文本的两倍大。
context.font="2em Arial"; context.fillText("Arial with a 2em size", 100, 50);
现在,如果你想完全摆脱绝对值,你将不得不设置父元素字体大小为%(你的画布),你将有一个完整的文本相对渲染。如今,尤其是随着智能手机和平板电脑的出现,如果你希望你的网络内容可以在任何平台上使用,照顾不同的配置是至关重要的。
如何建造它
Create a basic HTML5 page, and add a canvas element to it. `
` `
solution 6-7/title>
</head>
<body onLoad="drawText()">
<canvas id="textCanvas" width="400" height="300" style="font-size:100%"></canvas>
</body>
</html>`
<p>添加 canvas 元素时,将 CSS 字体大小设置为 100%。因此,您的画布字体大小将根据父画布调整字体大小。</p>
</li>
<li>
<p>现在,您可以添加 JavaScript 代码来绘制文本。在这里,您将编写一个<code>drawText()</code>函数,它将在页面加载时被调用,以便在浏览器中加载页面时立即呈现您的文本。正如在前面的解决方案中看到的,您将首先通过 DOM 访问您的画布,然后通过它的上下文对象进行绘制。然后你将开始定义和绘制你的文本。`<script type="text/JavaScript" language="JavaScript"></p>
<p>function drawText(){<br>
var canvas=document.getElementById('textCanvas');</p>
<p>var context=canvas.getContext('2d');`</p>
</li>
<li>
<p>现在你想写一些文本,大小是标准画布文本的 0.8 倍。为此,您需要重新定义上下文字体属性来覆盖您刚才使用的字体属性,将大小设置为 0.8em,然后使用<code>fillText()</code>方法绘制新的字符串。<code>context.font="0.8em Arial"; context.fillText("Canvas Text Arial size 0.8em", 10, 50);</code></p>
</li>
<li>
<p>如果您想写两倍于画布中定义的高度的文本,您可以根据需要覆盖 context.font 属性:<code>context.font="2em Arial"; context.fillText("Canvas Text Arial size 2em", 10, 100);</code></p>
</li>
<li>
<p>To use pixel sizes in the same context, you would do the following:. `context.font="14px Arial";<br>
context.fillText("Canvas Text Arial size 14 pixels", 10, 140);</p>
<p>}</p>
<p></script>`</p>
<p>你现在可以关闭你的功能。加载页面时,您应该会看到以下视觉效果:</p>
</li>
</ol>
<p><img src="https://gitee.com/OpenDocCN/vkdoc-html-css-zh/raw/master/docs/h5-solu-essen-tech-h5-dev/img/0620.jpg" alt="images" loading="lazy"></p>
<p><strong>图 6-20</strong> 用相对字体大小绘制的文本</p>
<p>这是该示例的完整代码:</p>
<p>`<!DOCTYPE HTML></p>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>solution 6-7
`
专家提示
如果你想让你的字体大小调整到客户端浏览器的默认值,就像你在 HTML4 中做的那样;也就是说,在 CSS 文件中将页面的默认字体大小设置为 100%。画布作为一个子元素,现在将基于这个值设置它的字体大小。
解决方案 6-8:将形状保存为 PNG 文件
假设您使用 canvas 和 drawing API 在 HTML5 中制作了一个小应用,用户可以在其中生成一些自定义结果(例如,通过绘图应用生成自定义图形或设计),并且您希望他或她能够保存它。canvas 对象提供了一个方法,可以让您将 canvas 上下文转换成 PNG 格式的文件。(也有其他图像格式,但是 PNG 是唯一一种支持 HTML5 规范的格式,所以我们将重点讨论它。)
涉及到什么
要从画布中检索图像数据,您将使用 canvas 对象的toDataURL()
方法。
调用时,该方法返回一个 URL,其中包含以 PNG 文件形式表示的画布内容。
var canvas=document.getElementById('myCanvas'); var imageDate=canvas.toDataURL();
检索到的dataUrl
的内容将以data:image/png;base64
的格式返回,并且是您的 PNG 图像数据的 base64 字符串。如果画布没有像素(即。它的高度和宽度都是空的),那么这个方法只返回字符串data
。然后,您可以检索并保存它。
如何建造它
用画布创建一个 HTLM5 页面,就像你在本章中对其他解决方案所做的那样。`
solution 8-1/title>
</head>
<body onload="init()">
<canvas id="canvas" width="400" height="400"></canvas>
</body>
</html>`
</li>
<li>
<p>Add a button in the body of your page with the call to the function <code>saveCanvas</code> on its click event. <code> <button onclick="saveCanvas();">Save PNG</button></code></p>
<p>HTML5 本身也就这么多了。对于这个解决方案,从现在开始您将只处理 JavaScript。</p>
<p>因为您希望您的画布和上下文可用于您将要编码的几个函数,所以从定义它们各自的变量开始。然后,在一个单独的函数中检索画布和上下文对象,加载页面时将调用该函数。最后,您将调用一个函数(您将在此之后编写该函数)来处理绘制一个简单的形状,并且您将调用<code>drawShape()</code>。</p>
<p><code><script type="text/Javascript" language="Javascript"> var canvas; var context; function init(){ canvas=document.getElementById('canvas'); context=canvas.getContext('2d'); drawShape(); }</code></p>
</li>
<li>
<p>对于您的函数<code>drawShape()</code>,让它变得非常简单,只需在画布上画一个 200 像素宽的红色方块。<code>function drawShape(){ context.fillStyle="red"; context.fillRect(10,10,200,200); }</code></p>
</li>
<li>
<p>Use the function called on your button's click event, <code>saveCanvas(),</code> where you will retrieve the PNG image data of your canvas. `function saveCanvas(){<br>
urlData=canvas.toDataURL();<br>
window.location=urlData;</p>
<p>}`</p>
<p><code>urlData</code>变量现在将包含 PNG 图像数据的 base64 字符串。</p>
</li>
<li>
<p>Call your <code>init()</code> function upon the loading of your page. <code><body onload="init()"></code></p>
<p>当你启动你的页面时,你会看到红色方块和你的按钮,“保存 PNG”如果你点击按钮,它将打开一个新的网页,其网址是你的画布作为一个 PNG 文件,因此你的 PNG 在浏览器中。你可能会反对说这并没有真正挽救你的形象,这是非常正确的。然而,如果你想只使用 JavaScript,如果你需要一个可靠的解决方案,这是可以做到的,因为 JavaScript 是一种客户端语言。在下面的专家提示部分,您将看到如何通过 PHP 和 HTML5 来实现这一点。</p>
</li>
</ol>
<h4 id="专家提示-40">专家提示</h4>
<p>如果将 Save PNG 按钮与 AJAX 和 PHP 结合使用,就可以从画布中检索 PNG 并强制下载,这样客户机就可以保存 PNG 文件。您可以采用与上面相同的代码,但是当涉及到<code>saveImage()</code>函数时,您将进行不同的处理。</p>
<p>正如您所看到的,通过调用<code>dataToURL</code>方法,您检索了编码为 base64 字符串的 PNG 格式的画布。您可以通过<code>POST</code>方法发送这个变量来做几件事情(<code>GET</code>在大多数情况下不起作用,因为它受到字符大小的限制,图像可以产生很长的字符串):您可以将它保存在服务器端,并允许用户下载它。首先让我们看看如何通过 AJAX 用一个<code>POST</code>变量发送你的图像。为此,您将重写您的<code>saveCanvas()</code>函数。</p>
<p><code>function saveCanvas(){ var dataUrl=canvas.toDataURL(); var xtr=new XMLHttpRequest(); xtr.open("POST",'saveImage.php',true); xtr.setRequestHeader('Content-Type','application/upload'); xtr.onreadystatechange=function(){ if(xtr.readyState==4 && xhr.responseText=="ok"){ window.location="download.php"; } } xtr.send(dataUrl); }</code></p>
<p>如果您不熟悉 AJAX,让我们快速浏览一下这段代码。您在这里所做的是将您的 PNG <code>DataURL</code>作为一个<code>POST</code>变量发送到“<code>saveImage.php</code>”中的 PHP 脚本。为了发送画布上下文<code>dataURL</code>,您将使用 AJAX <code>XMLHttpRequest</code>对象。</p>
<p>当您的 PHP 脚本接收到这个变量时,它将解码 base64 编码的文件,创建一个 PNG 文件,将其命名为“canvas.png”,并将其保存在服务器上。如果一切顺利,它将发回字符串“ok”(注意,在这个例子中,您不会处理所有的错误消息。)</p>
<p><code>XMLHttpRequest</code>对象有一个名为<code>readyState</code>的属性。这是存储服务器响应状态的地方。有了它,你可以通过<code>onreadystatechange</code>属性监听服务器的响应。如果<code>readyState</code>改变了,意味着我们有了服务器响应,并且它等于 4(这意味着来自服务器的响应是完整的),那么您可以检查 PHP 脚本发回的<code>responseText</code>值。如果是字符串“ok”(由您的 PHP 脚本发送的字符串,通知您的 PNG 已被正确创建),您可以进入下一个也是最后一个步骤:通过“download.php”中的另一个小 PHP 脚本强制下载 PNG。</p>
<p>现在让我们看看 PHP 脚本。首先,让我们看一下<code>saveImage.php</code>页面,它在服务器端保存画布图像:。</p>
<p>`<?php<br>
if(isset(<span class="math inline">\(GLOBALS["HTTP_RAW_POST_DATA"])){
\)</span>imageData=<span class="math inline">G L O B A L S [ ′ H T T P R A W P O S T D A T A ′ ] ; </span>filteredData=substr(<span class="math inline">i m a g e D a t a , s t r p o s ( </span>imageData,",")+1);<br>
<span class="math inline">d e c o d e d D a t a = b a s e 64 d e c o d e ( </span>filteredData);<br>
<span class="math inline">f p = f o p e n ( ′ c a n v a s . p n g ′ , ′ w b ′ ) ; i f ( f w r i t e ( </span>fp,<span class="math inline">\(decodedData)){
fclose(\)</span>fp);<br>
echo 'ok';<br>
}</p>
<p>}<br>
?>`</p>
<p>这个脚本通过 PHP 函数<code>base64_decode(,)</code>对 base64 数据进行解码,然后将解码后的数据保存到服务器上名为“canvas.png”的 PNG 文件中。如果文件创建和保存正确,它将返回字符串“ok”。</p>
<p>用<code>download.php</code>页面调用的 PHP 脚本将强制下载保存的<code>canvas.png</code>文件,如下所示:</p>
<p><code><?php fsize=filesize('canvas.png'); header('Content-Disposition:attachment;filename="canvas.png"'); header('Content-type:application/force-download'); header('Content-type:image/png'); header('Content-length:'. fsize.''); readfile('canvas.png'); ?></code></p>
<p>现在,当用户单击 Save PNG 按钮时,它将首先在画布中创建一个 PNG,将其命名为“<code>canvas.png</code>”,如果成功,它将打开一个保存对话框窗口,允许用户将其保存到自己的系统中。</p>
<p>这是该示例的完整代码:</p>
<p>HTML5 页面:</p>
<p>`<html lang="en"></p>
<head>
<meta charset="UTF-8" />
<title>solution 6-8-2</title>
<script type="text/Javascript" language="Javascript">
var canvas;
var context;
function init() {
canvas=document.getElementById("canvas");
context=canvas.getContext("2d");
draw();
}
function draw() {
context.strokeStyle="#993300";
context.fillStyle="#ffff09";
context.font="bold 40px arial";
context.textAlign="center";
context.lineWidth="2";
context.lineJoin="round";
context.fillText("Saved Image",220,140);
context.strokeText("Saved Image",220,140);
}
<p>function saveCanvas(){<br>
var dataUrl=canvas.toDataURL();<br>
var xtr=new XMLHttpRequest();<br>
xtr.open("POST",'saveImage.php',true);<br>
xtr.setRequestHeader('Content-Type','application/upload');<br>
xtr.onreadystatechange=function(){<br>
if(xtr.readyState<mark>4 && xtr.responseText</mark>"ok"){<br>
window.location="download.php";<br>
}<br>
}<br>
xtr.send(dataUrl);<br>
}<br>
</script></p>
</head>
<body onload="init()">
<canvas id="canvas" width="400" height="400"></canvas>
<button onclick="saveCanvas();">Save as PNG</button>
<div id="link"></div>
</body>
</html>`
<p>saveImage.php 电码:</p>
<p><code><?php if(isset(GLOBALS["HTTP_RAW_POST_DATA"])){ $imageData=$GLOBALS['HTTP_RAW_POST_DATA']; $filteredData=substr($imageData,strpos($imageData,",")+1); $unencodedData=base64_decode($filteredData); $fp=fopen('canvas.png','wb'); if(fwrite($fp,$unencodedData)){ fclose($fp); echo 'ok'; } } ?></code></p>
<p>download.php 电码:</p>
<p><code><?php fsize=filesize('canvas.png'); header('Content-Disposition:attachment;filename="canvas.png"'); header('Content-type:application/force-download'); header('Content-type:image/png'); header('Content-length:'.$fsize.''); readfile('canvas.png'); ?></code></p>
<h3 id="总结-5">总结</h3>
<p>如您所见,HTML5 canvas drawing API 提供了广泛的可能性,允许您直接在网页和应用中进行绘制。您可以使用路径绘制基本或复杂的形状,使用颜色和渐变,以及绘制文本。结合所有这些功能,您无需使用任何第三方插件就可以创建出色的 web 内容。</p>
<p>在下一章,你将看到如何进一步使用 HTML5 画布绘制 API。您将能够操纵画布上下文中的元素来创建强大的 web 内容,并直接使用 HTML5 为其添加交互性和动画。</p>
<h1 id="七html5-画布">七、HTML5 画布</h1>
<hr>
<p>我们在前一章中看到了如何使用 HTML5 画布绘制 API。我们现在将更深入地操作 canvas,看看它如何用于动态渲染图形、游戏图形或其他视觉图像。正如您之前所了解的,canvas 是一个矩形区域,您可以将它添加到 HTML5 页面中,并且可以使用它通过几种可用的方法来呈现和操作图形。在这一章中,你将看到你能用 canvas 和它的其他 API 做什么。</p>
<p>苹果最初创建 canvas 元素是为了使用 Mac OSX WebKit 在 Safari 浏览器中制作 dashboard widgets。后来它被其他浏览器采用,并最终成为 WHATWG (Web 超文本应用技术工作组,<code>[</code>whatwg.org/html<code>](http://whatwg.org/html)</code>)接受的标准。</p>
<p>在第六章中,我们介绍了如何使用 canvas 元素绘制形状和文本。然而,你可以做的不仅仅是用画布绘制静态位图。canvas 元素为 HTML 提供了广泛的可能性——您可以渲染图形并使其具有交互性、处理图像、制作动画、构建游戏等等。</p>
<p>在 canvas 元素出现之前,每当您想在 HTML4 中这样做时,您都必须依赖第三方专有插件,如 Flash、Silverlight 等。现在,您可以通过 canvas 元素在 HTML5 文档中使用所有这些功能。</p>
<p>通过 canvas 元素可以实现很多事情,我们不可能在一章中涵盖所有内容。尽管如此,在这一章中,你将看到如何使用 canvas 元素在浏览器中轻松地添加丰富的图形。我们涵盖了 canvas 元素的以下方面:</p>
<ul>
<li>画布 API 概述</li>
<li>如何检测画布元素和画布文本支持</li>
<li>基于屏幕的标准坐标系和变换</li>
<li>如何在画布中操作像素</li>
<li>如何对图形元素应用阴影和模糊</li>
<li>如何使用 canvas 元素构建动画</li>
</ul>
<h3 id="解决方案-7-1了解-canvas-apis">解决方案 7-1:了解 canvas APIs</h3>
<p>如前一章所见,canvas 元素是一个矩形区域,允许您在 web 浏览器中动态呈现图形和位图图像。因此,它提供了广泛的图形可能性,允许您控制其内容的所有像素,并以多种方式操纵它们。当我们谈论渲染图形时,我们首先必须指出画布本身没有绘制或渲染功能,并且您将在其中渲染的一切都将通过使用 JavaScript 代码来实现。</p>
<h4 id="涉及到什么-48">涉及到什么</h4>
<p>你可以在你的 HTML5 页面中声明一个<code><canvas></code>元素,如第六章中的所示,如下所示:</p>
<p><code><canvas id="aCanvas" width="640", height="480"> Your browser doesn't support the canvas element! </canvas></code></p>
<p>这将声明一个宽 640 像素、高 480 像素、ID 为“aCanvas”的<code><canvas></code>元素。您的画布的任何子元素都将被您的浏览器视为后备内容,只有当您的浏览器不兼容画布时,它才会显示。HTML5 规范建议您的回退内容尽可能匹配您的画布内容。</p>
<p>要在画布上做任何事情,首先需要访问<code><canvas></code>元素,并从那里访问它的绘图上下文对象,在那里进行实际的绘图和像素处理。如前所述,您将使用 JavaScript 代码完成所有这些工作。由于 canvas 元素可以通过 DOM 获得,就像任何其他元素一样,您将通过它的“id”属性来访问 canvas:</p>
<p><code>var canvas=document.GetElementbyId('aCanvas');</code></p>
<p>您还可以通过编程方式添加画布,方法是使用 JavaScript 直接将画布插入 DOM,然后添加到页面:</p>
<p>`<head></p>
<script type="text/Javascript" language="javascript">
var canvas=document.createElement("canvas");
canvas.setAttribute('width', 640');
canvas.setAttribute('height','480');
canvas.setAttribute('id', 'aCanvas');
document.body.appendChild(canvas);
</script>
</head>`
<p>这将产生完全相同的结果:一个 640 像素乘 480 像素的画布,ID 为“a canvas”。</p>
<blockquote>
<p><em>提示:设置画布的宽度和高度会自动重置整个画布元素。如果在运行一个应用的过程中,你需要清空你的画布,只需要重新设置它的高度和宽度就可以很容易做到。</em></p>
</blockquote>
<p>在撰写本文时,HTML5 规范中只支持 2D 上下文,所以我们将只讨论这个上下文。然而,值得注意的是,通过 WebGL 支持,3D 上下文的使用已经取得了广泛的进展,这显示了在 canvas 元素中处理 3D 图形的巨大前景。我们期待它很快也能上市,开启新的和令人惊叹的 HTML5 可能性。</p>
<p>通过使用从 DOM 中检索的 canvas 对象的<code>getContext()</code>方法,获取对 2D 上下文的引用,如下所示:</p>
<p><code>var context=canvas.getContext('2d');</code></p>
<p>然后,通过其上下文,您可以使用其绘图 API 中可用的工具集,例如:</p>
<ul>
<li>变换(包括缩放和旋转)</li>
<li>阴影</li>
<li>复杂的形状,包括贝塞尔曲线和圆弧(见第六章</li>
</ul>
<p>关于可用的 2D 上下文 API 方法的完整列表和概述,请参考这个有用的 canvas 2D 上下文备忘单:<code>[</code>www.nihilogic.dk/labs/canvas_sheet/HTML5_Canvas_Cheat_Sheet.pdf<code>](http://www.nihilogic.dk/labs/canvas_sheet/HTML5_Canvas_Cheat_Sheet.pdf)</code></p>
<p>需要理解的重要一点是,画布使用即时模式渲染器。这意味着,当您调用一个方法在画布中进行绘制时,浏览器将在继续下一行代码之前立即呈现该更改。因此,无论何时您想要在画布上更改任何内容,您都必须重新发出在画布上使用的所有绘图命令,即使更改只影响一个元素。也就是说,canvas 元素提供了两种方法,让您可以随时存储和重置画布的状态。(画布绘制状态是已应用的所有样式、剪辑和变换值的快照。)</p>
<ul>
<li><em>save():</em> 这将保存当前的样式、剪辑和变换值。</li>
<li><em>restore():</em> 这将把样式和转换值重置为您上次在上下文中调用<code>save()</code>方法时的值。如果你还没有保存任何东西,那么这个方法就没有任何作用。</li>
</ul>
<p>例如,假设您在画布上下文上画了一个圆,其<code>strokeStyle</code>设置为灰色,然后您在上下文上调用<code>save()</code>方法,并用白色<code>strokeStyle</code>画另一个圆。如果您随后调用<code>restore()</code>,两个圆圈都将有一个灰色的笔划,并且您上次保存画布状态时定义的所有样式属性现在都将被恢复。请记住,我们处于即时呈现模式,浏览器会在浏览代码的过程中呈现更改。在处理 canvas 转换方法时,我们将更清楚地看到如何使用这些信息。</p>
<h4 id="如何建造它-48">如何建造它</h4>
<ol>
<li>
<p>Create a canvas in a regular HTML5 page:<br>
`<!DOCTYPE HTML></p>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Canvas
`
这里您正在创建一个名为init()
的函数,它将使用 JavaScript 从头开始创建您的画布。画布大小为 500 x 500,有 1 个像素的灰色边框。使用window.onLoad=init;
,您可以告诉页面在加载完成后运行init
函数并添加画布。为什么要使用这个全局事件处理程序,而不是仅仅用onLoad
将代码添加到 body 标签中?这两种方式都可以,但是如果你想在几个页面中重复使用相同的 JavaScript 代码(通过从单独的源调用),window.onLoad=init;
将更具适应性。
您也可以使用以下代码实现相同的结果(我们在这个解决方案代码中使用了它):
`
solution 7-1
`
根据你的需要,你可以使用任何一种可能性,后者更简单,对于简单的画布来说绰绰有余。
Retrieve the canvas context, and get the access to the API set of tools by adding the following in your init()
function after having defined or retrieved your canvas object:
var context=canvas.getContext('2d');
从现在开始,您可以访问 canvas context API 可用的所有方法。
With a new draw()
function, draw three differently-sized rectangles and use the save()
and restore()
methods to set their respective styles:
`function draw(){
context.fillStyle='#000099';
context.fillRect(0,0,200,155);
context.save();
context.fillStyle=#FF66FF';
context.globalAlpha=.6;
ccontext.fillRect(15,15,120,120);
context.save();
context.fillStyle='#993333';
context.globalAlpha=1;
context.fillRect(30,30,90,90);
}`
这段代码绘制了三个简单的矩形,一个蓝色的,一个较小的粉红色的,alpha 值为 0.6(正如你在解决方案 6-4 中看到的,颜色混合,使它们看起来更紫),一个栗色的,alpha 值为 1。在绘制了前两个形状之后,您分别保存了画布上下文状态,如果需要的话,一个接一个地存储它们以供将来使用。
Add two other simple rectangles, and set their styles by restoring the states you previously saved.
` context.restore();
context.fillRect(145,15,40,120);
context.restore();
context.fillRect(55,55,40,40);
}`
这将首先恢复之前保存的状态,然后绘制一个矩形。应用于该形状的设置是在您刚刚恢复的状态中定义的:alpha 为 0.6,粉红色为fillStyle
。
然后再次调用restore()
方法,它将恢复到您刚才应用的状态之前的状态;即原始状态值(您保存的第一个值),呈现另一个蓝色形状。
Close your draw()
function, and call it in the init()
function.
function init(){ var canvas=document.getElementById('canvas'); draw(); }
加载你的页面会产生如图 7-1 所示的结果。
图 7-1。 使用save()
和restore()
方法绘制的形状
以这种方式使用save()
和restore()
方法,虽然不实际,但提供了对其结果的简单理解。当你必须处理像素变换和交互性时,它们的效用就更加明显了,这一点你将在本章后面看到。这也清楚地显示了画布是如何通过顺序处理命令来处理图形渲染的。
此示例的完整代码如下所示:
`
solution 7-1
`
专家提示
每个画布只能有一个上下文。这意味着一旦你在上面画了东西,如果你要做任何改变,即使是对许多元素中的一个元素,整个场景都要被重画。有时,使用几个画布作为层,一个在另一个之上,以便只重画特定事件所需的内容,这可能会很有趣。
您可以在一个小示例中看到这一概念,在该示例中,您绘制了两个基本的重叠图形,并更改了每个图形的值,而不必通过使用单独的层来重绘另一个图形。(这里我们不会费心去画刻度值,只会用一个普通的图形。)
在常规<div>
元素中创建两个高度和宽度相同的画布。
The two canvases are here, but right now they are just next to another like any regular HTML element. To make them overlap each other, you'll use CSS to position them and set their z-index. You can either put this directly in your HTML5 page inside a <style></style>
tag, or in an external CSS file. In this example, let's just have everything on the same page for code readability.
`#graphs{
position:relative;
width:500px;
height:220px;
}
first_layer{
z-index: 1;
position:absolute;
left:20px;
top:0px;
}
second_layer{
z-index: 2;
position:absolute;
left:20px;
top:0px;
}`
要分别修改两个画布,请在画布下方添加一些按钮来调出不同的值:
<button id="graph1" onClick="drawGraph(context,data,'#ccc')">graph1 - 2001</button>
<button id="graph2" onClick="drawGraph(context,data2,'#ccc')">graph1 - 2005</button> <button id="graph3" onClick="drawGraph(context2,data3,'red')">graph2 - 2001</button> <button id="graph4 onClick="drawGraph(context2,data4,'red')">graph2 - 2005</button>
现在添加您的 JavaScript 代码来定义您的drawGraph()
函数。首先检索两个画布及其各自的上下文区域,然后定义您将需要的变量:
``
首先,该函数将使用clearRect()
方法清除上下文,在空白画布上开始,然后它将使用所需的值绘制图形。因此,每次你点击一个按钮,它只会用你想要的值重新绘制选中的图形,如图 7-2 所示。当然,您可以在一个层中实现相同的视觉效果,但这意味着要重新绘制每个事件的所有内容,甚至是具有相同值的其他图形。根据您的需要,您可以使用任何一种方法。使用多个画布作为层可以增强动画和游戏的性能,这有助于提高您的应用在移动浏览器上的性能。
图 7-2。 使用多个画布作为图层的图形
解决方案 7-2:检测画布和画布文本支持
每当您想要交付 web 内容时,确保兼容性是首要任务。尽管 HTML5 获得了主流浏览器的进一步支持,但所有浏览器(尤其是旧版本)都不支持 canvas 元素。另外,有些支持 canvas,但仍然不支持 canvas 文本 API。
处理浏览器不支持 canvas 的情况的基本方法是在你的<canvas></canvas>
标签中添加后备内容,正如你在第六章中看到的,尽可能匹配你的 canvas 内容。然而,这可能并不总是准确的,并且您可能会受到限制,因为回退内容不能提供您的画布所做的一切,例如交互式内容。因此,您可能希望在加载页面时动态检查浏览器支持,并提供比依赖 canvas 标记的后备内容更符合您需求的其他内容。您甚至可以选择相应地修改整个页面,并创建一个合适的替代页面来确保更好的用户体验。例如,您可以用 JavaScript 编写一个替代内容,它比简单的图像或纯文本更匹配您的画布内容。
涉及到什么
检查浏览器是否支持 canvas 元素的一种简单方法是在检索 canvas 对象及其上下文时进行。如果浏览器不支持 canvas 元素,那么当通过 DOM 检索 canvas 对象时,您将只有一个基本的对象,没有任何特定于 canvas 的内容。所以如果你试图调用getContext()
方法,它不会返回任何函数。您可以编写一小段代码来实现这一点:
` var canvas=document.getElementById('canvas');
if(!!canvas.getContext){
}`
因此,如果浏览器不支持 canvas 元素,您可以添加任何替代元素。否则,就像往常一样检索画布上下文。
else{ var context=canvas.getContext('2d'); }
如果你的浏览器支持 canvas 元素,并不一定意味着它支持 canvas text API。因此,如果您想在画布上绘制任何文本,也需要检查这一点。遵循相同的过程:调用特定于 canvas text API 的方法,并查看它返回的内容。如果它不返回任何函数,那么它就不被支持。
if(!!context.fillText){ //the Canvas text API is supported and you can proceed with your code } else{ // you can add an alternative text, or add any other element to replace it through the DOM. }
如何建造它
创建两个简单的函数来检查 canvas 和 canvas 文本支持。首先,编写一个canvasSupport
函数。原理是创建一个画布,通过 DOM 访问它的上下文对象。如前所述,如果 canvas 元素不受支持,canvas 对象将无法调用getContext()
方法,它将返回一个未定义的值。然后你可以使用 JavaScript 双负技巧将返回值强制转换为布尔值(真或假):如果为真,则意味着定义了上下文,并且支持画布;否则就不是了。
function canvasSupport(){ return !!document.createElement("canvas").getContext; }
通过从画布上下文调用fillText()
方法,创建一个函数来检查画布文本支持。如果你的浏览器不支持它,它将返回一个未定义的值,而不是一个函数。同样,您将返回值强制转换为布尔值。
在加载页面事件上调用您的函数:`window.onload=init;
function init(){
if(canvasSupport){
// proceed with the use of the canvas APIs
If(canvasTextSupport){
//proceed with your code using the Canvas text API
}
}
else{
//add any alternative you wish in complement of the fallback content if you
wish.
}
}`
现在看一下前面的例子,在这个例子中,您使用了几个画布作为层,以交互的方式显示图形。您仍然希望没有 HTML5 兼容浏览器的用户能够看到您的图表,因此您只需编写一个小的替代程序,让他们能够看到您不同图表的图片。在这里,他们不会看到 HTML5 中图形的超级拼版,但他们仍然会有经典的按钮和图像。这段代码只会在不兼容的浏览器上加载,如下:
`
solution 7-2
</head>
<body>
<div id="graphs" >
<canvas id="first_layer" width="500" height="200" ></canvas>
<canvas id="second_layer" width="500" height="200"></canvas>
</div>
<div id="nav">
<button id="graph1" onClick="drawGraph(context,data,'#ccc','graph1')">graph1 2001</button>` `<button id="graph2" onClick="drawGraph(context,data2,'#ccc','graph2')">graph1 2005</button>
<button id="graph3" onClick="drawGraph(context2,data3,'red','graph3')">graph2 2001</button>
<button id="graph4" onClick="drawGraph(context2,data4,'red','graph4')">graph2 2005</button>
</div>
</body>
</html>`
专家提示
如果不想自己写函数检查浏览器支持,可以用 Modernizr。它是一个开源的 JavaScript 库,检测对许多 HTML5 特性(如地理定位、视频、本地存储等)以及 CSS3 的支持。由于页面上的元素很有可能比画布上的元素多,所以您可以使用它来处理您可能需要的各种支持检测。
至于画布和画布文字,真的很简单。首先包括库(在撰写本文时,我们使用的是最新版本):
`
if (Modernizr.canvas) {
var c=document.createElement('canvas');
c.setAttribute('width', '500');
c.setAttribute('height','500');
c.setAttribute('id', 'canvas'); var context=c.getContext('2d');
}
else{
//no canvas support
var alt_canvas=document.createElement("div");
alt_canvas.setAttribute('width', '500');
alt_canvas.setAttribute('height','500');
alt_canvas.setAttribute('id', 'alt_canvas');
document.body.appendChild(alt_canvas);
}`
…对于画布文本支持:
if (Modernizr.canvastext) { // draw your text context.fillStyle="#000"; context.font="bold 15px Arial"; context.fillText("Some text drawn in your canvas.",20,40); } else { //no canvas text support var alt_canvas_text=document.createElement("div"); alt_canvas_text.setAttribute('width', '500'); alt_canvas_text.setAttribute('height','40');
alt_canvas_text.setAttribute('id', 'alt_canvas_text'); document.body.appendChild(alt_canvas); document.getElementById('alt_canvas_text').innerHTML="Some alternative text." }
您可以从以下地址下载 Modernizr 的最新版本以及用户文档:[
www.modernizr.com/](http://www.modernizr.com/)
。
解决方案 7-3:理解标准的基于屏幕的坐标系和画布变换
HTML5 canvas 还提供了允许你进行变换操作的方法,比如缩放、旋转、平移,甚至变换矩阵操作,它们都是关于 canvas 坐标系的。事实上,当您在画布上下文上执行变换时,您正在根据您的目标(重新缩放、移动形状等)变换整个上下文的坐标系,而不是在其上绘制的内容。在这个解决方案中,您将看到如何操纵坐标系来实现这一点。
涉及到什么
您可以在画布上下文上执行一些转换操作。它们中的每一个都需要修改上下文的坐标。流程是先修改坐标系,然后照常在上面画。简而言之,你修改的不是上下文的内容,而是上下文本身。
Scaling canvas objects: You achieve this by using the scale()
method, which scales the canvas context itself; that is, the x and y-axis coordinates. This method takes two parameters: the scale factor in the horizontal direction, and the scale factor in the vertical direction. Those scale values are based on the unit size of 1: scale(1,1)
will keep the same scale as the original, values larger than 1 will increase it, and values smaller than 1 will decrease it. The values must always be positive.
context.scale(scaleX,scaleY);
注意:要获得比例缩放值,必须给出相等的 scaleX 和 scaleY 值。
平移画布对象: 如果你想移动画布上绘制的形状或任何东西,首先使用translate()
方法。它会将画布及其原点移动到网格中的另一个点,这个点是通过两个参数定义的。第一个参数是画布在水平轴上移动的量(以像素为单位)。第二个参数是它在纵轴上向上或向下移动的量。这个点就是新的原点(0,0),无论你画什么都以它为原点。
旋转画布对象 :rotate()
方法将上下文旋转到其参数给定的角度。角度值以弧度为单位。
context.rotate(angle);
使用转换矩阵: transform()
方法将改变转换矩阵以应用其参数给定的矩阵,如下所示:
context.transform(a,b,c,d,e,f);
这些参数对应于图 7-3 中所示的矩阵变换值。
图 7-3。 矩阵转换值
注意:只有对应于 a、b、c、d、e 和 f 的值可以修改。当一个矩阵没有引起变换时,你就有了所谓的“单位矩阵”
也可以用setTransform(a,b,c,d,e,f)
的方法。它将当前变换重置为单位矩阵(参见图 7-3 ,然后用其参数调用transform()
方法。
您还可以通过变换矩阵执行旋转、缩放和平移。好处是你可以一次全部执行。另一方面,如果您不熟悉 2D 矩阵系统,那么依次使用其他特定方法可能会更安全。
在应用任何变换之后,你可能想要回到原始坐标系进行下一次绘图或操作(除非你只是真的喜欢思维练习!).这就是画布的save()
和restore()
方法最有用的地方。(参见解决方案 7-1,了解save()
和restore()
方法)。在使用任何变换方法之前,您可以通过save()
方法保存画布状态来保留原始坐标系。然后,完成变换后,您只需从变换前阶段恢复画布状态,然后使用原始的常规坐标系执行任何新操作。我们建议您对想要执行的每个转换都使用这个过程。否则,它会很快变得复杂,因为每次转换都要重新定义坐标。
如何建造它
要了解如何使用坐标系统的变换,让我们使用所有变换方法创建一些绘图。此示例旨在展示这是如何工作的,而不是创建一个可用的绘图。
首先创建一个基本的 HTML5 页面,用一个画布和一个 onload 事件函数来检索画布及其上下文。
`
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>solution 7-3/title>
<script type="application/Javascript" language="Javascript">
var context;
window.onload=init;
function init(){
if(document.getElementById('canvas').getContext){
context=document.getElementById('canvas').getContext('2d');
draw();
}
else{
//add anything you want for non-compliant browsers
}
}
</head>
<body>
<canvas id='canvas' width= "500" height= "500">
</body>`
Code your draw()
function, which contains the commands to draw shapes with transformations. Start to draw a couple of round shapes and position them in a circular way using the rotate()
method.
`function draw(){
context.save();
context.translate(250,250);
for (var i=0;i<8;i++){
context.rotate(Math.PI2/(8));
context.fillStyle = 'rgb('+(30 i)+','+(10i)+','+(200-3 i)+')';
context.beginPath();
context.arc(0,12.5,4,0,Math.PI*2,true);
context.fill();
}
context.restore();`
首先,您开始保存原始上下文。然后用一个平移,你设置你想要你的绘图从原始画布的点(250,250)开始,原点将从现在开始平移到那个点。您使用一个循环绘制八个圆,并在绘图中以 360 度的值旋转画布八次(数学。π×2(弧度)除以 8,使它们呈圆形排列,并且彼此等距。
现在您在画布上绘制其他形状,但是您不希望应用当前的转换(画布已经旋转了 8 次并进行了平移),所以您调用restore()
并带回上次保存的绘制状态,或者在本例中是原始状态。
使用一个循环绘制另一组形状——这次是正方形。同样,从保存当前画布状态开始。首先,设置一个新的平移值,原点现在位于新的点(258,250)。
context.translate(258,250); for (var j=0;j<70;j++){ var sin=Math.sin(Math.PI/6); var cos=Math.cos(Math.PI/6); color=Math.floor(255 / 120 * j); context.fillStyle="rgb(" + color + "," + color + "," + color + ")"; context.transform(cos,sin,-sin,cos,j*2,j/2); context.globalAlpha=.8; context.lineWidth=.2; context.strokeStyle="#333"; context.fillRect(20,20,25,25); context.strokeRect(20,20,25,25) } }
这里你正在画正方形,在每一个正方形上你应用一个矩阵变换来旋转和平移它们,创建下图(见图 7-4 )。在这个循环结束时,画布上下文将被转换 70 次。因为您不再绘制任何东西,所以没有必要保存和恢复画布上下文。
图 7-4。 具有形状和颜色变换的绘图
此示例的完整代码如下所示:
`
solution 7-3
`
专家提示
如果您想要从图像的中心旋转图像,您首先需要转换上下文以移动原点。然后应用旋转并相应地绘制图像,如下所示:
`var img=new Image();
img.src='yourImagePath.jpg';
img.onload=function(){
context.translate(img.width/2, img.height/2);
context.rotate(90*Math.PI/180); // here we apply a 90 degres rotation
context.drawImage(img,-img.width/2,-img.height/2,img.width, img.height);
}`
解决方案 7-4:像素操作
正如您在前面了解到的,画布 API 提供了让您操纵画布内任何像素的方法。您看到了您可以绘制形状并对它们应用变换,但是也有一些方法允许您在画布上一个像素一个像素地绘制或应用变化。
涉及到什么
您可以通过使用ImageData
对象来实现对画布上像素的操作。它表示您选择的画布上下文区域的当前状态的像素数据。它有以下属性:以像素为单位的宽度和高度,以及“data”——一个包含图像数据的每个像素的颜色分量的canvasPixelArray
元素;即红色、绿色、蓝色和 alpha(每个值从 0 到 255)。像素从左到右和从上到下排序。通过这些图像对象数据,您将能够在画布上执行逐像素操作。
对于像素操作,canvas API 提供了三种方法来执行以下操作:
Creating image data: Call the createImageData()
method, which takes 2 parameters: width, and height of your image data in pixels. It will create a set of transparent black pixels that you can then manipulate by assigning values to the data canvasPixelArray
.
如果我们要做一个像素阵列的表示,它看起来会像图 7-5 所示的图像。
图 7-5。 像素阵列
var canvas=document.getElementById('canvas'); var context=canvas.getContext('2d'); var imagedata=context.createImageData(canvas.width,canvas.height);
这将创建一个包含整个画布上下文的像素数据的imagedata
对象。
注意:您可以在创建图像数据时指定坐标,以选择画布上下文的特定区域。默认情况下,它们被设置为 0,0,因此如果您只给定画布的宽度和高度作为参数,您的图像数据将覆盖整个画布。
var canvasPixelArray=imagedata.data;
现在你可以通过canvasPixelArray
访问每一个像素,你可以开始随心所欲地操作它。因为每个像素有四个颜色值(红色、绿色、蓝色和 alpha),所以滚动数组并为每个像素设置颜色值。
以下代码将为 imagedata 对象的每个像素分配以下 RGBA 值:255、200、125 和 150。
` for (var i=0;i< canvasPixelArray.length;i+=4) {
canvasPixelArray [i]=255 // red channel
canvasPixelArray [i+1]=200; // green channel
canvasPixelArray [i+2]=125; // blue channel
canvasPixelArray [i+3]=150; // alpha channel
}`
Retrieving a pixel array : You can also retrieve the pixel array from an existing canvas through the getImageData()
method.
它有四个参数——要检索的区域的 x 和 y 位置以及宽度和高度(以像素为单位)。
context.getImageData(x,y,width,height);
使用这种方法,您可以通过drawImage()
方法访问您在画布上绘制的形状的像素数组,或者您在画布上下文中添加的图像的像素数组。然后你可以做任何种类的像素操作来创建像滤镜这样的东西。
在上下文上绘制图像数据: 一旦你创建了一个imageData
对象并对其进行了任何类型的像素操作,你就可以通过调用putImageData()
方法在上下文上绘制你的imagedata
。它有三个参数:imagedata 对象及其 x 和 y 坐标。
Context.putImageData(imagedata,0,0);
如何建造它
为了查看像素操作的实际示例,我们将拍摄一张照片,并对其应用基本的颜色过滤器,然后在画布上的原始照片旁边显示操作后的版本,如图 7-6 所示。(这个例子是为了可读性,并没有特别优化。)
图 7-6 。通过像素操作应用了不同颜色滤镜的照片
为此,请遵循以下步骤:
用画布创建一个基本的 HTML5 页面。
`
solution 7-4/title>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
</body>
</html>`
</li>
<li>
<p>然后,在您的<code><head></head></code>标签中,检查画布支持,并在加载页面时加载一个图像。</p>
</li>
<li>
<p>当图像加载完成后,访问画布上下文,将照片添加到画布的左上角(原点)。然后通过调用<code>getImageData()</code>方法并传递画布上的照片坐标及其宽度和高度来获取照片的 imagedata 对象。通过这样做,imagedata 对象将只获得画布这个区域的像素数据。<br>
`function imageLoaded(evt){</p>
<p>context=document.getElementById('canvas').getContext('2d');<br>
originalPic=evt.target;<br>
context.drawImage(originalPic,0,0);<br>
var imgd=context.getImageData(0,0, originalPic.width,originalPic.height);</p>
<p>applyBluefilters(imgd);<br>
applyRedfilters(imgd);<br>
applyGreenfilters(imgd);</p>
<p>}`</p>
</li>
<li>
<p>Now that you have your photo's pixel data available, create some custom methods to apply basic color filters to them.<br>
`function applyBluefilters(img){</p>
<p>var pixelsArray=img.data;<br>
for(var i=0;i<pixelsArray.length;i+=4){</p>
<p>img.data[i]=img.data[i]/2;<br>
img.data[i+1]=img.data[i+1]/2;<br>
img.data[i+2]=img.data[i+2]*2.5;<br>
img.data[i+3]=img.data[i+3];</p>
<p>}</p>
<p>context.putImageData(img,170,0);<br>
}`</p>
<p>这里首先检索包含像素数据的数组。对于每个像素,首先获取其红色、绿色、蓝色和 alpha 值,并根据需要重新分配新值。为了实现这种蓝色滤镜效果,减少红色和绿色的值,并加入更多的蓝色。完成后,在上下文上绘制新的 imagedata。</p>
</li>
<li>
<p>根据您想要实现的效果,通过调整每个像素的 RGB 值来创建应用红色和绿色滤镜的方法。<br>
??` }</p>
<p>context.putImageData(img,0,170);<br>
}</p>
<p>function applyGreenfilters(img){</p>
<p>var pixelsArray=img.data;</p>
<p>for(var i=0;i<pixelsArray.length;i+=4){</p>
<p>r=img.data[i]<em>3;<br>
g=img.data[i+1]/2;<br>
b=img.data[i+2]/2;<br>
a=img.data[i+3];<br>
img.data[i]=r;<br>
img.data[i+2]=b;<br>
img.data[i+3]=a;<br>
r=img.data[i]/2;<br>
g=img.data[i+1]</em>4;<br>
b=img.data[i+2]/2.5;<br>
a=img.data[i+3];</p>
<p>img.data[i]=r;<br>
img.data[i+1]=g;<br>
img.data[i+2]=b;<br>
img.data[i+3]=a;</p>
<p>}<br>
context.putImageData(img,170,170);<br>
}<br>
</script>`</p>
</li>
<li>
<p>在你的图像上调用你的自定义方法<code>loadEvent()</code>。</p>
</li>
</ol>
<p>这里显示了这个例子的完整代码。(您需要将图像“<code>pic.jpg</code>”放在同一个文件夹中。)</p>
<p>`<!DOCTYPE HTML></p>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>solution 7-4
`
专家提示
操纵像素会降低性能。如果你多次使用你的画布上下文的同一个 imagedata,并且你需要恢复它(在一个帧事件,鼠标事件,或者在一个循环中,等等),你可以使用toDataUrl
方法代替putImageData()
(见第六章,解决方案 6-8 关于这个方法的更多细节)。
savedImagedata=new Image() savedImagedata.src=canvas.toDataURL("image/png");
每当您需要恢复它时,请使用以下代码:
context.drawImage(savedImagedata,x,y) ;
解决方案 7-5:应用阴影和模糊
canvas API 还提供了在画布上绘制的任何东西(形状、路径、文本、图像等等)上创建漂亮的模糊阴影效果的方法。
涉及到什么
正如您到目前为止所了解的关于 canvas API 的大部分内容一样,向您绘制的元素添加阴影意味着在绘制时将这些效果应用到 canvas 上下文本身,从而将其应用到您正在绘制的内容。canvas API 提供了与阴影相关的上下文属性,可以让你定义它的颜色、位置和模糊程度。(如果你熟悉用 CSS 应用阴影效果,你会发现自己在这里非常熟悉的领域。):
shadowColor: 这个属性设置上下文阴影的颜色。创建上下文时,它是透明的黑色。
context.shadowColor="#333";
shadowOffsetX :该属性返回并设置水平阴影偏移量,以像素为单位。context.shadowOffsetX=5;
shadowOffsetY: 该属性返回并设置垂直阴影偏移量,以像素为单位。
context.shadowOffsetY=5;
shadowBlur: 这个属性返回并设置模糊等级。其值必须大于 0。
context.shadowBlur=10;
设置上下文阴影属性后,它们将应用于您在画布上绘制的任何内容,直到您重置它们。
如何建造它
要查看如何在上下文中应用模糊阴影,让我们绘制简单的形状,圆角矩形,并在画布中对它们应用不同的阴影。它应该给出如图 7-7 所示的结果:
图 7-7。 使用阴影
用画布创建一个基本的 HTML5 页面。首先在加载时检查画布支持,然后访问画布上下文。
??`
solution 7-5/title></p>
<script type="text/Javascript" language="Javascript">
var context;
window.onload=init;
<p>function init(){<br>
if(!!document.getElementById('canvas').getContext){</p>
<p>context=document.getElementById('canvas').getContext('2d');</p>
<p>}<br>
else{<br>
// add anything you wish for non-compliant browsers<br>
}<br>
}<br>
</script></p>
</head>
<body>
<canvas id="canvas" width="300" height="340">
Your browser doesn't support HTML5 Canvas !
</canvas>
</body>
</html>`
</li>
<li>
<p>Now create a custom method to draw rounded squares using paths.<br>
`function drawRoundRect(x,y,width,height,radius){</p>
<p>context.beginPath();<br>
context.moveTo(x +radius, y);<br>
context.lineTo(x+width-radius, y);<br>
context.quadraticCurveTo(x+width, y, x + width,y+radius);<br>
context.lineTo(x+ width, y+height-radius);<br>
context.quadraticCurveTo(x+width,y+height, x+width-radius, y+height);<br>
context.lineTo(x+radius, y+height);<br>
context.quadraticCurveTo(x,y+height, x, y+height-radius);<br>
context.lineTo(x,y+radius);<br>
context.quadraticCurveTo(x, y,x+radius,y);<br>
context.closePath();</p>
<p>//defining the gradient fill<br>
gradient=context.createLinearGradient(x+width/2, y,x+width/2, y+height);<br>
gradient.addColorStop(0., "rgb(218, 51, 163)");<br>
gradient.addColorStop(0.5, "rgb(167, 0, 118)");<br>
gradient.addColorStop(0.5, "rgb(192, 69, 160)");<br>
gradient.addColorStop(1.0, "rgb(192, 64, 163)");<br>
context.fillStyle=gradient;</p>
<p>//defining the stroke color<br>
context.strokeStyle="#B3B3B3";</p>
<p>//rendering the shape on the context<code> </code> context.stroke();<br>
context.fill();</p>
<p>}`</p>
<p>该函数接受这些参数:正方形在画布网格上的 x 和 y 位置,它的宽度和高度,以及要应用的圆角半径。它使用二次曲线路径来创建圆角。你也可以用“类似 web 2.0”的渐变填充你的形状。</p>
</li>
<li>
<p>Create a custom method to apply blurred shadows to your context. By doing this, you can re-use it several times.<br>
`function applyShadow(r,g,b,a,posX,posY,blur){</p>
<p>context.shadowColor='rgba('+r+','+g+','+b+','+a+')';<br>
context.shadowOffsetX=posX;<br>
context.shadowOffsetY=posY;<br>
context.shadowBlur=blur;</p>
<p>}`</p>
<p>这些参数是阴影颜色的红色、绿色、蓝色和 alpha 值,它相对于在上下文中绘制的元素的水平和垂直位置,以及高斯模糊级别。</p>
</li>
<li>
<p>When you apply shadow properties to your context, they will then apply to anything you draw on it. Thus you will make a small custom method to reset those values to default in case you want to draw anything without a shadow.<br>
`function resetShadow(){</p>
<p>context.shadowColor='rgba(0,0,0,0)';<br>
context.shadowOffsetX=0;<br>
context.shadowOffsetY=0;<br>
context.shadowBlur=0;</p>
<p>}`</p>
<p>默认的上下文阴影颜色是透明的黑色,所以你只需要重置它。</p>
</li>
<li>
<p>现在使用这些方法,通过改变上下文阴影属性,用不同的阴影参数绘制形状。然后,画一些没有任何阴影的文字。<br>
??` applyShadow(102,102,102,.8,2,2,10);<br>
drawRoundRect(140,140,100,100,10);</p>
<p>resetShadow();</p>
<p>context.fillStyle='#333';<br>
context.font='Bold 22px Arial';<br>
context.fillText('Using shadows',60, 275);</p>
<p>}</p>
<p>}`</p>
</li>
</ol>
<p>因为这是一个简单的例子,当调用<code>init()</code>函数时只需画出你的形状。</p>
<p>首先定义上下文阴影属性,使第一个形状为深灰色,alpha 值为 0.8,位置为–2,模糊度为 5。然后画出你的第一个圆角正方形。因为您希望下一个方块有不同的阴影,所以再次调用自定义的<code>applyShadow()</code>方法,为上下文阴影设置一个新值,并绘制第二个方块。对你的最后一个方块使用相同的程序。</p>
<p>因为您希望您的文本没有任何阴影,所以调用<code>resetShadow()</code>方法,它会将所有值重置为 0。然后,您可以使用阴影绘制文本。</p>
<p>此示例的完整代码如下所示:</p>
<p>`<!DOCTYPE HTML></p>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>solution 7-5
Your browser doesn't support HTML5 Canvas !
`
专家提示
画阴影真的很消耗处理能力。因此,我们建议如果你想达到一个平滑的效果,不要在动画中大量使用它。
解决方案 7-6:制作画布动画
canvas 的一个真正令人兴奋的特性是能够在 canvas 内部创建动画,使您的绘图移动。最大的优点是它让你可以直接在你的 HTML 页面中创建动画,而不需要依赖第三方插件。这是全新的吗?不完全是。在 HTML4 中,使用 CSS 和/或 SVG 结合 JavaScript 创建动画已经成为可能。你可以在网上找到一些惊人的例子。然而,有了画布的绘图 API,实现这一点变得容易得多,并且它有一些非常令人兴奋的可能性。
canvas 元素中的动画暗示了什么?如果你喜欢动画,你很可能听说过翻页书。如果没有,它们是一叠代表动画不同状态的图画(例如,一个人走路),你拿在手上,当你快速翻阅页面时,翻页书通过创造连续动作的幻觉使其内容移动。这就是你如何用 canvas 元素创建动画的方法;也就是说,通过在一段时间内用不同的内容(形状、图像或任何可以在画布上绘制的内容)重复重绘画布。
涉及到什么
让我们来看看在画布上制作动画的步骤:
设置一个循环,以规定的时间间隔重复。
在每个间隔:
在画布上用新的位置、形状等绘制出您需要的任何动画。
在画布上绘制图形。
清理画布。(显然,如果你不清理你的画布,每隔一段时间,每幅新的画都会叠加在另一幅上。)
重复循环。
如果你想让你的动画停止,只需清除间隔。(你也可以有一个无限循环。)
您可能已经猜到,处理画布的计时和重绘意味着使用 JavaScript。原理相当简单:创建一个循环间隔,并在指定的时间间隔重新绘制画布。
要执行上述过程,您可以使用 JavaScript 定时事件,具体来说,就是以下方法:
setInterval(callback,time)方法: setInterval
每隔一定时间调用一个函数,以毫秒为单位定义,作为第二个参数。这可用于定期更新画布元素上所绘制内容的位置、颜色和/或样式。
clear interval(回调,时间)方法: clearInterval()
清除间隔。
这里有一个例子:
`var context;
var canvas;
var posX=0;
var posY=0;
window.onload=init;
function init(){
canvas=document.getElementById('canvas')
context=canvas.getContext('2d');
setInterval(drawShape,20); // calls the drawShape() function every 20 milliseconds.
}
function drawShape(){
if(posX<=canvas.width-30){
clearContext();
ctx.fillRect(posX++,posY,30,30);
}
else{
clearInterval(drawShape,20);
}
}
function clearContext(){
context.clearRect(0,0,canvas.width,canvas.height);
}`
在这个小例子中,我们每隔 20 毫秒用一个新的 x 位置重画一个正方形,方法是在每个新的时间间隔增加 x 位置并清除上下文,这样就给人一种它向右移动的错觉。因为我们希望正方形在到达画布右侧时停止移动,所以我们在每个循环间隔检查它的位置,当它到达边缘时,我们清除间隔。
setTimeout(回调,时间)和 clearTimeout(回调,时间)方法: 这些方法用于在给定的时间后调用一个函数。下面的代码将具有与前面的例子相同的视觉效果,但是它使用了不同的方法:
`function init(){
canvas=document.getElementById('canvas')
context=canvas.getContext('2d');
drawShape();
}
function drawShape(){
if(posX<=canvas.width-30){
clearContext();
context.fillRect(posX++,posY,30,30);
setTimeout(drawShape,20); // will call the drawShape function in 20
millisecond
}
}
function clearContext(){
context.clearRect(0,0,canvas.width,canvas.height);
}`
SetTimeOut() method: This can be used if you want to set several timings in your animation, as you can check how much time has passed once the function is called. This lets you to plan your animation based on time rather than on positions if needed. To clear the delay set by setTimeOut()
, use the clearTimeOut()
method.
注意:在所有方法中,时间都是以毫秒为单位定义的(1000 毫秒=1 秒)。
更新画布上下文渲染是在 HTML5 中构建动画所需要做的。之后,更多的是关于你如何用你的代码和我们提到的不同的 API 图形工具来管理它。
如何建造它
要构建一个球在画布上移动并检测墙壁碰撞的简单动画,请遵循以下步骤。
用画布创建一个基本的 HTML5 页面。
`
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>solution 7-6</title>
<body>
<canvas width="500" height="250" id="canvas">
Your browser doesn't support the HTML5 Canvas element !
</canvas>
</body>
</html>`
检查画布支持,并在页面加载时通过 DOM 访问画布及其上下文。您还可以定义代码动画所需的变量。
??`window.onload=init;
function init(){
if(!!document.getElementById('canvas').getContext){
canvas=document.getElementById('canvas');
context=canvas.getContext('2d');
}
else{
//add anything you want for non-compliant browsers
}
}`
写一个函数来画你的球。当你想让它在画布上移动时,将其坐标设置为参数:
` function drawBall(x,y){
context.beginPath();
context.arc(x,y,20, 0, Math.PI*2, true);
context.fillStyle="red";
context.fill();
}`
Write a function to clear the context. As the animation in the canvas is redrawing the context with each new step, you need to reset your canvas to a blank area each time:
` function clearContext(){
context.clearRect(0,0,canvas.width,canvas.height);
}`
要清除上下文,在整个画布区域使用clearRect()
方法。(记住clearRect
清除所有像素的指定区域。)
Now you are ready to bring in some animation. For this, use the setInterval
method to call an animate()
function containing the new position of your ball on each new frame. As you want your animation to start when the page loads, add it in your init()
function.
`function init(){
if(!!document.getElementById('canvas').getContext){
canvas=document.getElementById('canvas');
context=canvas.getContext('2d');`
设定间隔(动画,20);
} }
这将每 20 毫秒调用一次animate()
函数。
现在让我们看看你需要改变的不同变量,以确保你的球在画布上移动。首先,设置xpos
和ypos
变量,它们将在每个新的间隔定义球坐标。还要定义一个速度变量,一个dirX
变量,用来设置球在 x 轴上的方向,一个dirY
用来设置球在 y 轴上的方向。
var xpos=50; var ypos=50; var speed=3; var dirX=1; var dirY=1;
在开始时,你的球将被放置在(50,50)点上,它的速度将为每帧 3 个像素,并从那里下降。
现在,您已经拥有了编写animate()
函数使您的球移动的所有元素:
`function animate(){
clearContext();
xpos+=speed;
ypos+=speed;
drawBall(xpos,ypos);
}`
你要做的第一件事是通过调用你为此目的编写的小函数来清空你的画布。现在,您可以通过将速度值添加到球的最后位置来设置球的新 x 和 y 位置。使用drawBall()
函数用这些新值重画你的球。现在,在每个间隔,球的 x 和 y 坐标将增加 3 个像素,如果您加载页面,球将从画布的左上角开始,无限期地沿对角线下落。当它到达画布的边缘时,它就会消失。
现在我们希望球从画布边缘弹开。为此,在您的animate()
函数中添加一个简单的碰撞测试。这非常简单,你只需检查你的球的最后位置,看看它是否到达画布区域坐标(减去球的半径)。如果是这样,通过将方向变量乘以-1 来改变方向。
if (xpos>=canvas.width-20 || xpos<=20){ dirX*=-1; } if(ypos>=canvas.height-20 || ypos<20){ dirY*=-1; }
现在,您可以通过将速度乘以方向变量来设置球的方向。您的animate()
函数将完成这些任务:清除上下文、检查碰撞、更改 x 和/或 y 方向(如果需要的话)、在画布区域为您的球设置新坐标,最后使用新坐标在画布上绘制球。
`function animate(){
clearContext();
if (xpos>=canvas.width-20 || xpos<=20){
dirX=-1;
}
if(ypos>=canvas.height-20 || ypos<20){
dirY =-1;
}
xpos+=speeddirX;
ypos+=speed dirY;
drawBall(xpos,ypos);
}`
不要忘记关闭您的脚本标签。
`
`
此示例的完整代码如下所示:
`
solution 7-6
</head>
<body>
<canvas width="500" height="250" id="canvas">
Your browser doesn't support the HTML5 Canvas element !
</canvas>
</body>
</html>`
专家提示
画布上的动画是一门非常宽泛的学科,要学的东西很多。然而,如果你想开始用 canvas 制作动画,这里有几个小技巧会对你非常有用。
重绘画布会消耗大量的 CPU 周期,所以在使用移动浏览器时,你必须考虑到这一点。这里有一些你可以做的事情来优化你的动画(或者你的游戏)。
在解决方案 7-1 中,你看到了使用几个画布作为层是可能的。嗯,对动画更有用。例如,如果您的动画只有一个正在变化的绘制元素,那么只重新绘制这个元素会非常有益,这样可以避免执行不必要的任务。这对于游戏也是相关的。
要清除上下文,有一个简单的技巧可以节省另一个绘制操作:如果您重置画布的宽度和高度,它会立即清除它。
在某些情况下,可以使用缓冲技术来避免画布闪烁,例如,在同一屏幕位置创建两个画布,并在绘制完成后使用 visibility 属性显示缓冲区。
总结
正如您所看到的,HTML5 canvas 及其 API 提供了一种新的、强大的方式来创建引人入胜的 web 内容和应用,同时本机增加了用户体验。事实上,你现在可以使用和操作图像和渐变;直接在 HTML 页面中绘制、转换和动画化内容,并随时更新,从简单的应用到游戏,为您提供无限的编码可能性。
在下一章,你将学习如何使用 HTML5 通信 API。
八、HTML5 通信 API
在本书的这一点上,已经很清楚 HTML5 提供了许多新的工具来创建应用,这些应用以一种更本地的方式与服务器交互。
在这一章中,我们将探讨避免浏览器沙箱安全限制的技术和解决方案,并使用一种新技术来创建从不同域进行通信的文档(跨文档消息传递),这涉及到 postMessage API 的使用。你将学习什么是跨源资源共享(CORS)以及如何使用它。很简单,CORS 是一种浏览器技术规范,它定义了 web 服务为来自相同起源策略下的不同域的沙箱脚本提供接口的方式。
接下来,我们探索客户机和服务器之间实时通信的新方法,这些方法允许您找出服务器开始与客户机交互的位置。这是由于服务器发送事件规范,该规范指示 API 打开 HTTP 连接以接收来自服务器的推送通知。
最后,我们将讨论 XMLHttpRequest Level 2 中的最新特性。
注意:XMLHttpRequest (XHR)是 web 浏览器脚本语言(如 JavaScript)中可用的 API。它用于将 HTTP 或 HTTPS 请求直接发送到 web 服务器,并将服务器响应数据直接加载回脚本。数据可能以 XML 文本或纯文本的形式从服务器接收(来自维基百科[
en.wikipedia.org/wiki/XMLHttpRequest](http://en.wikipedia.org/wiki/XMLHttpRequest)
) 。
了解后期消息 API
浏览器中加载的所有 web 应用都受浏览器沙盒安全性的影响,沙盒安全性是一种用于在受限环境中运行应用的安全机制。
这意味着,如果有人试图利用浏览器执行恶意代码,浏览器沙箱会阻止这些代码对主机造成损害。
这就是为什么 web 应用不能创建、修改或读取主机文件系统上的文件或任何信息。此外,它们不能加载和访问不同位置的页面上的脚本,这些页面不使用相同的协议、端口号和主机域。
当然,您可以通过各种技术来克服这种限制。一种是使用 web 服务器代理——不是直接对 web 服务进行 XMLHttpRequest 调用,而是对 web 服务器代理进行调用。然后,代理将请求传递给 web 服务,并将响应数据传递回客户端应用。
但是,使用 HTML5 和新的 postMessage API,您现在可以启用跨源通信,这样您的 web 应用就可以使用一种受控的机制来启用跨站点脚本,而无需使用任何类型的变通方法。
根据[
dev.w3.org/html5/postmsg/](http://dev.w3.org/html5/postmsg/)
,后消息语法如下:
window.postMessage(message, targetOrigin [, ports ])
消息参数包含要发布的消息,targetOrigin
表示为了调度事件,otherWindow
的来源必须是什么,ports
属性可以选择包含给定窗口的端口数组。
如果目标窗口的原点与给定的原点不匹配,则消息被丢弃。这可以防止信息泄露。
要向目标发送消息,不考虑来源,将目标来源设置为"*"
。要将消息仅限制到相同来源的目标,而不需要显式声明来源,请将目标来源设置为"/"
。
这里有一个实际的例子来解释这种机制:想象一个场景,其中,hostPage.htm
web 页面包含一个 iframe 元素,该元素包含embeddedPage.htm
页面。hostPage.htm
页面中的一个脚本调用了embeddedPage.htm
页面的窗口对象上的postMessage()
,然后一个消息事件在该对象上执行,并被标记为来自hostPage.htm
页面的窗口。
这将是在hostPage.htm
页面中编写的代码:
var element = document.getElementsByTagName('iframe')[0]; element.contentWindow.postMessage('Hello postMessage API', 'http://www.mydomain.com/');
当调用postMessage()
时,一个消息事件被分派到目标窗口。此接口具有公开数据属性的类型消息事件,该属性返回消息的数据。
您可以使用addEventListener()
为消息事件注册一个事件处理程序:
window.addEventListener('message', messageHandler); function messageHandler(e) { alert(e.data); }
在messageHandler
事件处理程序中,我们只显示一个警告窗口,其文本包含在数据属性中。但是,最好检查您收到的邮件是否来自预期的域。为此,可以使用 origin 属性,该属性返回消息的来源:
if (e.origin == 'http://www.mydomain.com/') {
然后我们可以检查消息文本:
if (e.data == 'Hello postMessage API') {
最后,通过使用 source 属性,您可以决定是否将消息发送回最初发送消息的 HTML 页面,该属性返回源窗口的WindowProxy
:
e.source.postMessage('Hello', e.origin); } else { alert(e.data); } }
确保邮件后通信的安全
这种跨站点脚本的新方法非常强大和有用,但是它可能会使服务器暴露于攻击之下。这就是为什么小心谨慎地使用 postMessage API 非常重要的原因:
不要在包含任何机密信息的消息中的targetOrigin
参数中使用*
:
window.postMessage('I'm sending confidential information', '*');
相反,请指定一个域,否则无法保证消息只传递给目标收件人:
window.postMessage('I'm sending confidential information', 'http://www.mydomain.com/');
始终检查 origin 属性,以确保只接受来自预期接收消息的受信任域的消息:
if (e.origin == 'http://www.mydomain.com/') { // whatever }
甚至:
if (e.origin !== 'http://www.mydomain.com/') return;
这样,您可以避免其他页面出于恶意目的欺骗此事件。
为了更加安全,请检查接收到的数据是否是预期的格式:
`if (e.origin == 'http://www.mydomain.com/ ')
{
if (e.data == 'Hello postMessage API')
{
}
}`
尽量避免使用innerHTML
或outerHTML
来注入接收到的数据消息,因为该消息可能包含一个脚本标签并立即执行。相反,使用textContent
属性来编写消息字符串。
解决方案 8-1:检查邮件 API 浏览器支持
postMessage API 允许您找到一种跨浏览器窗口传递基于文本的消息的方法。iframes、窗口和弹出窗口之间可以进行通信。以下浏览器支持邮件后 API:
Internet Explorer 8.0 以上
火狐 3.0 以上版本
Safari 4.0 以上版本
谷歌浏览器 1.0+
Opera 9.5+版本
不管支持与否,检查加载页面的浏览器的 API 兼容性仍然是一个最佳实践,特别是因为如果不支持 postMessage API,应用可能会暴露出严重的问题。
涉及到什么
为了能够执行支持检查,使用 JavaScript typeof
操作符,它允许您检查其操作数的数据类型。表 8-1 显示了运算符类型返回的可能值列表。
表 8-1。 JavaScript 运算符的数据类型
| **数据类型** | **描述** |
| :-- | :-- |
| 数字 | 表示一个数字 |
| 线 | 表示一个字符串 |
| 布尔 | 表示布尔值 |
| 目标 | 表示一个对象 |
| 空 | 表示为空 |
| 不明确的 | 表示未定义 |
| 功能 | 表示一个函数 |
使用typeof
操作符非常简单;您只需将命令放在您想要检查的变量之前,它将自动返回其数据类型:
var num = 1; var str = "Hello HTML5"; alert( typeof num ); // it returns number alert( typeof str ); // it returns string
为了检查 postMessage API 的浏览器支持,让我们创建一个条件来确保 postMessage 的typeof
不会返回一个未定义的值。
如何建造它
要检查浏览器对 postMessage API 的支持,您只需在编写使用新 API 的函数之前,在脚本块中插入以下 JavaScript 条件:
if (typeof window.postMessage != 'undefined') { alert ('The postMessage API is supported'); }
此代码必须位于页面的脚本块之间,如以下示例所示:
`
Solution 8-1: Checking for postMessagi API browser support
Solution 8-1: Checking for postMessageAPI browser support
`
为了检查对 postMessage API 的支持,您可以简单地点击按钮输入类型,JavaScript 函数将被执行。
跨文档信息传递和 CORS
在前面的段落中,我们讨论了加载到浏览器中的所有 web 应用如何受到其沙箱安全性的影响。这意味着浏览器不允许访问主机服务器之外的资源。如果一家公司有一个大型网站,包括几个运行特定活动的子域,如托管应用、数据库或部门数据,这可能是一个问题。
这就是为什么开发人员试图找到克服这一限制的方法。最流行的方法之一是创建服务器端代理,但是开发人员社区要求一种原生的跨域请求方法。
这导致 W3C 在许多浏览器中引入了一种新的方法来解决这个问题。它通常被称为 CORS,或跨源资源共享。
CORS 是一种浏览器技术规范,它定义了 web 服务为来自相同起源策略下的不同域的沙箱脚本提供接口的方式。
这种方法提供了自定义 HTTP 头的使用,它告诉浏览器如何与服务器通信。这样,浏览器和服务器就有足够的信息来判断请求或响应是否会失败。
想象以下场景:一个资源有一个简单的文本资源驻留在www.domainA.com
,它包含字符串“你好,CORS!”,并且您希望www.domainB.com
能够访问它。如果服务器决定请求应该被允许,服务器返回一个与Access-Control-Allow-Origin
头结合的响应给www.domainA.com
。
基本上,Access-Control-Allow-Origin
头返回相同的原点。(如果是公共资源,它将返回通配符*。)
此跨源共享标准用于为以下项目启用跨站点 HTTP 请求:
跨站点方法中 XMLHttpRequest API 的调用
Web 字体(用于 CSS 中@font-face 中的跨域字体使用),以便服务器可以部署 TrueType 字体,这些字体只能跨站点加载并由允许这样做的网站使用
在本章中,您将使用 CORS 创建跨文档消息传递(解决方案 8-2)和跨源 XMLHttpRequest(解决方案 8-6)。
解决方案 8-2:在窗口和 iframes 之间发送消息
在上一节中,我们讨论了跨文档消息传递和 postMessage APIs,以及它们如何允许我们在不同的资源之间实现跨来源的通信。
在这个解决方案中,我们将展示如何让 iframe 与它的主机网页通信,主机网页将被有意地发布在不同的域上。
涉及到什么
您将用来创建父页面和不同域上发布的 iframe 之间的通信的对象是postMessage()
方法。
该方法的语法有三个参数:消息、targetOrigin
的 URL 和端口。
window.postMessage('Hello postMessage API', 'http://www.mydomain.com/');
当调用postMessage()
方法时,在目标窗口中调度一个消息事件。此接口有一个公开数据属性的事件类型消息,该属性返回消息的数据。
您可以使用addEventListener()
为消息事件注册一个事件处理程序:
window.addEventListener('message', messageHandler); function messageHandler(e) { alert('This is the origin of the message received: ' + e.origin + ' /n And this is the message: ' + e.data); }
在messageHandler
事件处理程序中,我们简单地显示了一个警告窗口,其文本包含在数据属性中。但是,最佳做法是使用 origin 属性检查您接收的消息是否来自预期的域,该属性返回消息的来源:
if (e.origin == 'http://www.mydomain.com/') { alert('This is the origin of the message received: ' + e.origin + ' /n And this is the message: ' + e.data); } else { // prevent from receiving this message as the target origin does not match ! }
让我们通过一个例子来讨论这个解决方案。
如何建造它
首先,你需要提供一个很好的应用上下文描述。首先,下载在以下域发布的Solution_8_2.html
文件:[
casario.blogs.com](http://casario.blogs.com)
,在文件夹([
casario.blogs.com/files/Solution_8_2.html](http://casario.blogs.com/files/Solution_8_2.html)
)中。
这个保存为chatIframe.html,
的文件充当 iframe 的容器。该文件发布在域[
www.comtaste.com](http://www.comtaste.com)
的演示文件夹:[
www.comtaste.com/demo/chatIframe.html](http://www.comtaste.com/demo/chatIframe.html)
中。
您将创建的示例允许在不同域上发布的两个文件进行通信。
让我们处理第一个文件——充当 iframe 容器的文件。从主体中声明的几个用户界面元素开始:
<body> <h2>Solution 8-2</h2>
插入一条消息,并点击按钮向下面的 iframe 发送消息:
`
Send Message
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
2023-08-19 赏味不足:详细来聊下轻资产运作,我从不做重资产
2023-08-19 老隋:什么赚钱就做什么,记住轻资产运营,试错成本低
2023-08-19 iBooker 技术评论 20230819:打工是风险最高的事情
2023-08-19 卓钥商学苑:创业期间被合伙人背叛了怎么办?处理方式有哪些?