六、移动网络应用
到目前为止,我们只看了桌面浏览器;然而,HTML5 真正伟大的方面之一是它在包括手机、平板电脑和电视在内的各种设备上的支持程度。据 Html5Test.com 报道,在撰写本文时,Chrome 和 Opera 平台分别以 523 分和 489 分领先。但亚马逊丝绸,火狐手机,安卓和黑莓以 468,456,452 和 449 分紧随其后,满分为 555 分。
在移动设备上,您将使用本地应用和 web 应用。原生应用是为特定的移动平台开发的,并安装在设备上,或者通过电话服务下载。原生应用通常可以提供最佳的用户体验,因为它们可以最大限度地利用设备的特定硬件和操作系统功能。然而,部分由于 HTML5 的流行,网络应用的需求也越来越大。它们的开发几乎和桌面浏览器一样容易。
使用模拟器
要查看您的网站在移动设备上如何工作,您可以使用许多电话模拟器应用。虽然这些功能可能不完全像实际的硬件,但它们提供了一个合理的近似值。我将向您展示如何安装和使用几个更常见的实用程序。
使用 Opera 移动模拟器
Opera 提供了一个免费的移动模拟器应用,你可以从 www.opera.com/developer/tools/mobile
下载。这个工具特别好的一点是,你可以从一个很长的列表中选择你想要模拟的设备。安装好这个应用后,启动它,你应该会看到如图 6-1 所示的启动窗口。
图 6-1。
The Opera emulator launch window
当您选择一个设备时,窗口会显示硬件详细信息,如屏幕分辨率。选择 LG Optimus One 设备,然后单击启动按钮。有了这个模拟器,你可以使用设备键盘或桌面键盘。从第四章的中输入你的站点的 URL,它应该看起来像图 6-2 。
图 6-2。
Emulating the LG Optimus One device
请注意,页面被缩放以适合屏幕,这使得它几乎不可读。你将在本章的后面处理这个问题。您可以尝试一些其他设备,如诺基亚 N800,如图 6-3 所示。正如您所料,较大的外形尺寸可以更好地处理页面。
图 6-3。
Emulating the Nokia N800
安装镀铬波纹
在 Chrome 和 Firefox 上模拟移动设备需要一种不同的方法,即使用桌面浏览器的附加组件。使用仿真器时,实际上是使用带有一些附加功能的桌面浏览器来模拟设备的外形。
启动 Chrome 桌面浏览器,点击应用图标,选择网络商店应用。在搜索框中,选择扩展选项并键入 ripple emulator。结果应该如图 6-4 所示。单击免费按钮安装扩展。
图 6-4。
The Ripple emulator in the web store
安装完插件后,使用 Chrome 浏览器并输入第四章网站的 URL。右上角有一个按钮,如图 6-5 所示,用于启动模拟器。
图 6-5。
Launching Ripple
点击该按钮,然后点击使能按钮,如图 6-6 所示。
图 6-6。
Enabling the Ripple emulator
这将使用模拟器模式显示当前页面。第一次为一个特定的 URL 启动 Ripple,你会看到如图 6-7 所示的提示。点击 BlackBerry 10 WebWorks 按钮,选择要模拟的平台。
图 6-7。
Selecting the desired platform
你在 BlackBerry 10 设备上的网页应该如图 6-8 所示。
图 6-8。
The web page on the BlackBerry 10 device
浏览器窗口的左上角和右上角有两个带箭头的小按钮。使用这些来显示/隐藏选项窗口。例如,如图 6-9 所示,左边的界面允许您更改设备方向或选择不同的平台进行仿真。它还提供了当前设备的一些技术细节,如屏幕分辨率。右边的按钮包括“设置”标签,您可以在暗主题和亮主题之间切换。
图 6-9。
Displaying the emulator options
模拟其他设备
要在 iPhone 上模拟你的网站,使用 Chrome 浏览器,进入这个网站: http://iphone-emulator.org
。当模拟器显示时,在设备上的搜索框中输入第四章网站的 URL。该站点将如图 6-10 所示。
图 6-10。
Emulating the web page on an iPhone
请注意页面顶部的按钮,这些按钮使您能够模拟其他设备,如 iPad 和 Android。
Tip
还有其他模拟器可用;我只讲了几个。如果您想查看其他选项,请尝试在 https://www.browserstack.com/list-of-browsers-and-platforms?product=live
浏览堆栈。您还可以在 www.asp.net/mobile/device-simulators
查看 ASP.net 上的资源。
处理外形尺寸
创建在移动设备上运行良好的 web 应用时,最大的挑战是处理各种形状因素。在较大的设备上,你会希望利用额外的空间,同时在较小的设备上也有一个合理的外观。到目前为止,在我展示的示例中,设备要么缩放页面以适应页面,要么裁剪页面。这两种方法都不是最佳的。
有三种技术可以帮助你改善你的网站在各种外形下的外观。
- 媒体查询:这允许您根据现有视口的属性应用不同的样式。我将用这一章的大部分时间来证明这一点。
- 使用 CSS flexbox 布局:这类似于使用 Windows Presentation Foundation(WPF)设计表单,允许浏览器根据窗口大小动态调整大小或移动元素。我将解释这是如何工作的,然后您将使用它来配置导航链接。
- 灵活的图像和视频:这只是指示浏览器拉伸或收缩图像,以适应可用的空间。
Tip
各种仿真器做的事情之一是根据设备特性限制窗口大小。您可以通过简单地调整浏览器窗口的大小来完成同样的事情。对于您的初始测试,您可以缩小窗口并查看布局如何响应。然后使用模拟器进行最后的测试。
了解媒体查询
CSS 2.1 引入了media
关键字,允许你定义一个打印机友好的样式表。例如,您可以使用这样的内容:
<link rel="stylesheet" type="text/css" href="screen.css" media="screen" />
<link rel="stylesheet" type="text/css" href="print.css" media="print" />
然后,您可以为浏览器(屏幕)定义一个样式表,并为网页的打印版本定义一个不同的样式表。或者,您可以在单个样式表中嵌入特定于媒体的样式规则。例如,这将改变打印时的字体大小:
@media print
{
h1, h2, h3
{
font-size: 14px;
}
}
Tip
还支持其他媒体类型,包括aural
、braille
、handheld
、projection
、tty
和tv
。如您所见,媒体类型最初用于表示呈现页面的设备类型。此外,支持all
类型,但是如果没有指定媒体类型,也是隐含的。带有all
类型的样式适用于每个设备。
在 CSS3 中,这一点得到了显著的增强,允许您查询各种属性来确定合适的样式。例如,当窗口宽度为 600 像素或更小时,您可以应用如下样式:
@media (max-width:600px)
{
h1
{
font-size: 12px;
}
}
媒体查询中可以选择的功能如下:
width
height
device-width
device-height
orientation
aspect-ratio
device-aspect-ratio
color
(0 表示单色或用于指定颜色的位数)color-index
(可供选择的颜色数量)monochrome
(彩色为 0,灰度为位数)resolution
(在 dpi 或 dpcm 中指定)scan
(对于电视,指定扫描模式)grid
(1 如果是网格设备如 TTY 显示,0 如果是位图)
其中大多数都支持min-
和max-
前缀,这意味着您不必使用大于号或小于号运算符。例如,如果您想要一个介于 500 像素和 700 像素之间的窗口样式,您可以指定如下:
@media screen and (min-width: 500px) and (max-width: 700px)
注意,在这个例子中,我还包括了screen
媒体类型。在这种情况下,对于所有其他类型,如print
,该样式将被忽略。
Tip
关于这些特性的完整定义,请参见 W3 规范的 www.w3.org/TR/css3-mediaqueries/#media1
。
使用媒体查询
您可以通过媒体查询做很多事情来动态设计您的网页。例如,您可以使用color
和monochrome
功能在单色设备上显示时应用更合适的样式。color
功能返回支持的颜色数量,因此(min-color: 2)
将选择所有颜色设备。您也可以使用(orientation: portrait)
和(orientation: landscape)
根据设备的方向排列元素。
在本演示中,您将关注窗口的宽度,但相同的基本概念也适用于其他功能。随着窗口宽度的缩小,样式将逐渐调整以适应窗口的大小,同时尽可能多地保留原始布局。
一个典型的方法是计划三种不同的风格:大型、中型和小型。大字体可能是网站最初设计的方式,就像你的第四章网站一样。有侧栏和多列内容。中等风格将保持相同的基本布局,但开始根据需要缩小区域。一个有用的技巧是使用相对大小,这样当窗口缩小时,每个元素也逐渐缩小。小型样式将用于手持设备,您通常会将布局保持为单列。由于页面现在会变得更长,页面上的书签链接变得更加重要。
修改第四章站点
为了演示这些技术,您将向您在第四章中构建的站点添加一些额外的样式规则。您将使用媒体查询来根据窗口的宽度有选择地应用这些样式。
Tip
章节 4 站点是使用 WebMatrix 应用创建的。但是,源代码下载提供了 WebMatrix 项目和 Visual Studio 项目。你会在章节 4 文件夹中找到这些。你可以用你喜欢的任何一个。该说明将告诉您如何修改Default.cshtml
文件。如果您使用的是 Visual Studio,这将是Index.cshtml
文件;这两个文件中的更改是相同的。我使用在第五章中修改过的版本,用 JavaScript 将内部链接设置为绿色。
打开章节 4 项目并运行应用。我们将继续使用 Chrome 浏览器,但大多数浏览器将支持本章演示的样式特性。尝试缩小浏览器窗口的宽度。请注意,页面根本没有缩放;浏览器简单地截取不适合窗口的内容。这是你有工作要做的第一个线索。网页应该是流畅的,并根据窗口大小进行调整。
Caution
为了使更改更容易理解,您只需将附加的样式规则附加到您的style
标签的末尾。正如我在第四章中提到的,相同的选择器会覆盖文件中早先定义的样式。然而,使用相同的选择器是很重要的。您可以编写一个类似的选择器,它将返回相同的元素,但是如果它被认为不够具体,它不会覆盖前面的样式。例如,在本文档中,nav a
将返回与nav ul li a
相同的元素,但是后者被认为更具体,并且将优先于前者,即使它在文件中更靠前。
配置媒体布局
您的网页的当前布局基于相对较大的窗口,如桌面浏览器。设计网页时,还应该考虑适合小型设备的布局。我建议为小分辨率设备(如典型的移动设备)创建一个单独的设计。在本章中,您将使用媒体查询来实现小型、中型和大型配置。然而,中型布局往往是小型和大型之间的妥协。从大布局开始,然后设计小布局通常效果最好。
水平滚动不直观,应该尽可能避免。所以如果你的分辨率很窄,你应该垂直堆叠元素。例如,aside e
元素需要放在页面的底部。你可以考虑去掉图片或者改变字体大小。
一旦你有了一个小的布局,你可以随着宽度的缩小而逐渐引入这些变化。我喜欢采取的方法是逐渐开始缩小浏览器窗口的宽度,看看有什么会中断。然后进行更正,以处理这一点,并尝试缩小一些。有了已经设计好的小布局,当你在这个迭代过程中进行调整时,你就会知道你要去哪里。
现在,您将定义中小型布局的样式,从中等开始。在中型设备上,您将使用相同的基本布局,但只是缩小一些元素。对于此站点,中等将被定义为宽度介于 600 像素和 940 像素之间。网页的大小是 940px,所以如果窗口比这个宽,就不需要调整。600 像素的最小尺寸有些随意。稍后我会解释我如何得出这个数字。
媒体布局需要一点调整。您将使用一个简单的技巧来定义具有相对大小的元素。这允许它们在调整窗口大小时自动收缩或伸展。打开Default.cshtml
文件,将清单 6-1 中所示的规则添加到现有的style
元素中。将此添加到所有现有规则之后。
Listing 6-1. Defining the Medium Layout
@@media screen and (max-width: 940px)
{
body
{
width: 100%;
}
aside
{
width: 30%;
}
nav ul li a
{
width: 100px;
}
}
Note
在 Razor 语法中,“与”符号(@
)用来表示跟在它后面的是代码,而不是内容。要在媒体查询这样的上下文中包含一个&符号,您需要使用一个双&符号。
通过将body
宽度设置为 100%,它将自动缩小以适应窗口。但是,它不会超过 940 像素,因为这种样式仅在宽度小于 940 像素时应用。aside
元素设置为 30%。当前比率(280 像素/940 像素)约为 30%。当你继续缩小窗口时,nav
元素中的链接最终会被剪切掉,所以这种样式也会减少它们的宽度,使它们靠得更近。
运行应用并尝试缩小窗口。你应该注意到一个很好的流体布局,它可以根据窗口大小进行调整,如图 6-11 所示。
图 6-11。
Displaying the medium layout
配置小型布局
然而,最终布局效果并不好,如图 6-12 所示。
图 6-12。
The medium layout when shrunk too much
这里有几个你需要解决的问题。
- 页眉文本换行并重叠。
- 文本列太窄;这个大小不足以支持三个内容列。
调整布局的主要变化是将aside
元素移到页面底部,而不是其他内容旁边。当你调整窗口大小时,其他的变化是渐进的,但是这个变化会导致一个跳跃。主要内容将从窗口大小的 70%变为 100%。您需要确定应该触发更改的适当宽度。我选择 600 像素,但是您可以尝试其他值,看看页面如何工作。
将清单 6-2 中的代码输入到现有style
元素的末尾。
Listing 6-2. Defining the Small Layout
@@media screen and (max-width: 600px)
{
/* Move the aside to the bottom of the page */
#contentArea, #MainContent, aside
{
display: block;
}
aside
{
width: 98%;
}
/* Use a single column for the article content */
.otherContent
{
-webkit-column-count: 1;
column-count: 1;
}
/* Fix the line spacing of the header */
h2, h3
{
line-height:normal;
}
/* Force the intro element to stretch to fit the content */
.intro
{
height: min-content;
}
/* Move the book images to the left */
.book img
{
float: left;
margin-right:
10px;
margin-bottom: 5px;
}
/* Make the book elements tall enough to fit the image */
.book
{
min-height: 120px;
}
}
Note
您之前为中等尺寸添加的样式也适用于小尺寸样式,因为两者都适用于小于 940px 的宽度。小样式将定义额外的规则,但是记住前面的样式也适用。
小布局规则进行以下调整:
aside
元素被移动到底部。这是通过撤销您在第四章中输入的表格和单元格属性,然后将宽度更改为 98%来完成的。以前,#contentArea
元素的display
属性被设置为表,#mainContent
和aside
元素被设置为table-cell
。通过将这三个都设置为block
,虚拟表被移除。- 内容显示在两列中,这将减少到一列。
- 由于标题文本现在可以使用多行,请更改行高,使各行不重叠。
- 强制
intro
部分垂直拉伸,以确保所有内容适合。 - 将图书图像向左移动,将相应的文本向右移动。
- 确保书籍元素足够大以适合图像。
显示包含这些更改的网页并调整窗口大小。如果窗口足够窄,链接会换行,如图 6-13 所示。
图 6-13。
The web page with a narrow window
使用 Flexbox
为中等大小的窗口创建样式时,您减小了链接的宽度,以便它们适合较小的窗口。您可以再次这样做,这样它们仍然合适,但最终您将需要一个更好的解决方案。如果您需要添加另一个或两个链接,或者其中一个链接的长度超过了您指定的固定宽度,该怎么办?为了解决这个问题,你将使用一个灵活的盒子,或 flexbox。flexbox 允许您定义根据可用空间自动排列的内容块。
使用 flexbox 时,您需要配置容器以及容器中包含的物件。这些有时被称为父节点及其子节点。例如,文档中的nav
标签有以下内容。ul
标签包含一系列的li
标签。
<nav>
<ul>
<li><a href="#feature">Feature</a></li>
<li><a href="#other">Article</a></li>
<li><a href="#another">Archives</a></li>
<li><a href="
http://www.apress.com
</ul>
</nav>
配置容器
在容器元素上,在本例中是ul
,您将把display
属性设置为flex
,以指示应该使用 flexbox。然后您可以指定flow-direction
,它可以是row
或column
。方向设置为row
时,项目从左到右水平对齐;有了column
,它们就垂直堆叠,从上到下。您还可以添加–reverse
来颠倒顺序(从右到左或从下到上)。这些是允许的值:
row
:水平,从左到右(这是默认值)row-reverse
:水平,从右到左column
:垂直,从上到下column-reverse
:垂直,从下到上
然后您可以指定flex-wrap
属性,该属性决定当项目不适合分配的空间时会发生什么。以下是可能的设置:
nowrap
:内容显示在一行(或一列)中,并在必要时进行剪裁(这是默认值)。wrap
:项目按相同方向换行到下一行或下一列。wrap-reverse
:项目反方向绕到下一行(或列)。例如,如果flow-direction
是row
(从左到右),第二行将从右到左。
flex-direction
和flex-wrap
可以合并成一个包含方向和环绕选项的flex-flow
属性。例如,flex-flow: row wrap
将水平对齐项目,并允许换行到下一行。
Note
方向指定主轴,水平或垂直。有几个属性可用于配置对齐;有些影响沿主轴的对齐,有些适用于副轴。我将假设使用了row
方向来讨论这些。这是最常见的,使用这些术语会更容易理解。CSS 属性和值是有意通用的,使用像开始/结束这样的词,而不是顶部/底部或左侧/右侧。如果您使用的是column
方向,属性和值同样适用,但是描述它们的词语会有所不同(例如,顶部对齐而不是左侧对齐)。
以下属性可用于进一步配置项目的对齐方式:
justify-content
:主轴,影响一行内项目的水平间距align-items
:短轴,影响一行内的垂直对齐align-content
:短轴,影响行与行之间的垂直间距
您可以指定项目沿主轴对齐的方式,这将决定如何分配任何额外的空间。justify-content
属性支持以下选项:
flex-start
:默认。项目靠左对齐(如果使用列方向,则靠上对齐)。所有多余的空间都在末尾。flex-end
:项目右对齐(或下对齐)。center
:物品放在中间,首尾分开多余的空间。space-between
:第一个项目左对齐,最后一个项目右对齐,多余的空间分布在项目之间的空间。space-around
:类似于space-between
,除了在第一个项目之前和最后一个项目之后也增加了额外的空格。
align-items
属性指定项目如何在短轴上对齐。例如,如果flow-direction
是row
,那么justify-content
属性描述了水平间距是如何排列的。然而,align-items
属性指定了行内的项目如何垂直对齐。例如,如果行中的项目具有不同的高度,您可以选择对齐顶部或底部。以下是可用的选项:
flex-start
:默认。这会对齐顶部。flex-end
:这将底部对齐。center
:这使每个项目居中,在顶部和底部之间均匀分布额外的空间。stretch
:将项目拉伸到行中最大项目的大小。baseline
:使用基线对齐项目。
Tip
基线一词来自印刷业,在印刷业中,字符沿着基线对齐。这不一定是文本的底部,因为字体有衬线以及上标和下标。但是,基线为放置字符提供了直观的指导,因此线条看起来是直的。在 CSS 中,这个概念甚至更复杂,因为我们不仅仅是在处理文本。如果你想深入研究这个课题,这里有一篇很好的文章: www.smashingmagazine.com/2012/12/17/css-baseline-the-good-the-bad-and-the-ugly
。
如果有多行项目,align-content
属性指定如何在这些行周围放置额外的垂直间距。这支持与justify-content
相同的选项:flex-start
、flex-end
、center
、space-between
和space-around
。它还支持stretch
选项。
配置项目
有几个属性可以应用于子项,这些属性会影响子项的对齐方式。您可以为flex-grow
和flex-shrink
属性分配一个数值。这表示该项相对于其他项可以增大或缩小多少。例如,值为 2 的项目的增长是值为 1 的项目的两倍。此外,flex-basis
属性用于指示项目初始大小的基础。这是一个数值,表示要使用的初始值。如果设置为默认值auto
,这将是项目的实际宽度。
您也可以为order
属性指定一个数值。默认情况下,项目按照它们在 HTML 内容中出现的顺序显示。然而,order
属性,如果使用的话,将会覆盖它。
正如我之前解释的,沿着短轴的对齐是由align-items
属性决定的。但是,这可以在一个或多个项目上被覆盖。例如,如果您将 al ign-items
设置为flex-start
,这将使它们沿顶部对齐。您可以通过设置单个项目的align-self
属性来覆盖它。这与align-items
属性采用相同的值。
Note
你可以在 www.w3.org/TR/css-flexbox-1
找到完整的规格。在撰写本文时,它处于工作草案状态。通过一些直观的例子可以更好地理解其中的一些概念。这里有一篇很好的文章演示了这些技巧: https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Flexible_boxes
。
调整链接
现在,您将修改您的示例页面,以使用 flexbox 修复链接对齐。将以下内容添加到style
标签的末尾。这将配置ul
元素以水平显示元素,并在必要时启用换行。链接也将是左对齐的。链接的宽度被设置为auto
,因此它可以容纳长和短的元素。
nav ul
{
display: flex;
flex-flow: row wrap;
justify-content: flex-start;
}
nav ul li a
{
width: auto;
}
如果链接需要包装,您还需要固定nav
元素的大小,以便它可以垂直增长。height
当前被设置为30px
。将height
属性更改为min-height
,如下面的代码所示。这将设置初始高度为30px
,但允许它增长以适应内容。
/* Make the radius half of the height */
nav
{
min-
height: 30px;
border-radius:15px;
}
保存您的更改并刷新网页。尽可能缩小窗口。即使在这个尺寸下,页面布局看起来还是不错的,如图 6-14 所示。
图 6-14。
The web page with its smallest size
当结合媒体查询时,flexbox 工作得很好。一个例子是当窗口宽度较小时,将链接的方向配置为垂直。尝试在您的style
元素末尾输入以下内容来演示这一点:
@@media screen and (max-width: 400px)
{
nav ul
{
flex-direction: column;
}
}
如果你将页面缩小到足够小,链接会垂直对齐,如图 6-15 所示。
图 6-15。
Using vertical links
使用灵活的图像
如果您有大的图像,您可能会发现它们被裁剪。为了防止这种情况,将max-width
属性设置为 100%。这将导致调整图像大小以适合容器的宽度。这不是在媒体查询中完成的,这种格式将应用于所有分辨率。例如,您可以指定以下内容来配置电话亭图像:
#phone
{
max-width: 100%;
height: auto;
}
将height
设置为auto
将改变高度以保持现有的纵横比。您可以使用下面的样式规则对video
元素做同样的事情:
.video embed, .video object, .video iframe
{
width: 100%;
height: auto;
}
在移动设备上查看页面
最后一个测试,使用 Chrome 显示站点,并启用 Ripple 模拟器,就像我之前演示的那样。选择 PhoneGap 平台。你的页面应该如图 6-16 所示。
图 6-16。
The web page as seen on the Ripple emulator
摘要
在本章中,我向您展示了如何安装和使用几个移动设备模拟器,其中包括:
- Opera 移动仿真器
- 铬波纹附加组件
- iPhone 模拟器
为了处理各种形状因素,媒体查询被用来根据窗口宽度有选择地应用样式。您实现了大、中、小布局,它们可以随着窗口大小的调整而整齐地缩放。此外,通过将宽度设置为 100%,您可以自动调整图像和视频的大小。最后,您使用 flexbox 来动态排列导航链接。
七、支持旧浏览器
现在你有了在第四章中创建的这个漂亮的、符合 HTML5 的网页。你想炫耀它,所以你给一个碰巧还在使用 IE 8 的同事发了一个链接,他们看到了类似图 7-1 的东西。
图 7-1。
The CSS demo as shown in IE 8
这个页面看起来很糟糕,一点也不像你所期待的。你肯定不会因此获得任何奖项。不要被吓到,你把链接发给你的老板,事情变得更糟。你的老板正在使用 IE 7,看到类似图 7-2 的东西。
图 7-2。
The CSS demo as shown in IE 7
侧边栏不再在边上,而是被钉在页面的底部。你的老板开始想知道你在业余时间都做了些什么。你刚从惨痛的教训中学到了两个重要的教训。
- 始终控制您的演示环境;在这种情况下,让他们在您的浏览器上看到该页面。
- 更重要的是,在几种不同的浏览器上测试你的网站。
在这一章中,我将向你展示一些相当简单的技术,让你的页面即使在老版本的浏览器上看起来也是最好的。您不必编写太多代码,因为有许多开源代码,您可以轻松地将它们添加到您的站点中。
做一些简单的改变
有几个非常简单的改变会让网页看起来更好。您将从这些开始,稍后我将向您展示一些更复杂的解决方案。
模拟旧浏览器
要使用一些旧版本的 Internet Explorer 测试您的网页,您将在模拟器模式下使用 IE 11。启动 Internet Explorer 后,按 F12 显示开发人员工具窗格。默认情况下,浏览器会使用“edge”模式,这是最新版本。要改变这一点,点击边缘下拉菜单,如图 7-3 所示。
图 7-3。
Modifying the emulation mode
对于本章,使用你在第四章 (Visual Studio 版本)中创建的同一个项目,你可以从 www.apress.com
下载。
使用 Modernizr
当支持旧的浏览器时,您应该做的第一件事是使用 Modernizr 开源 JavaScript 库。这个库执行两个基本功能。
- 检测当前浏览器的可用功能,并将此信息作为可查询属性提供。例如,在您的 JavaScript 中,您可以像这样放置条件逻辑:
if (!Modernizr.cssanimations) {
alert("Your browser does not support CSS animation");
}
- 提供垫片来实现缺失的功能。这包括 html5shim 库,它允许您使用新元素,如
header
、footer
、nav
和aside
,对内容进行样式化。
Tip
欲了解更多信息,请访问 Modernizr 网站 http://modernizr.com
。
因此,让我们将 Modernizr 库添加到您的页面中,看看会发生什么!在第五章中,我简要解释了作为客户端包管理器的 Bower 工具。现在,您将看到它的实际应用。因为这个项目是使用空模板创建的,所以您需要安装 Bower。从工具菜单转到 NuGet PackageManager。在搜索框中输入鲍尔,选择鲍尔,如图 7-4 所示。单击安装按钮开始安装。
图 7-4。
Adding Bower to the project
现在您还需要创建bower.json
文件。从解决方案浏览器中,右键单击第七章项目,并选择 Add 和 New Item 链接。选择 Bower JSON 配置文件选项,如图 7-5 所示。文件名应该默认为bower.json
。
图 7-5。
Adding the bower.json file.
在以下代码中添加粗体显示的行:
{
"name": "Chapter``7
"private": true,
"dependencies": {
"modernizr": "2.8.3"
}
Tip
正如我在第五章中解释的,当你编辑这个文件时,Visual Studio 提供了智能感知。从列表中选择modernizr
并输入冒号后,将显示当前版本。截至本文撰写之时,它是 2.8.3。您应该使用 IntelliSense 显示的最新版本。
您可以安装 Grunt 或 Gulp,并设置一个任务来自动更新 Modernizr 文件,并将其复制到wwwroot
文件夹中。为简单起见,您将手动执行此操作。在 Solution Explorer 中,展开Dependencies
文件夹并右键单击 Bower 项。选择恢复包链接。这将强制下载最新版本。
在 Solution Explorer 中,右键单击wwwroot
文件夹,选择 Add 和 New Item 链接,并输入名称 lib。然后右键单击Dependencies\Bower
文件夹中的 modernizr 项,并选择在文件浏览器中打开链接。这将在此位置打开 Windows 资源管理器。你应该看到一个modernizr.js
文件;将它复制到解决方案资源管理器中的wwwroot\lib
文件夹。
现在安装了 Modernizr,您可以在页面中包含 Modernizer 库,方法是将它添加到您的Index.html
文件的顶部,就在DOCTYPE
标签之后:
<script type="text/javascript" src="lib/modernizr.js"></script>
使用 Internet Explorer 显示页面;然后转到开发者工具窗格,将浏览器模式更改为 IE 7,就像我前面解释的那样。你的页面应该如图 7-6 所示。
图 7-6。
The demo page with Modernizr as viewed in IE 7
请注意,现在显示了边框和背景颜色,这大大有助于使页面看起来像最初设计的那样。此外,导航链接是水平排列的。
Note
垫片是一种薄的物体,通常由木头制成,用于填充两个物体之间的间隙。在这种情况下,该术语指的是填补浏览器当前功能和完整 HTML5 规范之间空白的相对较小的一段代码。术语 shim 已经在软件开发界使用了很长时间。引入术语 polyfill 是为了指代与浏览器相关的填充。因此,在这种情况下,这两个术语是同义的。
添加更多聚合填充
现在你可能开始感觉好一点了。通过添加 Modernizr,页面看起来不错。但是,经过仔细检查,有一个相当长的功能列表不起作用,包括以下内容:
- 桌子
- 圆角
- 渐变背景填充
- 条纹物品
- 动画
- 3D 转换
- 多列
如果有足够的耐心和毅力(当然,还有时间),你可能会实现所有这些功能,这样你的页面在 IE 7 和最新版本的 Chrome 上都是一样的。但是,我不建议你这么做。本质上,你应该确保你的页面在最新的 HTML5 兼容浏览器上运行良好,并且在旧的浏览器上运行良好。它不需要在每个浏览器上都运行良好。请考虑以下几点:
- 大多数用户不会在多种浏览器上浏览你的网站,并比较每种浏览器的体验。您的页面不需要在每个浏览器中看起来都一样。
- 如果有人正在使用 IE 7,他们已经习惯了难看的网站。实现其中的几个聚合填充可能会让你的页面成为他们访问过的最好的站点之一。
- HTML5 应该让你作为 web 开发人员的工作更容易。然而,如果你试图让每一页都像旧浏览器上的原生 HTML5 一样工作,你将花费更多的时间,而不是更少。
对于您的页面使用的、通常使用的浏览器本身不支持的每个功能,您有以下选项:
- 失败:简单地显示一个错误,说明该浏览器不支持必要的功能,并提供一些建议使用的浏览器。例如,您在第五章中创建的示例站点的主要目的是演示如何使用 Web 工作器。如果网页是用不支持 Web 工作器 的浏览器浏览的,那么让网页正常工作就没有意义了。失败就好!
- Polyfill:实现替代解决方案以提供所需的功能。这可以是简单的解决方案,也可以是相当复杂的。例如,如果不支持渐变填充,您可以只使用纯色填充,或者您可以提供一个填充并使用 JavaScript 实现渐变。
- 忽略:只保留未实现的特性。例如,您可以忽略圆角;在旧的浏览器中,它们会是方形的角。
这里没有硬性规定;你需要根据具体情况来决定哪些功能对你来说是重要的,以及你愿意花多少时间让它们在老版本的浏览器上工作。在本章的其余部分,我将演示一些技术,使用公开可用的开源垫片来回填这些特性。然而,我不想给你留下这样的印象,你必须回填每一个特性。事实上,这个演示中的几个特性,包括多列支持、3D 转换和动画都将被忽略,因为它们相对来说比较难或者不那么重要。
Tip
有过多的垫片和多孔填料可供选择。如果你正在寻找一些特定的东西,这篇文章提供了一个很好的参考: https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills
。请记住,这些可能并不总是正常工作,所以测试他们,并保持什么工作。此外,组合各种垫片可以创建一些有趣的结果,因为一个垫片的副作用可以破坏另一个垫片。
显示表格
当你在几个浏览器中测试你的页面时,注意那些不能正常工作的特性,然后确定它们的优先级。在这种情况下,aside
元素应该在主要内容的旁边,而不是在页面的末尾。我认为,这是最关键的问题,因此应该首先解决。
Tip
IE 8 首先支持表格。如果你把浏览器模式改成 IE 8,你会看到侧边栏就在主要内容旁边。所以,表格支持只是 IE 7 和更老版本的问题。你可能会考虑简单地忽略这个问题,并解释说你的网站在 IE 8 和更新的版本上工作得最好。要想知道有多少用户会受到影响,请查看最新浏览器在 www.w3schools.com/browsers/browsers_stats.asp
的统计数据。根据这些统计数据,这仅占正在使用的浏览器总数的 0.1%。这些统计数据代表总体使用情况;您可能有特定的目标受众,他们可能有不同的特征。
为了支持 IE 7 中的表格,您将使用行为 CSS 扩展,它允许您在样式表中嵌入 JavaScript。通过添加如下规则来调用扩展:
header
{
behavior: url(customBehavior.htc);
}
该实现在扩展名为.htc
的 HTML 组件(HTC)文件中提供。有一些关于使用.htc
文件的事情你应该知道。
- 通常,您可以在浏览器中打开 HTML 文件,而无需使用 IIS。例如,您可以简单地用 Internet Explorer(或任何浏览器)打开
Index.html
文件,页面就会正常工作。但是,如果页面实际上不是由 web 服务器(如 IIS 或 Apache)提供的,则.htc
文件会被忽略。 - 您可能需要在 web 服务器上定义 HTC 内容类型。默认情况下,IIS 和 IIS Express 都支持这一功能,但是您可能需要在 Apache 或其他 web 服务器上添加这一功能。
- 尽管通常在 CSS 文件中引用
.htc
文件,但是在behavior
属性中指定的 URL 必须相对于调用样式表的 HTML 文档的位置。如果你把.htc
文件放在css
文件夹中(和所有其他样式表一起),你需要用一个相对路径css/customBehavior.htc
来引用它。
为了显示表格,您将使用可以从 http://tanalin.com/en/projects/display-table-htc
下载的开源 HTC。
EXERCISE 7-1. SUPPORTING TABLESIn the Solution Explorer, right-click the wwwroot
folder and select the Add and New Folder links. Enter the name css. Download the latest .zip
file from this site: http://tanalin.com/en/projects/display-table-htc
. (The latest file as of this writing is display-table.htc_2011-11-25.zip
.) This file contains an uncompressed and a minimized version. Copy the display-table.htc
file to the css
folder in the Solution Explorer. Open the Index.html
and find the portion where the table is defined. Add the code shown in bold from Listing 7-1 to the existing style rules. This specifies a vendor-prefixed version of the display
attribute and invokes the display-table.htc
component. Listing 7-1. Defining a New Table
/* Setup a table for the content and sidebar */
#contentArea
{
display: table;
-dt-display: table;
behavior: url(css/display-table.htc);
}
#mainContent
{
display: table-cell;
-dt-display: table-cell;
padding-right: 2px;
behavior: url(css/display-table.htc);
}
aside
{
display: table-cell;
-dt-display: table-cell;
width: 280px;
behavior: url(css/display-table.htc);
}
Save your changes and view the Index.html
page in Internet Explorer. Change the emulator mode to IE 7, and you should now see a table set up, as shown in Figure 7-7.
图 7-7。
The table support added in IE 7 Note
display-table.htc
文件使用自己的、非标准的、特定于供应商的前缀。因此,您需要添加–dt-display
属性。您也可以忽略因此而产生的警告。
Now there’s one more thing that needs to be fixed. You’ll notice that the aside
element is missing some styles such as background-color
and padding
. This is a side effect of the CSS extension. To create the table in your page, this code created real table elements for you such as tr
and td
. So, once the JavaScript runs, the aside
element is removed and replaced with rows and cells. Since there is no aside
element anymore, you can’t use an element selector to style it. However, there is only one aside
element in your source document, and it has the sidebar id
attribute. Close the browser. Replace all aside
selectors with #sidebar
, including the one you just added. There are several places in the Index.html
file that you’ll need to change. View the page again and change the emulator mode to IE 7. The sidebar should now have a background color, and there is also padding around the text.
添加圆角
如果浏览器不支持圆角,您可以通过 Dave Methvin 编写的 jQuery 插件轻松添加圆角。除了圆角,该插件还可以创建许多其他图案,这些图案显示在 http://jquery.malsup.com/corner
处。这是通过开源许可证提供的,因此您可以免费下载并在您的应用中使用它。
您将使用这个插件来实现nav
和footer
元素的圆角。但是,只有在圆角本身不受支持的情况下,才应该这样做。所以,第一个问题是,如何知道浏览器是否支持圆角?答案还是现代化。添加这样的语句将有条件地调用自定义方法:
if (!Modernizr.borderradius)
Tip
在本章的后面,我将向你展示另一种圆角技术。
EXERCISE 7-2. ADDING ROUNDED CORNERSGo to http://jquery.malsup.com/corner
. Click the jquery.corner.js link near the top of the page. This will download the latest version. Save the file in your wwwroot\lib
folder in the Solution Explorer.
注意,这个插件不能通过 Bower 获得,所以您需要以传统的方式下载并安装它。
This function is based on jQuery, so you’ll also need to reference that in your page. Open the bower.json
file and add the lines shown in bold (don’t forget to add the comma at the end of the previous line).
{
"name": "Chapter``7
"private": true,
"dependencies": {
"modernizr": "2.8.3"
,
"jquery": "2.1.4",
"jquery-validation-unobtrusive": "3.2.2"
}
}
After saving the bower.json
file, you should see a jquery item in the Dependencies\Bower
folder. Like you did for Modernizr, right-click the jquery item and select the Open in File Explorer link. Go to the dist subfolder and copy the jquery.js
file to the wwwroot\lib
folder in the Solution Explorer. In the same way, copy the jquery.validation.unobtrusive.js
file to the wwwroot\lib
folder. Open the Index.html
file and add these references near the top of the page, just after the Modernizr script:
<script type="text/javascript" src="lib/jquery.js"></script>
<script type="text/javascript" src="lib/jquery.corner.js"></script>
Now invoke this by adding this script
element at the end of the Index.html
file, after the footer element and just before the body closing tag.
<script type="text/javascript">
if (!Modernizr.borderradius) {
$("nav").corner("15px");
$("footer").corner("25px");
}
</script>
This code uses the jQuery selector to find the nav
and footer
elements and calls the corner()
method specifying the radius. Save your changes and view the page using Internet Explorer. Switch the emulator mode to IE 7, and your page should look like Figure 7-8.
图 7-8。
The demo page with rounded corners
添加渐变
接下来,您将使用 PIE(progressive Internet Explorer)的另一个开源解决方案向 intro 部分添加渐变背景。这是作为 HTC 文件实现的,就像您之前添加的表支持一样。一旦下载了组件,只需使用样式表规则的 behavior 属性调用它。
EXERCISE 7-3. ADDING BACKGROUND GRADIENTSGo to the http://css3pie.com
site and click the Download button. This will download a PIE-1.0.0.zip
file (you may see a different version number; just download the latest version). There are several files inside this .zip
file. Copy the PIE.htc
file to your wwwroot\css
folder. From Solution Explorer, right-click the wwwroot\css
folder and click the Add and Existing Item links. Navigate to the css
folder and select the PIE.htc
file. Open the Index.html
file and find where the rules for the .intro
class are defined. Add the following lines shown in bold. This code will add another vendor-prefixed attribute (-pie-
) and then invoke the PIE component using the behavior
property.
/* Gradients */
.intro
{
border: 1px solid #999999;
text-align:left;
margin-top: 6px;
padding-left: 15px;
border-radius:25px;
background-image: linear-gradient(45deg, #ffffff, #6699cc);
-pie-background: linear-gradient(45deg, #ffffff, #6699cc);
behavior: url(css/PIE.htc);
}
Save your changes, view the page using Internet Explorer, and switch the emulator mode to IE 7. You should now have a linear gradient that looks just like the native gradient. You might have also noticed that the corners are rounded as well. The PIE.htc
shim also supports rounded corners and took care of that for you. Note
PIE 旨在回填几个 CSS3 特性,本文列举了: http://css3pie.com/documentation/supported-css3-features
。它将尝试处理引用PIE.htc
垫片的元素中包含的任何这些特性。但是,它不会对本机支持的功能做任何事情。
该页面现在应该如图 7-9 所示。
图 7-9。
The demo page with a gradient background
将书单分条
回想一下第四章中的,图书列表是使用:nth-child
选择器设计的,所以交替的元素会有不同的背景。在不支持这一功能的旧浏览器中,您可以用传统的方式来实现,即在 JavaScript 中迭代列表,并改变交替元素的样式。
然而,关键在于确定:nth-child
选择器是否可用,因为 Modernizr 不提供这个功能。在撰写本文时,Modernizr 正在开发包含这一功能的版本 3 beta。更多详情,请查看 http://v3.modernizr.com/download
的功能列表。
Note
这里提供的解决方案基于 Lea Verou 的一篇文章。然而,我不得不调整它以适应 IE。更多详情,请查看 http://lea.verou.me/2011/07/detecting-css-selectors-support-my-jsconf-eu-talk/
的文章。
EXERCISE 7-4. STRIPING THE BOOK LISTOpen the Index.html
file and add the following code to the script
element at the top of the file:
function supportsSelector(selector) {
var el = document.createElement('div');
el.innerHTML = ['', '<style>', selector, '{}', '</style>'].join('');
try
{
el = document.body.appendChild(el);
var style = el.getElementsByTagName('style')[0],
ret = !!(style.sheet.rules || style.sheet.cssRules)[0];
}
catch(e){
ret = false;
}
document.body.removeChild(el);
return ret;
}
This code creates a new style
element and adds the selector in question. It then checks to see whether it is actually there. If not, the selector is not supported. This is done in a try/catch
block in case older browsers do not support either the style.sheet.rules
or style.sheet.cssRules
property. Now with your handy supportsSelector()
function, you can implement the manual striping technique. Add the following code to the script
element at the bottom of the file after the existing function you added for the rounded corners:
if (!supportsSelector(":nth-child(2n+0)")) {
var titles = document.getElementById("titles");
var articles = titles.getElementsByTagName("article");
for (var i = 0; i < articles.length; i++) {
var title = articles[i];
if (i % 2) {
title.style.background = "#6699cc";
title.style.border = "1px solid #c0c0c0";
}
else {
title.style.background = "#c0c0c0";
title.style.``bor
}
}
}
If the :nth-child
selector is not supported, this code gets the #titles
element using the getElementById()
function. This is the section
element that contains a series of article
elements, one for each book. It then gets an array of child article
elements using the getElementsByTagName()
function. Note that this method is invoked on the titles
object and not the document
object. Once it has the array of elements, the code simply iterates the array, modifying the background
and border
properties. Save your changes and view the page using the IE 7 emulation mode. The page should look like Figure 7-10.
图 7-10。
The aside element with manual striping
隐藏不支持的元素
正如本章前面所述,对于每一个不支持的特性,你需要决定这是否是一个交易破坏者,页面是否需要失败,你是否想要填充这个特性,或者你是否想要在旧的浏览器上忽略它。从最初的不支持的更改列表中,还有三项您尚未实现:
- CSS 动画
- 3D 转换
- 多列
您可以通过使用 JavaScript 在计时器到期时改变背景图像来轻松实现动画。在我们有 CSS 动画之前,这是通常的做法。然而,在旧的浏览器中实现 3D 变换是行不通的。我认为这两个功能都很好,但是不值得这么麻烦,所以如果浏览器不支持这些功能,我们就把它们去掉。
最好模仿的一个特性是多列支持。对此有垫片可用,如 GitHub 提供的这种: https://github.com/gryzzly/CSS-Multi-column-Layout-Module-Polyfill/blob/master/index.html
。也许有足够的时间和耐心,你可以得到一些工作,但这是其中一个艰难的决定。值得努力吗?在某些特殊的情况下,可能是这样,但是一般来说,你不应该把 80%的时间花在一个只会影响百分之几的预期观众的功能上。
但是,您应该考虑的一件事是隐藏没有功能的元素。例如,月亮的静态图片不是很有趣,所以您将通过将其大小设置为 0 来隐藏该元素。
EXERCISE 7-5. HIDING ELEMENTSAdd the following code to the script
element at the bottom of the Index.html
file:
if (!Modernizr.cssanimations) {
document.getElementById("moon").style.width = "0px";
document.getElementById("moon").style.height = "0px";
}
Finally, after all this work, you should try the page in a browser that supports all these features to make sure it still looks great there. The final version in Chrome should look like Figure 7-12.
图 7-12。
The final demo page as shown in Chrome This code simply shrinks the moon div
if CSS animations are not supported. View the page in Internet Explorer and switch the emulator mode to IE 7. The final web page should look like Figure 7-11.
图 7-11。
The final demo page as shown in IE7 Tip
源代码下载包含完整的Index.html
文件。如果对具体应该如何或在哪里进行更改有任何疑问,请参考本指南。
摘要
在这一章中,我向你展示了一些让你的网页看起来很棒的技巧,即使是在不支持 HTML5 新特性的旧浏览器上。这些技术包括以下内容:
- 使用 Modernizr 进行特性检测和基本元素支持
- 显示表格
- 添加圆角
- 支持渐变背景图像
- 手动条带化列表
- 隐藏不支持的元素
对于每个不受支持的功能,您需要确定以下事项:
- 该特性是否对页面至关重要(如果是,页面应该失败)
- 该特征是否容易聚合填充
- 该功能是否可以忽略
这是一种平衡行为,因为您希望页面在所有浏览器中都很好看,但又不想花费过多的时间来支持每种可能的浏览器。
演示页面的最终实现达成了一个很好的妥协。该网站看起来很棒,功能正常。虽然省略了一些新的 HTML5 功能,但考虑到浏览器支持,总体来说它仍然是一个很好的网站,额外的工作也很少。
在下一章,我将向你展示如何使用 HTML5 中引入的新的audio
和video e
元素。
八、音频和视频
在这一章中,我将演示 HTML5 中引入的新的audio
和video
元素。这两个元素在它们的属性以及它们支持的方法和事件方面是相同的。我将用这一章的大部分时间来讨论和演示audio
元素,但请记住,我向你展示的一切也适用于视频。本章末尾的一些练习将把这些相同的技术应用到video
元素上,这样你就可以自己看到了。
我将演示如何使用浏览器提供的本地控件添加audio
和video
元素。这种方法使得在网站中嵌入音频和视频成为添加一些简单标记的小事。然而,如果你想写你自己的控件,本章也将演示如何去做,以及如何用 JavaScript 连接所有的事件。
因为每个浏览器支持不同的媒体格式,所以您可能需要对媒体文件的多个版本进行编码。然而,现在大多数主流浏览器都支持 MP3 和 MP4 格式,所以这不再是个问题。audio
和video
元素可以支持多个来源,因此每个浏览器都可以选择合适的版本来使用。
Note
video
元素支持audio
元素不支持的三个附加属性(width
、height
和poster
)。我将在本章后面解释这些。
使用音频元素
我将从一个非常简单的练习开始,向网页添加一个audio
元素。然后,您将支持多种格式,并在各种 bowsers 上尝试您的站点。
创建示例项目
在本章中,您将创建一个网站项目,您将使用它来尝试 HTML5 的audio
和video
元素。现在,您将创建一个空网站,然后在本章中逐步向其添加功能。
EXERCISE 8-1. ADDING AUDIO TO A PAGEStart Visual Studio 2015 and click New Project. Select the ASP.NET Web Application project template and enter Chapter 8 for the name. In the second dialog, select the ASP.NET 5 Empty project. In Solution Explorer, right-click the wwwroot
folder and click the Add and New Folder links. Enter the name Media. You’ll need an MP3 file to use as a sample audio clip. The file I’m using is copyrighted, so I can’t include it with the source code. You should be able to find one on your computer or download one from the internet. You can also rip a CD through Windows Media Player and select MP3 as the format. Drag the MP3 file from Windows Explorer to the wwwroot\Media
folder in Visual Studio. Now you’ll add the web page that you’ll be working on throughout this chapter. From Solution Explorer, right-click the wwwroot
folder and click the Add and New Item links. In the Add New Item dialog, select HTML Page and enter the name Index.html, as shown in Figure 8-1.
图 8-1。
Adding the Index.html page Open the Index.html
file. In the empty body
that was created by the file template, create a div
element. Inside that div
, enter <audio src=, and you should see a link that you can use to select the source from a file in your project. Select the Media
folder and then your MP3 file, as shown in Figure 8-2.
图 8-2。
Using a link to select the source Add the autoplay
attribute and close the audio
element. Add text inside the audio
element like this: <body>
<div>
<audio src="Media/Linus and Lucy.mp3" autoplay>
<p>HTML5 audio is not supported on your browser</p>
</audio>
</div>
</body>
Save your changes, make sure Internet Explorer is chosen as your default browser for debugging, and browse the Index.html
page. Open the Startup.cs
file and comment out the implementation of the Configure()
method. Once the page has loaded, your audio clip should start playing. The page, however, will be blank. Press F12 to open the Developer Tools pane, if not already opened. Change the browser mode to IE 8. The music will stop, and you’ll see the “HTML5 audio is not supported on your browser” text displayed.
第一个练习演示了audio
元素的基本用法。您只需输入src
属性,它指定音频文件的 URL。当浏览器不支持audio
元素时,使用audio
元素内的内容。由于 IE 8 不支持audio
元素,因此显示包含在p
标签中的文本。您可以利用这一点简单地显示一条消息,就像您在这里所做的那样。但是,您可以使用它来提供一个下载文件的链接,或者使用一个插件来实现一个后备解决方案。
使用本机控件
就 UI 而言,基本上有三个选项:
- 无控件:音频会播放,但用户没有可用的控件。当使用
autoplay
属性加载页面时,剪辑可以自动开始。您还可以使用 JavaScript 开始、暂停和停止音频剪辑。 - 本机控件:浏览器为用户提供播放、暂停和停止音频剪辑以及控制音量的控件。
- 定制控件:页面提供了通过 JavaScript 与
audio
元素交互的定制控件。
要启用本地控件,只需像这样添加controls
属性:
<audio src="∼/Media/Linus and Lucy.mp3" autoplay``controls
保存您的更改并浏览到您的页面,本地控件应该类似于图 8-3 所示。
图 8-3。
Displaying the native audio controls in Internet Explorer
在 Opera 和 Chrome 中,控件看起来像图 8-4 。
图 8-4。
The audio controls in Opera
在 Firefox 中,控件看起来如图 8-5 所示。
图 8-5。
The audio controls in Firefox
在 Safari 中,音频控制看起来像图 8-6 。
图 8-6。
The audio control in Safari Tip
Windows 上的 Safari 要求安装 QuickTime 以支持audio
元素。你可以从这个网站下载: https://support.apple.com/kb/DL837?locale=en_US
。安装 QuickTime 后,您可能需要重新启动电脑,Safari 才能工作。
如您所见,每个浏览器中的控件都有不同的样式。使用原生控件,您几乎无法控制音频控件的显示方式。您可以通过设置style
属性来改变宽度,这将拉伸进度条。超出正常高度只会在控件顶部增加空白,如图 8-7 所示。然而,在 IE 中,降低高度会缩小控件;在 Chrome 中,它会对其进行剪辑。
图 8-7。
Extending the size of the native controls
查看浏览器支持
虽然所有主流浏览器都支持audio
元素,但它们并不都支持相同的音频格式。直到最近,浏览器通常要么支持 MP3,要么支持 Vorbis,你需要两者都提供。然而,现在大多数浏览器都支持 MP3 和 MP4 格式的视频。如果需要,HTML5 提供了一种提供多种格式的方法来支持旧的浏览器。
Tip
这里有一个方便的页面测试浏览器对audio
和video
元素的支持: http://hpr.dogphilosophy.net/test/
。它还概述了对各种浏览器的支持。
audio
元素允许您指定多个源,浏览器将遍历这些源,直到找到它支持的一个。不使用src
属性,而是在audio
元素中提供一个或多个source
元素,如下所示:
<audio autoplay controls>
<source src="Media/Linus and Lucy.ogg" />
<source src="Media/Linus and Lucy.mp3" />
<p>HTML5 audio is not supported on your browser</p>
</audio>
浏览器将使用它支持的第一个源,因此如果这对您很重要,您应该首先列出首选文件。例如,Chrome 同时支持 MP3 和 Vorbis 格式。如果您希望使用 MP3 文件,您应该在。ogg
文件。
虽然像这样只列出源代码是可行的,但是浏览器必须下载文件并打开它,看看它是否能够播放。下载了一个相当大的文件却发现它无法使用,这不是很有效率。您还应该包括type
属性,它指定了资源的类型。然后,浏览器可以通过查看标记来确定该文件是否受支持。type
属性像这样指定 MIME 格式:
<source src="Media/Linus and Lucy.ogg"``type="audio/ogg"
<source src="Media/Linus and Lucy.mp3"``type="audio/mp3"
您也可以在type
属性中指定编解码器,如下所示:
<source src="Media/Linus and Lucy.ogg" type='audio/ogg; codecs="vorbis"' />
这将帮助浏览器更有效地选择一个兼容的媒体文件,我将在本章后面解释。请注意,编解码器值包含在双引号中,因此您需要在type
属性值两边使用单引号。现在,您将修改您的网页,以便它也可以在其他浏览器上工作。
EXERCISE 8-2. ADDING MULTIPLE SOURCESCreate a Vorbis-encoded audio file of your sample audio clip that has the .ogg
extension and copy this to the wwwroot\Media
folder.
提示我使用了一个叫做 XMedia Recode 的工具,你可以在 http://www.xmedia-recode.de/download.html
下载。您可以使用此实用程序来格式化音频和视频文件。安装此应用后,运行它,单击功能区中的打开文件按钮,然后选择 MP3 文件。在“格式”选项卡上,选择 OGG 格式。请注意,文件扩展名选项自动设置为。ogg
并且音频选项卡上的编解码器选项设置为 Vorbis。单击功能区中的“添加到队列”按钮。选择“队列”选项卡,查看已定义的转换该文件的作业。在窗口底部,您可以指定新文件的保存位置。点击浏览按钮,导航到Chapter
8
\Media
文件夹。最后,单击 Encode 按钮启动作业。将显示一个对话框,显示作业的进度。
In Solution Explorer, right-click the Media
folder and click the Add and Existing Item links. Navigate to the Chapter
8
\wwwroot\Media
folder and select the .ogg
file that you just encoded. In the Index.html
file, replace the audio
element with the following code (you’ll need to adjust the actual file name to match yours): <audio autoplay controls >
<source src="Media/Linus and Lucy.ogg" type="audio/ogg" />
<source src="Media/Linus and Lucy.mp3" type="audio/mp3" />
<p>HTML5 audio is not supported on your browser</p>
</audio>
Save your changes and browse to your page. Open the page using several browsers and verify that the controls are displayed and the audio starts playing when the page is loaded.
构建您自己的控件
所有的 DOM 元素和事件都在 JavaScript 中可用,所以创建自己的控件来处理audio
元素是一个相当简单的过程。然而,有几个方面你需要控制,所以这不是一个微不足道的练习。您需要解决三个方面的问题:
- 播放/暂停
- 显示进度和快进/快退
- 调节音量/静音
您需要响应来自自定义控件和audio
元素的事件。在本练习中,您将从向页面添加所有必要的控件开始。然后,我将向您展示如何实现每个领域所需的事件处理程序。您将用来控制音频元素的输入元素如下:
- 播放/暂停按钮:标签将根据音频元素的状态在“播放”和“暂停”之间切换。
- 寻找:这是一个
range
控件(在第二章和第三章中介绍),既可以显示进度,也可以让用户寻找特定的位置。 - Duration:这是一个
span
元素,显示音频文件的当前位置和总时长。 - 静音按钮:标签将在“静音”和“取消静音”之间切换
- 音量:这是另一个用于指定音量级别的
range
控件。
您将为其提供处理程序的audio
事件包括:
onplay
:音频开始时触发onpause
:音频暂停时引发onended
:音频完成时触发ontimeupdate
:随着音频剪辑的播放周期性地触发ondurationchanged
:当持续时间改变时引发,这发生在加载文件时onvolumnechanged
:当音量改变或静音属性改变时触发
添加自定义控件
您将从添加自定义控件和定义所需的事件处理程序开始。然后我将解释您将用来实现它们的 JavaScript。
EXERCISE 8-3. ADDING CUSTOM CONTROLSOpen the Index.html
file and remove the controls
attribute. This will cause the audio
element to be hidden. Add the following div
after the audio
element. This will include all the input elements that you’ll need to control the audio. <div id="audioControls">
<input type="button" value="Play" id="play" onclick="togglePlay()" />
<input type="range" id="audioSeek" onchange="seekAudio()" />
<span id="duration"></span>
<input type="button" id="mute" value="Mute" onclick="toggleMute()" />
<input type="range" id="volume" min="0" max="1" step="any"
onchange="setVolume()" />
</div>
Modify the audio
element to add the necessary event handlers by adding the code shown in bold. This also defines the id
attribute that you’ll use to access the audio
and source
elements. <audio
id="audio"
autoplay
onplay="updatePlayPause()"
onpause="updatePlayPause()"
onended="endAudio()"
ontimeupdate="updateSeek()"
ondurationchange="setupSeek()"
onvolumechange="updateMute()" >
<source src="Media/Linus and Lucy.ogg" type="audio/ogg"
id="oggSource"
/>
<source src="Media/Linus and Lucy.mp3" type="audio/mp3"
id="mp3Source"
/>
<p>HTML5 audio is not supported on your browser</p>
</audio>
In Visual Studio, change the debug browser to use Opera. (You can also use Chrome if you prefer.) Save your changes and browse to your page. The page should look like Figure 8-8.
图 8-8。
The custom audio controls Close the browser and stop the debugger.
实现事件处理程序
现在,您已经准备好实现事件处理程序了。我将围绕三个主要方面(播放、搜索和音量)对它们进行分组,并一次解释一个部分。
支持播放和暂停
将清单 8-1 中所示的代码添加到外层div
之后,就在body
元素结束之前。
Listing 8-1. The Initial script Element
<script type="text/javascript">
var audio = document.getElementById("audio");
function setupSeek() {
var seek = document.getElementById("audioSeek");
seek.min = 0;
seek.max = Math.round(audio.duration);
seek.value = 0;
var duration = document.getElementById("duration");
duration.innerHTML = "0/" + Math.round(audio.duration);
}
function togglePlay() {
if (audio.paused || audio.ended) {
audio.play();
}
else {
audio.pause();
}
}
function updatePlayPause() {
var play = document.getElementById("play");
if (audio.paused || audio.ended) {
play.value = "Play";
}
else {
play.value = "Pause";
}
}
function endAudio() {
document.getElementById("play").value = "Play";
document.getElementById("audioSeek").value = 0;
document.getElementById("duration").innerHTML = "0/" + Math.round(audio.duration);
}
</script>
这段代码首先声明引用audio
元素的audio
变量。因为大多数函数都使用它,所以获取一次并将其存储在所有函数都可以访问的变量中会更有效。
第一个方法setupSeek()
被调用以响应ondurationchange
事件。当页面第一次加载时,它不知道音频剪辑有多长,直到打开文件并加载元数据。一旦加载了元数据,就可以确定持续时间,并引发事件。duration
属性以秒表示。setupSeek()
函数使用duration
属性来设置audioSeek
范围控件的max
属性。它也用于设置span
元素的初始值。请注意,调用了Math.round()
函数来将该值四舍五入为最接近的整数。
当用户点击播放按钮时,调用togglePlay()
方法。如果audio
元素的当前状态是暂停或结束,它调用play()
函数。否则,它调用pause()
方法。updatePlayPause()
方法设置播放按钮的标签。如果音频当前正在播放,文本将更改为“暂停”,因为这将是单击按钮的结果。否则,文本被设置为“播放”
Tip
togglePlay()
函数响应播放按钮被点击,updatePlayPause()
函数响应audio
元素被启动或暂停。当点击按钮时,togglePlay()
方法将改变audio
元素的状态。这种状态变化将引发一个onplay
或onpause
事件,这两个事件都由updatePlayPause()
函数处理。这样做是因为可以通过点击播放按钮以外的方式来播放或暂停音频。例如,如果您保留了controls
属性,那么您将同时拥有本地控件和自定义控件。响应onplay
和onpause
事件确保按钮标签总是正确的,不管audio
元素是如何操作的。
最后,当音频播放完毕时,调用endAudio()
函数。这执行一些同步,包括设置按钮标签和初始化range
和span
控件。
支持进步和寻求
接下来,将清单 8-2 中所示的函数添加到同一个script
元素中。
Listing 8-2. Functions to Support the range Control
function seekAudio() {
var seek = document.getElementById("audioSeek");
audio.currentTime = seek.value;
}
function updateSeek() {
var seek = document.getElementById("audioSeek");
seek.value = Math.round(audio.currentTime);
var duration = document.getElementById("duration");
duration.innerHTML = Math.round(audio.currentTime) + "/" + Math.round(audio.duration);
}
就像播放按钮一样,有一个事件处理程序seekAudio()
响应输入元素,还有一个单独的事件处理程序updateSeek()
响应audio
元素。当用户移动range
控件上的滑块时,调用seekAudio()
函数。它只是使用由range
控件选择的值来设置currentTime
属性。
当audio
元素引发ontimeupdate
事件时,调用updateSeek()
函数。这将更新range
控件,以反映文件中的当前位置。它还更新了span
控件来显示实际位置(以秒为单位)。同样,currentTime
属性被四舍五入为最接近的整数。
控制音量
最后一组功能用于支持音量控制和静音按钮。将清单 8-3 中所示的代码添加到您一直在使用的同一个script
元素中。
Listing 8-3. Controlling the Volume
function toggleMute() {
audio.muted = !audio.muted;
}
function updateMute() {
var mute = document.getElementById("mute");
if (audio.muted) {
mute.value = "Unmute";
}
else {
mute.value = "Mute";
}
}
function setVolume() {
var volume = document.getElementById("volume");
audio.volume = volume.value;
}
顾名思义,toggleMute()
函数切换audio
元素的muted
属性。当这被改变时,onvolumechange
事件由audio
元素引发。updateMute()
函数响应该事件,并根据muted
属性的当前值设置按钮标签。同样,这样做可以确保按钮标签是正确的。
最后,当用户移动第二个range
控件上的滑块时,调用setVolume()
函数。它将audio
元素的volume
属性设置为在range
控件上选择的内容。
Note
volume
属性的值介于 0 和 1 之间。你可以认为这是 0%和 100%。当您定义range
控件时,min
属性被设置为 0,max
被设置为 1,因此比例是正确的。您可以简单地使用范围值设置volume
属性。如果要显示volume
属性的实际值,只需将其转换成百分比即可。
现在,您已经准备好尝试自定义控件了。保存您的更改并浏览到您的页面。该页面应类似于图 8-9 。
图 8-9。
The completed custom controls
您没有提供任何样式规则,因此控件使用默认样式显示。但是现在你可以访问单独的控件了,所以你可以按照你想要的方式来排列和样式化它们(关于 CSS 样式的使用,请参考第四章)。
更改音频源
在这个例子中,音频源是在标记中定义的。但是,您可以使用 JavaScript 轻松控制这一点。如果您像最初一样使用单个的src
属性,那么您只需要修改这个属性来引用一个不同的文件。然而,如果你使用多个source
元素,你需要更新所有这些元素,然后调用load()
方法。
为了演示这一点,当剪辑完成时,您将把源更改为第二个音频剪辑,并播放该音频剪辑。
EXERCISE 8-4. CHANGING THE AUDIO SOURCEClose the browser and stop the debugger. Create another audio file in both .mp3
and .ogg
formats and copy these to the wwwroot\Media
folder. In Solution Explorer, right-click the wwwroot\Media
folder and click the Add and Existing Item links. Navigate to the Chapter
8
\wwwroot\Media
folder and select both the .mp3
and .ogg
files that you just encoded. Open the Index.html
file. At the top of the existing script
element, declare the following variable. This will be used to keep track of how many songs were played. var songCount = 0;
Add the following code shown in bold to the endAudio()
function. If the audio clip that just finished is the first one, the code will change the src
attribute to reference the second file. The file is then loaded and played. (Note you’ll need to change these file names to use the actual files that you included in your project.) function endAudio() {
document.getElementById("play").value = "Play";
document.getElementById("audioSeek").value = 0;
document.getElementById("duration").innerHTML = "0/" + Math.round(audio.duration);
if (++songCount < 2) {
document.getElementById("oggSource").src = "Media/Sample.ogg";
document.getElementById("mp3Source").src = "Media/Sample.mp3";
audio.load();
audio.play();
}
}
Save your changes and browse to your page. The initial song should start playing. To save some time, fast-forward the clip to almost the end of the file and then wait for it to finish. The second file should automatically start playing. You should notice the span
control was updated to show the duration of the second file.
检测音频支持
您可以通过调用audio
元素上的canPlayType()
方法,传入type
属性,以编程方式确定浏览器是否支持某个源。为了演示这一点,将以下代码添加到script
块的开头,在audio
变量声明之后:
var sources = audio.getElementsByTagName("source");
for (var i = 0; i < sources.length; i++) {
alert("[" + sources[i].type + "] - " + audio.canPlayType(sources[i].type));
}
这段代码遍历所有的source
元素,并显示一个弹出窗口,指示该类型是否受支持。保存您的更改,并尝试在 Opera 中运行。您应该会看到如图 8-10 和 8-11 所示的结果。
图 8-11。
The canPlayType( ) results for audio/ogg
图 8-10。
The canPlayType( ) results for audio/mp3
你觉得怎么样?你问一个是或否的问题,然后得到一个“可能”或“也许”的回答。事实证明,canPlayType()
函数既不返回“否”也不返回“是”。相反,它返回“可能”、“可能”或空字符串。空白字符串可以解释为“不”。我们来谈谈“可能”和“可能”
源文件是一个容器,除了实际数据之外,它还提供有关媒体的元数据。指定像 audio/ogg 这样的 MIME 类型仅仅表示容器的类型,而没有明确说明数据是如何编码的(使用什么编解码器)。如果浏览器支持该容器类型,canPlayType()
返回“可能”没有证据表明它不受支持,但也不能确定它是否受支持。如果容器类型不受支持,如 audio/mp3,则返回一个空字符串(意味着它不受支持)。
Note
空字符串用于表示“不支持”,因为在 JavaScript 中,空字符串是假值,而“否”是真值。所以,你可以编码if (canPlayType(type))
,只有“可能”和“大概”的结果会被选中。
无论如何,用canPlayType()
方法,“可能”是你能得到的最好的结果;你从来没有得到一个“是”表 8-1 列出了可能的响应。
表 8-1。
canPlayType( ) Method Responses
| 多媒体数字信号编解码器 | 支持容器 | 不支持容器 | | --- | --- | --- | | 不明确的 | “也许” | 空白的 | | 支持 | “大概” | 空白的 | | 不支持 | 空白的 | 空白的 |了解视频格式
正如我在本章开始时所说的,video
元素的工作方式和audio
元素一样,所以到目前为止你所学到的一切也适用于视频。
查看浏览器支持
视频文件通常包含音频和视频,所以我前面提到的所有音频类型和编解码器仍然适用。此外,视频部分可以各种方式编码。幸运的是,该行业似乎正在缩小到三种主要形式:
- MP4 (*.mp4):使用 H.264 视频编码和 MP3 音频编码
- WebM (*。webm):使用 VP8 视频编码和 Vorbis 音频编码
- Ogg (*。ogv):使用 Theora 视频编码和 Vorbis 音频编码
表 8-2 列出了目前主流浏览器支持的格式。
表 8-2。
Video/Audio Codec Support
| 浏览器 | MP4 | WebM | Ogg | | --- | --- | --- | --- | | IE 11 | 是 | 不 | 不 | | 火狐浏览器 | 是 | 是 | 是 | | 铬 | 是 | 是 | 是 | | 旅行队 | 是 | 不 | 不 | | 歌剧 | 是 | 是 | 是 |如您所见,MP4 格式已经成为所有主流浏览器的标准。但是,与音频一样,您可以用多种格式对视频文件进行编码,并提供多个来源,以便浏览器可以选择它支持的来源。
下载示例视频
在本章的剩余部分,我将使用一个演示视频,你可以通过这个网站从微软下载: http://ie.microsoft.com/testdrive/graphics/videoformatsupport/default.html
。这是大巴克兔子电影的预告片。这个网站提供了这个视频剪辑的几个版本,所以你可以看到不同的浏览器是如何显示它们的。
使用 Internet Explorer 打开此网站。右键单击 H.264 基线配置文件视频,然后单击“视频另存为”链接。浏览到Chapter
8
\wwwroot\Media
文件夹,将文件另存为BigBuckBunny.mp4
。使用 Chrome 或 Opera 打开同一个站点,右键单击 WebM 版本。点击“视频另存为”链接,浏览到Chapter
8
\wwwroot\Media
文件夹。将此另存为BigBuckBunny.webm
。
使用视频元素
现在您已经有了一些视频文件,您将向您的应用添加一个video
元素。您将像处理audio
元素一样添加两个源。在第一个练习中,您将使用本地控件并在几个浏览器中测试应用。然后,您将添加像音频控件一样实现的自定义控件。
向演示页面添加视频
添加视频很简单,只需将媒体文件添加到项目中,然后在页面中添加一些简单的标记来播放视频。
EXERCISE 8-5. ADDING A VIDEO ELEMENTIn Solution Explorer, right-click the wwwroot\Media
folder and click the Add and Existing Item links. Select both versions of the Big Buck Bunny video files. In the Index.html
file, remove the autoplay
attribute from the audio
element. You will autoplay the video, and removing this will keep them from both playing at the same time. Add the following markup, just after the div
that contains the audio controls: <video id="video" autoplay controls loop>
<source src="Media/BigBuckBunny.webm" type="video/webm" />
<source src="Media/BigBuckBunny.mp4" type="video/mp4" />
<p>HTML5 video is not supported on your browser</p>
</video>
Save your changes and browse to your page. Try it in several browsers to make sure the video can be played in each.
对于video
元素,您包括了autoplay
、controls
和loop
属性。在处理audio
元素时,您已经使用了autoplay
和controls
属性。loop
属性将导致视频结束时从头开始。这也受到audio
元素的支持。
Tip
您之前在音频源上使用的canPlayType()
方法也可以用于视频文件。它的工作方式是一样的,要么返回一个空字符串表示该文件不受支持,要么返回一个“可能”或“很可能”的结果。
添加自定视频控制
现在您将为video
元素添加自定义控件,就像您为audio
元素所做的那样。
EXERCISE 8-6. ADDING CUSTOM VIDEO CONTROLSModify the markup in the Index.html
file by replacing the video
element with this: <video id="video"
onplay="updatePlayPauseVideo()"
onpause="updatePlayPauseVideo()"
onended="endVideo()"
ontimeupdate="updateSeekVideo()"
ondurationchange="setupSeekVideo()"
onvolumechange="updateMuteVideo()">
<source src="Media/BigBuckBunny.webm" type="video/webm" />
<source src="Media/BigBuckBunny.mp4" type="video/mp4" />
<p>HTML5 video is not supported on your browser</p>
</video>
Add a new div
after the video
element and enter the following markup for it: <div id="videoControls">
<input type="button" value="Play" id="playVideo" onclick="togglePlayVideo()" />
<input type="range" id="videoSeek" onchange="seekVideo()" />
<span id="durationVideo"></span>
<input type="button" id="muteVideo" value="Mute" onclick="toggleMuteVideo()" />
<input type="range" id="volumeVideo" min="0" max="1" step="any"
onchange="setVolumeVideo()" />
</div>
Add a new script
element using the code shown in Listing 8-4. This code is identical to the script used for the audio
element except it uses the video
element and the video controls.
清单 8-4。视频控件的 JavaScript 函数
<script type="text/javascript">
var video = document.getElementById("video");
function setupSeekVideo() {
var seek = document.getElementById("videoSeek");
seek.min = 0;
seek.max = Math.round(video.duration);
seek.value = 0;
var duration = document.getElementById("durationVideo");
duration.innerHTML = "0/" + Math.round(video.duration);
}
function togglePlayVideo() {
if (video.paused || video.ended) {
video.play();
}
else {
video.pause();
}
}
function updatePlayPauseVideo() {
var play = document.getElementById("playVideo");
if (video.paused || video.ended) {
play.value = "Play";
}
else {
play.value = "Pause";
}
}
function endVideo() {
document.getElementById("playVideo").value = "Play";
document.getElementById("videoSeek").value = 0;
document.getElementById("durationVideo").innerHTML = "0/"
+ Math.round(video.duration);
}
function seekVideo() {
var seek = document.getElementById("videoSeek");
video.currentTime = seek.value;
}
function updateSeekVideo() {
var seek = document.getElementById("videoSeek");
seek.value = Math.round(video.currentTime);
var duration = document.getElementById("durationVideo");
duration.innerHTML = Math.round(video.currentTime) + "/"
+ Math.round(video.duration);
}
function toggleMuteVideo() {
video.muted = !video.muted;
}
function updateMuteVideo() {
var mute = document.getElementById("muteVideo");
if (video.muted) {
mute.value = "Unmute";
}
else {
mute.value = "Mute";
}
}
function setVolumeVideo() {
var volume = document.getElementById("volumeVideo");
video.volume = volume.value;
}
</script>
This code is almost identical to the code you added earlier to support the custom audio controls. Save your changes. Select Opera as the debug browser and browse to your page. Try the video controls, which should look like Figure 8-12.
图 8-12。
The video element and controls
添加海报
属性由video
元素支持(但不支持audio
元素)。在视频开始之前,您可以使用poster
属性来指定显示的图像。如果没有指定,浏览器通常会打开视频并显示第一帧。要添加海报,只需将图像包含在项目中,并在poster
属性中引用它。
然而,有一件事需要小心。如果你定义了一个海报,video
元素的初始大小将是海报图像的大小。如果这与视频不同,视频开始播放时,大小会发生变化。您应该确保图像大小相同,或者明确调整video
元素的大小,这将拉伸(或收缩)海报图像以适合它。
EXERCISE 8-7. ADDING A POSTER IMAGEDownload an image file to use as the poster image. You can find images from this site: http://wiki.creativecommons.org/Case_Studies/Blender_Foundation
. Save the picture in the Chapter 8
\wwwroot\Media
folder and name it BBB_Poster.png. In Solution Explorer, right-click the wwwroot\Media
folder and click the Add and Existing Item links. Browse to the Chapter
8
\wwwroot\Media
folder and select the BBB_Poster.png
image. Modify the markup of the video
element by adding the poster
, width
, and height
attributes shown in bold. <video id="video"
poster="Media/BBB_Poster.png" width="852" height="480"
Save your changes and browse to your page. You should now see the poster image until the video is started, as shown in Figure 8-13.
图 8-13。
The video element showing the poster image
摘要
在本章中,您创建了一个简单的 web 页面,展示了audio
和video
元素的特性。浏览器已经标准化了音频的 MP3 格式和视频的 MP4 格式。然而,通过提供多种格式的媒体文件,可以实现额外的支持,这样浏览器就可以使用它所支持的格式。这很容易做到,而且相当有效,因为浏览器通常可以从标记中确定下载哪个文件。
我向您展示了如何创建自己的控件来播放、暂停和查找音频和视频文件。通过连接一些简单的 JavaScript 事件处理程序,制作一个定制的媒体播放器变得非常简单。
在下一章,我将演示如何利用 HTML5 中的可缩放矢量图形(SVG)。
九、可缩放矢量图形
在这一章中,我将向你展示如何使用 Visual Studio、ASP.NET MVC 和 SQL Server 在 HTML5 web 应用中使用可缩放矢量图形(SVG)。使用 SVG 可以做很多非常酷的事情。我挑选了一个有趣的演示,可以很容易地应用于许多业务应用。但是首先,让我给你介绍一下什么是 SVG。
大多数人认为图形元素是某种形式的位图,由像素的行和列组成,每个像素都分配有特定的颜色。然而,相比之下,矢量图形将图像表示为公式的集合。例如,画一个圆心在点 x,y,半径为 r 的圆。更复杂的图像被定义为图形元素的集合,包括圆、线和路径。虽然渲染引擎将最终确定需要设置的特定像素,但图像定义是基于公式的。这一根本区别为使用矢量图形提供了两个显著的优势。
首先,顾名思义,矢量图形是可缩放的。如果您想要扩大图像的大小,渲染引擎只需根据新的大小重新计算公式,不会损失清晰度。如果你放大位图图像,你会很快看到颗粒感,图像变得模糊。
第二,图像中的每个元素都可以独立操作。例如,如果图像中有几个圆圈,您可以通过简单地改变该图像的颜色来突出显示其中一个。由于矢量图形是基于公式的,您可以轻松地调整公式来修改图像。特别有用的是,这些元素可以使用 CSS 样式化,使用我在第四章中展示的强大的选择器和格式化功能。
SVG 简介
首先,您将创建一个使用简单几何形状来绘制图片的页面。然后,您将使用 CSS 对这些形状应用样式。我将向您展示如何将这些标记元素保存在一个.svg
图像文件中。该图像文件可以像其他图像文件一样使用,例如。jpg
和。png
文件。
创建示例项目
您首先需要创建一个 Visual Studio 项目。这将使用与您在前面章节中使用的不同的项目模板。对于本章中的一个练习,您需要连接到 SQL Server 数据库。您将使用网站模板,而不是手动连接,它会为您完成大部分工作。
EXERCISE 9-1. CREATING THE VISUAL STUDIO PROJECTStart Visual Studio 2015. In the Start Page, click the New Project link. In the New project dialog box, select the ASP.NET Web Application template. Enter the project name Chapter 9 and select a location for this project. In the next dialog box, select the ASP.NET 5 Web Site template and make sure the “Host in the cloud” check box is not selected. Click the OK button, and the project will be created (this may take a minute). Right-click the Views\Home
folder and click the Add and New Item links. In the Add New Item dialog box, select the MVC View Page template, enter the name Snowman.cshtml, and click the Add button.
添加一些简单的形状
为了演示svg
元素是如何工作的,您将添加一些简单的形状,如圆形、矩形和线条。正如我将在这里演示的,大多数图像可以表示为几何形状的集合。
EXERCISE 9-2. ADDING A SNOWMANReplace the initial contents of the Snowman.cshtml
file with the following: <svg xmlns:svg="
http://www.w3.org/2000/svg
" version="1.2"
width="100px" height="230px"
xmlns="
http://www.w3.org/2000/svg
"
xmlns:xlink="
http://www.w3.org/1999/xlink
">
</svg>
注意width
和height
属性定义了元素的内在维度。在 IE 9 中,你可以省略这些,页面会根据实际使用的空间正确显示。对于其他浏览器,如果没有指定width
和height
,图像将被裁剪为某个默认大小。
Inside the svg
element, add the following elements. These are just simple shapes, mostly circle
elements with a rectangle (rect
), line
, and polygon
. <circle class="body" cx="50" cy="171" r="40" />
<circle class="body" cx="50" cy="103" r="30" />
<circle class="body" cx="50" cy="50" r="25" />
<line class="hat" x1="30" y1="25" x2="70" y2="25" />
<rect class="hat" x="40" y="10" width="20" height="15" />
<circle class="button" cx="50" cy="82" r="4" />
<circle class="button" cx="50" cy="100" r="4" />
<circle class="button" cx="50" cy="118" r="4" />
<circle class="eye" cx="42" cy="42" r="4" />
<circle class="eye" cx="58" cy="42" r="4" />
<polygon class="nose" points="45,60 45,50 60,55" />
A circle
is expressed as a center point, cx
and cy
, and a radius, r
. A line
is specified as a beginning point, x1
and y1
, and an endpoint, x2
and y2
. A rectangle (rect
) element is described by the top-left corner location, x
and y
, a width
, and a height
. A polygon
is defined by a set of points in the form of x1,y1 x2,y2 x3,y3
. You can specify any number of points. It is rendered by drawing a line segment between each of these points and a line segment from the last point, back to the first point. Open the HomeController.cs
file (in the Controllers
folder) and add the following action. This will allow you to navigate to the new view. public IActionResult Snowman()
{
return View("∼/Views/Home/Snowman.cshtml");
}
Save your changes and press F5 to view the application. To get to the new page, add /Home/Snowman
to the URL. The page should look like Figure 9-1 (you will also see the ASP.NET default header and footer on your page).
图 9-1。
The initial SVG image without styling
添加样式
这些元素的默认样式是纯黑色填充,并且因为这些形状中的一些在彼此之上,所以有几个当前是不可见的。请注意,您为每个元素分配了一个class
属性。现在您将使用class
属性为这些元素应用样式。将清单 9-1 中所示的代码添加到svg
元素中,就在您之前添加的元素之前。
Listing 9-1. Adding SVG Styles
<style type="text/css" >
.body
{
fill: white;
stroke: gray;
stroke-width: 1px;
}
.hat
{
fill: black;
stroke: black;
stroke-width: 3px;
}
.button
{
fill: black;
}
.eye
{
fill: black;
}
.nose
{
fill: orange;
}
</style>
保存这些更改并刷新浏览器以查看更新后的网页,该网页应如图 9-2 所示。
图 9-2。
The SVG images with styling applied
使用 SVG 图像文件
除了嵌入一个svg
元素之外,您还可以用。svg 扩展。该文件可以像其他图形图像一样使用。我将向您展示如何创建一个独立的 SVG 图像,然后在页面上使用它。
创建 SVG 图像
我将首先向您展示如何创建一个独立的.svg
文件,然后使用它作为背景图像。这也将展示 SVG 图像的可伸缩性。
EXERCISE 9-3. CREATING AN SVG IMAGEFrom the wwwroot\images
folder, click the New and File links. In the Add New Item dialog box, select Text File, enter the name snowman.svg, and click the Add button. Enter the following markup instructions: <?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.2//EN"
"
http://www.w3.org/Graphics/SVG/1.2/DTD/svg12.dtd
">
Copy and paste the entire svg
element from the Snowman.cshtml
file, including the style
element to the new text file. Click the Save button. To test your image, from Solution Explorer, right-click the snowman.svg
file and click the “Open with” link. Then select Internet Explorer in the Open With dialog box. This should launch a browser and display the snowman image.
使用 SVG 背景
现在您有了一个图像文件,可以像使用其他图像一样使用它。为了演示这一点,您将向页面添加一个div
元素,并将snowman.svg
文件用作背景图像。您还将调整div
的大小,这样您就可以看到调整大小时图像的样子。
EXERCISE 9-4. ADDING A BACKGROUND IMAGEIn the Snowman.cshtml
file, add the following code after the svg
element: <div id="container"></div>
This simply defines a div
element. Now you’ll use CSS to configure it. Add the following style
element before the svg
element: <style type="text/css">
#container {
height: 800px;
width: 350px;
background-image: url("../img/snowman.svg");
background-size: contain;
}
</style>
Press F5 to debug your application. In addition to the small image, you should also see a larger version of your image, as shown in Figure 9-3. Notice that there is no loss of image quality when expanding the size of the image.
图 9-3。
The page with the snowman background
创建交互式地图
绘制雪人的图片可能很有趣,但是让我们继续讨论 SVG 的一些更实际的用途。您将创建一个美国地图,每个州用一个单独的 SVG path 元素表示,我将在后面解释。您将在 SQL Server 数据库中存储路径定义。我将向您展示如何使用模型类访问数据库,然后使用视图定义显示它。一旦显示了地图,我将向您展示一些使用静态和动态样式来设置地图样式的 CSS 技巧。最后,您将添加一些动画来为您的 web 页面增添一点趣味。
使用路径元素
path
元素是所有 SVG 元素中最通用的。它是“移动到”、“直线到”和各种“曲线到”命令的集合。该形状是按照路径命令绘制的。每个命令从当前位置开始,或者移动到新位置,或者绘制一条线到下一个位置。这里有一个例子:
- 移动到 25,50。
- 画一条线到 50,50。
- 画一条线到 50,25。
- 画一个弧线到 25,50。
这表示如下:
<path d="M25,50 L50,50 L50,25 A25,25 0 0,0 25,50 z" />
“移动到”和“行到”命令非常简单。“弧到”命令,以及所有其他曲线命令,更复杂,因为你需要提供额外的控制点,描述如何绘制曲线。每个命令使用一个字母,如表 9-1 所示。
表 9-1。
The Available Path Commands
| 命令 | 缩写 | 描述 | | --- | --- | --- | | 移到 | M | 移动到指定位置 | | 行到 | L | 在指定位置绘制一条线 | | 水平线至 | H | 在指定的 x 坐标处画一条水平线 | | 垂直线至 | V | 在指定的 y 坐标上画一条垂直线 | | 弧形到 | A | 在指定位置绘制弧线 | | 弯曲到 | C | 绘制三次贝塞尔曲线 | | 速记曲线到 | S | 绘制简化的三次贝塞尔曲线 | | 二次曲线到 | Q | 绘制二次贝塞尔曲线 | | 速记二次曲线到 | T | 绘制简化的二次贝塞尔曲线 | | 关闭路径 | Z | 通过在起始位置画一条线来闭合图形 |对于这些命令中的每一个,当使用绝对坐标时,使用大写字母。您还可以指定相对坐标,并使用小写字母来表示这些值相对于当前位置。有关构造路径元素的更多信息,请参见文章 http://www.w3.org/TR/SVG/paths.html#PathData
。
正如您可能想象的那样,绘制一个像阿拉斯加州这样的复杂形状需要很多命令。您不会想要手动编辑它。幸运的是,有工具可以帮助构建路径定义。例如,在 http://code.google.com/p/svg-edit
有一个免费的网络工具。笑一笑,列表 9-2 显示了阿拉斯加的path
元素。
Listing 9-2. The Path Element Definition for Alaska
<path d="M 158.07671,453.67502 L 157.75339,539.03215 L 159.36999,540.00211 L 162.44156,540.16377 L 163.8965,539.03215 L 166.48308,539.03215 L 166.64475,541.94205 L 173.59618,548.73182 L 174.08117,551.3184 L 177.47605,549.37846 L 178.1227,549.2168 L 178.44602,546.14524 L 179.90096,544.52863 L 181.0326,544.36697 L 182.97253,542.91201 L 186.04409,545.01361 L 186.69074,547.92352 L 188.63067,549.05514 L 189.7623,551.48006 L 193.64218,553.25833 L 197.03706,559.2398 L 199.78529,563.11966 L 202.04855,565.86791 L 203.50351,569.58611 L 208.515,571.36439 L 213.68817,573.46598 L 214.65813,577.83084 L 215.14311,580.9024 L 214.17315,584.29729 L 212.39487,586.56054 L 210.77826,585.75224 L 209.32331,582.68067 L 206.57507,581.22573 L 204.7968,580.09409 L 203.98849,580.9024 L 205.44344,583.65065 L 205.6051,587.36885 L 204.47347,587.85383 L 202.53354,585.9139 L 200.43195,584.62061 L 200.91693,586.23722 L 202.21021,588.0155 L 201.40191,588.8238 C 201.40191,588.8238 200.59361,588.50048 200.10863,587.85383 C 199.62363,587.20719 198.00703,584.45895 198.00703,584.45895 L 197.03706,582.19569 C 197.03706,582.19569 196.71374,583.48898 196.06709,583.16565 C 195.42044,582.84233 194.7738,581.71071 194.7738,581.71071 L 196.55207,579.77077 L 195.09712,578.31582 L 195.09712,573.30432 L 194.28882,573.30432 L 193.48052,576.6992 L 192.34888,577.1842 L 191.37892,573.46598 L 190.73227,569.74777 L 189.92396,569.26279 L 190.24729,574.92094 L 190.24729,576.05256 L 188.79233,574.75928 L 185.23579,568.77781 L 183.13419,568.29283 L 182.48755,564.57462 L 180.87094,561.66472 L 179.25432,560.53308 L 179.25432,558.26983 L 181.35592,556.97654 L 180.87094,556.65322 L 178.28436,557.29986 L 174.88947,554.87495 L 172.30289,551.96504 L 167.45306,549.37846 L 163.41152,546.79188 L 164.70482,543.55866 L 164.70482,541.94205 L 162.92654,543.55866 L 160.01664,544.69029 L 156.29843,543.55866 L 150.64028,541.13375 L 145.14381,541.13375 L 144.49717,541.61873 L 138.03072,537.73885 L 135.92912,537.41553 L 133.18088,531.59573 L 129.62433,531.91905 L 126.06778,533.374 L 126.55277,537.90052 L 127.68439,534.99062 L 128.65437,535.31394 L 127.19941,539.67879 L 130.43263,536.93055 L 131.07928,538.54716 L 127.19941,542.91201 L 125.90612,542.58869 L 125.42114,540.64875 L 124.12785,539.84045 L 122.83456,540.97208 L 120.08632,539.19381 L 117.01475,541.29541 L 115.23649,543.397 L 111.8416,545.4986 L 107.15342,545.33693 L 106.66844,543.23534 L 110.38664,542.58869 L 110.38664,541.29541 L 108.12338,540.64875 L 109.09336,538.22384 L 111.35661,534.34397 L 111.35661,532.5657 L 111.51827,531.75739 L 115.88313,529.49413 L 116.85309,530.78742 L 119.60134,530.78742 L 118.30805,528.20085 L 114.58983,527.87752 L 109.57834,530.62576 L 107.15342,534.02064 L 105.37515,536.60723 L 104.24352,538.87049 L 100.04033,540.32543 L 96.96876,542.91201 L 96.645439,544.52863 L 98.908696,545.4986 L 99.717009,547.60018 L 96.96876,550.83341 L 90.502321,555.03661 L 82.742574,559.2398 L 80.640977,560.37142 L 75.306159,561.50306 L 69.971333,563.76631 L 71.749608,565.0596 L 70.294654,566.51455 L 69.809672,567.64618 L 67.061434,566.67621 L 63.828214,566.83787 L 63.019902,569.10113 L 62.049939,569.10113 L 62.37326,566.67621 L 58.816709,567.96951 L 55.90681,568.93947 L 52.511924,567.64618 L 49.602023,569.58611 L 46.368799,569.58611 L 44.267202,570.87941 L 42.65059,571.68771 L 40.548995,571.36439 L 37.962415,570.23276 L 35.699158,570.87941 L 34.729191,571.84937 L 33.112578,570.71775 L 33.112578,568.77781 L 36.184142,567.48452 L 42.488929,568.13117 L 46.853782,566.51455 L 48.955378,564.41296 L 51.86528,563.76631 L 53.643553,562.958 L 56.391794,563.11966 L 58.008406,564.41296 L 58.978369,564.08964 L 61.241626,561.3414 L 64.313196,560.37142 L 67.708076,559.72478 L 69.00137,559.40146 L 69.648012,559.88644 L 70.456324,559.88644 L 71.749608,556.16823 L 75.791141,554.71329 L 77.731077,550.99508 L 79.994336,546.46856 L 81.610951,545.01361 L 81.934272,542.42703 L 80.317657,543.72032 L 76.922764,544.36697 L 76.276122,541.94205 L 74.982838,541.61873 L 74.012865,542.58869 L 73.851205,545.4986 L 72.39625,545.33693 L 70.941306,539.51713 L 69.648012,540.81041 L 68.516388,540.32543 L 68.193068,538.3855 L 64.151535,538.54716``L``62.049939,539.67879 L 59.463361,539.35547 L 60.918305,537.90052 L 61.403286,535.31394 L 60.756645,533.374 L 62.211599,532.40404 L 63.504883,532.24238 L 62.858241,530.4641 L 62.858241,526.09925 L 61.888278,525.12928 L 61.079966,526.58423 L 54.936843,526.58423 L 53.481892,525.29094 L 52.835247,521.41108 L 50.733651,517.85452 L 50.733651,516.88456 L 52.835247,516.07625 L 52.996908,513.97465 L 54.128536,512.84303 L 53.320231,512.35805 L 52.026941,512.84303 L 50.895313,510.09479 L 51.86528,505.08328 L 56.391794,501.85007 L 58.978369,500.23345 L 60.918305,496.51525 L 63.666554,495.22195 L 66.253132,496.35359 L 66.576453,498.77851 L 69.00137,498.45517 L 72.23459,496.03026 L 73.851205,496.67691 L 74.821167,497.32355 L 76.437782,497.32355 L 78.701041,496.03026 L 79.509354,491.6654 C 79.509354,491.6654 79.832675,488.75551 80.479317,488.27052 C 81.125959,487.78554 81.44928,487.30056 81.44928,487.30056 L 80.317657,485.36062 L 77.731077,486.16893 L 74.497847,486.97723 L 72.557911,486.49225 L 69.00137,484.71397 L 63.989875,484.55231 L 60.433324,480.83411 L 60.918305,476.95424 L 61.564957,474.52932 L 59.463361,472.75105 L 57.523423,469.03283 L 58.008406,468.22453 L 64.798177,467.73955 L 66.899773,467.73955 L 67.869736,468.70951 L 68.516388,468.70951 L 68.354728,467.0929 L 72.23459,466.44626 L 74.821167,466.76958 L 76.276122,467.90121 L 74.821167,470.00281 L 74.336186,471.45775 L 77.084435,473.07437 L 82.095932,474.85264 L 83.874208,473.88268 L 81.610951,469.51783 L 80.640977,466.2846 L 81.610951,465.47629 L 78.21606,463.53636 L 77.731077,462.40472 L 78.21606,460.78812 L 77.407756,456.90825 L 74.497847,452.22007 L 72.072929,448.01688 L 74.982838,446.07694 L 78.21606,446.07694 L 79.994336,446.72359 L 84.197528,446.56193 L 87.915733,443.00539 L 89.047366,439.93382 L 92.765578,437.5089 L 94.382182,438.47887 L 97.130421,437.83222 L 100.84863,435.73062 L 101.98027,435.56896 L 102.95023,436.37728 L 107.47674,436.21561 L 110.22498,433.14405 L 111.35661,433.14405 L 114.91316,435.56896 L 116.85309,437.67056 L 116.36811,438.80219 L 117.01475,439.93382 L 118.63137,438.31721 L 122.51124,438.64053 L 122.83456,442.35873 L 124.7745,443.81369 L 131.88759,444.46033 L 138.19238,448.66352 L 139.64732,447.69356 L 144.82049,450.28014 L 146.92208,449.6335 L 148.86202,448.82518 L 153.71185,450.76512 L 158.07671,453.67502 z M 42.973913,482.61238 L 45.075509,487.9472 L 44.913847,488.91717 L 42.003945,488.59384 L 40.225672,484.55231 L 38.447399,483.09737 L 36.02248,483.09737 L 35.86082,480.51078 L 37.639093,478.08586 L 38.770722,480.51078 L 40.225672,481.96573 L 42.973913,482.61238 z M 40.387333,516.07625 L 44.105542,516.88456 L 47.823749,517.85452 L 48.632056,518.8245 L 47.015444,522.5427 L 43.94388,522.38104 L 40.548995,518.8245 L 40.387333,516.07625 z M 19.694697,502.01173 L 20.826327,504.5983 L 21.957955,506.21492 L 20.826327,507.02322 L 18.72473,503.95166 L 18.72473,502.01173 L 19.694697,502.01173 z M 5.9534943,575.0826 L 9.3483796,572.81934 L 12.743265,571.84937 L 15.329845,572.17269 L 15.814828,573.7893 L 17.754763,574.27429 L 19.694697,572.33436 L 19.371375,570.71775 L 22.119616,570.0711 L 25.029518,572.65768 L 23.897889,574.43595 L 19.533037,575.56758 L 16.784795,575.0826 L 13.066588,573.95097 L 8.7017347,575.40592 L 7.0851227,575.72924 L 5.9534943,575.0826 z M 54.936843,570.55609 L 56.553455,572.49602 L 58.655048,570.87941 L 57.2001,569.58611 L 54.936843,570.55609 z M 57.846745,573.62764 L 58.978369,571.36439 L 61.079966,571.68771 L 60.271663,573.62764 L 57.846745,573.62764 z M 81.44928,571.68771 L 82.904234,573.46598 L 83.874208,572.33436 L 83.065895,570.39442 L 81.44928,571.68771 z M 90.17899,559.2398 L 91.310623,565.0596 L 94.220522,565.86791``L
Tip
该数据以及所有其他状态的数据都是从 http://en.wikipedia.org/wiki/File:Blank_US_Map.svg
下载的。去 http://commons.wikimedia.org
,在搜索条件中输入 svg map,可以找到很多类似的素材。
实现初始地图
您将从创建没有应用任何样式的初始地图开始。实际的路径元素将存储在 SQL 数据库中。您将创建数据库,添加一个State
表,并存储路径定义。然后,您将使用实体框架创建一个模型来提供状态数据。最后,您将创建一个显示地图的新视图,然后提供一个访问它的链接。
创建数据库
路径元素可能很长并且是静态的(阿拉斯加的形状不可能很快改变),因此它们可以存储在数据库中并由检索。NET 来呈现页面。您使用的 MVC 项目模板已经为数据库连接进行了配置。您需要创建State
表,并用适当的路径定义填充它。
EXERCISE 9-5. CREATING THE STATE TABLEThe database used by .NET is not actually created until the first time it is accessed. The easiest way to create the database is to register yourself. Press F5 to debug the application. Click the Register link in the header and enter a username and password. Once the registration is done, you can close the browser window, which will also stop the debugger. Start SQL Server Management Studio (SSMS). In the Connect to Server dialog box, enter the server name as (LocalDB)\MSSQLLocalDB and use Windows authentication, as shown in Figure 9-4. Click the Connect button to open the database.
图 9-4。
Connecting to SQL Server After connecting, you should see the database in Object Explorer, as shown in Figure 9-5.
图 9-5。
The database contents If you don’t have SQL Server Management Studio, you can access the database through Visual Studio. From the View menu, click the SQL Server Object Explorer link. You can then navigate to your database, as shown in Figure 9-6.
图 9-6。
Selecting the Chapter9 database In the download that is available at www.apress.com
, you’ll find a States.sql
file in the Chapter9 folder. Open this file in SSMS and click the Execute button. This will create the State
table using the following script and then populate it with a record for each state: CREATE TABLE State(
Id int identity NOT NULL,
StateCode nchar(10) NOT NULL,
StateName nvarchar(50) NOT NULL,
Path ntext NULL,
CONSTRAINT PK_State PRIMARY KEY CLUSTERED
(
Id ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
To verify the data was loaded correctly, open another query window using the New Query button. After connecting, select the Chapter9 database and execute this query: select * from State
You should see results similar to Figure 9-7.
图 9-7。
The contents of the State table Tip
如果您使用的是 Visual Studio 而不是 SSMS,可以在服务器资源管理器中右键单击数据库,然后单击“新建查询”链接。然后在查询窗口中选择章节 9 数据库。
创建模型
创建一个使用 SQL 表的模型是一项非常简单的任务。您将使用实体框架来创建一个模型类,该类提供指定表中的数据。
EXERCISE 9-6. CREATING AN ENTITY FRAMEWORK MODELFrom Solution Explorer, right-click the Models
folder and click the Add and New Item links. In the Add New Item dialog box, select the Class template and enter State.cs for the name. Add the following using
statements at the top of the file: using System.ComponentModel.DataAnnotations;
using Microsoft.Data.Entity;
Add the following properties to the State
class. These will map to the columns in the State
table that you just created. [Key] public int Id { get; set; }
public string StateCode { get; set; }
public string StateName { get; set; }
public string Path { get; set; }
Add the following class in this same State.cs
file, after the State
class definition. This class configures the Entity Framework so it can create a State
class for each record in the State
table. public class StateDbContext : ApplicationDbContext
{
public DbSet<State> States { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseSqlServer(Startup.Configuration
.Get("Data:DefaultConnection:ConnectionString"));
base.OnConfiguring(options);
}
}
Open the Startup.cs
class and add the static
attribute to the Configuration
property, as shown here in bold: public
static
IConfiguration Configuration { get; set; }
You now have a model that you can use to provide the state details for the map. Rebuild the project. This will make the model available for linking to a view.
创建地图视图
已经定义了一个模型,现在您将创建显示模型元素的视图。
EXERCISE 9-7. CREATING THE MAP VIEWRight-click the Views\Home
folder and click the Add and New Items links. In the Add New Item dialog box, select the MVC View Page template, enter the name Map.cshtml, and click the Add button. Replace the entire contents with the following. This will set up the view to use the State
model and enable the code to access its properties. @model IEnumerable<
Chapter 9
.Models.State>
@using
Chapter 9
.Models
Add the following code, which defines an svg
element just like you did earlier. It then uses a foreach
loop to create a path
element for each State
defined in the model. Notice that it is storing the StateCode
column in the id
attribute and the StateName
column in the class
attribute. <svg xmlns:svg="
http://www.w3.org/2000/svg
" version="1.2"
width="959" height="593" id="map">
@foreach (State s in Model)
{
<path id="@s.StateCode.Trim()" class="@s.StateName.Trim()" d="@s.Path" />
}
</svg>
Now you’ll need to implement a controller action that will display the map view. Open the HomeController.cs
class and add the following namespace: using Chapter
9
.Models;
Then add the following method to the HomeController
class. This executes a query to extract all the records from the State
table and provide it to the view. public ActionResult Map()
{
StateDbContext DC = new StateDbContext();
var states = from s in DC.States select s;
return View(states);
}
Now you’ll add a link that will display the map page. Go to the _Layout.cshtml
file in the Views\Shared
folder and add the following line after the existing asp-action
statements: <li><a asp-controller="Home" asp-action="Map">Map</a></li>
Press F5 to build and run the application. Click the Map link. You should see a map of the United States, and all of the states are filled with the default color (black).
设置状态元素的样式
现在所有的机械工作都完成了,你可以享受一下设计path
元素的乐趣。正如我前面用雪人图像演示的那样,每个元素都可以使用特殊的样式表进行样式化。您还可以使用 JavaScript 动态地设计它们的样式。我将向您展示如何使用纯色填充、渐变和背景图像来格式化每个元素。
使用基本填充颜色
Note
在这一章中,你将使用不同的颜色来设计不同的状态。在本书的印刷版本中,其中一些颜色在转换为灰度时可能显示不佳。您可能希望完成练习或下载项目,以查看应用样式的结果。
您将从添加一些简单的填充规则开始。使用一个简单的元素选择器,将stroke
颜色设置为黑色,将fill
颜色设置为卡其色。然后,为了增加一些变化并演示如何使用属性选择器,您将根据州代码更改填充颜色。id
属性包含两个字母的州代码,class
属性包含州名。使用id
属性的第一个字母,你将如下设置填充颜色:
- 甲:红色
- n:黄色
- 男:绿色
- 姜敏赫
- 奥:紫色
- I:橙色
在foreach
循环之前的svg
元素内输入清单 9-3 中所示的style
元素。
Listing 9-3. Adding Basic Fill Definitions
<style type="text/css" >
path
{
stroke: black;
fill: khaki;
}
path[id^="A"]
{
fill: red;
}
path[id^="N"]
{
fill: yellow;
}
path[id^="M"]
{
fill: green;
}
path[id^="C"]
{
fill: blue;
}
path[id^="O"]
{
fill: purple;
}
path[id^="I"]
{
fill: orange;
}
</style>
刷新你的浏览器,地图现在应该看起来如图 9-8 所示。
图 9-8。
The map with some basic styling
当您在地图上移动鼠标时,最好突出显示鼠标当前指向的州。将以下规则添加到现有规则之后的style
元素中:
path:hover
{
opacity: .5;
}
使用渐变填充
您可以对 SVG 元素使用渐变填充,但是它们的实现方式不同于典型的 HTML 元素。您首先必须定义渐变,然后使用 URL 引用它。
在svg
元素内部和style
元素之前添加以下defs
元素:
<defs>
<linearGradient id="blueGradient"
x1="0%" y1="0%"
x2="100%" y2="100%"
spreadMethod="pad">
<stop offset="0%" stop-color="#ffffff" stop-opacity="1"/>
<stop offset="50%" stop-color="#6699cc" stop-opacity="1"/>
<stop offset="100%" stop-color="#4466aa" stop-opacity="1"/>
</linearGradient>
</defs>
defs
元素用于定义文档中稍后可以引用的内容。在被实际引用之前,它不会做任何事情。在这里,您定义了一个linearGradient
元素,并赋予它id blueGradient
。您将使用id
属性来引用它。
这些属性与你在《??》第四章中使用的渐变不同,但基本上完成了同样的事情。x1
、y1
、x2
和y2
属性定义了一个指定渐变方向的向量。在这种情况下,它将从左上角开始,到右下角。这指定了三个颜色值,用于定义起点、中点和终点的渐变颜色。
现在在 style 元素的末尾添加下面的path
规则。这将使用怀俄明州的新渐变。
path[id="WY"]
{
fill: url(#blueGradient);
}
刷新浏览器,你应该会看到怀俄明州的渐变填充,如图 9-9 所示。
图 9-9。
Using a gradient fill
使用背景图像
您也可以使用图像文件作为形状背景。您需要首先在defs
元素中将它定义为pattern
,然后像处理渐变一样引用它。在本练习中,您将使用德克萨斯州的州旗图像,并将其作为该州的背景。
EXERCISE 9-8. USING A BACKGROUND IMAGEIn the source code download for Chapter 9 there is a TX_Flag.jpg
file; copy this to the wwwroot\images
folder in Solution Explorer. Add the following code to the defs
element to define the background image. This specifies that the pattern should use the TX_Flag.jpg
image file and stretch it to 377 x 226 pixels. This will make it large enough to cover the path element without needing to repeat. <pattern id="TXflag" patternUnits="objectBoundingBox" width="1" height="1">
<image xlink:href="∼/img/TX_Flag.jpg" x="0" y="0"
width="377" height="226" />
</pattern>
Add the following path rule, which will use the new pattern for the state of Texas. path[id="TX"]
{
fill: url(#TXflag);
}
Save your changes and refresh the browser. You should see the background image, as shown in Figure 9-10.
图 9-10。
Using a background image
因为这是关于 SVG 的一章,所以我觉得使用位图图像有点滑稽。当图像被拉伸时,您可以看到图像质量下降。德克萨斯州的州旗是最容易用 SVG 绘制的州旗之一,但是我想演示位图图像可以在 SVG 定义中使用。但是为了记录起见,清单 9-4 显示了用 SVG 表示的标志(这是从我前面提到的同一个 Wikimedia Commons 站点下载的,并稍微进行了重新格式化)。
Listing 9-4. The Texas State Flag in SVG
<rect width="1080" height="720" fill="#fff"/>
<rect y="360" width="1080" height="360" fill="#bf0a30"/>
<rect width="360" height="720" fill="#002868"/>
<g transform="translate(180,360)" fill="#fff">
<g id="c">
<path id="t" d="M 0,-135 v 135 h 67.5"
transform="rotate(18 0,-135)"/>
<use xlink:href="#t" transform="scale(-1,1)"/>
</g>
<use xlink:href="#c" transform="rotate(72)"/>
<use xlink:href="#c" transform="rotate(144)"/>
<use xlink:href="#c" transform="rotate(216)"/>
<use xlink:href="#c" transform="rotate(288)"/>
</g>
请注意,组元素g
用于定义一条路径。这是旋转五个不同的角度,创造一个五角星。
用 JavaScript 改变样式
这种应用的主要用途之一是根据一些外部数据动态地设计每个元素的样式。例如,您可能希望突出显示销售地点所在的州。或者,您可能希望根据某种类型的人口统计数据(如人口)来设置颜色。到目前为止,您只使用了静态样式,但是您可以使用 JavaScript 轻松地设置样式。
在这个例子中,您将首先使用 JavaScript 将所有path
元素上的fill
属性设置为卡其色。这将替换设置默认颜色的 CSS 属性。这段代码将为弗吉尼亚州设置path
元素的填充颜色。在实际的应用中,您通常会根据外部数据来定义样式。
这个练习还将向您展示如何使用 JavaScript 来响应onmouseover
和onmouseout
事件。您将替换path:hover
规则,并使用这些事件处理程序来完成。
EXERCISE 9-9. ADJUSTING STYLES USING JAVASCRIPTAdd the following script element in the map.cshtml
file, just before the defs
element: <script type="text/javascript">
function adjustStates() {
var paths = document.getElementsByTagName("path");
for (var i = 0; i < paths.length; i++) {
paths[i].setAttributeNS(null, "fill", "khaki");
}
var path = document.getElementById("VA");
path.setAttributeNS(null, "fill", "teal");
}
</script>
In the svg
element, add the onload
attribute using the code shown in bold: <svg xmlns:svg="
http://www.w3.org/2000/svg
" version="1.2"
width="959" height="593" id="map"
onload="adjustStates()"
>
In the style
element, remove the default khaki fill like this: path
{
stroke: black;
/*
fill: khaki;
*/
}
Refresh the browser, and Virginia should no longer use the default color, as shown in Figure 9-11.
图 9-11。
Virginia styled with JavaScript Now you’ll also use JavaScript to implement the hover
style. You can use the event.target
property to get the path
element that triggered the event. You can then determine the state code by accessing its id
attribute. Add the following methods to the existing script
element: function hoverState(e) {
var event = e || window.event;
var state = event.target.getAttributeNS(null, "id");
var path = document.getElementById(state);
path.setAttributeNS(null, "fill-opacity", "0.5");
}
function unhoverState(e) {
var event = e || window.event;
var state = event.target.getAttributeNS(null, "id");
var path = document.getElementById(state);
path.setAttributeNS(null, "fill-opacity", "1.0");
}
Then bind the mouseover
and mouseout
event handlers by adding the code shown in bold to the adjustStates()
function. This uses the addEventListener()
method to bind hoverState()
and unhoverState()
event handlers to each path
element. function adjustStates() {
var paths = document.getElementsByTagName("path");
for (var i = 0; i < paths.length; i++) {
paths[i].setAttributeNS(null, "fill", "khaki");
paths[i].addEventListener("mouseover", hoverState, true);
paths[i].addEventListener("mouseout", unhoverState, true);
}
var path = document.getElementById("VA");
path.setAttributeNS(null, "fill", "teal");
}
注意在 Internet Explorer 中,event
对象不会传递给事件处理程序。相反,它通过全局window.event
属性变得可用。通过如下设置事件变量,可以对事件处理程序进行编码,使其适用于任何一种模型:var event = e || window.event
。这将使用传入的对象,如果可用,如果不可用,将使用全局window.event
对象。然而,要做到这一点,您必须使用addEventListener()
方法注册事件处理程序。您不能简单地设置onmouseover
属性。
Remove the path:hover
style rule like this: /*
path:hover
{
opacity: .5;
}
*/
Save your changes and refresh the browser. As you move the mouse around, the states should highlight just like they did with the path:hover
style.
添加动画
像这样的地图的典型应用将允许用户选择一个区域,并作为选择的结果发生一些事情。该页面将根据所选择的项目显示一些信息。为了演示这一点,您将在用户单击一个状态时添加一些动画。这个例子将使用 3D 转换和 Opera,所以我将使用–webkit-
供应商前缀。
我在第四章给你看的 CSS 动画对 SVG 元素不起作用。相反,您将使用 JavaScript 实现动画。当一个状态被选中时,您将首先复制所选的元素。然后你将使用一个计时器来逐渐改变它的旋转角度。你需要做一个拷贝,这样当图像旋转时就不会在地图上留下一个洞。此外,新元素将位于所有其他元素之上,因此您不必担心它会被其他元素隐藏。
一旦元素的副本完成动画,您就可以将它从文档中移除。然后您将显示一个警告,显示所选择的path
的州代码和州名。
EXERCISE 9-10. ADDING ANIMATIONBecause this uses a 3D transform, you’ll need to set some of the transform properties on the path
elements. Add the following rule to the style
element: path
{
-webkit-perspective: 200px;
-webkit-transform-style: preserve-3d;
}
Then add the code shown in Listing 9-5 to the script
element.
清单 9-5。添加支持动画的功能
// Setup some global variables
var timer;
var stateCode;
var stateName;
var animate;
var angle;
function selectState(e) {
var event = e || window.event;
// Get the state code and state name
stateCode = event.target.getAttributeNS(null, "id");
stateName = event.target.getAttributeNS(null, "class");
// Get the selected path element and then make a copy of it
var path = document.getElementById(stateCode);
animate = path.cloneNode(false);
// Set some display properties and add the copy to the document
animate.setAttributeNS(null, "fill-opacity", "1.0");
animate.setAttributeNS(null, "stroke-width", "3");
document.getElementById("map").appendChild(animate);
angle = 0;
// Setup a timer to run every 10 msec
timer = setInterval(function () { animateState(); }, 10);
}
function animateState() {
angle += 1;
// If we've rotated 360 degress, stop the timer, destroy the copy
// of the element, and show an alert
if (angle > 360) {
clearInterval(timer);
animate.setAttributeNS(null, "visibility", "hidden");
var old = document.getElementById("map").removeChild(animate);
alert(stateCode + " - " + stateName);
return;
}
// Change the image rotation
animate.style.webkitTransform = "rotateY(" + Math.round(angle) + "deg)";
}
selectState()
函数从选中的path
元素中获取状态代码和状态名。然后它获取path
元素并使用它的cloneNode(
方法来复制它。因为鼠标当前位于所选路径上,所以它的不透明度将设置为 50%。因此,这段代码将副本的不透明度更改为 100%。它还设置了描边宽度,以便为该元素提供更宽的边框。然后将副本添加到文档中,并启动计时器来播放动画。
每隔十毫秒,调用一次animateState()
函数,增加角度并重新绘制图像。如果旋转达到了 360 度,这个方法取消计时器并删除path
元素的副本。它还会发出显示州代码和州名称的警报。
Add another event handler by adding the code shown in bold to the adjustStates()
function. This will call the selectState()
method when the user clicks a path
element. function adjustStates() {
var paths = document.getElementsByTagName("path");
for (var i = 0; i < paths.length; i++) {
paths[i].setAttributeNS(null, "fill", "khaki");
paths[i].addEventListener("mouseover", hoverState, true);
paths[i].addEventListener("mouseout", unhoverState, true);
paths[i].addEventListener("click", selectState, true);
}
var path = document.getElementById("VA");
path.setAttributeNS(null, "fill", "teal");
}
Change the debug browser to Opera. Press F5 to start the application and go to the map page. Click a state and you should see it fly off the page, as shown in Figure 9-12.
图 9-12。
Animating the selected state The image will then fly back into place, and an alert will appear, as shown in Figure 9-13.
图 9-13。
The alert showing the name of the selected state
摘要
在这一章中,我用几个相当简单的应用介绍了 SVG。SVG 图像由多个元素组成,这些元素可以是简单的元素,如直线、圆形和矩形,也可以是更复杂的选项,如多边形和路径。SVG 的关键特性是每个单独的元素都可以静态和动态地独立样式化。这实现了更好的控制和交互。此外,由于图像基于表达式,因此可以在不影响图像质量的情况下缩放图像。
在本章的练习中,您执行了以下操作:
- 使用简单的几何图形设计图像
- 创建了一个独立的
.svg
图像文件 - 将地图显示为
path
元素的集合 - 在 SVG 元素上实现动画
您还使用 Entity Framework 实现了一个访问 SQL Server 数据库的模型类,然后设计了一个使用模型元素创建 SVG 图像的视图。
在下一章,我将向您展示如何使用canvas
元素在 HTML5 中构建图形元素。