面向安卓的 HTML5 和 JavaScript 学习手册(二)

原文:Learn HTML5 and JavaScript for Android

协议:CC BY-NC-SA 4.0

五、移动 CSS3

移动开发最令人兴奋的一个方面是在最新的智能手机上通过浏览器支持 CSS3。在 CSS3 之前,我们依赖于使用 JavaScript 来提供令人眼花缭乱的动画和过渡,只需将样式应用于 DOM 元素,如父元素中的最后一个元素或交替的表格行。

在这一章中,你将学习一些 CSS3 的新特性,比如动画和过渡。您将了解 CSS3 如何提供与最基本的动画概念相似的特性,称为关键帧

您将学习如何在移动 web 应用中导入新的字体,这将为您的受众提供更广泛的字体集。您还将了解 CSS3 的一些关键特性,如文本阴影、选择器、渐变和新的边框属性。此外,您将简要地接触 CSS 媒体查询,这将帮助您应用基于屏幕分辨率和像素密度的样式。

最后,您将看到语法上非常棒的样式表(SASS)形式的 CSS 预编译器的强大功能,通过它您将了解如何简化您的 CSS 工作流并减少编码时间。

特定于供应商的属性

在撰写本文时,许多 CSS3 属性,如border-radiusopacity,已经被标准化。然而,浏览器制造商可以开发他们自己的新 CSS 属性的实现。为了避免语法差异引起的冲突,尚未标准化的新 CSS 属性通常会以供应商前缀开头。例如,在border-radius的标准化之前,在 CSS3 中有几种可能的方法来声明它。

  • -moz-border-radius
  • -o-border-radius
  • -webkit-border-radius
  • border-radius

正如你所看到的,这个列表中的最后一个声明是现在的标准化版本,对于基于 Gecko 的浏览器(Firefox),特定于供应商的实现以-moz-为前缀;对于 Opera,以-o-为前缀;对于基于 Webkit 的浏览器(Chrome、Android 浏览器、Dolphin),以-webkit-为前缀。

还有更多特定于厂商的前缀,但一般来说,对于 Android 来说,-moz--o--webkit-应该足够了。始终包括标准实现是很重要的。

有一些方法可以避免在需要时声明所有四个 CSS 属性,我将在本章后面的“CSS 预编译器(SASS)”一节中对此进行解释。

CSS 动画和过渡

CSS3 引入了 DOM 元素的 CSS 过渡和转换。您可以使用这些来代替传统的动画 DOM 元素的方法,方法是使用 JavaScript 中的计时器来操纵它们的 CSS 属性。你可能会问自己,为什么我要用 CSS 做动画而不是 JavaScript?当然,CSS 应该用于样式,JavaScript 应该用于交互。事实是,通过使用 CSS3 制作动画,您可以将大量经常使用 JavaScript 传递给设备 CPU 的繁重工作卸载到设备的 GPU(如果它有 GPU 的话)。这可以使动画更加流畅。

过渡

CSS 过渡允许您在两种 CSS 样式之间创建过渡。您可以通过创建一个 CSS 样式并向其添加另一个样式来调用转换。CSS 转换将处理两种状态之间的变化。

在 CSS3 中创建一个过渡非常简单。首先你创建你的div元素。

<div class="test"></div>

接下来,为 CSS 元素创建一个样式。在这个样式中,将widthheight设置为100px,将position设置为absolute,因为您将把元素移动到页面上的不同位置。您也可以通过将border-radius设置为50px来将正方形变成圆形。您还明确地将topleft位置设置为0px,将background-color位置设置为blue

.test {    width: 100px;    height: 100px;    position: absolute;    top: 0px;    left: 0px;    border-radius: 50px;    background-color: blue; }

这将呈现出类似于图 5-1 中所示的图像。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-1。 渲染一个 CSS 圆圈

现在你需要为球设置下一个状态。这就像创建一个具有不同属性的新样式一样简单。

.second-position {    left: 50%;    background-color: yellow; }

正如您所看到的,新的属性将圆设置为位于屏幕的中间,其background-coloryellow。将这个 CSS 类添加到测试中div

<div class="test second-position"></div>

现在你会看到一个类似于图 5-2 中所示的屏幕。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-2。 测试分区最终位置

最后要做的是给.test类添加一个过渡。这将规定应该如何过渡和过渡什么属性,以及过渡的时间。

过渡属性目前是特定于供应商的,并且,像往常一样,包含所有供应商属性是一种好的做法。下面的代码将为test元素的所有属性创建一个转换。

.test {    width: 100px;    height: 100px;    position: absolute;    top: 0px;    left: 0px;    border-radius: 50px;    background-color: blue;    transition: all 2s;    -moz-transition: all 2s;    -webkit-transition: all 2s;    -o-transition: all 2s; }

为了让转换工作,您需要动态地将second-position类添加到元素中。您可以使用 JavaScript 来做到这一点。下面的脚本将搜索类名为test的第一个元素,并将second-position类追加到其中。您应该将它放在测试元素的下面,如图所示。

`

 

`

当您将页面加载到移动设备上时,圆圈应该会在屏幕的中心出现,并逐渐变为黄色。

还可以通过指定属性、持续时间、计时函数和延迟来控制应该转换哪些属性,如下面的示例所示。

[-moz-|-o-|-webkit-]transition: property transition-duration transition-timing- function transition-delay [, property duration timing-function delay]

使用这种简单的方法,您可以指定任意多的属性来制作动画。表 5-1 列出了可能的值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

例如,您可能想要在颜色过渡开始五秒钟后开始过渡左侧位置,并减缓左侧位置。在这种情况下,您可以使用下面的代码。

.test {    width: 100px;    height: 100px;    position: absolute;    top: 0px;    left: 0px;    border-radius: 50px;    background-color: blue;    transition: left 5s ease-out 5s, background-color 5s ease 0s;    -moz-transition: left 5s ease-out 5s, background-color 5s ease 0s;    -webkit-transition: left 5s ease-out 5s, background-color 5s ease 0s;    -o-transition: left 5s ease-out 5s, background-color 5s ease 0s; }

动画

有时,您可能希望对动画有更多的控制。例如,如果您可以从一个位置到另一个位置制作动画,同时在动画中的某些点更改某些 CSS 属性,这不是很好吗?这被称为关键帧。如果您有 flash 动画方面的经验,您会更好地了解如何在 Flash 时间轴中对对象进行重大更改,并在它们之间创建补间动画。关键帧现在在 CSS 中可用。与往常一样,在撰写本文时,这是特定于供应商的,因此为了兼容,请使用所有可用的供应商。在这个演示中,您将在屏幕上制作一个圆圈的动画,并使其弹跳。

在开始创建弹跳球动画之前,请看图 5-3 中所示的动画。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-3。 期望的动画序列

从图 5-3 中的动画序列可以看出,其目的是模仿一个弹跳的球。CSS 关键帧功能允许您指定您希望以百分比增量制作动画的 CSS 样式。我们可以使用图 5-3 中所示的信息来创建关键帧规则。

首先,使用@keyframes规则和关键帧的名称创建一个新的关键帧定义,如下面的代码所示。

@keyframes bouncyball { }

接下来,使用百分比标记和 CSS 样式指定动画附加元素的开始位置。

@keyframes bouncyball {    0% { top: 0px; left: 0px; } }

这里,您已经指定了关联的元素应该从左上角开始。

接下来,指定动画中的各个分段。以图 5-3 为指导,有 0%、12.5%、25%、37.5%、50%、62.5%、75%、87.5%、100%的 CSS 规则。

@keyframes bouncyball {    0% { bottom: 100%; left: 0px; }    12.5% { bottom: 0px; left: 12.5%; }    25% { bottom: 50%; left: 25%; }    37.5% { bottom: 0px; left: 37.5%; }    50% { bottom: 25%; left: 50%; }    62.5% { bottom: 0px; left: 62.5% }    75% { bottom: 12.5%; left: 75% }    87.5% { bottom: 0px; left: 87.5% }    100% { bottom: 0px; left: 100% } }

现在是时候为你的球创建一个新的 CSS 规则了。下面的代码将从一个正方形创建一个圆,并将动画应用到元素。

.ball {    background: black;    width: 100px;    height: 100px;    position: absolute;    border-radius: 50px;    animation: bouncyball 2s ease-in-out;    -moz-animation: bouncyball 2s ease-in-out;    -webkit-animation: bouncyball 2s ease-in-out; }

这个例子中的动画 CSS 属性是用速记写的,并且同样是特定于供应商的。表 5-2 列出了动画属性按顺序取的参数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

当您将动画加载到设备上时,它应该会自动播放。它不是一个非常平滑的弹跳球,但这只是为了证明 CSS 可以成为动画的一个非常强大的工具,不费吹灰之力。您还可以使用 JavaScript 来动态编写 CSS 动画脚本。对于更密集的动画,也有 HTML5 画布。

CSS3 新特性

除了动画、转换和过渡,CSS3 规范还有几个值得注意的新特性。在本节中,您将学习如何使用@font-face通过导入字体文件将新的字体引入到您的移动 web 应用中。

您还将学习如何使用几个新的边框样式元素,比如border-radius(这将允许您在元素上创建圆形边框,而不需要额外的标记或 JavaScript)、box-shadowborder-image。您还将学习如何根据文档的大小创建可缩放的 CSS3 渐变,而不需要重复的背景图像并节省带宽。

这一节还介绍了几个新的 CSS3 选择器,它们使基于状态和层次的 DOM 元素样式化变得更加容易。

@font-face

@font-face是 CSS3 的一项新的标准化功能,允许您使用网页安全字体列表之外的字体(如 Arial 和 Times New Roman 等字体,它们通常在大多数设备上都能找到)。这给了你更多的自由去创造你的字体。在@font-face之前,使用非网页安全字体的非标准化方法包括 cufon(一种利用 Canvas 和 SVG 的技术)、sIFR(虽然现在不再保留,但 sIFR 使用了 Flash)和标准 CSS 图像替换(一种利用预先渲染的文本图像作为屏幕上应显示文本的背景图像的方法)。

重要的是要记住,虽然你对你使用的字体有完全的自由,但你必须确保字体确实与你的内容和受众相关。同样重要的是要记住,有些字体适用于标题,但不适用于正文,因为在较小的字体下会变得不可读(见图 5-4 )。例如,对于正文来说,漫画 Sans 是一个糟糕的字体选择。

漫画无字体是独一无二的:在全世界都被使用,它是一种不想成为铅字的字体。它看起来很普通,是手写的,对于我们认为有趣和自由的事情来说是完美的。对玩具店的遮阳篷来说很好,但对新闻网站、墓碑和救护车的侧面来说就不那么好了。”

www.bbc.co.uk/news/magazine-11582548

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-4。 Hello World with a web 字体

在网络上使用@font-face有几个注意事项。最大的问题是关于许可。为了在浏览器中呈现字体,字体必须是可下载的。当使用可能附有许可证的购买字体时,这可能会带来潜在的问题。在您的项目中使用 web 字体之前,您应该检查许可证是否允许使用@font-face下载或交付字体。如果你不能使用你想要的字体,你可以使用谷歌字体目录,使用开源网络字体中的任何字体(见图 5-5 )。谷歌还提供了一种便捷的方法来嵌入托管在其服务器上的网络字体。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-5。 谷歌网页字体

网络字体的第二个警告是它们的文件大小。使用单一的 web 字体不会对加载时间产生太大的影响,但是如果您使用多种活动的 web 字体或具有多种字体样式的 web 字体,您可能会遇到页面加载时间缓慢的问题。因此,只包含 web 应用所需的字符集和字体样式很重要,这样可以减少字体的负载。

Android 浏览器足够智能,只在页面上实际使用时加载一个字体族。例如,如果您定义一个h4元素来使用 web 字体,那么除非该元素存在于页面上,否则 web 字体不会下载,即使 CSS 类中有该字体的定义。

在撰写本文时,Android 浏览器仅支持 TTF 和 SVG 字体,这是两种最大的未压缩字体格式。其他格式包括 EOT 和 WOFF。当声明@font-face支持其他浏览器和时,包含所有字体格式是很重要的,这样当 Android 浏览器开始支持其他格式时,它们可以被加载而不需要改变你的代码。顺序应该是大小优先(从最小的开始),因为 Android 浏览器将选择第一个可用的格式来使用。万一 Android 可能已经包含了你想在设备上使用的字体,你也可以先指定字体的本地名称。如果找到了该字体,就不需要从网上加载和下载该字体。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 5-6。**Android 版谷歌浏览器中的字体负载

@font-face声明用于声明新字体。CSS 文档中的每个新字体声明都使用@font-face {}

@font-face {    font-family: "MyFont";    src: url(’/path/to/my/font.otf’); }

从这里开始,你可以定义font-family来引用 CSS 中的字体。最后,声明字体的来源。这可以是服务器上的路径,也可以是远程服务器上的字体。

然后,您可以使用传统方法在 CSS 中的任何地方自由使用该字体系列。

h1 {    font-family: "MyFont"; }

下面的代码示例展示了如何使用@font-face的完整声明。

@font-face {    font-family: "My Font With Spaces";    src: local("My Font With Spaces"),       url("/path/to/fonts/my-font-with-spaces.woff") format("woff"),       url("/path/to/fonts/my-font-with-spaces.eot") format("embedded-opentype"),       url("/path/to/fonts/my-font-with-spaces.svg") format("svg"),       url("/path/to/fonts/my-font-with-spaces.ttf") format("truetype");    font-style: normal;    font-weight: normal; }

文本阴影和文本描边

允许你使用 CSS 在文本后面创建不同数量的阴影。text-stroke允许您在文本的内侧边缘绘制轮廓。text-shadowtext-stroke也可以用在@font-face字体上。

要在文本周围创建一个基本的阴影,只需在 CSS 中添加text-shadow属性。属性接受下列值和格式。

text-shadow: horizontal-offset vertical-offset blur color;

例如,下面的 CSS 样式将产生类似于图 5-7 所示的结果。

h1 {    text-shadow: 10px 10px 10px #000000; }

也可以使用负数表示阴影的位置。这将使水平偏移的阴影向左偏移,垂直偏移的阴影向上偏移。

通过以像素为单位指定笔画宽度及其颜色来定义text-stroke属性。text-stroke属性接受以下格式的值。

text-stroke: width color;

它的用法与text-shadow非常相似,如下面的代码片段所示。

h1 {    text-stroke: 1px #000000; } 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-7。 文字阴影效果(左)和笔画效果(右)

选择器

选择器允许您使用 CSS 对 DOM 元素应用样式。通常有两种类型的选择器:常规的 CSS 类和元素和 ID 选择器,如.elementclass#elementidelement。还有赝选者,比如:link:visited:hover:active

CSS3 引入了几个新的选择器,允许您根据属性值、输入状态和元素在 DOM 中的位置来选择元素。

有用的表单选择器

表单选择器将使您能够根据表单输入的状态或类型来设计它们的样式。在 CSS3 之前,您需要手动将类分配给文本、复选框、单选按钮、提交字段和按钮,因为没有明确的方法将样式应用于这些字段。这是因为它们都是<input />元素,所以任何为输入元素创建全局样式的尝试都会使所有字段类型的样式完全相同。

使用 CSS3,您现在可以使用新的属性选择器将样式应用于特定的输入类型。表 5-3 给出了属性选择器格式。

您可以更改属性和值以匹配任何元素。例如,要选择表单中的所有文本字段,可以使用下面的 CSS。

input[type="text"] {    border: 1px solid #000000; }

这将在所有文本元素周围创建一个单像素的边框。

您也可以使用表 5-4 中给出的伪选择器选择所有被选中、启用或禁用的元素。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

你可以组合和链接 CSS 选择器。例如,如果您想要选择所有被禁用的文本表单字段,您可以使用下面的 CSS。

input[type="text"]:disabled {    opacity: 0.5; }

替代 JavaScript 的有用选择器

使用 JavaScript 选择另一个元素的最后一个子元素,并对其应用一个类来移除浮动元素的边距或填充,这是很常见的。如果您有一个多行的三列布局,您也可以使用 JavaScript 选择元素中的每三个子元素,并对其应用类。有了 CSS3,您不再需要这样做。

您可以使用:last-child伪类选择元素的最后一个子元素。例如,如果您想选择一个ul中的最后一个li,您可以使用下面的 CSS。

ul li:last-child {    margin-right: 0px; }

您也可以做同样的事情来选择任何元素的第 n 个子元素。使用:nth-child:nth-last-child:nth-of-type:nth-last-of-type可以根据下级指标和下级类型及指标进行选择,如表 5-5 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

例如,如果您想选择一个ul中的每三个li并使文本变成灰色,您可以使用下面的 CSS 样式。

ul li:nth-child(3) {    color: #CCCCCC; }

正如您所看到的,有许多新的 CSS 选择器可以使您的移动 web 应用的样式更加简单。还有更高级的选择器可供选择。

渐变

CSS3 渐变允许您向元素添加背景渐变,而无需使用重复的图像。这可以节省带宽,并允许您根据屏幕大小和方向创建可缩放的渐变背景。目前,CSS3 渐变是特定于供应商的。每个供应商似乎都有自己的方式来产生 CSS3 渐变。这一节将重点介绍 WebKit 的实现。

有两种类型的渐变你可以在 CSS3 中使用:线性和径向。线性渐变将从屏幕的一侧流向另一侧,径向渐变将从中心点向外发散,如图图 5-8 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-8。 线性(左)和径向(右)渐变

线性渐变

线性渐变具有以下语法,必须使用background属性作为背景应用。

.box {    background: -webkit-linear-gradient(start, start-color, end-color); }

您可以将起始位置指定为单个位置(左、上、右、下)或这些位置的组合。例如,要从左下角开始线性渐变,可以使用下面的代码。

.box {    background: -webkit-linear-gradient(bottom left, green, red); }

图 5-9 显示了这个代码片段的结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-9。 从左下角开始的线性渐变

您也可以用度数来指定渐变的起点。例如,将起点设置为45deg将与将起点设置为bottom left具有相同的结果。

.box {    background: -webkit-linear-gradient(45deg, green, red); }

除了标准的双色渐变,您还可以在渐变背景中使用多种颜色。您只需在位置后指定更多颜色。例如,下面的代码将使用线性渐变创建一面爱尔兰国旗,如图 5-10 所示。

.box {    background: -webkit-linear-gradient(left, green, white, orange); } 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-10。 使用 CSS3 渐变创建爱尔兰国旗

CSS3 渐变也支持色标。色标允许您指定渐变在渐变线上的停止位置。例如,你可以在 CSS3 中创建一个真正的爱尔兰国旗,没有任何渐变,使用停止。为了做到这一点,您应该指定绿色将在元素的 33%(三分之一)处停止,然后白色将在 33%处开始并在 33%处停止。这将在绿色和白色之间创建一条直接的颜色线,而不是渐变。从这里开始,您将使用另一种白色,并指定在屏幕的 66%停止;最后是橙色,它将在 66%处停止,形成另一条颜色线。

代码如下所示,你可以在图 5-11 中看到结果。

.box {    background: -webkit-linear-gradient(left, green 33.3%, white 33.3%, white 66.6%, orange 66.6%); } 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-11。 使用 CSS3 渐变颜色创建爱尔兰国旗

径向梯度

径向渐变比线性渐变稍微复杂一些。您可以指定渐变的起始位置及其形状。径向渐变具有以下语法。

.box {    background: -webkit-radial-gradient(center, [circle|elipse]    [closest-side|closest-corner|farthest-side|farthest-corner|contain|cover],    start-color, stop-color); }

您可以以像素为单位指定中心位置,或者指定左侧和顶部位置的百分比。第二个参数接受 shape 关键字,它可以是圆形或椭圆形。第二个参数也接受一个 size 关键字,它们是closest-sideclosest-cornerfarthest-sidefarthest-cornercontaincover。最后,渐变还接受十六进制、关键字、RGB 或 RGBA 颜色作为开始和结束颜色。

例如,你可以使用以下代码用 CSS3 制作一面日本国旗,其结果可以在图 5-12 中看到。

.box {    background: -webkit-radial-gradient(center, circle contain, red, white); } 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-12。 带有放射状渐变的日本国旗

您可以使用与线性渐变示例中相同的颜色停止技术来删除径向渐变上的渐变,并创建一个完整的圆。您可以使用以下代码来实现这一点,图 5-13 显示了结果。

.box {    background: -webkit-radial-gradient(center, circle contain, #C00C00 70%, white 70%); } 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-13。 移除径向渐变的日本国旗

边框

有了 CSS3,你现在可以应用新的边框样式,比如border-radiusbox-shadow

边界半径

属性允许你在元素上创建圆角。在具备这种能力之前,为了制作具有圆角的灵活元素,您可以使用几个图像来模拟圆角,或者使用 JavaScript 助手,如 Curvy Corners,它将生成大量的div元素,并将它们定位以模拟圆角。

border-radius允许你使用 CSS3 生成圆角,无需任何图像或 JavaScript 的额外帮助。它现在是 CSS3 规范的一部分,使用下面的 CSS 可以创建一个圆角边框。

.box {    border-radius: 10px; }

这将创建一个半径为 10 像素的边界。你也可以使用下面的语法指定你的元素的每个角的半径,其结果你可以在图 5-14 中看到。

.box {    border: 1px solid #000000;    border-top-left-radius: 5px;    border-top-right-radius: 10px;    border-bottom-left-radius: 15px;    border-bottom-right-radius: 20px;    width: 100px;    height: 100px; }   外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 5-14。**边境半径

箱形阴影

属性允许你在块级元素上创建阴影。当设计需要不同大小的投影时,这很方便。现在,您可以使用几行 CSS 代码,而不是使用几个图像来创建不同的阴影样式。

box-shadow属性具有以下格式。

box-shadow: horizontal-offset vertical-offset blur spread color inset;

horizontal-offsetvertical-offset属性以像素为单位指定阴影的位置,blur以像素为单位设置模糊量,spread以像素为单位设置阴影扩散,color设置阴影的颜色,inset设置阴影应该在元素的内部还是外部。inset属性的值为 inset 或 nothing。

例如,下面的 CSS 将产生类似于图 5-15 的结果。

.box {    width: 100px;    height: 100px;    border: 1px solid #000000;    box-shadow: 10px 10px 20px 5px #000000; } 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-15。 框影

box-shadow的值与text-shadow的作用相同,如果指定负偏移值,阴影将呈现在屏幕的左上方。

CSS 媒体查询

CSS 媒体查询允许您根据特定条件获取 CSS 样式。这些条件可以包括表 5-3 中所示的条件。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建媒体查询背后的想法不一定是构建针对特定设备的媒体查询(例如,不专门针对平板电脑或手机),而是迎合特定的屏幕尺寸并调整内容以适应它。

通过这样做,您可以确保您的 CSS 应用于可用空间,而不是目标设备。我们称之为响应式网页设计

丹尼尔·文的网站([danielvane.com/](http://danielvane.com/))展示了一个响应式网页设计的很好的例子。通过提供所有视窗尺寸的样式、最大 480 像素的显示样式和最大 768 像素的显示样式,网站可以适当地响应任何手机或平板设备上的可用空间,如图 5-16 和图 5-17 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-16。 Daniel Vane 的响应式网站(平板电脑在左边,手机在右边)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-17。 Daniel Vane 的纵向响应网站(左边是平板电脑,右边是手机)

安迪·克拉克和基思·克拉克设计了一套媒体查询,你可以用它来定位逐渐变大的显示器。这背后的想法是用颜色和排版为最小的屏幕尺寸设计样式,然后以特定的屏幕增量逐步增强网站,直到屏幕尺寸超过 992 像素。该组媒体查询还包括针对具有高像素密度的目标显示器的媒体查询。

`

`

您应该检查他们的 GitHub 项目,查看在[github.com/malarkey/320andup/](https://github.com/malarkey/320andup/)找到的这组规则的更新。

CSS 预编译器(SASS)

如果你过去有过使用 CSS 的经验,你就会知道它的一些局限性。例如,您不能定义可能影响 CSS 显示方式的变量,也不能重用代码元素。随着应用的增长,在 CSS 中生成和维护一个长的继承链也是一件痛苦的事情,如下面的代码所示,其中一个元素中有几个元素需要类似的样式。

`/**
 * A common way to style a block in CSS
 **/

.block {
   /** style your block here **/
}

.block h1.heading {
   /** style your header here **/
}

.block ul.alternating {
   /** style your block ul here **/
}

.block ul.alternating li {
   /** style your alternating li here /
} .block ul.alternating li a {
   /
style your li link here **/
}

/** and the story continues **/`

语法上令人敬畏的样式表(SASS)通过使用嵌套、变量、混合和选择器继承来帮助摆脱这种麻烦。SASS 不是 CSS,需要编译器将其编译成 CSS。

从前面的 CSS 可以看出,很多代码是重复的。不幸的是,没有办法以一种浏览器可以识别的方式删除大块,但有一种方法可以做到这一点,即你编写的 CSS 更容易维护和移植。这在 SASS 中被称为嵌套

在本节中,您将学习如何使用 SASS 来生成有组织的、可重用的、具体的 CSS。您将了解 SASS 如何改进您的开发工作流程并改变您对 CSS 的看法。

您还将了解 SASS 如何消除在整个样式表中使用类似 CSS 样式的大量重复工作,并为面向对象的 CSS 铺平道路,这是一种思考 CSS 和 HTML 之间关系的方式,它将每个设计元素视为其自己独立的设计对象。

筑巢

嵌套允许你嵌套 CSS 样式。例如,前面的嵌套 SASS 代码如下所示。

`/**
 * The SASS way to style a block in CSS
 **/

.block {
   /** style your block here **/

h1.heading {
      /** style your header here **/
   }

ul.alternating {
      /** style your block ul here /       li {
         /
style your alternating li here **/

a {
            /** style your li link here **/
         }
      }
   }
}

/** and the story continues **/`

这段代码更容易维护。如果您要更改块的类名,只需在嵌套样式中更改一次类名。如果您需要添加更多的元素,您只需要在适当的位置添加另一个您想要样式化的类或元素。例如,如果您想在标题中设置链接的样式,您可以使用首选的 SCSS 格式执行以下操作。

`.block {
   /** style your block here **/

h1.heading {
      /** style your header here /
      a {
         /
style your heading link here **/
      }
   }

ul.alternating {
      /** style your block ul here **/

li {
         /** style your alternating li here **/

a {
            /** style your li link here **/
         }
      }
   }
}`

编译

前面的代码需要编译成 CSS,以便网络浏览器能够理解。不要将 SASS 文件直接链接到 HTML 文档中;相反,您可以链接生成的 CSS 文件。您可以使用内置工具直接从 Aptana Studio 编译 SASS 文件。

要在 Aptana Studio 中编译 SASS 文件,在项目中的任意位置创建一个名为mobile.scss的新文件(之后可以删除它)并添加以下代码。

`.test {
   background: #000000;

.test2 {
      background: #FFFFFF;
   }
}`

点击命令 SASS 编译 Sass。这将在 SCSS 文件所在的位置生成一个新的 CSS 文件。您需要刷新应用浏览器才能看到新文件。编译 SASS 的捷径是 cmd+Shift+r(Windows 和 Linux 上的 CTRL + Shift + r)。当出现图 5-18 中所示的对话框时,按 1。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-18。 使用 cmd + shift + r 命令编译 SASS】

新的 CSS 文件出现后,打开它。您应该会看到下面的代码。

.test {    background: #000000; } .test .test2 {    background: #FFFFFF; }

偏音

一个大的 SASS 文件可能会变得很难维护,并且需要很长的滚动时间!在 Aptana Studio 中,可以使用代码折叠来显示和隐藏 SASS 样式,以便于浏览,如图图 5-10 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-19。 代码折叠在阿普塔纳与 SCSS 的文件中

虽然这很方便,但 SASS 也支持使用常规 CSS 中相同的@import语法从外部 SASS 文件导入部分样式表。SASS 实现和常规样式表中的实现之间的区别在于,SASS 将在编译时拉入文件,而不是使用 HTTP 请求将所有文件一个接一个地加载到常规 CSS 文件中。这为在编译时导入特定于对象或节的分部提供了空间。下面的代码显示了一个示例。

`/** mobile.scss **/

@import “partials/tablet”;
@import “partials/phone”;

/** partials/_tablet.scss **/

.test-tablet {
   background: url(’…/themes/mytheme/common/logo.png’) no-repeat top left
#FFFFFF;
}

/** partials/_phone.scss **/
.test-phone {
   background: url(’…/themes/mytheme/common/logo.png’) no-repeat top left
#FFFFFF;
}`

编译完成后,CSS 将如下所示。

`.test-tablet {
   background: url(“…/themes/mytheme/common/logo.png”) no-repeat top left
white; }

.test-phone {
   background: url(“…/themes/mytheme/common/logo.png”) no-repeat top left
white; }`

从这个例子中可以看出,每个部分的文件名都应该以 _(下划线)为前缀,导入中的引用应该包含相对文件夹和部分名称,而不包含 _ 前缀或 SCSS 文件名。您可能会注意到,SASS 还会将编译后的 CSS 中的#FFFFF转换为white

变量和插值

您最终必然会产生基于颜色/主题的样式表(例如,同一个样式表可能引用相同的图像,但是来自不同的图像文件夹,或者具有不同的颜色主题)。

传统上,您会使用 PHP、Python 或。NET 动态生成这些样式表。SASS 通过使用变量消除了这种需要。

SASS 中变量的行为与其他语言中的行为非常相似。它们可以是任何类型(字符串、CSS 属性值、整数、像素、em、%)并且可以添加到 SCSS 样式中以对样式表进行全局更改。

例如,以 partials 部分中的示例代码为例,我们可以对其进行修改,以便您可以从主(移动)样式表中更改主题文件夹和颜色。

`/** mobile.scss **/

$theme: “bentley”;
$color: #000000;

@import “partials/tablet”;
@import “partials/phone”;

/** partials/_tablet.scss **/

.test-tablet {
   background: url(’…/themes/#{KaTeX parse error: Expected 'EOF', got '}' at position 6: theme}̲**/common/logo.…color;
}

/** partials/_phone.scss **/

.test-phone {
   background: url(’…/themes/#{KaTeX parse error: Expected 'EOF', got '}' at position 6: theme}̲**/common/logo.…color;
}`

正如你在mobile.scss中看到的,你用一串"bentley"定义了一个主题变量。然后在下面的线上定义一个黑色。@import则用来导入分音。在每个分部中,您会注意到背景声明被修改如下。

background: url(’../themes/**#{$theme}**/common/logo.png’) no-repeat top left **$color**;

有两种方法可以将变量添加到 SASS 文件中。要将变量添加为 CSS 字符串的一部分,如背景图像路径,请使用以下语法。

#{$myvariable}

这就是所谓的插值,你也可以用它来改变一个 CSS 属性而不是它的值。例如,border-#{$position}-radius:其中position是由变量定义的位置。

第二种方法是简单地使用$myvariable重复变量名。这是您在定义颜色、宽度或高度等 CSS 属性值时应该使用的内容。

混合蛋白

SASS 更受欢迎的特性之一是 mixins。Mixins 允许您在一个地方定义一段代码,并在 SASS 样式表的任何地方使用它。例如,您可能有一个跨浏览器渐变的大 CSS 声明,如下面的代码所示。

.myelement {    background: rgb(206,220,231);    background: -moz-linear-gradient(-45deg, rgba(206,220,231,1) 0%,       rgba(89,106,114,1) 100%);    background: -webkit-gradient(linear, left top, right bottom,       color-stop(0%,rgba(206,220,231,1)),       color-stop(100%,rgba(89,106,114,1)));    background: -o-linear-gradient(-45deg, rgba(206,220,231,1) 0%,       rgba(89,106,114,1) 100%);    background: -ms-linear-gradient(-45deg, rgba(206,220,231,1) 0%,       rgba(89,106,114,1) 100%);    background: linear-gradient(-45deg, rgba(206,220,231,1) 0%,       rgba(89,106,114,1) 100%); }

代码太多了。如果你想用在别的地方呢?最有效的方法是简单地将更多的类添加到您想要使用它的定义中。

.myelement, .mysecondelement {    background: rgb(206,220,231);    background: -moz-linear-gradient(-45deg,       rgba(206,220,231,1) 0%, rgba(89,106,114,1) 100%);    background: -webkit-gradient(linear, left top, right bottom,       color-stop(0%,rgba(206,220,231,1)),       color-stop(100%,rgba(89,106,114,1)));    background: -o-linear-gradient(-45deg,       rgba(206,220,231,1) 0%,rgba(89,106,114,1) 100%);    background: -ms-linear-gradient(-45deg,       rgba(206,220,231,1) 0%,rgba(89,106,114,1) 100%);    background: linear-gradient(-45deg,       rgba(206,220,231,1) 0%,rgba(89,106,114,1) 100%); }

您可以使用 mixin 来定义渐变,并使用以下代码将其包含在您的样式中。

`@mixin specialgradient {
   background: rgb(206,220,231);
   background: -moz-linear-gradient(-45deg,
      rgba(206,220,231,1) 0%, rgba(89,106,114,1) 100%);
   background: -webkit-gradient(linear, left top, right bottom,
      color-stop(0%,rgba(206,220,231,1)), color-stop(100%, rgba(89,106,114,1)));
   background: -o-linear-gradient(-45deg, rgba(206,220,231,1) 0%,
      rgba(89,106,114,1) 100%);
   background: -ms-linear-gradient(-45deg, rgba(206,220,231,1) 0%,
      rgba(89,106,114,1) 100%);
   background: linear-gradient(-45deg, rgba(206,220,231,1) 0%,
      rgba(89,106,114,1) 100%);
}

#my-first-element {
   @include specialgradient;
}

#my-second-element {
   @include specialgradient;
}`

然而,这将是一个坏主意,因为产生的 CSS 将在样式表中包含两次渐变,这增加了膨胀,并不是我们想要的。选择器继承应该是这方面的首选。当您有一大块 CSS 将在其他 CSS 规则中重复时,或者更好的是,当您有在整个样式表中重复的 CSS 时,例如特定于供应商的样式(例如,渐变和边框图像)需要为每个浏览器多次定义相同的 CSS 时,Mixins 就很方便了。

为了实现这一点,您可以将参数传递到 mixins 中。现在,您可以使用以下代码在一行中的任意位置生成 CSS 渐变。

`@mixin gradient($start, $stop, KaTeX parse error: Expected '}', got 'EOF' at end of input: …ckground: rgba(start, 1);
   background: -moz-linear-gradient($degrees, $start 0%, $stop 100%);
   background: -webkit-gradient(linear, left top, right bottom,
      color-stop(0%, $start), color-stop(100%, s t o p ) ) ;    b a c k g r o u n d : − o − l i n e a r − g r a d i e n t ( stop));    background: -o-linear-gradient( stop));  background:olineargradient(degrees, s t a r t 0 start 0%, start0stop 100%);
   background: -ms-linear-gradient($degrees, $start 0% s t o p 100    b a c k g r o u n d : l i n e a r − g r a d i e n t ( stop 100%);    background: linear-gradient( stop100  background:lineargradient(degrees, $start 0%, $stop 100%);
}

#my-first-element {
   @include gradient(rgba(206,220,231,0.5), rgba(89,106,114,1), -45deg);
}

#my-second-element {
   @include gradient(rgba(206,220,231,1), rgba(89,106,114,1), -45deg);
}`

如您所见,首先定义一个名为gradient的 mixin,它有三个参数:$start$stop$degrees。在这个 mixin 中,首先为不支持渐变的设备定义标准背景。使用rgba SASS 函数定义背景颜色的值。在这里,您显式地将背景色设置为没有 alpha 透明度的起始色。使用下面几行,您只需将开始颜色、结束颜色和度数传递给适当的供应商渐变声明。现在,您可以使用@include gradient(start-color, finish-color, degrees);在样式表的任何地方使用参数来绘制渐变。生成的 CSS 如下所示。

`#my-first-element {
   background: #cedce7;
   background: -moz-linear-gradient(-45deg, rgba(206, 220, 231, 0.5) 0%, #596a72
100%);
   background: -webkit-gradient(linear, left top, right bottom,
      color-stop(0%, rgba(206, 220, 231, 0.5)), color-stop(100%, #596a72));
   background: -o-linear-gradient(-45deg, rgba(206, 220, 231, 0.5) 0%, #596a72
100%);
   background: -ms-linear-gradient(-45deg, rgba(206, 220, 231, 0.5) 0% #596a72
100%);
   background: linear-gradient(-45deg, rgba(206, 220, 231, 0.5) 0%, #596a72
100%); }

#my-second-element {
   background: #cedce7;
   background: -moz-linear-gradient(-45deg, #cedce7 0%, #596a72 100%);    background: -webkit-gradient(linear, left top, right bottom,
      color-stop(0%, #cedce7), color-stop(100%, #596a72));
   background: -o-linear-gradient(-45deg, #cedce7 0%, #596a72 100%);
   background: -ms-linear-gradient(-45deg, #cedce7 0% #596a72 100%);
   background: linear-gradient(-45deg, #cedce7 0%, #596a72 100%); }`

注意#my-first-element中的 CSS 在第一个描述符中将背景色作为常规的十六进制颜色,其余的是 RGBA 颜色。此外,即使在 mixin 调用中使用 RGBA 设置了停止颜色,它也是一种十六进制颜色,因为不透明度设置为 1,而开始颜色设置为 0.5。SASS 将选择最有效的方式输出您的颜色。

选择器继承

当然,在整个 SASS 文件中使用 mixins 是很诱人的,即使 CSS 可能完全相同。选择器继承允许您在放置于 SASS 文件中的规则中使用相同的 CSS 规则。例如,在 CSS 中可以使用下面的代码。

.my-element-one, .my-element-two, .my-element-three {    /** insert common CSS style here **/ }

虽然效率很高,但是很容易忘记哪些 CSS 规则与一组规则相关联。您可能需要在文档中搜寻,以找到那组规则以及与之相关联的元素、类和 id。更令人困惑的是,样式可能位于单独的 CSS 文件中。

选择器继承有助于克服这个问题。选择器继承允许您生成与刚才所示相同的代码,但是以一种对开发人员更加友好的方式。

使用 mixins 一节中的例子,您可以定义一种类型的渐变,并在 SASS 文件中的任何地方根据相关规则使用它,而不会在 CSS 文件中多次生成结果渐变。

.block {    @include gradient(rgba(206,220,231,0.5), rgba(89,106,114,1), -45deg); } .sidebar-block {    border-radius: 10px;    @extend .block; }

如您所见,.sidebar-block.block规则相似,除了圆形的边框。生成的 CSS 如下所示。

`.block, .sidebar-block {
   background: #cedce7;
   background: -moz-linear-gradient(-45deg, rgba(206, 220, 231, 0.5) 0%, #596a72
100%);
   background: -webkit-gradient(linear, left top, right bottom,
      color-stop(0%, rgba(206, 220, 231, 0.5)), color-stop(100%, #596a72));
   background: -o-linear-gradient(-45deg, rgba(206, 220, 231, 0.5) 0%, #596a72
100%);
   background: -ms-linear-gradient(-45deg, rgba(206, 220, 231, 0.5) 0% #596a72
100%);
   background: linear-gradient(-45deg, rgba(206, 220, 231, 0.5) 0%, #596a72
100%); }

.sidebar-block {
   border-radius: 10px; }`

您可以看到 SASS 已经将border-radius属性分离出来,并将其放在自己的.sidebar-blockCSS 规则中。

您还可以将链接的类添加到 block 元素中,它将为.sidebar-block.block规则生成边界案例。

`/**
 * mobile.scss
 */

.block {
   @include gradient(rgba(206,220,231,0.5), rgba(89,106,114,1), -45deg);
}

.block.wide {
   width: 100px;
}

.sidebar-block {
   border-radius: 10px;
   @extend .block;
}

/**
 * mobile.css
 */

.block, .sidebar-block {
   background: #cedce7;
   background: -moz-linear-gradient(-45deg, rgba(206, 220, 231, 0.5) 0%, #596a72
100%);
   background: -webkit-gradient(linear, left top, right bottom,
      color-stop(0%, rgba(206, 220, 231, 0.5)), color-stop(100%, #596a72));    background: -o-linear-gradient(-45deg, rgba(206, 220, 231, 0.5) 0%, #596a72
100%);
   background: -ms-linear-gradient(-45deg, rgba(206, 220, 231, 0.5) 0% #596a72
100%);
   background: linear-gradient(-45deg, rgba(206, 220, 231, 0.5) 0%, #596a72
100%); }

.block.wide, .wide.sidebar-block {
   width: 100px; }

.sidebar-block {
   border-radius: 10px; }`

总结

从这一章开始,你应该对 CSS3 的新特性有了很好的理解。您应该准备好一个小工具箱,从中可以进行扩展,包括如何执行基本动画,如何为动画“补间”和创建关键帧,以及如何将它们应用到元素。您还应该了解,大多数浏览器都支持 CSS3 的一些特性,但它们仍处于草案阶段,这就是为什么有时您需要多次编写相同的代码。

您还应该对 SASS 有深入的了解,以及它如何通过大幅减少您必须编写的代码量来提高您的生产率。

六、奠定 CSS3 基础

在上一章中,您重点学习了 CSS3 的一些新特性,以及如何使用 SASS 使您的生活变得更加轻松。在这一章中,你将把这些新知识应用到实践中,开始创建你的移动网络应用的可视化基础。momemo 应用中的大多数元素,如搜索、观看和喜欢的电影,都是用 JavaScript 处理和生成的,因此这些元素的样式将在第八章中讨论。

在您开始创建任何应用之前,您通常需要完成费力的引导任务。这需要设置好一切,比如你将要构建的应用的框架。虽然这是一个非常卑微和无聊的任务,但把它做好是很重要的,因为应用的其余部分可以从坚实的基础中受益。

在这一章中,你将学习如何利用 SASS 中的片段来组织你的 CSS 文件,这样就不会影响加载时间。您还将创建应用的基本框架,包括创建样式表以提高高分辨率显示器上的图像质量,以及创建应用的基本布局。

您需要下载应用的映像包,并将其放在应用映像(img)文件夹中。

井井有条

让我们从在应用文件夹中创建相关文件夹开始。在应用文件夹的 CSS 文件夹中,创建两个名为 mixins 和 partials 的文件夹,并在 CSS 文件夹中创建一个名为 mobile.scss 的新的 sass 文件。你的文件夹结构应该类似于下面的图 6-1 。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-1。 CSS 文件夹结构

这个文件夹结构将允许你把你的表单、布局和排版的 CSS 分成单独的 SASS 文件。mobile.scss 文件只是一个主 SASS 文件,它将提取所有部分。这意味着,如果您想为只有版式的旧移动设备创建一个样式表,您可以创建一个新的主 SASS 文件,并只获取版式 SASS 文件,而不必复制任何 CSS。

打开 mobile.scss 文件并添加以下 SASS 代码:

`@import ‘mixins/animations’;
@import ‘mixins/gradient’;
@import ‘mixins/box-sizing’;
@import ‘partials/reset’;
@import ‘partials/typography’;
@import ‘partials/layout’;
@import ‘partials/forms’;

@media only screen and (-webkit-min-device-pixel-ratio : 1.5),
       only screen and (min-device-pixel-ratio : 1.5) {
  @import ‘partials/highres’;
}`

如第五章所示,这将在编译 SASS 文件时导入适当的 SASS 文件。

您还会注意到,在上面的代码中有一个媒体查询。此媒体查询将允许您为高分辨率设备获取高分辨率图形。在媒体查询中,您可以看到没有显式添加 CSS,而是导入了 highres 部分。这有助于防止任何 CSS 被添加到主 mobile.scss 文件中。mobile.scss 文件应该被简单地看作是一个 SASS 文件,用来把所有的东西放在一起,理想情况下应该只包含媒体查询和导入。

在编译 mobile.scss 文件之前,您需要创建适当的 SASS 文件。

  • mixin/_ animations . SCS
  • mixins/_ box-size . scss
  • mixins/_gradient.scss
  • partials/_forms.scss
  • partials/_highres.scss
  • partials/_layout.scss
  • partials/_reset.scss
  • partials/_ 印刷术. scss

继续创建它们,记住 SASS partials 需要在文件名的开头有一个 _(下划线),以便导入时被识别。

您需要创建如图 6-2 中所示的空文件。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-2。 萨斯巴勒斯

您会注意到有一个名为 _reset.scss 的文件。如果您不熟悉 Eric Meyer 的 reset css 文件,reset 样式表用于创建跨浏览器 css 样式的公平竞争环境。这是因为浏览器可以为某些元素设置不同的默认样式,比如不同的边距、填充和字体大小。重置样式表为最常用的 HTML 元素设置新的可预测默认值。要注意的是,有些,特别是 Eric Meyer 的会重新设置字体,这样它们就没有任何风格了,这是有用的,但是你必须记住,对于有默认字体风格的元素,比如<pre />会有和<body />一样的字样。

在 mixins 文件夹中,你会看到几个看起来像 css 属性的文件,如 _animation.scss 和 _gradient.scss。这些文件有助于通过使用 mixins 创建属性的通用版本来消除一些特定于供应商的 CSS 对主 SASS 文件的污染。您可以开始向这些文件添加内容。

打开 empty _animations.scss 文件。这个 mixin 将用于创建动画,并应用于所有供应商。如果创建了一个新的特定于供应商的动画属性,它可以被添加到一个地方,而不是跨多个 SASS 文件添加。将以下代码添加到打开的文件中。

@mixin animation ($values) {         animation: $values;         -moz-animation: $values;         -webkit-animation: $values; }

如您所见,它只是作为基于标准的 Mozilla 和 webkit 动画属性的代理,接受一组属性,然后将它们传递给供应商特定的动画属性。保存文件并关闭它。

打开 empty _box-sizing.scss 文件。这个 mixin 提供了对盒子大小的支持。关于 CSS 中的灵活布局,最令人沮丧的一个问题是,当您将元素设置为 100%宽(父元素的宽度)并带有填充时,浏览器通常会将填充添加到元素的宽度,即使宽度被指定为 100%,因此结果是您的元素会因您添加的填充量而过度拉伸,有时会将元素稍微推出屏幕或其父元素之外。框大小属性通过以下方式帮助克服这一问题:

  • 使用内容框值时,从元素的宽度和高度中排除任何填充、边距或边框
  • 使用填充框值时,包括元素宽度和高度的任何填充
  • 当使用边框值时,包括任何填充和边框宽度以及元素的宽度和高度

@mixin box-sizing ($value) {     -moz-box-sizing: $value;     -webkit-box-sizing: $value;     box-sizing: $value; }

同样,这个 mixin 只是通过将值传递给属性来充当供应商特定属性的代理。

最后,打开 _gradient.scss 文件,并向其中添加以下代码。

@mixin gradient($start, $stop, $degrees) {   background: rgba($start, 1);   background: -moz-linear-gradient($degrees,  $start 0%, $stop 100%);   background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, $start), color-stop(100%, $stop));   background: -o-linear-gradient($degrees,  $start 0%,$stop 100%);   background: -ms-linear-gradient($degrees,  $start 0% $stop 100%);   background: linear-gradient($degrees,  $start 0%, $stop 100%); }

你可能在上一章已经看过这个 mixin 了。它只是为供应商特定的渐变代码创建 CSS 渐变。它比其他 mixins 稍微复杂一点,因为在编写本文时,每个供应商都有自己的 CSS 渐变实现,这使得接受单个值并将其传递给供应商属性变得不可能。

创造分音

混音创建完成后,现在是创建分音的时候了。如前所述,通过在常规 CSS 文件中使用传统的@import,partials 将有助于将 CSS 的不同部分分成不同的文件,而不会对最终用户产生影响,这将对加载时间产生很大影响。

您可以从打开 partials 目录中的 empty _reset.scss 文件开始。您不必手动将下面的代码键入这个 SASS 文件,您可以从 Eric Mayar 的网站Meyer web . com/Eric/thoughts/2011/01/03/reset-revisited/复制它。下面列出的代码仅供您参考。

`/* http://meyerweb.com/eric/tools/css/reset/
   v2.0b1 | 201101
   NOTE: WORK IN PROGRESS
   USE WITH CAUTION AND TEST WITH ABANDON */

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, figcaption, figure,
footer, header, hgroup, menu, nav, section, summary,
time, mark, audio, video {
  margin: 0;   padding: 0;
  border: 0;
  outline: 0;
  font-size: 100%;
  font: inherit;
  vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
  display: block;
}
body {
  line-height: 1;
}
ol, ul {
  list-style: none;
}
blockquote, q {
  quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
  content: ‘’;
  content: none;
}

/* remember to define visible focus styles!
:focus {
  outline: ???;
} */

/* remember to highlight inserts somehow! */
ins {
  text-decoration: none;
}
del {
  text-decoration: line-through;
}

table {
  border-collapse: collapse;
  border-spacing: 0;
}`

保存并关闭文件。您需要添加代码的下一个文件是 _typography.scss 文件。在 Aptana Studio 中打开它。_typography.scss 文件将简单地设置文本在应用中的显示方式。正如您在下面的代码中看到的,您将简单地设计主体和标题的样式。

`body {
  font-size: 0.75em;
  font-family: Arial, Helvetica, sans-serif;
}

h1, h2, h3, h4 {
  font-family: ‘Arimo’, sans-serif;
  font-weight: bold;
  margin-bottom: 0.5em;
  font-size: 1em;
}

h3 { font-size: 1.25em; }
h2 { font-size: 1.5em; }
h1 { font-size: 1.9em; }`

你用 em 代替像素作为字体大小。将正文的字体大小设置为 0.75em 相当于 12px,如上面的代码片段所示,其中正文的字体大小被声明为 em。1em 相当于浏览器的默认字体大小,即 16px。要计算出 10px 在 em 中应该是多少,您可以使用 10 / 16,它等于 0.625,因此 10px 应该是 0.625em。em 是有用的,因为值是相对的。例如,如果您将 div 的字体大小设置为 0.75em (12px),然后将其中的任何元素设置为 1em,则该字体大小将与父元素的字体大小相关。所以子元素中的 1em 变成了离父元素 0.75em。试图找出 em 的字体大小可能是一场噩梦,riddle.pl/emcalc/有一个解决方案,允许你建立一个基于 DOM 的像素字体大小树,网络应用会为你将它们转换为 EM,并考虑父元素的字体大小。

保存并关闭 _ topology . scss 文件。打开 _layout.scss 文件。_layout.scss 文件将控制应用中元素的位置、尺寸、颜色和一般布局。

首先要做的是对 body,html,#shoe 和。应用的甲板元素。您可以在顶部设置它们的样式,这样它们就可以在样式表的后面被覆盖。

body, html, #shoe, .deck {   height: 100%;   width: 100%;   overflow: hidden;   margin: 0px; }

正如你所看到的,高度和宽度已经被设置为 100%,所以它跨越了屏幕的宽度和高度。溢出:隐藏;添加了,以便元素之外的任何内容都被截断,不会影响布局,为了更好地测量,添加了 0px 边距,以防止元素之间出现任何间隙。

接下来要做的是对#card-movie_search_results 卡片进行样式化。进行搜索时,卡片应显示在页面上所有元素的上方。您可以通过设置 z 索引来实现这一点。z-index 指示元素在元素堆栈中的位置。设置较高的数字通常会将元素放在堆栈的顶部。这种情况下用 50。

/**  *  Individual Card Styles  */ #card-movie_search_results {   z-index: 50; }

下一步是设置卡片组和卡片样式。如您所见,SASS 嵌套在这里用于嵌套卡片组中不同的卡片状态。当呈现 SASS 文件时,将生成适当的 CSS。您需要将甲板位置设置为相对位置。这将允许卡片组中绝对定位的卡片相对于父卡片组而不是整个视窗定位。

`/**
 *  Deck styles
 */
.deck {

position: relative;

}`

现在需要用。卡牌类也是。每张牌的宽度和高度都应该相同,但是要放在屏幕之外,这样用户一开始就看不到它。当。活动类被添加到任何。卡元素,它应该被带回到视图中。这可以通过将初始左侧位置设置为负值来实现,负值相当于卡片的宽度,在本例中为-100%。当您希望卡片重新出现在视图中时,会为设置 0px 的位置。主动造型。

`/**
 *  Deck styles
 */
.deck {

position: relative; **  .card {**
**    height: 100%;**
**    width: 100%;**
**    left: -100%;**
**    position: absolute;**
**  }**

**  .card.active {**
**    left: 0px;**
**  }**

}`

接下来要设计的是屏幕条。滚动条将位于屏幕的顶部和底部。这些需要以统一的方式设计,以便用户可以很容易地找到它们。正如你在下面看到的,渐变混合被用来创建一个 CSS3 渐变作为这个元素的背景。

`/**
 *   Header taskbar styles
 */

.screenbar {
  @include gradient(#7D9DCE, #ABC1E1, 90deg);
}`

任务栏相当复杂,因为它包含应用的徽标、搜索栏和清除按钮。任务栏需要和屏幕一样宽,搜索栏需要灵活,这样无论屏幕大小如何,它都能占据大部分空间。

从下面的代码中可以看到,你将任务栏的字体颜色设置为白色,溢出区被设置为隐藏,这样它将包围所有浮动的元素。任务栏也有 10px 填充,底部有红色边框。

header#taskbar {   color: #FFFFFF;   overflow: hidden;   padding: 10px;   border-bottom: 1px solid #BF2628; }

现在,您需要设计应用的品牌/徽标。为此,您可以使用 header (h1)元素,然后使用图像替换技术来显示徽标。这可以通过将 h1 的宽度和高度设置为与徽标相同的宽度和高度来实现,将 text-indent 属性设置为一个高的负任意值,以便文本位于屏幕之外,在这种情况下使用-10000px。最后,将徽标的背景设置为徽标。

h1 元素也浮动在任务栏的左侧,以便搜索表单可以占据剩余的可用空间。

`header#taskbar {
  …

**  h1.branding {**
**    margin: 0px;**
**    float: left;**
**    width: 73px;**
**    height: 32px;**
**    text-indent: -10000px;**
**    overflow: hidden;**
**    background: url(‘…/img/momemo.png’) no-repeat top left;**
**  }**

}`

接下来要做的是设置清除搜索链接。您使用与前面相同的图像替换技术来替换清除搜索链接中的文本。这次这个按钮被浮动到右边并隐藏起来,这样它就不会马上被看到。

`header#taskbar {
  …

h1.branding {
    …
  }

**  .clear-search {**
**    float: right;**
**    width: 35px;**
**    height: 35px;**
**    display: none;**
**    overflow: hidden;**
**    text-indent: -10000px;**
**    background: url(‘…/img/clear.png’) 50% 50% no-repeat;**
**  }**

}`

最后要添加到 _layout.scss 文件中的是 searchactive 覆盖。当您添加 css 类时。searchactive 添加到 header#taskbar 元素,它将显示 clearsearch 按钮,并通过向 add-movie 表单添加右边距来为它提供足够的空间。这可以防止。searchactive buttom 从下拉到新行。

`header#taskbar {
  …  
}

header#taskbar.searchactive {

**  .clear-search {**
**    display: block;**
**  }**

**  form#add-movie {**
**    margin-right: 40px;**
**  }**

}`

您的 final _layout.scss 文件应该类似于下面的代码。

`header#taskbar {
  color: #FFFFFF;
  overflow: hidden;
  padding: 10px;
  border-bottom: 1px solid #BF2628;

h1.branding {
    margin: 0px;
    float: left;
    width: 73px;
    height: 32px;
    text-indent: -10000px;
    overflow: hidden;
    background: url(‘…/img/momemo.png’) no-repeat top left;
  }

.clear-search {
    float: right;
    width: 35px;
    height: 35px;
    display: none;
    overflow: hidden;
    text-indent: -10000px;
    background: url(‘…/img/clear.png’) 50% 50% no-repeat;
  }

}

header#taskbar.searchactive {

.clear-search {
    display: block;
  }   form#add-movie {
    margin-right: 40px;
  }

}`

接下来要做的是设计表单的样式。所以打开 _forms.scss 文件。首先要做的是为所有表单元素设置框的大小,使添加的任何填充或边框成为整体宽度的一部分。下面一行将使用盒子大小的 mixin 来实现这一点。

input, select, textarea, button {   @include box-sizing(border-box); }

然后,您需要设置文本输入的样式,您可以使用新的 CSS3 属性选择器来完成这一工作,而不是像以前那样向每个文本输入元素添加 CSS 类。从下面的代码片段中可以看到,下面的文本输入有 1 像素的黑色边框和 5 像素的填充,而提交输入只有 10 像素的填充。应用中没有使用提交按钮,所以对它进行样式化还没有意义。

input[type="text"] {   border: 1px solid #000000;   padding: 5px; }

目前,只有一个输入元素应该跨越其父元素的整个宽度。将来您可能想要添加更多这样的元素,所以将它转换成一个可以重用的 CSS 类是一个好主意。

input.full-width {   width: 100%; }

通过在搜索表单中添加 80px 的左边距(大于或等于徽标的宽度),表单中的任何内容都将出现在徽标旁边。

form#add-movie {   margin-left: 80px; }

这是一个比浮动 add-movie 表单好得多的解决方案,因为如果不使用 JavaScript 来计算它的大小,它将不再能够拥有其父任务栏元素的全部宽度。

下面的代码简单地设计了搜索字段的样式。正如你所看到的,这里第一次使用了背景尺寸。background-size 属性允许您指定背景应该有多大,以像素为单位,或者以背景所添加到的元素的百分比来表示。

input.search {   padding-left: 30px;   background: url('../img/search.png') 5px 50% no-repeat transparent;   background-size: auto 50%;   border: none;   border-bottom: 1px solid #BF2628;   color: #FFFFFF;   font-size: 1.5em; }

背景尺寸属性接受宽度和高度,这两个属性可以是不同的单位。例如,在本例中,宽度设置为自动,高度设置为 50%。这允许高度为元素高度的 50%,但宽度将根据背景图像的高度成比例调整,这样它就不会出现扭曲。

以下样式使用供应商特定的伪样式。-webkit-input-placeholder 和-moz-placeholder 允许您设置输入元素上使用的占位符文本的样式。例如,搜索框的背景在蓝色背景上是透明的,所以默认的灰色几乎看不见。文本需要是白色的,因此占位符伪对象允许您自定义占位符文本的呈现方式。

input.search::-webkit-input-placeholder, input.search::-moz-placeholder {   color: rgba(255, 255, 255, 0.5); }

虽然这在 Android 4 上不会立即可见,但下面的样式会在搜索框中显示一个加载指示器,同时在后台搜索电影。

input.search.loading {   background-image: url('../img/loading.gif'); }

最终的表单 SASS 文件应该类似于下面的代码。

`input, select, textarea, button {
  @include box-sizing(border-box);
}

input[type=“text”] {
  border: 1px solid #000000;
  padding: 5px;
} input.full-width {
  width: 100%;
}

form#add-movie {
  margin-left: 80px;
}

input.search {
  padding-left: 30px;
  background: url(‘…/img/search.png’) 5px 50% no-repeat transparent;
  background-size: auto 50%;
  border: none;
  border-bottom: 1px solid #BF2628;
  color: #FFFFFF;
  font-size: 1.5em;
}

input.search::-webkit-input-placeholder, input.search::-moz-placeholder {
  color: rgba(255, 255, 255, 0.5);
}

input.search.loading {
  background-image: url(‘…/img/loading.gif’);
}`

保存并关闭文件。最后,您需要打开 _highres.scss 文件。这个文件将简单地用于替换任何高分辨率显示的图形,使它们看起来清晰。将以下代码添加到文件中。

`header#taskbar {

h1.branding {
    background-image: url(‘…/img/momemo.png’);
    background-size: 73px 32px;
  }

}`

正如你所看到的,这里需要使用背景尺寸,因为尽管高分辨率文件的分辨率是低分辨率图像的两倍,CSS 在用作背景图像时仍然会使用图像的全尺寸。这将确保背景图像缩小到正确的像素大小。不使用这个和使用它的区别可以在下面的图 6-3 中显示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-3。 高密度显示器上的高分辨率图像(下图)与低分辨率图像(上图)

在 Aptana 中自动编译 Sass

直到现在,你还没有在 Aptana Studio 中编译过任何 SASS。在前一章中,您看到了如何使用 SASS 的内置 SASS 编译器命令来编译 SASS 文件。每当你想对你的 SASS 文件进行修改时,这会变得很麻烦。您可以通过使用 SASS 命令行自动编译您的 SASS 文件来解决这个问题。为了做到这一点,在 Aptana Studio 的应用浏览器中点击你的应用文件夹,然后点击命令图标,它看起来像一个齿轮,可以在图 6-4 中看到。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-4。 命令菜单

点击打开终端菜单项。这将打开类似于图 6-5 的终端视图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-5。 终端视图

在终端视图中,输入以下命令并按回车键。

sass --watch css/*.scss

这将查找您的 SASS 文件中的任何更改,并自动为您生成 CSS 文件。你应该会看到类似于图 6-6 的东西。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-6。 萨斯-手表输出

这还会在您的部分文件中查找更改,然后用新的更改自动覆盖 mobile.css。

每次打开 Aptana Studio 时,您都需要运行这个命令,并且您还应该始终保持这个终端视图打开。

现在你的 CSS 文件已经成功生成,在 Aptana Studio 中运行你的网站,右击 index.html 进入运行为 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 ** JavaScript Web 应用**。它将在 Firefox 中启动,访问移动设备地址栏中显示的 URL。你现在应该会看到类似于图 6-7 的东西。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-7。 Momemo 带 CSS

如果你没有看到新的样式,回到 Aptana Studio,点击 CSS 文件夹,按键盘上的 F5 刷新它。应该会出现新的 mobile.css 文件。刷新你的手机上的网页,一切都应该看起来像它应该的样子。

总结

虽然这一章很短,但是您应该对如何真正利用 SASS 中的 partilals 和 mixins 以及如何为开始在之上构建 CSS/SASS 打下基础有了更深入的了解。

现在,您应该知道如何在主 SASS 文件(mobile.scss)的 CSS 媒体查询中使用片段。

六、移动 JavaScript

自 1999 年第一款消费者 WAP 手机 Nokia 7110 问世以来,移动 JavaScript 已经走过了漫长的道路。从完全不支持到完全支持,仅仅 10 年时间,JavaScript 已经让我们的移动网络体验变得更加互动、有趣和令人满意。

今天的问题是,有了这么多的 JavaScript 支持,我们如何利用它,使它不引人注目,并为我们的用户提供良好和流畅的体验?

本章将指导你如何将 JavaScript 集成到你的项目中,使用不同类型的库来使你更容易的开发出可以在任何平台上运行的移动网络应用。您还将了解新的 HTML5 JavaScript APIs(如地理定位)、存储,以及如何利用它使用 HTML5 Canvas 元素为 Android 绘制基于矢量的图形。

面向对象的 JavaScript

JavaScript 是一种处理移动网站中用户交互的极好的语言。就像你为桌面 web 编写 JavaScript 一样,你也可以利用同样的设计模式和方法为移动设备编写。您可以用两种方式之一编写 JavaScript。其中一个方法是程序性的,如下面的代码所示。

function sayHelloWorld(foo){    alert(foo); }

第二种方法是面向对象的,如下所示。

`var World = function(){
   this.say = function say(hello){
      alert(hello);
   }
}

var myworld = new World();
myworld.say(‘Hello’);`

正如您所看到的,您可能需要为面向对象的方法编写更多的代码,但是有几个好处。

  • 面向对象的方法允许扩展您的代码。
  • 面向对象的方法可以更有组织性。
  • 面向对象的方法允许封装,这意味着对象中的变量或属性可以是公共的或私有的。
  • 面向对象的方法允许您将对象传递给其他对象。这通常被称为对象依赖性

**注意:**在基于类的语言中,比如 Java、Objective-C 和 PHP,类在被使用new ClassName实例化之前是一个对象。一个对象是一个类被实例化后的实例。JavaScript 有创建对象的基本方法,不幸的是,它不完全支持封装、继承、抽象和开箱即用的接口。您可能需要创建自己的方法和实践来实现这一点。JavaScript 也是一种基于对象的语言,所以尽管感觉像是在创建类,但实际上是在代码中为对象创建结构。

前面的两个代码片段具有相同的结果;然而,面向对象的方法将World视为一个对象,并将该对象中的函数this.say视为可以在其上执行的方法。

面向对象的方法还允许您创建一个对象的多个实例。例如,你可以创建几个World实例,通过修改前面的代码,你可以开始创建只存在于每个World object范围内的实例变量,比如它的名字。

`var World = function(_name){

var name = _name;

this.greet = function(guest){
      alert('Hello ’ + guest + ’ my name is ’ + name);
   }
}

var venus = new World(‘Venus’);
var mars = new World(‘Mars’);

venus.greet(‘Antony’);
venus.greet(‘Dan’);`

从前面的例子中,您可以看到,要在 JavaScript 中创建一个对象,就像创建一个函数一样简单。使用函数的参数,您创建了所谓的构造函数。构造函数是一种在实例化时向对象传递参数的方法。这些参数通常用于将变量赋给对象本身的属性。

在普通的面向对象中,属性可以声明为 public 或 private。在 JavaScript 中,属性没有这样的简化。因此,属性可以是实例变量(私有)或公共属性(公共)。在这个实例中,name属性是一个实例变量,这意味着您不能使用例如venus.name从对象外部访问它。这通常被称为封装。对象的属性是一个变量,既可以在对象范围内使用this.propertyname访问,也可以在对象外部使用object.propertyname访问。例如,如果您试图从对象外部访问name实例变量,您将得到undefined作为输出。

您还可以创建对象方法,即可以从对象内部或外部使用this或从外部分配给实例化对象的变量来访问的函数。使用前面的例子,this.greet是一个公共对象方法,可以在对象外部访问。

从这里,您可以看到对象允许更易维护的代码。对象的属性存在于全局名称空间之外,因此可以避免变量名和特定对象的其他细节发生冲突。除此之外,您还可以为应用对象创建自己的名称空间。这有助于避免其他 JavaScript 库覆盖它们。下面的例子展示了为对象创建应用级名称空间的最基本的方法。

`var app = app || {};
app.world = function(_name){

var name = _name;

this.greet = function(guest){
      alert('Hello ’ + guest + ’ my name is ’ + name);
   }
}`

这允许您在单独的文件中创建属于您的应用的类。前面的代码示例中的第一行声明了全局名称空间中的变量app,如果它已经存在,则将app全局变量赋给它。如果它不存在,它会为您创建一个空对象,以便开始填充您的对象。如果您在开发过程中以这样一种方式组织您的对象,将它们保存在单独的文件中,然后在生产中合并,这将非常方便。您甚至可以更进一步,根据功能命名您的对象。

这些是现代移动浏览器支持的面向对象 JavaScript 的基础。

您以这种方式编码(而不是,例如,创建 jQuery 插件),因为它将您的应用代码与特定于供应商的代码分开,并减少了对第三方代码的依赖。您可以更进一步,遵循模型视图控制器(MVC)模式,将您的用户交互与您的域逻辑(真实世界的对象)和呈现给用户的结果视图分离开来。

与设计模式和面向对象一样,JavaScript 也是一种事件驱动的语言。这意味着在运行时,在应用的一个部分触发的事件可以在应用的完全不同的部分触发一段代码。

最简单的形式是,事件可以通过用户交互来触发。在移动领域,这通常被视为触摸事件,用户使用手指通过浏览器与你的应用进行交互。浏览器注册该事件,然后将该事件及其信息(如事件源自的元素)传递给应用中的任何订阅者。对于桌面环境,这些被称为鼠标事件

处理触摸事件

JavaScript 在处理桌面事件方面毫不逊色,在移动领域也是如此。事件可以由用户级事件(如触摸和拖动)或设备级事件(如方向变化或设备位置变化)组成。最基本的事件是用户级事件。可以在任何 DOM 元素上跟踪它们。对于移动设备,有四种主要的触摸事件:

  • touchstart
  • touchend
  • touchmove
  • touchcancel

当用户触摸屏幕上的元素时,touchstart事件将被触发。当用户触摸屏幕上的某个元素后,将手指从该元素上移开时,将触发touchend事件。touchmove事件将跟踪用户的动作,并随着每个动作触发事件。当用户通过移动到目标边界之外并释放屏幕来取消触摸事件时,将触发touchcancel事件。这一事件似乎无法预测。

为了响应事件,您必须使用element.addEventListener(event, callbackfunction);为它们创建事件监听器。该方法采用事件名称(touchstarttouchend等)。)和回调函数。有时,您可能希望阻止触发事件的默认操作。例如,如果您向链接添加一个事件监听器,您可能不希望该链接在被点击时打开一个新页面。为此,您必须向名为e的回调函数添加一个参数,并在回调函数结束时调用e.preventDefault()。这也将防止元素滚动和干扰touchmove事件,如下面的代码片段所示。

`

x: 0 y: 0 - not touching

`

这段代码将用黑色填充屏幕,用白色文本包含用户手指的当前坐标以及用户是否正在触摸屏幕。您可以通过点击传递给touchmove事件监听器的事件的触摸列表来获取当前坐标。你可以从列表中获取第一次触摸,并使用clientXclientY来检索 X 和 Y 坐标,就像这样:

e.touches[0].clientX, e.touches[0].clientY

如您所见,您可以通过调用e.preventDefault()来防止文档滚动。

当用户触摸并将手指抬离屏幕时,将调用 touchstart 和 touch end 的其他两个事件侦听器。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-1。 检测触摸和移动

获取用户的位置

当您知道用户需要将他们的当前位置输入到应用中时,获取用户的位置会很方便。这有助于查找和搜索他们周围的事物,如事件、地点和其他人。定位 API 非常简单,利用了移动设备内置的 GPS 芯片。

要获得用户的位置,可以使用下面的代码。它是异步的和非阻塞的,所以当设备搜索用户位置时,您可以继续在前台或后台处理 JavaScript 事件。

var showCurrentPosition = function(position){    alert('Lat: ' + position.coords.longitude + ' Lon: ' + position.coords.latitude); }

这是在移动设备具有用户的位置之后将被调用的功能。传递回回调函数的参数是一个扩展了Coordinates接口的对象,其属性如表 7-1 所示。

要检索设备的坐标,只需在设备上查询用户的位置,如下所示:

navigator.geolocation.getCurrentPosition(showCurrentPosition);

如果用户尚未授权您的应用访问他们的位置,他们将首先被要求批准位置请求。图 7-2 显示了这个对话框的样子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-2。 位置请求

这就出现了一个问题,因为你应该预料到一些用户可能不希望共享他们当前的位置,并可能点击拒绝按钮;或者可能只是检索用户当前位置的问题。这可以用一个错误事件处理程序来处理,它是getCurrentPosition()方法的第二个参数。为了处理检索用户当前位置的错误,您必须创建一个错误处理程序,它将接受 error 对象。

`var handleLocationError = function(error){
   alert(error.message);
}

navigator.geolocation.getCurrentPosition(showCurrentPosition,
handleLocationError);`

错误对象是PositionError接口的一部分,其属性如表 7-2 所示。

您应该使用PositionError常量PERMISSION_DENIEDPOSITION_UNAVAILABLETIMEOUT来适当地处理错误,而不是依赖错误消息或将错误代码与硬编码的整数进行比较。下一个代码示例展示了如何使用handleLocationError函数和switch语句来处理错误。

var handleLocationError = function(error){    switch(error.code){       case error.PERMISSION_DENIED:          /**           * Handle permission denied response here,           * potentially display a dialog to the user           */          var confirmed = confirm("We really need your location!");          if(confirmed){             navigator.getCurrentPosition(showCurrentPosition, handleLocationError);          }          break;       case error.POSITON_UNAVAILABLE:          /**           * Handle position unavailable response here,           * potentially display a dialog to the user and           * ask them to enter their location manually           */          var tryagain = confirm("Sorry, something serious is wrong, would             you like to try again?");          if(tryagain){             navigator.getCurrentPosition(showCurrentPosition, handleLocationError);          }          break;       case error.TIMEOUT:          /**           * Appologizies to the user for the delay and attempts           * to retrieve their location again           */          navigator.geolocation.getCurrentPosition(showCurrentPosition,             handleLocationError);          break;    } }

这些都是非常简单的错误处理程序,如果发生错误,可以对它们进行扩展,为用户提供更好的体验。

然后,您可以将坐标传递给地图服务,如谷歌地图,以显示用户的当前位置。下面的例子使用 Google Maps 静态 API 来生成用户当前位置的图像,并显示在移动设备上。

<img src="/map.jpg" id="map" alt="Map" />
`

var handleLocationError = function(error){
      alert(error.message);
   }

navigator.geolocation.getCurrentPosition(showCurrentPosition,
handleLocationError);
`

结果如图 7-3 中的所示。当然,您也可以订阅用户当前位置的重大变化。您可以使用navigator.geolocation.watchPosition方法来完成这项工作。这将监听用户当前位置的重大变化,并在每次用户位置变化时调用回调函数。watchPosition方法与getCurrentPosition方法采用相同的参数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-3。 显示用户在谷歌地图上的当前位置

用画布画画

HTML5 Canvas 允许您使用 JavaScript 绘制基于矢量的形状。HTML5 Canvas 元素没有提供太多固有的功能,但是它为您开始绘制对象提供了一个基础。把它想象成你设备的白板。下一个练习将带您了解如何创建画布,如何开始使用 JavaScript 绘制基本形状,以及如何制作它们的动画。

首先,在这个名为canvas的章节文件夹中创建一个新文件夹。在canvas文件夹根目录下创建一个js文件夹,包含一个名为canvas.js的新 JavaScript 文件和一个index.html文件,内容如下。

`

                        Canvas             


      

`

这将创建一个idplay的画布元素,宽 100 像素,高 100 像素。永远不要尝试使用 CSS 来调整 Canvas 元素的大小,因为它不会像预期的那样工作。这个 HTML 也将链接到canvas.js文件。

打开canvas.js文件,它将被用来控制你的画布。您将使用面向对象的 JavaScript 来创建和控制播放按钮。

在这个例子中,您将需要两个对象:一个track对象(它将模拟实际的音轨)和一个playButton对象(它将控制音轨进度的显示和音轨的播放/暂停)。track对象应负责以下内容:

  • 记录轨道的总长度
  • 保持曲目的当前状态(播放/暂停/停止)
  • 如果曲目正在播放,则保留曲目的当前时间
  • 播放、暂停和停止轨道

本例中的playButton对象将负责以下内容:

  • 绘制播放按钮
  • 显示播放进度
  • 显示轨道回放状态
  • 通过显示播放或停止符号来表示轨道的状态
  • 通过移动播放头来表示音轨的播放进度

受 iTunes 播放控件的启发,如图 7-4 所示,你将创建类似的东西。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-4。 iTunes 预览播放控件

首先,在您的canvas.js文件中创建两个对象,如下面的代码片段所示:

`var app = app || {};

app.playButton = function(id, track){

}

app.track = function(length){

}`

如您所见,playButton的构造函数带有一个id,它将是画布元素的 ID,还有一个track,它将是app.track类的实例。track构造器简单地以秒为单位获取轨道的长度。

由于需要首先实例化track,您将从创建track类的代码开始。首先,在名为this.statetrack类中创建一个新属性,如下所示:

app.track = function(length){ **   this.state = {** **      STOPPED: 0,** **      PLAYING: 1,** **      PAUSED: 2** **   };** }

state属性包含可用于确定应用当前状态的变量。另一种方法是将当前状态存储为字符串(例如,播放、暂停或停止)。当您在应用中添加更多状态或更改状态名称时,这可能会有问题。通过这样做,改变应用的状态就像使用state = this.state.STOPPED一样简单。这也很有帮助,因为当你键入this.state.时,代码补全会向你显示可能的状态,这比你必须挖掘你的代码来找出可用的状态更好更有效。

接下来,在track类的范围内定义几个变量,如下面的代码片段所示:

`app.track = function(length){

**   var length = (length * 1000),**
**      currentTime = 0,**
**      interval,**
**      _self = this,**
**      state = this.state.STOPPED,**
**      updateInterval = 1000 / 30;**
}`

在 JavaScript 中,可以在一行中声明变量,用逗号分隔它们。这也适用于移动设备。

您的第一个变量length,通过乘以1000,将传递给该类的音轨长度从秒转换为毫秒。您还将currentTime设置为0,并声明一个名为interval的变量。interval变量负责保存对区间的引用,以重复调整轨道的定时。

这看起来很奇怪,但是你也声明了一个名为_self的变量,并将this赋给它。这创建了一个全局变量,这样事件监听器在对象范围之外调用的任何回调事件仍然能够访问父类,因为this将在回调事件或目标的范围内,而不是父类(在本例中是track)。

然后声明应用的当前状态,并将其默认状态设置为this.state.STOPPED

最后,您创建一个名为updateInterval的新变量,它将用于设置时间每秒更新的次数。例如,如果您想每秒更新间隔 500 次,您可以将updateInterval设置为updateInterval = 1000 / 500。增加该时间将对性能产生影响,因为这会影响画布动画的帧速率。

您将需要更新currentTimesetCurrentTime是一个私有方法,允许您设置播放头的当前时间。它还将回调任何使用_self.callbacks.didUpdateTime.call(_self, currentTime);将其自身指定为该方法回调的函数或方法。call是一个允许你在另一个对象范围内调用一个函数的方法。这将允许回调函数在其代码中使用this,并且this将是对进行回调的对象的引用,而不是回调函数的父对象。call的第一个参数是您想要从中传递范围的对象。之后的参数是回调方法将接受的参数。

接下来,您必须创建名为updateTime的私有方法。这将更新音轨的当前播放时间。该方法还检查currentTime是否达到了总轨迹长度。如果有,那么它将停止跟踪。

`app.track = function(length){

**      var setCurrentTime = function(time){**
**         currentTime = time;**
**         _self.callbacks.didUpdateTime.call(_self, currentTime);**
**      };**

**      var updateTime = function(){**

**         if(currentTime < length){**
**            setCurrentTime(currentTime + updateInterval);**
**         } else {**
**            _self.stop();**
**         }**

**   };**
}`

你会注意到这里使用了_self。这不是一个全局 JavaScript 变量,而是您之前声明的_self变量。updateTime在跟踪类/对象的范围之外被调用,所以_self维护一个对它的引用。这被更好地称为关闭

接下来,您将声明几个 getter 和 setter。创建它是为了能够访问对象范围之外的私有变量。当您不希望对象更改另一个对象的属性时,这很方便。例如,currentTime不应该在对象之外被操作,但是外部对象应该能够找出音轨的当前播放时间。使用不带 setter 的 getter 可以防止外部对象更改该值。

`app.track = function(length){

**   this.getCurrentTime = function(){**
**      return currentTime;**
**   };**

**   this.getLength = function(){**
**      return length;**
**   };    this.getState = function(){**
**      return state;**
**   };**
}`

本例中的 getters 将简单地返回私有变量;但是,您可以定义一个 getter,比如getCurrentTimeInSeconds,它将修改返回值,以便函数返回以秒为单位的回放时间。例如:

this.getCurrentTimeInSeconds = function(){    return (currentTime / 1000); }

接下来,您必须定义轨道的控件,例如播放、暂停和停止。

`app.track = function(length){

**   this.stop = function(){**
**      window.clearInterval(interval);**
**      state = _self.state.STOPPED;**
**      setCurrentTime(0);**
**      _self.callbacks.didStop.call(_self);**
**   };**

**   this.play = function(){**
**      if(state != _self.state.PLAYING){**
**         interval = window.setInterval(updateTime, updateInterval);**
**         state = _self.state.PLAYING;**
**         _self.callbacks.didStartPlaying.call(_self);**
**      }**
**   };**

**   this.pause = function(){**
**      window.clearInterval(interval);**
**      state = _self.state.PAUSED;**
**      _self.callbacks.didPause.call(_self);**
**   };**
}`

this.stop将停止跟踪,并使用window.clearInterval(interval)清除间隔计时器。stop方法还会使用state = _self.state.STOPPED将轨道的当前状态设置为0STOPPED。该方法还将重置当前时间,并调用didStop回调方法。

this.play将通过检查当前状态来查看曲目是否正在播放。如果曲目没有播放,那么它将创建一个新的间隔计时器。window.setInterval采用两个参数:回调方法和以毫秒为单位的间隔时间。如果您希望分配一个从设置初始间隔的函数中获取参数的回调,您可以使用以下方法:

var globalParam = 'foo'; window.setInterval(function(){    callbackFunction.call(this, globalParam); }, intervaltime);

记住globalParam必须用var声明,这样它才能存在于闭包中。

最后,定义默认的回调函数。

app.track = function(length){    ... **   this.callbacks = {** **      didUpdateTime: function(time){},** **      didStartPlaying: function(){},** **      didPause: function(){},** **      didStop: function(){}** **   };** };

如您所见,这些都是空函数。这允许你调用回调函数,即使它们没有被赋值。有四个回调函数:this.callbacks.didUpdateTimethis.callbacks.didStartPlayingthis.callbacks.didPausethis.callbacks.didStop

现在是时候开始创建播放按钮并深入画布了!在开始之前,了解 Canvas 的实际工作方式是很重要的。为了在画布上绘图,您需要获得它的上下文。如果你不熟悉什么是上下文,它就像一个隐藏的空间,你可以在那里画画。在您完成绘制后,上下文将呈现给用户。Canvas API 中目前只有一个上下文,所有的形状都绘制在它上面。在理想的情况下,您应该有几个上下文,在它们上面绘制单独的组件,并将每个上下文合并成一个上下文。目前,这是不可能的,将在本章中进一步解释。

画布上下文在一个基于坐标的系统上工作,从左上角开始,如图 7-5 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-5。 画布网格

首先,您需要定义几个全局变量。

app.playButton = function(id, track){ **   var canvas = document.getElementById(id),** **   context = canvas.getContext('2d'),** **   track = track,** **   _self = this;** }

如您所见,您通过使用getElementById获得了 Canvas 元素。然后通过使用canvas.getContext('2d')获得画布上下文,这将返回一个 2d 画布上下文供您绘制。然后显式声明 track 变量,并再次为任何回调方法将_self定义为this

通过为 canvas 元素创建新的属性,可以更容易地计算画布的某些方面。这是使用以下代码完成的:

`app.playButton = function(id, track){

**   canvas.center = {**
**      x: (canvas.offsetHeight / 2),**
**      y: (canvas.offsetHeight / 2)**
**   };    canvas.dimensions = {**
**      width: (canvas.offsetWidth),**
**      height: (canvas.offsetHeight)**
**   };**
}`

这将允许您快速检索中心坐标以及画布的宽度和高度,而无需将它们存储在全局变量中。例如,您可以简单地使用canvas.center.x来获取画布的中心 x 坐标。

接下来,您将需要为跟踪更新其计时器和跟踪暂停分配回调。

`app.playButton = function(id, track){

**   track.callbacks.didUpdateTime = function(time){**
**      _self.draw();**
**   };**

**   track.callbacks.didPause = function(){**
**      _self.draw();**
**   }**

}`

如您所见,两个回调都只是调用了playButton类中的draw方法。

接下来,您需要创建播放控制方法。这将用于通过播放按钮播放和停止曲目。这也允许其他对象或功能通过播放按钮开始或停止音轨。

`app.playButton = function(id, track){

this.togglePlay = function(){

**      switch(track.getState()){**
**         case track.state.STOPPED:**
**         case track.state.PAUSED:**
**            _self.play();**
**            break;**
**         case track.state.PLAYING:**
**            _self.stop();**
**            break;**
**      }**

**   };    this.play = function(){**
**      track.play();**
**   };**

**   this.stop = function(){**
**      track.pause();**
**   };**
}`

如你所见,有一个方法叫做this.togglePlay。切换播放方法将检查音轨的状态。如果停止或暂停,会触发play方法;如果它正在播放,就会触发stop方法。这些条件包含在一个switch语句中。为了减少混乱,switch语句是使用if语句的一个很好的选择。该声明由以下内容组成:

switch(**value**){    case **condition**:       /** condition code **/       break;    case **condition**:       /** condition code **/       break;    default:       /** default code **/       break; }

如你所见,这需要一个value。每个case代表一个condition来与value进行比较。如果condition匹配,则执行case内的代码,然后跳出switch。如果没有一个condition匹配,您可以使用default指定一个默认的动作。最佳实践是只比较switch语句中的整数值。

随着togglePlay方法的完成,this.playthis.stop方法都作为暂停或播放音轨的包装器。

音轨的完整代码如下:

`app.track = function(length){

this.state = {
      STOPPED: 0,
      PLAYING: 1,
      PAUSED: 2
   }; var length = (length * 1000),
      currentTime = 0,
      interval,
      _self = this,
      state = this.state.STOPPED,
      updateInterval = 1000 / 30;

var setCurrentTime = function(time){
      currentTime = time;
      _self.callbacks.didUpdateTime.call(_self, currentTime);
   };

var updateTime = function(){

if(currentTime < length){
         setCurrentTime(currentTime + updateInterval);
      } else {
         _self.stop();
      }

};

this.getCurrentTime = function(){
      return currentTime;
   };

this.getLength = function(){
      return length;
   };

this.getState = function(){
      return state;
   };

this.stop = function(){
      window.clearInterval(interval);
      state = _self.state.STOPPED;
      _self.setCurrentTime(0);
      _self.callbacks.didStop.call(_self);
   };

this.play = function(){
      if(state != _self.state.PLAYING){
         interval = window.setInterval(updateTime, updateInterval);
         state = _self.state.PLAYING;
         _self.callbacks.didStartPlaying.call(_self);
      }
   };    this.pause = function(){
      window.clearInterval(interval);
      state = _self.state.PAUSED;
      _self.callbacks.didPause.call(_self);
   };

this.callbacks = {
      didUpdateTime: function(time){},
      didStartPlaying: function(){},
      didPause: function(){},
      didStop: function(){}
   };

};`

现在该画停止按钮了。draw 方法在this.draw方法中被调用,上下文取自类中的私有变量。

绘制停止图标

停止按钮为 20px × 20px,应为实心矩形。要绘制任意比例的矩形,可以使用context.fillRect()方法。fillRect方法采用表 7-3 中所示的四个参数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

要绘制一个 20px × 20px 的简单矩形,可以使用以下代码:

context.fillStyle = '#000000'; context.fillRect(0, 0, 20, 20);

这将产生一个类似于图 7-6 所示的矩形。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-6。 一个 20px × 20px 的长方形

context.fillStyle将任何新闭合形状的填充设置为黑色或#000000,如矩形或圆形。

在为停止符号在播放按钮上绘制矩形的代码中,您需要考虑停止符号相对于画布的位置。您需要将停止符号直接放在画布的中心。要使停止符号居中,您需要计算停止符号左上角的 x 和 y 偏移量。要计算这一点,您需要将画布的宽度和高度分成两半,以得到偏移量。然后,您可以从形状的中心减去画布中心,得到 x 和 y 坐标。这个方法简单地将要绘制的形状的中心与画布的中心对齐。

下面的代码将使停止图标相对于画布本身的大小居中。

`app.playButton = function(id, track){

**   this.drawStop = function(){**
**      var width = 20,**
**         height = 20,**
**         x = canvas.center.x - (width / 2),**
**         y = canvas.center.y - (height / 2);**

**      context.beginPath();**
**      context.fillStyle = ‘#A0A0A0’;**
**      context.fillRect(x, y, width, height);**

**   };**
}`

正如您所看到的,您将停止图标的widthheight声明为20,以便以后可以引用它们。您还可以通过获得画布的中心 x 坐标减去矩形宽度的一半来计算 x 坐标。这将使停止图标水平居中。

接下来,y 坐标的设置方式也差不多,将画布(其中心)的高度减半,并减去停止图标高度的一半。然后将停止图标垂直放置在画布的中心,如图图 7-7 所示。结合这两种计算方法将使停止图标在画布中完全居中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-7。 将停止图标沿 x 轴和 y 轴居中

在开始在画布上绘制新形状之前,调用context.beginPath()是个好主意。这将在上下文中创建一个新路径,以便您开始绘制。这相当于在你在纸上画一个新的形状之前,把你的笔从纸上拿开。

接下来,你需要设置你要画的形状的fillStyle。2D 上下文 API 有几种绘图方法。API 最基本的方法和属性如表 7-4 和表 7-5 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

设置好样式属性后,您现在可以使用context.fillRect()绘制停止图标。

定义的方法将在当前上下文中绘制一个矩形。接下来,您需要创建一个方法来绘制播放按钮。

绘制播放图标

播放按钮稍微复杂一些。要绘制播放按钮,您需要下拉到上下文中的绘制路径。

为了绘制路径,您使用了moveTolineTo方法。这些方法允许你在不画线的情况下移动到某一点,在两点之间画一条线。

`app.playButton = function(id, track){

**   this.drawPlay = function(){**
**      var width = 20,**
**         height = 20,**
**         x = canvas.center.x - (width / 2),**
**         y = canvas.center.y - (height / 2);**

**      context.beginPath();**
**      context.moveTo(x, y);**
**      context.lineTo(x + width, y + (height / 2));**
**      context.lineTo(x, (y + height))**
**      context.fillStyle = ‘#A0A0A0’;**
**      context.fill();**
**   };**
}`

首先,将播放图标的widthheight设置为20(即 20px × 20px)。然后,将中心点设置为画布宽度的一半,减去播放按钮宽度的一半。您也可以对 y 轴进行同样的操作,就像您对停止图标所做的一样。

为了绘制 play 按钮,您需要在画布上预先映射三个点,以绘制起点和终点的直线。图 7-8 显示了在 20px × 20px 绘图环境中的大概坐标。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-8。 画一个三角形

如你所见,三角形有三个点:(0,0),(20,10),和(0,20)。就像你在正方形上做的一样,你必须根据它的宽度和高度来计算三角形上的点应该在哪里。你知道第一个点应该从 0,0 开始。第二个点应位于 x =形状宽度,y =形状高度/ 2 的位置。记住这一点,第三个也是最后一个点应该定位在 x =原点 x,y =造型高度的地方。这将创建一个等边三角形。下面的代码将创建这个:

`var width = 20, height = 20, startx = 0, starty = 0;
context.beginPath();
context.fillStyle = ‘#A0A0A0’;

context.moveTo(startx, starty);
context.lineTo((startx + width), (starty + (height / 2)));
context.lineTo(startx, (starty + height));
context.fill();`

正如你所看到的,你画了两条线形成等边三角形。您不必绘制另一条线来连接最终位置和原始位置。通过调用context.fill(),你将自动闭合原点和终点之间的间隙,并用context.fillStyle颜色填充矩形。前面的方法也考虑了绘制形状的起点。你可以改变startxstarty的值,它将总是在那个位置画一个等边三角形。

随着图标的创建,现在是时候设置播放头了。播放头简直就是一个逐渐打开的圆圈。半圆下面是另一个圆圈,有对比色帮助区分音频播放的进度,如图图 7-9 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-9。 回放头

绘制播放头

Canvas 的问题是你不能单独为每个形状制作动画。移动或激活画布元素需要完全重绘画布。在 Canvas 中制作动画需要在 JavaScript 中跟踪每个对象的状态,然后在每次调用draw时渲染它。这是一个漫长而费力的过程,但是通过正确的实现,可以减少耗时…

要跟踪跟踪的进度,您首先需要计算出其当前进度的百分比。

`app.playButton = function(id, track){
   …
**   this.draw = function(){**
**      var percentage = 100 - ((track.getCurrentTime() / track.getLength())**
*** 100);**

**   };**
}`

这简单地计算为(当前时间/长度)* 100。这将给你一个介于 1 和 100 之间的可预测的数字。您需要返回一个百分比,其中 100%表示音轨开始时,0%表示音轨结束时。为此,您只需从 100 中减去已播放的百分比,即可得到剩余曲目的百分比。

下一步是根据剩余曲目的百分比计算播放头的角度。你知道 2 * π (PI)的结果将等于以弧度表示的整圆的角度。0 * π (PI)会得出 0,会得出一个空圆。

`app.playButton = function(id, track){
   …
   this.draw = function(){
      var percentage = 100 - ((track.getCurrentTime() / track.getLength()) *
100);
**      var endradians = (percentage * (2 / 100)) * Math.PI;**

};
}`

图 7-10 显示了 PI 计算的重要位置。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-10。 n * π弧度

如您所见,0 从圆的右侧开始,2 也将导致相同的位置。如果将圆弧的起始位置设置为 0,结束位置设置为 2 * PI,结果将是空的,因为圆由于角度为 0 而没有圆周。

您将定期重绘画布,因此每次重绘时都需要清除画布,以防止在新的上下文形状下面显示以前的上下文形状。这可以通过调用context.clearRect(0, 0, canvas width, canvas height);来实现。这将在整个画布上绘制一个清晰的矩形。您不必担心矩形绘制后存在的内存或形状,因为只有当前上下文保存在内存中。

`app.playButton = function(id, track){
   …
   this.draw = function(){
      var percentage = 100 - ((track.getCurrentTime() / track.getLength()) *
100);
      var endradians = (percentage * (2 / 100)) * Math.PI;

**      context.clearRect(0, 0, canvas.dimensions.width,**
canvas.dimensions.height);
   };
}`

下一步是画一个圆圈,作为播放按钮的背景。画一个完整的圆,然后用黑色填充,就可以做到这一点。

`app.playButton = function(id, track){
   …
   this.draw = function(){
      var percentage = 100 - ((track.getCurrentTime() / track.getLength()) *
100);
      var endradians = (percentage * (2 / 100)) * Math.PI;

context.clearRect(0, 0, canvas.dimensions.width,
canvas.dimensions.height);

**      /****
**       * Draw the play button backdrop**
**       /*
**      context.beginPath();**
**      context.fillStyle = ‘#000000’;**
**      context.arc(canvas.center.x, canvas.center.y,**
**         canvas.center.x - 10, 0, 2 * Math.PI, false);**
**      context.fill();**

};
}`

下一步是画一个没有填充的圆,并对其应用一个笔画,以提供当游戏头移动时显示的背景。

`app.playButton = function(id, track){
   …
   this.draw = function(){
      var percentage = 100 - ((track.getCurrentTime() / track.getLength()) *
100);
      var endradians = (percentage * (2 / 100)) * Math.PI;

context.clearRect(0, 0, canvas.dimensions.width,
canvas.dimensions.height);

/**
       * Draw the play button backdrop
       */
      context.beginPath();
      context.fillStyle = ‘#000000’;
      context.arc(canvas.center.x, canvas.center.y,
         canvas.center.x - 10, 0, 2 * Math.PI);
      context.fill();

**      /****
**       * Draw the background for the play head**
**       /*
**      context.beginPath();       context.arc(canvas.center.x, canvas.center.y,**
**         canvas.center.x - 20, 0, 2 * Math.PI);**
**      context.lineWidth = 5;**
**      context.strokeStyle = “#FFFFFF”;**
**      context.stroke();**

};
}`

最后,这是一个根据音轨的当前播放位置绘制播放头的例子。代码与为播放头绘制背景的代码相同,除了结束角度被设置为代码中先前声明的endradians来表示轨道的进度。

`app.playButton = function(id, track){
   …
   this.draw = function(){
      var percentage = 100 - ((track.getCurrentTime() / track.getLength()) *
100);
      var endradians = (percentage * (2 / 100)) * Math.PI;

context.clearRect(0, 0, canvas.dimensions.width,
canvas.dimensions.height);

/**
       * Draw the play button backdrop
       */
      context.beginPath();
      context.fillStyle = ‘#000000’;
      context.arc(canvas.center.x, canvas.center.y,
         canvas.center.x - 10, 0, 2 * Math.PI);
      context.fill();

/**
       * Draw the background for the play head
       */
      context.beginPath();
      context.lineWidth = 5;
      context.strokeStyle = “#FFFFFF”;

context.arc(canvas.center.x, canvas.center.y,
         canvas.center.x - 20, 0, 2 * Math.PI);

context.stroke();

/**
       *** Draw the progress head**
       /
      context.beginPath();
      context.lineWidth = 5;       context.strokeStyle = “#A8A8A8”;
      context.arc(canvas.center.x, canvas.center.y,
**         canvas.center.x - 20, 0, endradians);
*
      context.stroke();

};
}`

该方法的最后一步是决定每次重绘画布时是在按钮上绘制停止图标还是播放图标。这是通过一个switch语句实现的。

`app.playButton = function(id, track){
   …
   this.draw = function(){
      var percentage = 100 - ((track.getCurrentTime() / track.getLength()) *
100);
      var endradians = (percentage * (2 / 100)) * Math.PI;

context.clearRect(0, 0, canvas.dimensions.width,
canvas.dimensions.height);

/**
       * Draw the play button backdrop
       */
      context.beginPath();
      context.fillStyle = ‘#000000’;
      context.arc(canvas.center.x, canvas.center.y,
         canvas.center.x - 10, 0, 2 * Math.PI);
      context.fill();

/**
       * Draw the background for the play head
       */
      context.beginPath();
      context.lineWidth = 5;
      context.strokeStyle = “#FFFFFF”;

context.arc(canvas.center.x, canvas.center.y,
         canvas.center.x - 20, 0, 2 * Math.PI);

context.stroke();

/**
       * Draw the progress head
       */
      context.beginPath();
      context.lineWidth = 5;
      context.strokeStyle = “#A8A8A8”;
      context.arc(canvas.center.x, canvas.center.y,          canvas.center.x - 20, 0, endradians);
      context.stroke();

/**
       *** Decide whether to draw the play or the stop button**
       */
      switch(track.getState()){
         case track.state.PAUSED:
         case track.state.STOPPED:
            this.drawPlay();
            break;
         case track.state.PLAYING:
            this.drawStop();
            break;
      }

};
}`

可以看到,如果曲目状态是暂停或停止,会绘制播放图标;如果曲目正在播放,则会绘制停止图标。

playButton的完整代码如下。您会注意到,在代码示例的底部,有一个事件侦听器为画布绑定触摸事件。这将触发togglePlay()方法。

`app.playButton = function(id, track){

var canvas = document.getElementById(id),
      context = canvas.getContext(‘2d’),
      track = track,
      _self = this;

canvas.center = {
      x: (canvas.offsetHeight / 2),
      y: (canvas.offsetHeight / 2)
   };

canvas.dimensions = {
      width: (canvas.offsetWidth),
      height: (canvas.offsetHeight)
   };

/**
    * Track callback methods
    */
   track.callbacks.didUpdateTime = function(time){
      _self.draw();
   }; track.callbacks.didPause = function(){
      _self.draw();
   }

/**
    * Track controls
    */

this.togglePlay = function(){

switch(track.getState()){
         case track.state.STOPPED:
         case track.state.PAUSED:
            _self.play();
            break;
         case track.state.PLAYING:
            _self.stop();
            break;
      }

}

this.play = function(){
      track.play();
   };

this.stop = function(){
      track.pause();
   };

this.drawStop = function(){
      var width = 20,
         height = 20,
         x = canvas.center.x - (width / 2),
         y = canvas.center.y - (height / 2);

context.beginPath();
      context.fillStyle = ‘#A0A0A0’;
      context.fillRect(x, y, width, height);

};

this.drawPlay = function(){
      var width = 20,
         height = 20,
         x = canvas.center.x - (width / 2),
         y = canvas.center.y - (height / 2);

context.beginPath();
      context.moveTo(x, y); context.lineTo(x + width, y + (height / 2));
      context.lineTo(x, (y + height))
      context.fillStyle = ‘#A0A0A0’;

context.fill();

};

this.draw = function(){
      // Draw the progress bar based on the
      // current time and total time of the track
      var percentage = 100 - ((track.getCurrentTime() / track.getLength()) *
100);
      var endradians = (percentage * (2 / 100)) * Math.PI;

context.clearRect(0, 0, canvas.dimensions.width,
canvas.dimensions.height);

context.beginPath();
      context.fillStyle = ‘#000000’;
      context.arc(canvas.center.x, canvas.center.y,
         canvas.center.x - 10, 0, 2 * Math.PI);
      context.fill();

context.beginPath();
      context.arc(canvas.center.x, canvas.center.y,
         canvas.center.x - 20, 0, 2 * Math.PI);
      context.lineWidth = 5;
      context.strokeStyle = “#FFFFFF”;
      context.stroke();

context.beginPath();
      context.arc(canvas.center.x, canvas.center.y,
         canvas.center.x - 20, 0, endradians);
      context.lineWidth = 5;
      context.strokeStyle = “#A8A8A8”;
      context.stroke();

switch(track.getState()){
         case track.state.PAUSED:
         case track.state.STOPPED:
            this.drawPlay();
            break;
         case track.state.PLAYING:
            this.drawStop();
            break;
      }

};    canvas.addEventListener(‘touchend’, function(e){
      _self.togglePlay();
      e.preventDefault();
   });

this.draw();
};` 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-11。 最终播放按钮

存储数据

传统上,要在移动 web 应用中持久化数据(如用户名),需要将这些信息存储在 cookie 中。cookies 的问题在于,虽然它们非常适合存储少量数据,但对于大量数据,例如 JavaScript 对象,它们很快就变得难以管理。当您希望存储应用的当前状态时,这可能是完美的,以便当用户返回时,他们可以从他们离开的地方继续,就像本机应用一样。

不幸的是,本地存储不支持存储对象,只支持字符串值。但是您可以使用 JSON.stringify 将对象转换为字符串,然后使用 JSON.parse 将它们转换回对象。

要使用本地存储来存储数据,只需使用localStorageAPI。这些包括表 7-6 中所示的属性和方法。

例如,如果您想存储一个包含用户名、电子邮件地址和联系电话号码的对象,您可以创建如下内容:

var user = {name: "John Seagate", email: "john.seagate@hello.com", contactNumber: "012345678910"} localStorage.setItem('user', JSON.stringify(user));

要检索该项,您可以使用以下代码:

var user = JSON.parse(localStorage.getItem('user'));

移动 JavaScript 库

JavaScript 库可以帮助减轻任何类型的前端开发的负担。它们可以帮助提供一致的 API,从 DOM 操作一直到厨房水槽。本章将使用三个库作为示例。

  • 李荣
  • 移动 jQuery
  • 煎茶触摸

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-12。【jQuery Mobile】(左),和的 Sencha Touch(右)

XUI 是专门针对移动设备的,它提供了类似 jQuery 的语法和轻量级的 DOM 操作,简单的 API 抽象来生成和处理 Ajax 请求,并执行基本的基于 JavaScript 的动画。它有一个很像 jQuery 的插件架构,所以你可以扩展 XUI 来满足你的需要,并为你的项目创建额外的插件。

jQuery Mobile 和 Sencha Touch 都是重量级产品,从某种意义上来说,它们不仅提供了一个你可能称之为“普通 JavaScript”的抽象,而且还提供了一个你可以轻松构建移动 web 应用的框架。

jQuery mobile 可以帮助您快速构建项目的原型,然后进一步对它们进行皮肤处理。它的用户界面依赖于传统的简单 HTML (POSH)。然后用 CSS 和 JavaScript 增强了这种时髦。jQuery 通过使用 CSS 媒体查询来改变布局,可以在基于手机和平板电脑的设备上运行。

Sencha Touch 提供了一种更复杂、功能更全的开发方法。你不用 HTML 写卡片或页面。相反,您通过 JavaScript 配置每个页面并提供内容。Sencha Touch 提供了许多 UI 增强和小部件,包括离线存储数据的能力和通过使用代理开箱即用的在线。这允许您通过一个通用界面存储和检索数据,并指定您在配置中使用的存储类型。

jQuery 和 Sencha Touch 都很棒;然而,您最终创建的是用 jQuery mobile 或 Sencha 思维方式构建的应用。使用移动库来完成一个基于移动的项目并没有错,但是在选择一个框架或库的时候,你应该注意你应该寻找的东西。

文件大小

确保任何库都具有较小的占用空间是很重要的。不幸的是,一些移动运营商不提供无限制的数据计划,所以确保访问你的移动网站不会对你的用户的口袋产生大的影响是很重要的。同样重要的是要记住,尽管 3G 和 LTE 提供相对较高的数据速度,但并非所有用户都可以随时访问 3G 或 LTE。这对加载时间有影响,因为 500kB 的库以及整个应用的图像和 CSS 素材可能需要几秒钟才能通过 3G/LTE 下载;这可能需要更长的时间。因此,迎合最小公分母,这将是优势,在这种情况下。

文件数量

在撰写本文时,移动浏览器可以发出的请求数量非常有限。这意味着,如果您有许多资源要下载到浏览器,这会影响加载时间。您可以通过确保您使用的 JavaScript 库提供以下功能来克服这个问题:

  • 源的缩小和连接版本
  • 图标和按钮等东西的 sprite 表
  • 内容交付网络(CDN)托管版本的库
活动

您应该查看库的最新更新是什么时候,以及新版本发布的频率(发布周期)。这对您的开发有影响,如果您是一个关注 bug 修复和主要发布版本的人,您可能会发现自己一直在用最新的发布版本更新代码;或者更糟糕的是,如果这个库被废弃并且不再被维护,这意味着你将不得不学习一个新的库或者维护刚刚被废弃的库。

CSS3 支持

许多库现在正在更新他们的代码,以利用 GPU 对 CSS3 动画和过渡支持的增强。您应该检查您的库,以确保它支持 CSS3 过渡和动画,或者具有针对 JavaScript 动画和过渡的路线图。这将为您的应用提供性能增强。

总结

从这一章开始,你应该对 JavaScript 和移动给你带来的新功能有了更深的理解。

您应该能够区分过程 JavaScript 和面向对象 JavaScript 之间的区别,并且将对象相互传递是一个比在全局名称空间中调用函数好得多的概念和原则。

您还应该对 JavaScript 中的作用域有一个简单的了解,以及如何通过闭包和使用_self = this在对象之间维护它。

您还应该对如何在 JavaScript 中处理触摸事件有一个简单的了解,通过利用这一点,您可以生成更加丰富的应用。

这一章非常详细地谈到了画布。你应该了解 HTML5 Canvas 是如何工作的,以及上下文和绘图 API 背后的想法,如arcfillRect,以及样式 API,如lineStylefillStyle