CSS-Windows8-应用开发教程-全-

CSS Windows8 应用开发教程(全)

原文:CSS for Windows 8 app development

协议:CC BY-NC-SA 4.0

零、简介

image 注意世界上的网络开发者数量巨大,这些开发者中的每一个人现在都是 Windows 8 的开发者——目标是有史以来最大的设备市场。

你是网页开发者吗?我是。我在 1994 年开始对网页进行逆向工程,从那以后我就被这个平台深深吸引住了。它从一开始就受到限制,但即使在大约二十年后的今天,它仍然吸引着开发者和消费者。我使用非常优雅和强大的 XAML(微软自己的用户界面(UI)技术)的经历偶尔会提醒我 HTML 的局限性,但我只是不断回到 web 堆栈,它具有开放和宽容的语法以及遍布全球的影响力。

也许你有一些网络技术的经验,你已经准备好为 Windows 8 用户编写一个应用程序。您需要掌握三种核心 web 技术:用于文档结构的 HTML(超文本标记语言)、用于布局和样式化这些文档的 CSS(级联样式表)以及用于实现应用程序逻辑的 JavaScript。这是一本关于 CSS 部分以及 CSS 在 Windows 8 应用开发中如何表现的书。无论您是否有 CSS 经验,您都将了解如何使用这种无处不在的样式语言。

我猜你至少对 Windows 8 有点熟悉。微软极受欢迎的 Windows 操作系统的最新版本是一个非常有趣的版本。这很有趣,因为它非常不同,因为开发人员可以使用一些不同的技术来开发应用程序,包括 HTML、CSS 和 JavaScript——我喜欢称之为 web 堆栈。

使用 Windows 8,您可以使用 web 堆栈创建应用程序,它们将在操作系统上本地运行。他们可以访问设备的传感器和其他本机工具,甚至可以进行硬件加速。

我想强调的是,我不希望这仅仅是一本技术参考书。当然,有事实需要传递,但比仅仅传递事实更重要的是传递一个概念。如果你只对 CSS 标准的定义或者 API 的语法感兴趣,你可以很容易地在网上找到。然而,在我看来,开发是教育和体验的一部分,也就是说,经历了足够多的成功和不成功的实现,从而真正学到了在线文档无法传达的东西。

顺便说一下,发展有第三个部分——灵感。你真的必须热爱你正在做的事情,并对你要去的地方有一个愿景,这必须在你身上产生足够的兴奋,以帮助你度过艰难的时期。如果你没有发现自己熬夜或早起写代码,那么你应该问问自己这是否真的是你的领域。

所以,这不仅是一本关于 CSS 和 Windows 8 的书,也是一本关于风格的书。可以把它看作一本关于生产力和美感,以及通过富有表现力的语法达到这些目的的书。

这本书的一个独特之处是,在整个过程中,我会指出标准属性和模板对 Windows 8 应用程序有没有帮助。我还将指出微软特定于供应商的属性和值,它们将为您增添魅力。您还将了解由 WinJS 库提供的 Windows 8 控件的内部结构,以及您需要知道哪些类名,以便能够正确地设计它们的样式。

既然你已经拿起了一本关于 Windows 8 中 CSS 的书,我想你很有可能正在编写一个应用程序。也许这个应用是你的创意,你真的希望看到它在市场上取得成功。如果是这样的话,那就太好了,因为你的应用程序成功的首要标准是用户的使用体验。你可能听说过这叫做 UX(用户体验)。当用户的体验让他兴奋,因为它节省了他的时间,给他带来了信息或见解,甚至只是因为它看起来像好的艺术而让他兴奋,他通常不愿意向你这个作者支付真金白银,他愿意高度评价和推荐你的应用程序。

微软在 Windows 8 中实现网络堆栈方面迈出了一大步。我认为这是朝着正确方向迈出的一步,而且我认为他们在如何做到这一点上非常明智。微软现在明白了,我们身处一个厌恶所有专有事物的世界,这是有充分理由的。我们不想拘泥于一种解决问题的方式。我们需要一般的技能和大量的选择。我们也不想与一家公司联姻,而是希望使用基于许多公司合作产生的标准的技术。

独立的标准是一件好事——事实上是一件伟大的事情,这就是为什么全世界都喜欢 web 栈。开发人员通常会选择开放标准,即使与专有的替代方案相比,最好的实现有所欠缺。

微软已经使用支持 Internet Explorer 10 的相同引擎来支持使用 web 堆栈制作的 Windows Store 应用程序。这意味着,几乎没有例外,如果你的标记和 CSS 在 IE10 中工作,它将在你的 Windows 应用中工作,反之亦然。

image 注意在实现 web stack 的过程中,微软已经彻底地遵守了标准。

当 HTML、CSS 或 JavaScript 对某个特性有了标准时,它就会被采用,通常,当某个特性不存在时,就会向 W3C 推荐一个标准。当无法在合理的时间内实现推荐的标准时,可以通过行业标准的特定于供应商的标记、属性和值来添加扩展功能。即使是最严格的标准也总会有漏洞,事实上供应商可以用他们自己的一些属性来填补这些漏洞是非常好的。更好的是,这些标签、属性和值都有前缀,这样它们就可以与标准区分开来。

例如,在 CSS 中,属性和值以破折号(-)开头,后跟特定于供应商的标识符、另一个破折号,最后是属性名。微软的特定于供应商的标识符是 ms,所以特定于供应商的标签的一个例子是 ms-grid。

开发人员喜欢他们的工具,我认为您很有可能已经安装并运行了您选择的工具。如果您是开发、web 开发的新手,或者只是在寻找一些关于使用什么来创建和维护 CSS 的指导,那么 Visual Studio 是不二之选。

Visual Studio 是世界上最强大和最流行的 IDE(集成开发环境)之一,这是有充分理由的。最新版本名为 Visual Studio 2012,你可以从微软网站http://aka.ms/win8downloads获得 Windows 8 的免费版本——Visual Studio Express 2012。这个快速版没有专业版的所有功能,但它拥有创建 Windows 8 应用程序所需的一切。

无论您是使用 HTML/JS 处理 web 应用程序还是 Windows 8 应用程序,Visual Studio 都为 CSS 提供了出色的支持。您将获得 IntelliSense 支持,它建议属性名和有效值,您还将获得一些其他有用的功能,如当您指定 CSS 颜色时呈现的字形。

Visual Studio 并不是您工具箱中的唯一工具。你也有混合物。不管你有 Visual Studio 的免费速成版还是付费版,Blend 都在同一个安装中附带了 Visual Studio。

Blend 和 Visual Studio 都是应用构建机器。你可以在这两种工具中从头到尾创建一个应用程序,但是 Blend 更像是一个设计者友好的工具,而 Visual Studio 更像是一个开发者友好的工具。您可以同时使用这两种文件,并且可以并排使用,因为它们都处理同一种项目文件。因此,如果你想要一些设计帮助,打开 Blend,如果你想做一些调试或编写一些重要的代码,打开 Visual Studio。

现在是时候深入研究 CSS 并发现使用它来设计 Windows 8 应用程序是多么有趣。

一、Windows 8 设计

image “现在和以前完全不同了”(巨蟒剧团)

重新构想的 Windows

毫无疑问,你听说过 Windows 8 被称为“Windows reimagined ”,你根本无法否认 Windows 8 和之前的 Windows 版本之间的范式转变有多么巨大。

我清楚地记得我第一次使用 Windows 8 时,试图抓住关于使用屏幕边缘和角落的概念,从新的用户界面识别桌面,从 Charms 栏在应用程序内搜索,甚至只是重新启动系统!

从第一个屏幕开始,对旧模式的背离就很明显。图 1-1 显示了用户登录 Windows 8 时看到的内容。

9781430249832_Fig01-01.jpg

图 1-1 。Windows 8 中的开始屏幕

把这个和图 1-2 对比一下,这是我们刚登录 Windows 7 后看到的。

9781430249832_Fig01-02.jpg

图 1-2 。Windows 7 中显示开始菜单的桌面

开始屏幕取代了开始菜单。眼球追踪研究证明,一旦用户打开 Windows 7 中的开始菜单,绝大多数人都不会看屏幕上的其他地方。全屏专用于启动体验的概念非常有意义。将开始屏幕视为一个应用程序,并了解 Windows 如何创建身临其境的开始体验。

事实上,在 Windows 8 带来的所有变化中,我认为与我们作为应用程序设计师最相关的是用户对其任务和内容的沉浸感。Windows 8 把屏幕的每一个像素都奉献给了你的应用,你的应用独自站在舞台上接受用户的尝试和评判。

你不需要走很远就能找到一个很好的应用程序的例子,它具有沉浸式的体验。图 1-3 中内置的地图 app 就是一个很优秀的。

9781430249832_Fig01-03.jpg

图 1-3 。地图应用很好地展示了 Windows 8 应用的沉浸感。毫无疑问,屏幕上的每个像素都是应用程序专用的

通过 Windows 8,微软带给我们的不仅仅是一个好的设计,还有一个好的设计框架。微软不仅提供了一种应用开发语言,还提供了一种 ?? 设计语言 ??。这种设计语言被完整地记录下来,并成为你的应用程序设计的基础。

Windows 8 的设计语言记录在http://design.windows.com。MSDN 强大的开发者网站的这一部分专注于一个伟大的应用程序所要求的整个设计过程。这包括对以下事项的建议:

  • 界定你的应用范围,帮助你决定你的应用擅长什么
  • 你的应用程序的盈利策略
  • 左边距的建议大小
  • 命令在应用程序栏中的位置

设计你的应用程序排除了你对用户界面语言的选择。你也可以使用微软的 XAML 语言实现好的设计,但是我们将使用 HTML 和 CSS。我们将首先进行设计,然后实现好的设计。有些设计原则一开始可能看起来有点深奥,但是看看它们能提供什么指导是很重要的。不要担心,因为当我们学习如何实现我们的好设计时,我们将在本章的结尾得到更多的具体内容。

我们将通过观察设计良好的应用程序的一些特征来探索良好的 Windows 8 应用程序设计。

出色的 Windows 8 应用程序设计的特点

http://msdn.microsoft.com/en-us/library/windows/apps/hh464920.aspx的 design.windows.com 网站中列举了一个设计良好的 Windows 8 应用程序的一些特征。在这一章中,我将一步一步地介绍这些特质,就像它们在微软的网站上呈现的那样,并且用我自己的观点和经验来逐一剖析它们。

使用微软设计风格

如果你从关于 Windows 8 设计的一章中学到一个概念,可能是这个: Windows 8 设计就是让用户沉浸在他们的内容中。

什么是内容?应用程序的内容是用户启动应用程序的原因。对于一个财经 app 来说,内容就是股价或者一篇财经文章。对于一个社交应用来说,内容就是朋友或者对话。对于照片应用程序来说,内容就是照片。

Windows 8 基本上只是显示内容,而不是用关于内容的辅助信息来包装内容。当你在看一张照片时,你通常只会看到一张从屏幕的一边延伸到另一边的照片,而当你在社交网络上查看朋友的个人资料时,你会看到一个专用于该朋友基本信息的视图。

这不是用户界面发展的下一步。这与当前的趋势有所不同,当前的趋势是将所有内容都塞进一个屏幕,这样只需点击一下鼠标就能看到所有内容。视图设计表面 25%甚至更少的部分用于内容本身并不罕见。然而,这种趋势的缺陷是,当一个视图中添加了太多内容时,它就不能达到让用户的生活更轻松的目的,因为单个部分失去了它们的意义,而所有部分都失去了用户。

屏幕上不是内容的都是 chrome 。镀铬是汽车行业的一个术语,添加抛光的金属部件来吸引买家,即使它们在车辆功能中不起作用。单纯的审美没有错,但 chrome 在应用程序中的问题是,它会分散用户对其内容的注意力,从而影响应用程序。

Windows 8 的设计原则试图将内容放在 chrome 之前。并不是说 chrome 永远不会存在,而是一个 app 设计师要小心翼翼的介绍。你的应用应该总是优先考虑内容,排除干扰。请注意图 1-4 中 Windows 8 中的 Xbox Music 应用程序如何只显示内容。

9781430249832_Fig01-04.jpg

图 1-4 。Windows 8 应用将 100%的空间用于内容

将 chrome 添加到视图中有三个经典原因:导航、交互和布局。然而,这些对应用程序的可用性起着重要的作用,所以我们必须替换它们,而不是删除它们。让我们来看看这些类型的 chrome 的功能是如何在不影响内容的情况下实现的。

航行

静态导航有罪,造了很多 chrome。我们都熟悉标准的网站模型,在页面的顶部有一个标题,在页面的下方或左侧有导航链接。

为了避免点击,许多人甚至在导航菜单中填充多级悬停激活弹出菜单,甚至包括整个网站结构的树形视图。选项卡是另一种流行的现代形式的静态导航。

静态导航的问题是,它不符合我们应用程序的主要目的,即传递用户的内容。静态导航是关于用户下一步可能想去哪里的信息,但它不会告诉用户他们在哪里,因此它不是 Windows 8 应用程序中的推荐做法。那么我们如何促进导航呢?我们通过设计一个清晰的信息层次结构来实现这一点,该层次结构可以自然地将用户引导到下级或后续内容。

Windows 8 应用有两种主要的推荐导航模式。我将简要介绍每一个,但在http://msdn.microsoft.com/en-us/library/windows/apps/hh761500.aspx#hierarchical_system会读到更多。

三层导航

第一个模型是三层的分层导航模型。在这个模型中,所有的网站内容都存在于三个层次中——中心部分细节。对于一个内容驱动的应用程序来说,这种导航模型的层级数量恰到好处。少于这一数量,应用程序就无法对所有内容进行分类,也就无法为导航提供便利。再多的话,用户就会迷失方向,找不到自己的位置。

*中心

中枢是应用程序的入口点,是整个应用程序的总览。当然,hub 不能显示应用程序中的所有内容,但它可以显示每个部分中足以引起用户兴趣的部分。例如,一个购物应用程序不会显示一个类别中的所有产品,但可能会显示前几个特色产品,并邀请用户查看更多。

部分

如果用户选择一个 hub 部分的标题,他们将被带到部分页面。区段页面负责传递有关该区段的任何一般信息,并为用户提供对该区段下所有单个项目或实体的访问权限。再次使用陈词滥调的产品和类别示例,工具部分将负责让用户使用所有可用的工具。这并不意味着所有的工具都会立即出现在页面上。通常过滤和排序功能使用户的生活更容易。

详述

当用户到达应用程序中的某个实体后,比如某个特定的产品,他们会被带到详细页面。详细页面负责显示关于该特定实体的所有信息。一个详细页面可能包含一张照片、一个描述、一些相关的实体、一个类别列表,或者谁知道还有什么。底线是细节页面是通知用户关于一个实体的专用地方。

9781430249832_Fig01-05.jpg

图 1-5。三层导航模型的图表,显示了中心层、部分层和细节层

平面导航

内容驱动的应用在三层导航结构中运行良好,但是有些应用并没有真正的内容结构。一些应用程序只是相似视图的集合——彼此都是对等的。Internet Explorer 是一个很好的例子,它遵循平面导航模式,因为会话中的浏览器实例彼此之间没有层次关系。

在实现平面导航的应用程序中,上面的应用程序栏(通常用于导航)提供对这些对等页面的访问,如图 1-6 所示。

9781430249832_Fig01-06.jpg

图 1-6 。平面导航模型的示意图

互动

将 chrome 添加到页面的第二个常见原因是为了交互。交互就像按钮一样,给用户一个与应用程序互动的机会。

让你的用户有机会与你的应用程序交互当然是一个好主意,但是设计界面上的传统控件不一定是提供这种交互的最佳方式。在设计界面上塞满用户可能需要或可能不需要的控件,对于让用户沉浸其中来说是次要的,同时仍然给他们机会在需要的时候调用一些交互。

屏幕上的内容通常不言自明,用户会很快知道或学会如何与之交互。一个用户(甚至是一个小孩)通常能够凭直觉感觉到触摸一些内容会给你带来更多的信息。同样,你不需要重复其他命令来影响屏幕上的内容,而是可以允许用户选择一个或多个项目,然后从应用程序栏中选择一个命令。在应用程序栏中放置命令可以让用户控制,因为应用程序栏根本不会占用任何空间,直到用户选择这样做。

我们希望设计我们的 Windows 8 应用程序,以便内容可以自己命令。通常,这仅仅意味着内容是可点击的。你可以在图 1-7 中看到内容命令自身的例子。

9781430249832_Fig01-07.jpg

图 1-7 。通常不需要额外的命令按钮来查看内容项的更多信息。最好让用户直接与内容互动

有时命令不能直接由内容来表示,在这种情况下,我们被建议利用边缘。这意味着一些原本会使我们的设计界面变得混乱的命令可以被放到系统级的 Charms 栏中,或者放到更低或更高的 app 栏中。这被称为利用边缘,因为这些条准确地“隐藏”在屏幕边缘,直到用户调用它们。在这里,我们再次将用户置于控制之下。

图 1-8 显示了一个应用程序,其应用程序栏可见。这个应用程序栏由用户从屏幕外通过滑动手势、通过右击鼠标或者通过按键盘上的 WIN + Z 来调用。该栏是属于应用程序的命令的主页。更具体地说,它是您放置影响用户当前在上下文中的视图的命令的地方。

9781430249832_Fig01-08.jpg

图 1-8 。应用程序栏随时准备为用户提供应用程序级别的命令,但是直到用户真正请求它时它才出现

图 1-9 显示了一个带有可见魅力栏的应用程序。该条由用户通过从屏幕右下滑动手势、用鼠标点击屏幕的右上角或右下角、或者按键盘上的 WIN + C 来调用。作为开发人员,我们实际上并没有改变 Charms 栏。它被窗户烤焦了。然而,我们能够在我们的应用程序中实现某些代码契约,因此我们的应用程序将响应用户激活的魅力。例如,我们的应用程序可以共享、搜索、共享到设备,并提供应用程序设置。

9781430249832_Fig01-09.jpg

图 1-9 。Charms 栏以可预测的方式为每个应用程序带来系统级命令

应用程序栏上的命令是特定于给定应用程序的,它们位于一致的位置很好,因为曾经使用过 Windows 8 的用户会确切知道在哪里可以找到它们。用户将很快习惯搜索和分享的工作流程,并乐于在每个应用程序中使用相同的工作流程。

请记住,如果一个命令是基本工作流的一部分,那么它不应该对用户隐藏,而是应该放在设计图面上。秒表应用程序的开始和停止按钮就是一个很好的例子。隐藏这些按钮毫无意义,事实上,可以说这些命令本身就是应用程序的内容。

布局

最后,应用程序被仅仅用来分离内容的 chrome 弄得杂乱不堪。我们在谈论线条、盒子和隔断。实际上,在大多数情况下,当导航和交互 chrome 消失时,布局 chrome 往往会消失,因为布局 chrome 经常将内容与导航或内容与交互分开,而不是内容与内容分开。

因为我们将整个屏幕作为应用程序的画布,而不是画线和框,我们可以将空间花在空间上——如图 1-10 所示的空白空间。在 Windows 8 中,我们不怕在事物之间留出喘息的空间。

9781430249832_Fig01-10.jpg

图 1-10 。Windows 8 不怕使用大量空间作为您内容的呼吸空间

我们也创造良好的布局与良好的排版。Windows 8 的字体设计有其意义和目的。标准字体和标准字体大小给了我们一个类型的斜坡,用于表示屏幕上元素之间的层次关系,并根据它们的角色或重要性自然地将它们分开,如图图 1-11 所示。

9781430249832_Fig01-11.jpg

图 1-11 。类型渐变传达了类型的重要性和层次

你的应用也应该实现所谓的 Windows 8 设计轮廓。设计轮廓是标准的页边距、页眉位置、内容填充、字体和字号,给用户一种强烈的亲切感,如图图 1-12 所示。这种一致性应该与独特的设计风格相结合,让你的应用程序不仅熟悉,而且有自己的个性。

9781430249832_Fig01-12.jpg

图 1-12 。标准的页边距和页眉位置为 Windows 8 用户提供了一定的一致性

快速流畅

Windows 8 应用应该始终以快速流畅的方式运行。一个快速流畅的应用程序反应迅速,流畅,使用有目的的动画和清晰的图形。

出色的用户体验的一部分是用户感觉他们是在直接与信息交互,而不是与传递信息的系统交互。触摸内容并实时拖动它是一种很好的用户体验,但前提是内容精确地跟随用户的手指,而不是抖动、滞后或落后。

在很大程度上,你创建的 Windows 8 应用会很快很流畅,即使你没有想到这一点。这是因为许多内置控件和函数在设计时都考虑到了这一点,也是因为 Windows API(Windows 运行时或 WinRT)只为任何可能需要太长时间(在这种情况下,太长时间被定义为 50 毫秒)的方法提供异步调用。

然而,有时候,作为开发人员,您将直接负责在屏幕上移动并与用户交互的元素的性能。无论何时出现这种情况,你都应该确保性能被认为是快速和流畅的

动画也有助于让用户感觉他在和一个有机系统互动。我们将在第六章更深入地讨论动画。

完美地捕捉和缩放

Windows 8 不受限于固定硬件。有数百种电脑型号和许多外形规格将乐于运行 Windows 8 和您的应用程序。因此,你的应用程序应该具有适应性,你应该有意识地考虑每个页面在每个可能的视图状态下的外观。

您将处理的典型 Windows 8 视图状态包括:

  • 全屏横向:整个屏幕,宽度大于高度
  • 全屏纵向:整个屏幕,其高度大于宽度
  • 抓拍:只是左边(或右边),是 320 像素的风景
  • 填充:当另一个应用程序被抓取时剩余的空间

您不必支持纵向视图,但必须支持快照和填充视图。无论你喜不喜欢,你的用户都可以抓拍你的应用或者你旁边的应用。即使你的应用没有刻意处理这些状态就通过了认证,当你的用户发现的时候也会很尴尬。

使用正确的合同

我们在电脑上安装操作系统的一个主要原因是,抽象出我们希望以一致的方式工作的所有琐碎任务,而不必为每个应用程序重新创建它们。打印机驱动程序过去并不存在,因此每个应用程序都必须被编程,以便与任何可能正在使用的打印机对话。对这项任务来说,艰巨是一个合适的词。

一个可能成为抽象概念的现代规范是社交网络,或者更普遍的说法是分享。Windows 8 从我们的应用程序中抽象出了分享的概念,并将其包装在一个所谓的合同中,作为开发人员,您可以实现该合同。如果你不熟悉的话,契约只是你的代码中遵循的一种模式,当它试图要求你的应用程序共享或搜索或其他内容时,它会满足 Windows 期望找到的内容。在代码中实现契约并不是一件困难的事情。这样做的巨大优势是,只要你的应用程序能够共享并正确实现契约,那么它突然就能够与参与同一契约的每个应用程序共享。它甚至能够与应用程序共享,而这些应用程序是在你将自己的应用程序放入商店后才出现的!

搜索是另一种流行且常用的契约。通过正确的契约实现(同样是一个相当简单的任务),用户甚至可以在启动应用之前搜索你的应用,你可以决定当你的应用被搜索时会发生什么,以及如何产生搜索结果。

有很多 Windows 8 合同,我建议将任何可以增加实际价值的合同添加到您的应用程序中。

投资一块大瓷砖

实时互动程序是用新信息或图像更新的开始屏幕互动程序。动态磁贴很有效,因为它们是信息性的。我发现自己有时只需快速浏览一下我的开始屏幕,就能得到我需要的所有信息。我可以看到当天的天气,我的下一个约会,以及头条新闻,而不用启动任何应用程序。

请记住,应用程序的磁贴不仅会通知用户,还会邀请他们进入你的应用程序。您希望您的用户使用您的应用程序并经常使用它,您可以通过投资于您的实时磁贴的质量和功能来促进这种使用。

没有理由不投入创建丰富的磁贴支持所需的微小时间。制作一个宽版本的磁贴非常好,可以让用户在开始屏幕上更突出你的应用。如果您制作宽切片,建议您为其提供实时切片功能。什么都不做的双宽有点让人失望。

感受联系和活力

Windows 8 应用程序的设计目标之一应该是帮助用户感觉不像是在与设备交互,而更像是在直接与信息交互。

到目前为止,我们谈论的许多原则都有助于实现这一点,但是让用户保持联系并让他们的信息充满活力的一个特别重要的方法是通过屏幕通知和音频提示来真正打断他们正在做的事情。毕竟,实时磁贴是信息丰富的,但是用户更多的时间将花在应用程序上,而不是盯着他们的开始屏幕。对于用户认为重要的通知,无论他们当时正在做什么,他们都希望被打断。

当用户收到电话或即时消息时,当他们在网上拍卖中出价被超过时,当然还有当他们的彩票被抽出时,通过通知他们,让用户感觉他们的设备是活的。

漫游到云端

漫游到云是一个朗朗上口的现代术语,简单地说就是我们将某些信息保存到互联网托管的位置,而不是保存到设备上。这样,当用户在他的设备上重新加载操作系统或登录到另一个设备时,他们过去选择的设置和首选项可以无缝地使用。

在 Windows 8 中漫游的伟大之处在于它对开发者来说极其简单。所有需要做的就是通过 Windows 提供的漫游存储 API 保存设置。该设置实际上存储在本地,但 Windows 负责将所有这些设置与用户的 Microsoft 帐户同步(而不是与他们的 SkyDrive 同步)。清单 1-1 显示了一个保存到漫游存储器的示例设置,只是为了向您展示它有多简单。

清单 1-1。 用户最喜欢的颜色被保存到漫游设置中,因此它将在任何设备上可用

//JavaScript snippet
var appData = Windows.Storage.ApplicationData.current;
appData.roamingSettings.values["favoriteColor"] = "blue";

不仅要考虑漫游用户的数据,还要考虑他们的任务。如果用户正在做某事,当他们在另一台计算机上继续时,帮助他们重新开始。如果你已经使用了 Windows 应用商店中的网飞应用,你可能会注意到它会保存代表你在观看的电影中所处位置的时间戳,并在你返回时恢复它,即使你返回到不同的设备。用户喜欢这样,你应该在你自己的应用程序中寻找这个原则的应用。

遵循设计原则

我们已经看到了设计良好的 Windows 8 应用程序的许多特征,我们还有一个特征。最后一个特点是,应用程序应该遵循 Windows 设计原则。接下来我会列举并解释设计原则。这些原则比我们一直在看的特征稍微不实用,似乎更深奥,但我要提醒你不要忽视它们。理解并遵循这些一般原则将指导你设计你的应用程序,即使你不是从一个项目模板开始。

微软设计原则

http://msdn.microsoft.com/en-us/library/windows/apps/hh464920.aspx#traits_8_embrace_metro的 MSDN 上,Windows 8 的设计原则得到了完整的记录和更广泛的解释。它们是简单、高层次的指导原则。这些是 Windows 8 应用程序应该尊重的价值观,目的是让应用程序做好自己的工作,让用户开心。

根据微软的说法,这些原则是:

  • 对工艺表现出自豪
  • 少花钱多办事
  • 快速流畅
  • 真正数字化
  • 团结一致赢得胜利

展现对工艺的自豪感

一个应用程序设计者/开发者应该对工艺表现出自豪的原则当然说明了这些原则是多么的普遍。这意味着你永远不应该避免花费额外的时间来获得正确的细节。你应该担心的只是动画的行为和颜色的匹配。您应该关注左边距的确切大小以及用户在边缘情况下的体验。

让我用一个我不幸经常碰到的例子来说明这个原则。告诉我你是否能看出图 1-13 中的视图有什么问题。

9781430249832_Fig01-13.jpg

图 1-13 。典型的网格布局,但有一个微妙的设计缺陷

我希望你注意到了最左边的列表条目几乎是对齐的,但并不完全对齐。即使你的用户没有有意识地注意到一些看起来如此琐碎的事情,他们也会下意识地注意到它,这将是一个贬损者。

事半功倍

用更少的资源做更多的事情意味着你打破了现代的轨迹,在屏幕上添加了太多的东西,以至于没有一个东西达到它的目的。

当你设计用户将遵循的使用流程时,要记住的词是本质。问问你自己,你是否在展示信息的本质并给用户本质命令。不必要的信息和命令不仅是多余的,更糟糕的是会分散注意力。

请记住,就像 Windows 本身一样,您并不试图最终被发现。最终身临其境要好得多。

快速流畅

这个原则听起来很熟悉,因为它是设计特征的重复,这说明了这个概念的重要性。当用户使用一个他会描述为“快速和流畅”的应用程序时,他的感觉是很难描述的,但当你看到它时你就知道了。

不要害怕花一些钱在专业的设计帮助上,这些帮助不仅在静态图形方面有经验,而且在动态屏幕和用户体验方面也有经验。

真正数字化

我相信在应用程序开发中,哲学比科学更重要。如果你缩小过去 30 年,看看计算的大规模采用,很明显开发者仍然在试图找出好的架构和可用性。

我们抓住我们知道的范例,把它们拖进我们的数字系统,同时对它们的不适合做鬼脸。也许只有现在我们才知道,数字系统有自己的规则,而且这些规则往往比我们模拟世界的规则更自由,用户可以学习甚至热爱数字世界。

在我们开发应用时,我们充分利用我们拥有的数字系统,避免应用设计接受与数字世界无关的模拟世界的约束。毕竟,我们是在和屏幕上的像素打交道——这是一个必须接受的基本事实。

众志成城

用户不使用 apps他们完成任务。有时,这些任务涉及单个应用程序,但通常它们涉及应用程序的编排。想象一下下面的场景。

你的应用程序可以帮助用户在餐厅预订。你的用户可能会单独使用你的应用,但更有可能你的用户会:

  • 在机场登上自动扶梯
  • 查看他们的旅行管理应用程序,找到他们酒店的位置
  • 查看酒店附近的餐馆
  • 根据用户评论选择餐馆
  • 在餐厅预订(使用您的应用程序!)
  • 将预订确认发送给同事,然后走下自动扶梯

那个用户是一个快乐的用户。他不仅仅使用了一个应用程序;他在很短的时间内完成了一些重要的事情。在乘坐自动扶梯的过程中,免费的和信息丰富的动画非常重要,每个应用程序的 UX 都非常重要,应用程序到应用程序的共享也非常重要。

要制作一个成功的 Windows 8 应用,你必须考虑用户。其中一部分是确保你的应用程序能够与其他应用程序和设备很好地配合,以完成整个使用场景。

这个设计原则的另一个方面是利用已建立的约定、标准和建议。通过这样做,你将利用用户已经了解的 Windows 8 应用。他们会在你的应用中感到安全和熟悉。

我们已经看到了一些设计特点和原则,希望能在你设计应用程序的过程中有所帮助。现在让我们来看看一个虚构的应用程序设计。

设计场景

在我看来,衡量一个成功应用的标准只有一个。不是安装数量的问题。不是发射次数的问题。不是很高的收视率或很好的评论,甚至不是你银行账户里的钱。

衡量一款成功应用的标准是它给用户生活带来的价值。

永远不要忘记这一点。如果你开发了一个能带来价值的应用,你就开发了一个能带来金钱的应用。不幸的是,我们不能直接量化附加值,所以我相信我们会继续根据许多其他因素来衡量我们的应用程序,我相信你会继续欣赏积极的现金流。

范围

在应用程序开发中获胜的最好方法之一就是做好定义你的应用程序的范围 的工作。你的 app 要做一件事,并且做好。我们称之为你的应用程序的最擅长的“陈述

你可能注意到了当今软件世界的趋势,尤其是在移动领域,从几个无所不能的应用转向许多每个都做得很好的应用。如果你的应用程序还能帮他们报税和煮咖啡,你就不会让任何人心碎。如果它做了它想做的事情,并且做得很好,你的用户会喜欢它,给它很好的评价,并给你送现金。

做好一件事意味着很好地界定你的应用。这意味着在你写任何代码之前,你要确定你的应用程序会做什么不会做什么。你可能已经知道,确定它不能做什么——对功能说——比添加新功能更困难。

明确地说,巨大的、健壮的和功能齐全的应用程序有它们的位置,如果你有办法承担这些,那就让它成为你的范围。然而,如果你是个人开发者,那么你需要练习裁剪特性。仅仅因为你实现某事,仅仅因为它真的很酷并不意味着你应该去做。作为开发人员,我们倾向于成为理想主义者,我们倾向于非常想添加一些特性来证明我们可以做到。

为了保持特性的统治地位,我推荐使用一个最擅长的语句。你最擅长的陈述是一个简单、简洁的句子,通过填写下面句子中的空格形成:

这款应用在 _______________ 是同类产品中最好的。

确保你的回答具体、简洁、清晰。这里有一个例子:

这款应用是同类应用中最好的,它帮助用户寻找当地志愿者的机会,帮助老年人或残疾人做家务、庭院工作或其他家务。

请注意,该声明是如何具体说明谁参与其中的——志愿者加上老年人和残疾人。声明也简短明了。该应用程序不会组织志愿者活动或帮助筹集资金,也不会接受非营利组织的捐款。它只会帮助用户找到一个志愿者的机会。

这句话通过清楚地陈述它做什么和排除它不会做什么,对定义你的应用程序的范围有很大的帮助。然而,帮助人们找到志愿者的机会可能涉及不止一个使用场景。

使用场景

我们的应用程序的愿景正在形成,但它仍然是抽象的,所以为了巩固它,我们将确定使用场景是什么。对于这个应用程序,以下内容如何:

  • 允许用户浏览当前位置附近和不久的将来(可能是接下来的两周)潜在的志愿者机会,并按位置和日期过滤机会。
  • 允许用户查看他们所有的当前志愿者机会
  • 允许用户与其他志愿者和接受帮助的人交流。

当你头脑风暴的时候,你可能会产生很多场景,但是这个列表应该被精简,只包括那些直接支持最佳陈述的场景。

这些使用场景中的每一个都暗示了许多支持特性或功能。例如,提供潜在的志愿者机会意味着我们向用户提供每个机会的完整细节,包括位置和持续时间,可能需要什么工具等等。通常,这些使用场景会形成我们将在 hub 页面上看到的部分。

在这一点上,有一些图表纸甚至一些专门的应用程序设计纸将想法写在纸上是非常有帮助的,而不会有实际实现的阻碍。我已经创建了一个 Windows 8 应用设计表,并在http://codefoster.com/designsheet以 PDF 格式提供。第一页包括为应用程序设计标准三层导航模型的框架,第二页只是一个 1366 x 768 的整页设计图面,带有浅色网格以及推荐边距、快照视图和应用程序栏的指南。希望这个资源可以帮助你设计你的应用。

9781430249832_Fig01-14.jpg

图 1-14。来自codefoster.com/设计表的 Windows 8 应用设计表

让我们使用这个设计表来勾画出一个枢纽网页。我们将我们的应用程序命名为好帮手,我们将有三个部分:我的演唱会连接附近的演唱会。这些部分直接对应于我们的三个使用场景。

请注意,该应用程序确实遵循了基本的 Windows 8 设计准则,但我们在布局上有点创意(可能还不够),让我们的应用程序有一些自己的个性。在我的演出部分和附近的演出部分中的机会都使用相对较大的矩形,以允许我们在必要时将一些图像和大量信息带到中心页面。连接部分提供了一列较小的矩形,类似于聊天会话,因为该部分有助于应用程序成员之间的通信。

请记住,hub 页面应该作为整个应用程序范围的一瞥,因此我们应该为每个使用场景找到一个位置。用户喜欢在应用程序中有方向感,一个设计良好的 hub 可以通过不断向用户提供概览来实现这一点,并使其易于深入了解关于给定部分或实体的更多信息。

很明显,hub 页面上没有足够的空间来放置应用程序中所有可用的内容。例如,nearly gigs部分可能有 400 个机会,但 hub 只会显示其中的一部分。在这种情况下,将最近的机会带到用户中心是有意义的,因为这些机会可能是他最有可能注册的。

该中心让我们不仅可以访问应用程序中可用的部分,还可以直接访问某些项目。在图 1-15 中的概念图中,有三个项目填充了我的工作部分,表明用户已经注册了这三个志愿者机会。触摸其中一个项目,用户将直接导航到该机会的详细信息。

9781430249832_Fig01-15.jpg

图 1-15 。Good Help 应用程序中心页面的第一张草图

让我们继续我们的应用程序的设计,并绘制一个页面的概念。我们将设计 gig 页面,它将传递任何一个 gig 的完整细节。这是用户通过触摸中心上的一个机会来导航到的页面。图 1-16 显示了这个概念。

9781430249832_Fig01-16.jpg

图 1-16 。详细页面的草图

这个设计场景就是这样。我希望这个明显虚构的应用程序在概念上足够接近您正在开发的应用程序,以便您能够快速应用这里的设计原则。

摘要

在这一章中,我们探讨了一个设计良好的 Windows 8 应用程序的一些特征,以及一些需要牢记的设计原则,以帮助指导您完成应用程序的设计和实现过程。

我们访问的最重要的设计特点鼓励我们使用微软的设计风格,包括让用户沉浸在他们的内容中,尽我们所能消除干扰。Windows 8 应用更注重内容的沉浸感和一致性,而不是可发现性。

应用程序开发人员必须打破为 chrome 使用设计界面的习惯,这可能是静态导航、用户交互或显式布局。相反,我们学习在不转移用户注意力的情况下替换这种功能的技术。例如,我们通过让内容执行自己的命令来学习如何导航。我们学习了如何让用户通过利用屏幕边缘进行交互——使用应用程序栏和魅力栏。最后,我们学习了如何通过有意识地使用空间和排版来布局我们的内容,而不需要显式的布局工件。

我们通过运行部分设计场景得出结论,在该场景中,我们处理应用程序的范围,然后粗略定义其中心页面和一个详细页面。这个练习与你创建的每个应用程序的过程大致相同(尽管规模较小)。

设计过程是伟大的,当然也是关键和先决条件,但现在是时候让我们动手,投入代码,并学习创建一个实际的 Windows 8 应用程序的基础知识了。*

二、Windows 8 开发简介

image 注意对于软件开发人员来说,这是一个激动人心的时刻。

这本书的目标是帮助你深入了解 CSS,尤其是当你为 Windows 8 开发应用程序时它的应用方式。然而,如果你没有任何开发 Windows 8 应用程序的经验,你将很难实践你所学的任何东西。因此,如果这对你来说是一个全新的世界,我们将利用这一章来了解 Windows 8 架构、对你开发应用有价值的工具,以及如何在本地和 Windows 应用商店分发你的应用。

Windows 8 架构

在 Windows 8 进入我们的生活之前,Windows 应用程序可以使用 C++编写,并且可以本地访问 Windows 核心 Win32 库。他们可能更愿意坐得高一点(逻辑上),用 C#或 Visual Basic 编写,在。NET 框架。或者,如果你稍微扩展一下你的思维,想象一个 web 应用程序是一个 Windows 应用程序,那么一个应用程序可以使用 web 语言编写,托管在一个 web 服务器上(无论是本地的还是远程的),然后甚至位于 web 浏览器的更高位置。

Windows 8 并没有抛弃任何现有的应用程序创建模式,但它确实引入了一种全新的、令人兴奋的模式。

Windows 8 引入 WinRT。WinRT 是一个全新的现代 API,是为现代应用程序而从头设计和构建的。它不仅仅是 Win32 库或。NET 框架。

Window 的新架构令人兴奋的原因之一是,它可以被几种不同的技术堆栈所瞄准。它可以被 C++和 XAML、C++和 DirectX、C#/VB 和 XAML 以及 JavaScript 和 HTML 作为目标。不同的语言被投射到 WinRT 域中,所以它们不构成额外的层次,并且该模型为将来将其他语言投射到 WinRT 中留下了可能性。图 2-1 显示了目前 WinRT 上的不同的语言堆栈。

9781430249832_Fig02-01.jpg

图 2-1 。可以针对 WinRT 的语言的宏观视图

这些堆栈中的每一个都有其独特的环境和优势。如果你用 C++创作,你可以使用一些现有的 C 和 C++组件。如果你是 C#/VB 和 XAML 的作者,你会对。NET 框架。它不是整个框架,但它是为这些现代应用程序量身定制的子集。

如果您使用 JavaScript 和 HTML 进行创作,您将获得广泛的、基于标准的 web 堆栈,供全球大量开发人员使用。你得到了浏览器的文档对象模型,你得到了我们非常喜欢但又不太喜欢的动态持久语言——JavaScript。也许您得到的最令人兴奋的东西是已经编写了数量惊人的 JavaScript 代码来做许多您可能想做的事情。

然而,当你使用 web stack 为 Windows 8 开发一个应用程序时,该应用程序并不在针对 web 服务器的浏览器中运行;它在平台上本地运行。该应用程序是硬件加速的,并由系统信任以访问系统本身和所有连接的硬件。这对开发者来说是一个全新的空间。

除了直接访问底层 WinRT 之外,Windows 8 开发人员还可以获得 Windows JavaScript 库(WinJS)的额外帮助。WinJS

WinJS 提供了一些 JavaScript 和一些 CSS 样式。稍后,我们将更深入地研究 WinJS 提供的 CSS 样式。

当我们开始编写应用程序时,我们需要一些好的工具,幸运的是,微软提供的工具是首屈一指的。接下来我们将对此进行研究。

工具

微软的旗舰开发工具是 Visual Studio。它已经存在了足够长的时间,在设计和功能上经历了一些重大的演变。除了 Visual Studio,开发人员也可以使用 Blend。

Visual Studio 2012

Visual Studio 2012 (VS2012)是最新的版本,这一版本的设计中没有遗漏任何改进!VS2012 在许多方面与它的前辈几乎没有相似之处,但它没有失去任何功能。您仍然可以创作许多不同的项目类型,现在也可以创作 Windows 8 应用程序。

VS2012 产品之一——Express——只针对 Windows 8 应用,完全免费。当然,它不具备高端付费产品的所有功能,但它仍将从头到尾管理你的应用。这对于学生开发者来说尤其令人兴奋,因为他们没有大的开发机会,也没有钱购买开发工具。

如果你想开发一个 Windows 8 的应用,你必须有 Windows 8,你必须有 Visual Studio 2012。早期版本的 Windows 将运行 VS2012,但无法创建 Windows 8 应用。

项目模板

面向 Windows 8 项目的 VS2012 项目模板将让您快速上手。这些基本模板中的任何一个都将引导你开始使用我们将在本书中学习的 CSS,所以不要觉得你已经走了很远。

让我们从简单的模板开始,然后逐步完善。从以下每个模板创建新项目,然后按照我指出的构成项目的各个页面的结构进行操作。

空白模板

空白模板的稀疏内容不会让您感到惊讶。毕竟,它应该是空白的。然而,如果你是那种对空白模板产生的少量明显的魔力感到好奇的人,你可以访问我的博客文章http://codefoster.com/Windows-8-Building-Up-to-Blank,阅读更多关于这个简单模板的内容。

要创建新的空白项目:

  1. 在运行 Windows 8 的计算机上启动 Visual Studio 2012

  2. Click File | New Project

    image 注意如果你使用的是 Visual Studio 的专业版,你会看到一个类似于图 2-2 的屏幕。如果你使用 Visual Studio 速成版,那么你的选择会更少,但概念仍然存在。

    9781430249832_Fig02-02.jpg

    图 2-2 。“新建项目”窗口为您提供了几个项目类型选项

  3. 从左侧窗格中选择“JavaScript”|“Windows 应用商店”,然后从模板列表中选择“空白应用”。确保您对项目的位置和名称满意,然后单击“确定”。

几秒钟后,你就可以运行一个成熟的 Windows 8 应用了。你可以点击工具栏上的绿色箭头在本地运行这个应用程序,但是结果并不令人兴奋。图 2-3 显示了当你运行一个从空白模板创建的项目时的样子。

9781430249832_Fig02-03.jpg

图 2-3 。从空白模板创建的项目的第一次运行。你几乎看不清的白色文字写着“内容在此处”,并作为占位符供你添加内容

让我们看一下为我们创建的项目,确保我们对所涉及的文件有一个坚实的理解。

如果您有任何使用网站的经验,您可能会注意到解决方案资源管理器中的文件在许多方面类似于 web 项目。像 web 项目一样,有像jsimagescss这样的文件夹,像 web 项目一样,在根级别有一个default.html文件。您可以深入到所有级别,亲自查看我们没有处理太多的文件。当你试图获得核心概念时,有一个简单的开始是很好的。

在我们查看所涉及的 HTML、CSS 和 JavaScript 文件之前,让我们先来看一个您可能不熟悉的文件— package.appxmanifest。这个文件包含了你的 Windows 8 应用的所有元信息。当应用提交到 Windows 应用商店时,这是描述应用的重要信息。

package.appxmanifest文件(也称为清单)只是一个简单的 XML 文件,但您很少需要直接盯着 XML,因为双击清单时 Visual Studio 启动的 GUI 设计器相当不错。

现在我们来看 HTML 文件。双击default.html打开它,或者如果你面前没有你的项目,就看一下清单 2-1 。

清单 2-1 。伴随一个新的空白项目的 default.html 文件

1<!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="utf-8" />
 5     <title>NewBlankApp</title>
 6
 7     <!-- WinJS references -->
 8     <link href="//Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />
 9     <script src="//Microsoft.WinJS.1.0/js/base.js"></script>
10     <script src="//Microsoft.WinJS.1.0/js/ui.js"></script>
11
12     <!-- NewBlankApp references -->
13     <link href="/css/default.css" rel="stylesheet" />
14     <script src="/js/default.js"></script>
15 </head>
16 <body>
17     <p>Content goes here</p>
18 </body>
18 </html>

我想强调的是,这个文件中完全没有任何专有的东西。如果你看过 HTML5,你会认出简洁的DOCTYPE指令,带有脚本和样式表链接的基本head部分,以及世界上最简单的内容体“内容在这里”。

第 13 行和第 14 行引用的脚本和样式表文件以正斜杠(/)开始,因此从项目的根开始,继续引用来自css文件夹的default.css文件和来自js文件夹的default.js。您将逐渐习惯这种约定,因为所有内置的模板文件都被分成了共享一个名称的.html.css.js文件。

清单 2-1 的第 8-10 行乍一看可能很奇怪,但是我会解释为什么这些都是由完全标准的 HTML5 标记组成的。统一资源标识符(URI),比如第 8–10 行的linkscript元素所包含的,以一个 scheme(比如 http)开始,然后是一个冒号(:)。冒号后的所有内容都由 scheme 定义。

对于流行的 http 方案,URI 的剩余部分被定义为http://{hostname}/{path},其中hostname通常是服务器的名称或 IP 地址,path是被请求文件的完整路径。

然而,清单 2-1 中的 URI 省略了这个方案,规则是如果 URI 的一个段被省略,那么它默认为当前请求的相同段的值。对于网站来说,默认请求通常是 times http,但 Windows 8 应用的方案是ms-appx。所以第 8-10 行可以写成类似于清单 2-2 的形式。

清单 2-2 。ms-appx 方案是 Windows 8 应用的默认方案

8 <link href="ms-appx: //Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />
 9  <script src="ms-appx: //Microsoft.WinJS.1.0/js/base.js"></script>
10   <script src="ms-appx: //Microsoft.WinJS.1.0/js/ui.js"></script>

ms-appx方案中,双斜杠后面的 URI 段是代码所在的包。在图 2-4 中可以看到,ui-dark.css文件(来自清单 2-2 的第 8 行)存在于项目引用下的Windows Library for JavaScript 1.0中,该引用包的包名为Microsoft.WinJS.1.0

9781430249832_Fig02-04.jpg

图 2-4 。ui-dark.css 文件位于被引用的包中

只要请求正在使用ms-appx方案,系统就知道它们是安全的请求,并且这些请求被认为是在本地上下文中。另一方面,对http方案的请求不被认为是安全的,Windows 8 在所谓的网络环境中运行它们。你可以通过访问http://aka.ms/win8contexts找到更多的信息。

好了,HTML 文件中没有什么神奇的事情发生,所以我要继续向你保证 CSS 或 JavaScript 文件中也没有发生。

空白模板的 CSS 文件位于css文件夹中,名为default.css。文件中没有功能代码,但是包含了一点框架来帮助您入门。清单 2-3 向您展示了在default.css的默认实例中您会发现什么。

清单 2-3 。 空白项目模板附带的 default.css 文件

body {
}

@media screen and (-ms-view-state: fullscreen-landscape) {
}

@media screen and (-ms-view-state: filled) {
}

@media screen and (-ms-view-state: snapped) {
}

@media screen and (-ms-view-state: fullscreen-portrait) {
}

这些都只是没有任何效果的样式规则和媒体查询的空壳。然而,这是你的空白画布。当第三章开始向你展示如何定义样式规则时,这就是它们要去的地方。

我们要看的最后一页是 JavaScript。default.js文件在清单 2-4 中,正如你所看到的,它比 HTML 和 CSS 文件要长。

清单 2-4 。空白项目模板中包含的 default.js 文件

// For an introduction to the Blank template, see the following documentation:
// http://go.microsoft.com/fwlink/?LinkId=232509
(function () {
    "use strict";

    WinJS.Binding.optimizeBindingReferences = true;

    var app = WinJS.Application;
    var activation = Windows.ApplicationModel.Activation;

    app.onactivated = function (args) {
        if (args.detail.kind === activation.ActivationKind.launch) {
            if (args.detail.previousExecutionState !==
                activation.ApplicationExecutionState.terminated) {
                // TODO: This application has been newly launched. Initialize
                // your application here.
            } else {
                // TODO: This application has been reactivated from suspension.
                // Restore application state here.
            }
            args.setPromise(WinJS.UI.processAll());
        }
    };

    app.oncheckpoint = function (args) {
        // TODO: This application is about to be suspended. Save any state
        // that needs to persist across suspensions here. You might use the
        // WinJS.Application.sessionState object, which is automatically
        // saved and restored across suspension. If you need to complete an
        // asynchronous operation before your application is suspended, call
        // args.setPromise().
    };

    app.start();
})();

我不会花时间解释这个文件中发生的所有事情,但是我想指出几件事情。

首先,您应该知道这整个文件是标准的 JavaScript——从技术上讲是 ECMAScript 5。

其次,流程生命周期管理(PLM)在这里处理。PLM 是您编写的代码,用于确保您的应用程序在启动、暂停、重新激活和终止时表现良好。如果你写对了代码,你的用户对你的应用的体验将会是快速和无缝的。

最后,注意 JavaScript 文件中的所有代码都包装在所谓的立即函数中,如清单 2-5 所示。

清单 2-5 。 一个立即函数包装代码,就像一个命名空间,并保持它与其他模块和全局命名空间隔离

(function () {

    ...

})();

这个立即函数定义一个函数,然后立即运行它。起初,这似乎是一件非常奇怪的事情。为什么需要定义一个函数并立即调用它?你就不能写代码让它执行吗?是的,你可以。immediate 函数是 JavaScript 中的一个常见技巧,它利用了 JavaScript 的函数作用域。功能就像拉斯维加斯。函数中发生的事情留在函数中。因此,你可以在即时函数中任意使用变量和函数定义,而不会添加一堆应用程序其他部分可以看到的垃圾,这些垃圾肯定会造成混乱和冲突。

记住,我们只是触及 JavaScript,因为这毕竟是一本关于 CSS 的书。不过,还有一件事我希望你至少能够认识到,那就是一个事件

事件在许多现代编程语言中都很流行。事件是当某件事情发生时触发的方法。如果用户单击一个按钮,就会触发一个事件。如果用户在屏幕上滚动某些东西,应该会触发一个事件(即使他们只滚动了一个像素)。

我们编写的 JavaScript 应该响应事件,为了做到这一点,我们需要定义方法并将它们绑定到正确的事件。我举一个很基本的例子。

让我们在用户点击按钮时显示一个对话框。为此,我们可以使用清单 2-6 中的代码。

清单 2-6 。 一个简单的事件处理程序,通过点击按钮来启动一个消息框

document.getElementById("myButton").onclick = function(e) {
    Windows.UI.Popups.MessageDialog("Hello").showAsync();
};

现在,让我们把这段代码拆开来理解到底发生了什么,以及事件处理程序是如何工作的。

第一部分说document.getElementById("myButton")只是引用了一个在 HTML 中定义的 id 为myButtonbutton

.onclick是该按钮的点击事件。它的值最初是null,但是它希望我们提供一个函数,当按钮被点击时调用这个函数。

清单的其余部分是函数,它只是显示一个对话框,在用户每次单击按钮时显示“Hello”。

为什么选择空白项目?

如果您的应用程序只需要一个页面,空白项目是一个很好的起点。如果你正在开发一个游戏,游戏中的所有动作都发生在一个canvas元素中,那么就没有理由将用户导航到另一个页面。

如果您不喜欢有人为您编写代码,您也可以选择使用空白模板。如果你想从零开始(或者几乎从零开始),那么这里有另一个很好的理由。

关于空白项目模板,还有很多东西需要学习,但这只是一个概述,在深入了解之前,我们将继续学习其他项目模板。接下来,我们将了解导航项目模板。

导航模板

图 2-5 展示了一个用导航模板创建的应用程序。它看起来几乎和图 2-3 中的空白模板一样稀疏,但是它确实有一个页眉。不过,页眉并不是唯一的区别。

9781430249832_Fig02-05.jpg

图 2-5 。从导航模板创建的项目的第一次运行。我们仍然有白色的小文本,上面写着“内容在这里”,但是现在我们也有了页眉

导航项目模板在空白模板之外添加了一些东西,主要目的是为您提供从一个页面到另一个页面的导航框架。这种改进的导航实际上是由 WinJS 库提供的。我将向您展示所有的差异,并告诉您为什么这个导航框架对我们来说是一个好东西。

如果您要使用文件差异实用程序来查看空白项目模板和导航模板,您会看到两者之间的以下差异。在导航模板中:

  • default.css 中有一些基本的样式更改,提供了符合 Windows 8 设计原则的页眉。
  • default.html 文件不再包含主要内容(“内容放在这里”),而是包含一个名为 contenthost 的东西。
  • 项目中的 js 文件夹包含一个 navigator.js 文件,该文件定义了 contenthost 的行为方式。default.html 页面引用 navigator.js
  • 该项目有一个 pages 文件夹,其中有一个 home 文件夹,home 文件夹包含它自己的 HTML、CSS 和 JavaScript 文件。按照惯例,每个页面都将位于 pages 文件夹中自己的文件夹中。

这些几乎是所有的差异。不是很多。为什么这很有帮助?

你可能知道,在 HTML 中,你可以创建超链接,并轻松地从一个文件导航到另一个文件,但至少有几个原因可以解释为什么超链接对于 Windows 8 应用程序来说是不够的。

首先,超链接创建一个新的 HTTP GET 请求,并依赖 GET 请求中可用的查询字符串参数作为传递数据的唯一方式。另一方面,使用导航框架,我们可以在从一个页面导航到下一个页面的过程中传递健壮的 JavaScript 对象。

第二,超链接把用户从一个页面带到另一个页面,让前一个页面的所有范围和上下文完全消失。每个请求都是一个全新的上下文。这给开发人员带来了真正的痛苦,尽管 web 开发人员会发现这是一个常见的问题,但在现代的客户端应用程序中,这是不必要的。导航框架在应用程序的整个生命周期中让用户停留在default.html页面上,只是通过替换default.html的 DOM 中的内容来将用户导航到另一个页面。这种通用模式被称为单页面架构(SPA ),它越来越受欢迎,甚至在公共网页上也是如此。

图 2-6 展示了超链接导航,而图 2-7 展示了使用单页面架构改进的导航。

9781430249832_Fig02-06.jpg

图 2-6 。超链接到 page2.html 打破了用户的上下文创建一个新的上下文

9781430249832_Fig02-07.jpg

图 2-7 。通过 WinJS 库导航保持了上下文的完整性,使开发人员的工作更加轻松

为什么要选择导航项目?

对于任何可能实现多视图的应用程序来说,导航项目都是一个很好的起点。如果你的应用程序要向用户显示产品列表,然后允许他们选择一个产品来查看更多信息,那么你可能需要导航。

我几乎从导航模板开始每个项目,即使我知道我将实现一个网格。导航模板提供的导航功能非常有用,但编写起来很繁琐,但我喜欢从头开始创建网格。

网格模板

接下来是网格项目模板,如图图 2-8 所示。这个庞大的模板包含了大量的代码,如果不小心的话,你会被吓到的。仅网格项目模板就包含了将近 500 行 JavaScript 代码。大小的原因是,除了实现导航框架(我们在上一节中讨论过),它还定义了一堆样本数据,并包含三个独立的完整页面,实现良好的三层导航。

9781430249832_Fig02-08.jpg

图 2-8 。从网格模板创建的项目的第一次运行。我们看到的是为我们创建的三层导航模型的中心

网格模板实现了一个名为 3 层导航的应用导航模型。强烈建议 Windows 8 应用采用这种三层导航模式。遵循它会给你的用户带来一致性,而设计你自己的模型会有迷惑或失去他们的风险。使用这种导航模型有两个重要原因。首先,它背后有大量的用户研究,其次,它是用户已经熟悉的惯例。

网格模板的核心是 ListView。我们将在本书中深入探讨列表视图。现在,只需知道 ListView 是为您提供熟悉的实体网格的控件。这些实体以瓦片的形式出现是相当普遍的,但这当然是可选的。

为什么选择网格项目?

当您的应用程序类似于基本的三层导航模型时,网格项目可以让您快速从 0 到 60。这是一个很好的学习工具,我建议你使用这个模板创建一个应用程序,然后浏览并尝试对代码进行逆向工程。网格模板最大的一个优点就是已经实现了 Windows 8 的设计原则。边距宽度合适,字体大小合适,ListView 组之间的间距合适。

当您更加熟练地创建自己的 ListViews 并使用 CSS 实现设计原则时,我建议您放弃这个模板,使用导航模板。我觉得网格模板引入的复杂性超过了它的价值,但这只是我的看法。

我已经向您介绍了我认为最重要的三个项目模板。还有更多我没讲的。拆分项目模板有它的位置,但它适用于相对狭窄的一组应用程序。固定宽度的项目模板添加的实际代码非常少,如果我需要它的功能,我倾向于自己编写。

唐总管

如果您使用过任何主流浏览器的开发工具,那么您应该熟悉获取 HTML 文档的实时版本的概念。这种对象形式的实时文档被称为文档对象模型(DOM)

当您使用 Visual Studio 2012 在调试模式下运行 Windows 8 的 HTML/JavaScript 应用程序时,您会在 Visual Studio 中获得另一个名为 DOM Explorer 的窗格。如果您没有看到 DOM Explorer,那么请在 Windows | DOM Explorer 下的调试菜单中查找。

DOM Explorer 为当前运行的应用程序提供了 DOM 的分层表示。它有效地向您展示了 HTML 的外观,但包括了您的脚本(或 WinJS 脚本)在应用程序运行时可能进行的任何动态添加或修改。如果您使用 JavaScript 向 HTML 页面动态添加一个按钮,您将不会在 HTML 文件中看到该按钮,但是它会在 DOM Explorer 中显示。

DOM Explorer 对于开发和调试非常有帮助。图 2-9 显示了 DOM Explorer 的典型视图。

9781430249832_Fig02-09.jpg

图 2-9 。当你的应用程序运行在调试模式时,Visual Studio 中的 DOM Explorer 显示你的文档对象模型(DOM)的实时视图

在 DOM 浏览器的左侧(在图 2-10 中突出显示),你可以看到整个 DOM。如果你的应用程序中没有任何花哨的 JavaScript,那么这个 DOM 看起来就像你的default.html文件。

9781430249832_Fig02-10.jpg

图 2-10 。DOM 包括您编写的 HTML 以及运行时动态注入的任何东西

在图 2-9 中的 DOM 上方,注意选择元素按钮。当您单击该按钮时,您的应用程序将成为焦点(在 Visual Studio 前面),您有机会选择您可能感兴趣的元素。一条细细的蓝线在这次冒险中帮助你。

DOM Explorer 的右窗格(在图 2-11 中突出显示)提供了五个面板,每个面板都给我们不同的视角来观察左侧突出显示的元素。

9781430249832_Fig02-11.jpg

图 2-11 。使用 CSS 的应用程序开发人员将从 DOM Explorer 中的五个选项卡中受益匪浅

当你设计你的应用时,这些额外的标签是非常重要的。当您按照这本书的内容创建应用于 UI 中的元素的 CSS 样式时,这些样式将出现在这些选项卡中。

前两个选项卡非常相似。 Styles 选项卡显示了当前应用于所选元素的所有样式的列表,按照它们来自的样式表排列。跟踪样式选项卡也显示了当前应用的所有样式,但是现在它们是按照 CSS 属性排列的。我发现“跟踪样式”选项卡更有帮助。布局选项卡显示了一个叫做盒子模型的东西,我们将在第五章中讨论。 Attributes 选项卡允许您向所选元素添加 HTML 属性,最后 Events 选项卡显示与所选元素相关的任何 JavaScript 事件。

调试

调试是 Visual Studio 最强大的功能之一。整个执行周期可以一行一行地进行,在应用程序运行时可以观察甚至修改值。深入的调试当然超出了我们的范围,但是我希望简短的介绍将有助于说服或鼓励您选择工具。

当需要运行你的应用程序并观察它的行为时,你有几个选择。标准工具栏上的调试目标选择框如图 2-12 所示,允许您在本地(本机)、远程机器或模拟器上运行应用程序。

您可能通常会在本地机器上调试应用程序。本地跑是最快最简单的路线。当您想要定位另一台机器时,您可以选择远程机器,当您需要模拟开发机器上没有的特性或环境条件时,您可以选择模拟器。

模拟器可以让你模拟触摸、地理位置、屏幕大小和屏幕分辨率。

9781430249832_Fig02-12.jpg

图 2-12 。Visual Studio 允许您在本地计算机、远程计算机或模拟器中调试应用程序

到目前为止,我们讨论的所有内容都涉及到 Visual Studio 来编写和编辑驱动应用程序的原始代码。这当然是典型的开发方法,但是历史已经表明许多公司尝试实现可视化工具来为我们编写代码。就我而言,我从未被打动过。我看到的每一次尝试都以某种方式失败,最终阻止我使用它,并迫使我回到 Visual Studio 中编辑原始代码。也就是说,直到混合。

Blend 是 Visual Studio 2012 附带的免费产品。它甚至附带了 Visual Studio Express 2012,这也是免费的,所以你可以使用 Blend 而不用支付一分钱。Blend 不同于 Expression Blend。Expression Blend 是一个设计 XAML 组件的工具,但 Blend 是一个设计 XAML 或 HTML/CSS 格式的 Windows 8 应用程序的工具。

Blend 是一个不可思议的工具。大多数视觉设计者强迫用户区分设计模式和执行模式;视图以一种模式设计,然后执行程序来查看结果。在 Blend 中,你总是在设计执行模式 。您的应用程序由 HTML、CSS 和 JavaScript 组成,在 Blend 中运行,就像在机器上运行一样。因此,Blend 可以给你一个动态的 DOM(就像 Visual Studio 的 DOM Explorer 一样,只是它总是动态的!)而且它可以带来你所有的真实数据。在图 2-13 中,你可以看到融合的效果。虽然一开始你可能不会注意到,但是图 2-13 中的网格项目块是从 JavaScript 数据中动态加载的。

9781430249832_Fig02-13.jpg

图 2-13 。图 2-8 中的 网格 app,现在却在交融中打开

当你点击一个视觉元素,比如一个灰色的图块(在图 2-14 中高亮显示),Blend 认为你想要改变它的设计,所以它渲染典型的选择矩形和手柄来调整它的大小。如果我们拖动一个句柄并调整对象的大小,那么决定该图块大小的 CSS 将被修改,并且您将看到所有其他图块的大小也发生了变化。

9781430249832_Fig02-14.jpg

图 2-14 。Blend 运行您的应用程序,但可以设计一个元素

正如我所说的,当你点击一个磁贴时,Blend 假设你对高亮显示磁贴感兴趣,这样你就可以设计它,但是你可能想充当应用程序的用户,并在你点击时实际导航。这就是交互模式的作用。在文档区的右上角,你会发现一个类似于图 2-15 的交互模式图标。激活交互模式会通知 Blend 你想表现得像一个用户,并且实际上使用该应用。

9781430249832_Fig02-15.jpg

图 2-15 。互动模式图标 在交融

最棒的是,当您再次单击交互模式按钮返回到设计模式时,Blend 会保持您的应用程序状态,并允许您从那里继续设计。

也许 Blend over Visual Studio 最重要的贡献是它让您在操作底层样式 HTML 和 CSS 代码时以可视化方式工作。您可能已经有足够的 CSS 经验,知道它的声明可能相当复杂。应用于任何给定元素的样式可以从任意数量的样式规则向下级联,每个规则覆盖下一个规则。尽管这个结构相当复杂,但是当你在 Blend 中改变一个属性时,它能够改变正确的样式。

此外,图 2-16 中的所示的【应用的样式规则】窗格(通常位于 Blend 工作区的右上角)给出了所有应用于各种样式表的 CSS 样式规则的列表。

9781430249832_Fig02-16.jpg

图 2-16 。应用的样式规则窗格 显示了应用于所选元素的整个样式层次

当您需要创建自己的全新样式规则时,样式规则选项卡 ( 图 2-17 )列出了当前页面引用的所有样式表,明确指出新样式将位于哪个样式表中,并让您有机会更改它。

9781430249832_Fig02-17.jpg

图 2-17 。样式规则页签 表示将在哪个样式表中创建新规则

我喜欢 Blend 的最后一点是 3 窗格视图,它非常适合同时编辑 HTML 文件及其相关的 CSS 和 JS 文件。

然而,有一点需要注意,Blend 在 JavaScript 方面并不出色。如果您正在处理编程任务,请使用 Visual Studio。幸运的是,这两个工具都共享一个公共的项目格式,所以它们之间的切换是无缝的。事实上,Visual Studio 提供了一个“以混合方式打开”的选项(图 2-18 ),右击一个项目即可使用。

9781430249832_Fig02-18.jpg

图 2-18 。项目快捷方式使从 Visual Studio 切换到 Blend 变得快速而简单

分发您的应用程序

当你完成第一个应用程序时,这是多么美好的一天。毫无疑问,你会有很多的自豪感和对美元符号的憧憬。在写这篇文章的时候,我在 Windows 商店里有两个应用程序,我希望能写更多。

将你的应用提交到 Windows 应用商店并不是一件困难的事情。过程非常简单,认证要求很明确,沿途的反馈也很有帮助。我不会花时间详细描述商店提交过程,因为商店几乎会全程牵着你的手。不过,我会介绍它,带你进去。

此外,当您的应用程序处于开发阶段并且在发布之前,您可能需要将其安装在另一台设备上进行测试,因此我也将向您展示如何进行测试。

侧装

在设备上安装 Windows 8 商店应用而不使用 Windows 商店的行为通常被称为侧装。这绝对是一个受支持的场景,我会让你知道怎么做。

显然,您可以将源代码复制到另一个安装了 Visual Studio 的系统中,打开项目并运行它。这将有效地把你的应用程序安装到第二台设备上。然而,并不是每个设备都有 Visual Studio,Windows RT 设备不能有 Visual Studio。不过,还有另一种方法。如果您想在应用程序完成之前进行练习,可以从一个默认的项目模板开始。以下是将你的应用下载到另一台设备的步骤:

  1. 在开发计算机上的 Visual Studio 中打开您的应用程序。

  2. Choose Create App Packages from the Store menu (the Store menu is under the Project menu in professional versions of Visual Studio) as shown in Figure 2-19.

    9781430249832_Fig02-19.jpg

    图 2-19 。商店菜单中的创建应用程序包选项

  3. Choose No when you are asked Do you want to build packages to upload to the Windows Store? as shown in Figure 2-20. Click Next.

    9781430249832_Fig02-20.jpg

    图 2-20 。如果您知道您只打算下载这个版本的应用程序,请选择“否”

    image你可以在http://aka.ms/devlicenseinfo获得更多关于开发者许可的信息,在http://aka.ms/sideloading获得更多关于侧装的信息。

  4. 对于选择和配置包步骤,默认值应该是好的。如果您需要针对不同的处理器架构,您可以这样做,如果您不知道这意味着什么,那么就不要去管它,您应该没问题。单击创建。

  5. 包创建应该完成并指明位置。按确定。

  6. The package that gets created consists of a folder and an .appxupload file. The .appxupload file is for uploading to the Windows Store when you’re ready to submit it, but the folder is what you want for installing on another device. Use whatever means necessary to get the entire contents of that folder (Figure 2-21) copied to another device.

    9781430249832_Fig02-21.jpg

    图 2-21 。创建后的示例应用程序包。ps1 文件负责安装打包的应用程序

  7. 在新设备上,您只需右键单击Add-AppDevPackage.ps1文件并选择使用 PowerShell 运行。PowerShell 将启动并询问您几个问题,您可以接受默认设置。

这就是相对简单的侧向装载过程。

当然,为了扩大你的覆盖范围并避免在目标机器上要求开发者许可,你需要将你的应用提交到 Windows 应用商店。

发布到 Windows 商店

Windows Store 将让你的应用出现在数千万(很快会是数亿)消费者的眼前。如果你提供给他们的生活增加价值的应用程序,他们会很高兴地安装并使用它,如果你决定收费,他们甚至可能会放弃一些零钱。

要完成提交应用程序的过程,请转到http://dev.windows.com并按照说明获取您的开发者帐户。设置好之后,请遵循以下一般步骤:

  1. 按照与创建用于侧面加载的包相同的步骤构建应用包,直到您进入创建向导的第一个屏幕,在该屏幕中,它会询问您是否要构建要上传到 Windows 应用商店的包。很明显,这次你会对这个问题做出肯定的回答。
  2. 回到dev.windows.com,找到并点击 Windows 应用商店应用、仪表板,然后提交应用。
  3. 填写关于你的应用程序的大量信息,并在适当的步骤中上传你在步骤 1 中生成的.appxupload文件。

有关提交应用程序的完整流程的更多信息,请参考http://aka.ms/appsubmit

摘要

欢迎来到极其激动人心的 Windows 8 开发世界。

在本章中,您已经了解了 Windows 8 的体系结构,它支持广泛的计算机体系结构,以及一套有价值的工具和开发语言。所有这些,至少部分是由一种新的编程模型和一种叫做 WinRT 的现代 API 结合在一起的。

然后,您将接触到世界一流的工具,在您构建应用程序的每一天,它都将是您最好的朋友。广受欢迎的 Visual Studio 的最新版本将 Windows 8 商店应用程序邀请到其目标项目中,并从一些好的项目模板开始,让你在几秒钟内从零到完成产品。

Visual Studio 还欢迎 Blend 作为一个免费的伴侣来帮助应用程序的可视化设计。这两个工具共享一个通用的项目格式,使得设计和开发之间的协作对每个人来说都是流畅的体验。

最后,向您介绍了在本地运行应用程序、在另一台设备上安装应用程序以及将应用程序提交到 Windows 应用商店的选项。

我希望在读完前两章后,你觉得自己至少具备了开始设计和开发你的第一个应用程序的基本技能。还有更多的东西要学,也有很多可以学习的资源。对于 CSS 部门,只要坚持住,因为我们就要深入下去了。

三、选择器和样式规则

image 注意 CSS 不过是样式规则的集合,每个样式规则都由一个选择器和一个定义组成。不要让事情变得更复杂。

样式规则的选择器决定它影响什么,而定义决定它如何被影响。

因此,正如 HTML 的全部工作是定义文档的结构一样,CSS 的全部工作是对该结构进行布局和样式设计,使其在应该出现的地方出现。

通过结合 HTML 的文档对象模型(DOM)和 CSS 的元素选择功能,我们有了一种非常有效且相对独特的方式来表达潜在的复杂结构,然后在该结构中单独设计独特的元素集,而不会过于模糊或重复。

我称之为相对独特,因为许多其他 UI 语言的样式往往与文档结构混在一起。然而,对于 HTML 和 CSS,两者可以保持分离。其优势(除了对开发人员来说更清晰)是能够轻松地交换样式表,并在这样做时,完全改变应用程序的布局和风格。

因此,CSS 的强大之处在于它能够定义样式规则,然后确定该规则应该应用于 UI 中的哪些元素。

在图 3-1 中,你可以找到领先于所有其他应用的应用:Windows 应用商店应用。

9781430249832_Fig03-01.jpg

图 3-1 。Windows 应用商店 app

这个应用程序有内容,但也有很多风格。该应用程序的标题是一个更大的字体和定位;这些应用程序有图像、标题和背景颜色,页面上的一切都有一个非常有意识的布局。总的来说,该应用程序有很好的内容和很好的内容呈现。这才是我们真正的目标。

样式格式和位置

CSS 适用于或描述 HTML。实际上,它一般描述 XML,但就我们创建 Windows 8 应用程序而言,HTML 是我们唯一的考虑因素。这意味着,任何时候你创建一个 HTML 文件,并希望用样式影响该文件中的元素,你需要编写和应用一些 CSS。

这些样式可以存在于三个地方:

  • 在一条直线上的
  • 在嵌入的样式表中
  • 在外部样式表中

内嵌样式

内联样式是在样式属性中定义的,该属性直接添加到它们应该影响的元素中。

内联样式通常被认为是糟糕的形式,因为它们混合了项目中的结构和样式,很快会变得难以处理。我通常从不使用内联样式,我强烈地感觉到,即使有,也是极少的情况下,它们应该被投入生产。它们可能对一些开发人员有帮助,比如快速原型或故障排除设计,但是它们不应该用在最终产品中。清单 3-1 展示了内嵌样式的样子。

清单 3-1 。 一个简单的 HTML5 文档,在 div 标签上有一个内嵌样式属性

<!-- default.html -->
<!DOCTYPE html>

<html lang="en" FontName2">http://www.w3.org/1999/xhtml ">
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <divstyle="font-weight: bold;" ></div>
</body>
</html>

嵌入样式表

嵌入式样式表是在它们影响的页面上定义的。一个style元素被添加到页面的head元素中,并包含样式规则来确定如何将样式属性应用于各种元素。嵌入式样式表通常没有第三个选项常见,在 Windows 8 应用程序中应该很少见。清单 3-2 是一个嵌入式 CSS 样式表的例子。

清单 3-2 。 一个简单的嵌入了样式标签的 HTML5 文档

<!-- default.html -->
<!DOCTYPE html>

<html lang="en" FontName2">http://www.w3.org/1999/xhtml ">
<head>
    <meta charset="utf-8" />
    <title></title>
    <style>
        #myDiv { font-weight: bold; }
    </style>
</head>
<body>
    <divid="myDiv" ></div>
</body>
</html>

外部样式表

最后,外部样式表是定义和应用样式的最佳方法。对于外部样式表,受影响的 HTML 文件不会被打印出来。事实上,唯一的痕迹是对样式表的引用,它只需要在head元素中使用一行代码,剩下的 HTML 代码完成它的工作,提供文档的结构。使用外部样式表的另一个主要优点是可以轻松地动态交换它们,以支持多主题、多语言甚至多平台。

清单 3-3 展示了一个引用外部样式表的 HTML 文件的好例子。

清单 3-3 。 一个简单的 HTML5 文档,引用了一个外部 CSS 样式表

<!-- default.html -->
<!DOCTYPE html>

<html lang="en" FontName2">http://www.w3.org/1999/xhtml ">
<head>
    <meta charset="utf-8" />
    <title></title>
    <link href="default.css" rel="stylesheet" />
</head>
<body>
    <div id="myDiv"></div>
</body>
</html>

/* default.css */
#myDiv {
    font-weight: bold;
}

这些样式表文件(。css)可以保存在任何地方,只要它们可以通过来自 HTML 页面的 HTTP 请求来访问。你可以链接到一个存储在互联网上完全不同的服务器上的样式表,或者它可以在你的 web 项目的中心文件夹中(也许叫做cssstyles,或者它可以就在你的 HTML 文件旁边,你可以在链接的href属性中使用绝对引用或相对引用。

在一个 web 项目中,将所有样式表放在一个中心位置是一个相当强的惯例,但这也很常见,尤其是在创建单页面 web 应用程序时,为项目中的每个 HTML 页面创建一个样式表,这是 Windows 8 应用程序中的惯例。当你设计你的 Windows 8 应用时,你可以选择把 CSS 文件放在你选择的任何地方,但是遵循惯例是一个好的实践。内置的 Windows-8 项目模板之一是导航应用程序模板,它遵循这一约定,将每个页面的 CSS 文件、HTML 文件和 JavaScript 文件放在各自的文件夹中,这些文件夹位于pages文件夹内。

基本样式规则语法

外部样式表的内容(就此而言,还有嵌入的样式表)很简单——只不过是由一个接一个的样式规则组成的大列表。有时候,样式规则可能会被媒体查询块包装起来,但是我们将稍后再讨论这个主题。

清单 3-4 展示了填充了一些规则的样式表(无论是外部样式表还是嵌入样式表)看起来会是什么样子。

清单 3-4 。 典型格式为样式规则

selector {
    property: value;
    property: value;
    property: value;
}

selector {
    property: value;
    property: value;
    property: value;
}

selector {
    property: value;
    property: value;
    property: value;
}

对于一些较短样式的规则,你可能像我一样,宁愿放弃规则之间的换行,而在一行上格式化你的规则,更像清单 3-5 中的。

清单 3-5 。 当样式规则简单时,用于节省垂直空间的更紧凑的格式

selector { property: value; property: value; }
selector { property: value; property: value; }
selector { property: value; property: value; }

这样可以节省一些垂直空间,也许还可以延长鼠标滚轮的寿命,但是和它的姐妹技术 HTML 一样,CSS 忽略了多余的空白,所以你可以随意设置格式,并且知道它会被同样地解释。

另一个典型且非常有用的约定是样式规则的缩进,它只影响前面样式规则下面的元素。在命令式编码语言中,缩进被广泛用于向开发人员传达代码结构,使其更易于阅读。在 CSS 中,缩进的代码表示样式规则之间的层次关系。记住空白被忽略,所以这个缩进对功能没有影响。请注意清单 3-6 中的第二个样式规则是如何包含与前一个规则相同的选择器(“selectorA”)的,以及一些更多的细节。这意味着它在它前面的规则的上下文中,这就是缩进所表示的。您将在本书的示例代码中看到这种约定。

清单 3-6 。 缩进严格地作为表示层次的视觉辅助

selectorA {
    property: value;
    property: value;
    property: value;
}

    selectorA selector1 {
        property: value;
        property: value;
        property: value;
    }

selectorB {
    property: value;
    property: value;
    property: value;
}

你现在知道什么是风格规则了,是时候学习如何作曲了。我们将从讨论选择器开始。

选择器

想象你正在用一个典型的文字处理器工作。当您完成了文档的撰写和编辑后,您的工作就变成了格式化。要确定文档各部分的格式,需要采取两个步骤:用鼠标拖动选择要格式化的区域,然后应用各种样式规则,通常是通过单击命令按钮或键盘快捷键。例如,您可以选择整个第一段,然后决定将字体大小增加到 16 磅。或者,您可以只选择一个单词,并将其设置为带下划线。

这些正是您将对 CSS 采取的步骤,只是您使用选择器进行选择,并使用样式属性应用样式。您的选择器指定了您想要选择的 HTML 元素,并且相关联的样式属性决定了该选择应该发生什么。

因此,编写能够准确选择您想要的内容的选择器显然是非常重要的一步,我恳请您不要跳过这一部分。事实上,我发现比那些缺乏格式化技巧的开发人员更缺乏 CSS 选择技巧的开发人员。这相当于浪费时间、空间和复杂性,要么用不必要的类和 ID 装饰符过度定义 HTML,要么在 CSS 中使用冗余或冗长的样式规则。任何一个失误都会增加项目的复杂性,即使网站或应用程序运行良好,也会有明显的不利影响。

选择器是一个表达式,它被求值并产生一个零到多个 DOM 元素(或者伪元素,但我们稍后会讲到)的列表。

例如,对选择器的评估可能导致选择页面上的每个div元素、body中的第一个p或页面footer元素。然而,选择器可以变得非常具体,导致排序列表中的每三个列表项(li)或称为myDivdiv第三段的第一个字母。

定义选择器的目标是选择您想要的,仅此而已。定义了一个选择器和样式规则,却发现规则影响的元素比预期的要多,这是很常见的。

有几种不同的方式来引用同一个元素。你可以通过它的类型(也就是它的标签名或元素名,即divpvideobody等)来引用它。).如果它有唯一的id属性,你可以引用它。您可以通过它的class属性中的任何值来引用它。或者,最后,你可以用其他创造性的方法来引用它,我们会在适当的时候讲到。

在我们深入研究不同的选择技术之前,让我们定义几个术语:

  • style rule。样式规则是一个完整的声明,它将决定哪些元素将受到样式的影响,并定义将应用的样式。它由选择器和声明块组成。
  • selector****。选择器是在 DOM 中定位一个或多个元素的模式。选择器可以由多个简单的选择器和组合子组成。
  • combinator****。组合符是一个符号,它定义了左边的简单选择器所指向的元素和右边的简单选择器所指向的元素之间的上下文关系。
  • declaration block****。样式规则的声明块定义了样式属性及其值,它们将应用于样式规则选择器的结果。

图 3-2 说明了这些术语。

9781430249832_Fig03-02.jpg

图 3-2 。CSS 样式规则的剖析

现在,让我们学习选择元素的各种方法。

类型选择器

你应该知道的第一个选择器是型选择器。类型选择器只是 HTML 元素的名称;使用一个会选择该类型的每个元素。清单 3-7 中的选择器将定位页面上的所有div元素,并将文本加粗。

清单 3-7 。 一个针对文档中每个 div 的类型选择器

/* CSS snippet */
div {
    font-weight: bold;
}

清单 3-8 将整个正文中的所有文本加粗。从技术上讲,这将尝试定位所有的 body 元素,但是让我们希望你只有一个!

清单 3-8 。 针对文档正文的类型选择器

/* CSS snippet */
body {
    font-weight: bold;
}

如果您没有指定类型,而是使用星号(*),那么您使用的是通用类型选择器,它匹配所有类型。清单 3-9 中的 CSS 将匹配整个页面上的每个元素,并将其字体设置为粗体!

清单 3-9 。 显式使用通用类型选择器

/* CSS snippet */
* {
    font-weight: bold;
}

这个通用选择器本身并不经常使用,但是理解它是很重要的,原因我们很快就会谈到。

不要忘记,这些类型选择器单独使用时,将匹配该类型的任何和所有匹配元素。为了只选择一个 div 元素,您必须更加具体(我们将在后面介绍更多)。

这很重要。在表达式中,类型选择器必须位于任何其他选择器之前。如果你已经做过 CSS 开发,这可能会让你大吃一惊。你可能使用过选择器,比如清单 3-10 中的选择器。

清单 3-10 。 一些选择器其中包含了隐式使用的通用类型选择器

/* CSS snippet */
.first {
    font-size: 18pt;
}

#firstName {
    border: 1px solid black;
}

:hover {
    color: green;
}

在这个例子中,在类选择器、ID 选择器和伪类选择器之前似乎没有类型选择器。但是请注意,如果您没有显式指定类型选择器,那么通用类型选择器(*)会隐式地为您分配。因此,清单 3-11 中的选择器与前面的例子是一样的。

清单 3-11 。 同样的三个规则加上一个显式的通用类型选择器

/* CSS snippet */
*.first {
    font-size: 18pt;
}

*#firstName {
    border: 1px solid black;
}

*:hover {
    color: green;
}

这是有意义的,因为你想用类似于#firstName的东西来表示的是任何带有firstNameid的元素。

班级选择器

与类型选择器不同,类选择器除了简单地声明元素之外,还需要你在 HTML 文件中做一些工作。具体来说,你需要用一个类来修饰你的元素。清单 3-12 展示了给一个div元素一个important类的样子。

清单 3-12 。 一个指定了类的 div 元素

<!-- HTML snippet -->
<div class="important"></div>

理解类选择器的预期用途很重要。许多开发人员在应该使用 ID 选择器的地方使用了它们(即将推出)。

添加到 HTML 元素中的类名通常应该是与它们相关的元素的形容词。类描述了它们的元素的一些永久或暂时的特征。当一个div包含应该在首页突出显示的内容时,它可能有一个featured类。当用户选择一个列表项时,它可能有一个selected类。或者一个视频可能有一个hidden类,当它应该等待用户动作来显示它时。

注意,我说过这些类可能是一个瞬态特征。在您的 HTML 应用程序中,您可能会根据应用程序的逻辑使用 JavaScript 在某些元素中添加或删除类。当用户选择一个元素时,JavaScript 事件可能会触发,并将selected类添加到该元素中。这样做时,元素会突然采用适用于所选元素的样式规则。例如,在清单 3-13 中,带有mydividdiv并没有启动类,但是当 JavaScript 代码片段中的代码触发时(这可能是对用户所做的事情的反应),将bigAndBold类添加到元素中,由该样式规则定义的所有样式属性突然生效。

清单 3-13 。 以编程方式添加类

<!-- HTML snippet -->
<div id="mydiv">Lorem ipsum</div>

/* CSS snippet */
.bigAndBold {
    font-size: large;
    font-weight: bold;
}

// JavaScript snippet
document.querySelector("mydiv").className = "bigAndBold";

用一个类值来修饰你的元素本身并不会对你的元素做任何事情,除非这个类为它定义了一个样式规则。将视频元素的类设置为hidden并不会隐藏它,除非你也为隐藏的类编写一个样式规则,比如display:nonevisibility:hidden

要将某个类作为所有元素的目标,只需在类名前加一个句点(。)在您的选择器中,如清单 3-13 中的所示。

清单 3-13 。 一个引用元素的类选择器带有一个类【重要】

/* CSS snippet */
.important {
    color: red;
}

元素不限于单个类。例如,清单 3-14 中的 HTML 标记定义了三个不同的类,完全有效。

清单 3-14 。 一个元素定义了三个类

<!-- HTML snippet -->
<div class="important featured hidden"></div>

在这种情况下,我们描述的是一个重要的、有特色的、隐藏的div。这没什么不好。

类似地,也许很明显,同一个类名可以修饰任意多的元素。重要的是你永远不要使用一个类来唯一地标识一个元素。唯一地标识元素是id属性和 ID 选择器的工作,我们很快就会看到。当您试图指定一组具有共同点的实体时,应该使用类。类本质上与元素集相关。

如果内容的结构可能会改变,那么应该用类选择器来代替类型选择器。当您在选择器中指定一种类型时,比如div,您就使您的样式依赖于文档结构,并使将来更改该结构变得更加困难。例如,你可以从定义菜单项的div标签列表开始,如清单 3-15 所示。

清单 3-15 。 使用 div 定义的菜单

<!-- HTML snippet -->
<div class="menu">
    <div>Home</div>
    <div>About</div>
    <div>Products</div>
    <div>Services</div>
    <div>Contact</div>
</div>

然而,你应该能够预见这种变化的可能性,比如清单 3-16 中的无序列表。

清单 3-16 。 同一个菜单现在被定义为列表项的无序列表

<!-- HTML snippet -->
<ul class="menu">
    <li>Home</li>
    <li>About</li>
    <li>Products</li>
    <li>Services</li>
    <li>Contact</li>
</ul>

在这种情况下,您可以通过按类(menu)引用菜单来保持 CSS 样式的抽象性,而无需指明类型。那么可以在不影响样式的情况下更改实际使用的元素类型。

所以你应该从清单 3-18 中选择一个,而不是清单 3-17 中的样式规则。

清单 3-17 。 一种类型菜单元素的具体引用方式

/* CSS snippet */
div.menu {
    /* menu styles */
}

清单 3-18 。 一种更好、更抽象的指代菜单元素的方式

/* CSS snippet */
.menu {
    /* menu styles */
}

第一个要求你有一个menu类的div。第二个也是首选的例子只需要你有类menu的任何元素。第一个例子只适用于第一个例子(使用div元素的例子),但是第二个例子适用于两个例子。

类选择器也可以相互链接,只匹配指定类的所有元素。

ID 选择器

我提到元素的类值应该被认为是元素的形容词——比如hiddenbiggeremphasizedheader。元素的id值应该是它的名字,并且必须是唯一的。一个img标签可能有mainLogoid,这给了我们一个明确的定义,以供将来参考,并且整个页面上不会有其他的mainLogo

您也必须确信这一点,因为如果您获得具有相同id值的多个元素,可能会出现一些奇怪的行为。

当您通过类引用元素时,您可以期望找到从零到许多的任何位置,但是当您通过 ID 引用元素时,您应该只期望一个,因为正如我们已经提到的,ID 应该总是唯一的。

要按 ID 选择元素,可以在元素前使用井号(#)。例如,清单 3-19 显示了一个选择器,它将选择一个id值为mainLogo的元素,并将其width设置为80px

清单 3-19 。 一个 ID 选择器,它应该只匹配一个元素

/* CSS snippet */
#mainLogo {
    width:80px;
}

没有必要明确指出类型,因为 ID 选择器总是引用单个特定的元素。

ID 选择真的没有比这更多的了。很简单。

属性选择器

到目前为止,我们已经看到了如何通过类型、类和 ID 来选择元素。然而,元素通常都有属性,可以根据这些属性的值来选择它们。

可以想象,这开启了很多可能性。HTML 元素的数量是有限的,但是有大量可能的属性,甚至每个属性可能有更多的值。

查看清单 3-20 中的选择器,它将为页面上所有的 ListView 控件设置 120 像素的左边距。

清单 3-20 。 匹配 ListView 控件的属性选择器

/* CSS snippet */
 [data-win-control='WinJS.UI.ListView'] {
    margin-left: 120px;
}

这里最大的优势是我们的 HTML 元素不需要比现在更具描述性。 div 已经通过属性声明它是一个 ListView,所以我们可以使用它,而不是浪费字节来添加 ID 或类属性。

最后一个例子使用了等式选择器([=])来检查data-win-control属性的值是否为WinJS.UI.ListView。不过,也可以使用其他属性选择器。

如果您只在方括号中指定了一个值,它将检查该属性是否存在,而不管它的值是什么(或者它是否有值!).清单 3-21 将把所有具有required属性的input元素的文本改为红色。

清单 3-21 。 一个简单检查某个属性是否存在的属性选择器

/* CSS snippet */
input[required] {
    color: red;
}

请记住,HTML5 中的required属性不像在 HTML 的以前版本中那样需要值。因此,这种存在比较对于这些“无价值”的属性是有帮助的,并且可能还有许多其他原因。

当指定属性的值完全等于指定值时,使用[|=]连字符选择器将会匹配,但如果属性的值中有后跟连字符的指定值,它也会匹配。因此清单 3-22 中的属性选择器将匹配阿郎值为enen-usen-au的段落,但不匹配es

清单 3-22 。 所有的 lang 属性都带有英语的派生词

/* CSS snippet */
p[lang|='en'] {
    direction:ltr;
}

^=]选择器被称为前缀选择器 ,但我记得是“开头为”。使用这个来查看属性是否有以某个值开始的值。清单 3-23 将为所有 ID 以item开头的div元素添加一些左边距。

清单 3-23 。 【始于】选择器

/* CSS snippet */
div[id^='item'] {
    margin-left:10px;
}

这将匹配itemitem1item02item-3等等,但不会匹配selecteditem

[]选择器也是类似的。它被称为后缀选择器 (我称之为“ends with”),当属性以您指定的值结束时,它将进行匹配。清单 3-24 将匹配selecteditemfirstitemitem,但不匹配itemOne.

清单 3-24 。 【结尾用】选择器

/* CSS snippet */
div[id$='item'] {
    margin-left:10px;
}

子串选择器 [ *= ]更容易记为“包含”,当属性值的任何部分匹配时,它就匹配。清单 3-25 将匹配所有 ID 包含字符串‘sidebar’的 div 元素。

清单 3-25 。??【包含】选择器

/* CSS snippet */
div[id*='sidebar'] {
    margin-left:10px;
}

最后,有一个空格选择器 [ ∼= ],它将在一个空格分隔的值列表中匹配您的值。这个有时会非常有用。看看清单 3-26 中的 span,它有一个名为data-food-types,的自定义属性,该属性将在一个空格分隔的属性值中包含类似meatvegetabledairy的内容。在这种情况下,空白选择器非常适合确定meat是否是列表中的一个值。值必须完全匹配;它们不能是部分匹配。

清单 3-26 。 空白选择器的一个用例

<!-- HTML snippet -->
<span data-food-types="dairy meat">meal</span>

/* CSS snippet */
span[data-food-type∼='meat'] {
    font-style:italic;
}

像类和 ID 选择器一样,属性选择器默认为通用类型选择器,所以下面两行是相同的:

[data-win-control='WinJS.UI.ListView']
*[data-win-control='WinJS.UI.ListView']

顺便说一下,在这种情况下,让显式类型选择器(div)处于关闭状态非常有意义,因为data-win-control只会出现在div元素上。

您可以为任何可能描述一个HTML元素的属性创建一个属性选择器。对于 HTML,只有被识别的属性才被认为是有效的,但是对于 HTML5,任何被识别的属性或者任何以 data- 开头的属性都被认为是有效的,所以开发者可以自由添加以data-开头的自定义属性。可能的属性选择器的数量是无限的。

清单 3-27 展示了一些在使用 HTML5 的 Windows 8 应用程序中使用属性选择器的实际例子。

清单 3-27 。Windows 8 应用中的属性选择器示例

/* CSS snippet */
section[role=main] {
    /* styles to affect the main section */
}

[data-win-control] {
    /* styles to affect all Windows 8 control in the document */
}

[data-win-control^='WinJS.UI'] {
    /* styles to affect just the UI controls */
}
属性选择器
标志
---
[=]
[]
&#124;=
[^=]
[$=]
[*=]
[∼=]

您可能会感兴趣地注意到,您实际上可以使用属性选择器通过 ID 来选择元素,但是这种形式很糟糕,因为有一个 ID 选择器就是专门为此设计的。

伪类和伪元素选择器

我希望你现在已经有了类的概念。记住,类是一个形容词。它描述了它所修饰的元素,并且是由您——开发人员——显式添加的。另一方面,伪类是固有的。它们是形容词,就像类一样,但是你不需要在 HTML 中指定任何东西。

例如,当用户悬停在一个表格行上时,单词 hover 可以用来描述该行,对吗?其中一个伪类是:hover(如您所见,伪类前面有一个冒号),只要用户将鼠标悬停在该元素上,就可以用它来选择该元素。向选择器中添加伪类比以编程方式向元素中添加类更容易、更简洁。

像其他选择器一样,可以单独指定一个伪类并调用默认的类型选择器。所以:hover*:hover是一样的,将匹配悬停在上面的任何元素。

伪元素的概念与伪类的概念非常相似。就像伪类的行为类似于类,但它是固有的,不需要由开发人员指定一样,伪元素的行为类似于元素,但它是固有的,也不需要由开发人员指定。

列表中的第一个列表项就是这样,不管你是否明确指定,对吗?其中一个伪类是:first-child,它将选择第一个列表项。这里,我们再次选择元素,而不必被迫编写无关的标记。

您可能会对伪类和伪元素之间的区别感到困惑,这是因为在 CSS3 之前的标准中语法没有区别。CSS3 引入了双冒号(::)语法来引用伪元素,并将单冒号(:)留给伪类。为了向后兼容,以前使用单个冒号的伪元素保留在规范中,所以在下面标准伪类和伪元素的完整列表中,您将会看到它们两次:::after, ::before, ::first-letter, ::first-line, ::selection, :active, :after, :before, :checked, :default, :disabled, :empty, :enabled, :first-child, :first-letter, :first-line, :first-of-type, :focus, :hover, :in-range, :indeterminate, :invalid, :lang(), :last-child, :last-of-type, :link, :not(), :nth-child(), :nth-last-child(), :nth-last-of-type(), :nth-of-type(), :only-child, :only-of-type, :optional, :out-of-range, :read-only, :read-write, :required, :root, :target, :valid, :visited.

除了标准的伪类和伪元素之外,还有一些特定于微软供应商的伪类和伪元素,在 Windows 8 应用中工作时,您应该熟悉它们。分别是:-ms-input-placeholder:-ms-keyboard-active::-ms-browse::-ms-check::-ms-clear::-ms-expand::-ms-fill::-ms-fill-lower::-ms-fill-upper::-ms-reveal::-ms-thumb::-ms-ticks-after::-ms-ticks-before::-ms-tooltip::-ms-track::-ms-value。供应商特定的伪选择器通常用于访问各种控件的子元素。

我不会带您浏览伪选择器的整个列表,但是我想突出几个我认为最有帮助的。

:词根

:root伪类有一个简单的功能,那就是在上下文中定位文档的根元素。例如,当 CSS 描述一个 HTML 文档时,根总是 HTML 元素。你永远不知道这个什么时候会派上用场,所以把它放在你的后口袋里。

*第一个字母

::first-letter非常适合在段落中创建首字下沉。我无法想象它还有什么其他用途,但是正如它的名字所表明的,它代表了它所应用的元素的文本中的第一个字母。请记住,有时伪类或伪元素被显式地附加到选择器中的元素上,如在p::first-letter中。其他时候,它是隐含在div ::first-letter中的。不要错过其中的细微差别。前者的意思是“段落标记的第一个字母”。后者在 div 后面有一个空格,因此意味着“作为 div 后代的每个元素的第一个字母”。

清单 3-28 定义了::first-letter伪元素最常见的用法——创建首字下沉。图 3-3 展示了最终的首字下沉。

9781430249832_Fig03-03.jpg

图 3-3 。样式定义创建首字下沉效果

清单 3-28 。 使用::首字母伪元素定位段落中的首字母

/* CSS snippet */
.dropCap::first-letter {
    font-size: 500%;
    float: left;
    line-height: 0.8em;
    padding: 0 4px 0 0;
    font-family: Serif;
    font-weight: bold;
    position: relative;
    left: -3px;
}

:勾选

:checked 是一个伪类(你可以通过前面的单个冒号来辨别),它会将选中的复选框(或单选按钮)限制为已经被选中的复选框。清单 3-29 和图 3-4 展示了:checked伪类的作用。

9781430249832_Fig03-04.jpg

图 3-4 。标记为选中的复选框已获得边框样式

清单 3-29 。??:选中的伪类选择器在起作用

<!-- HTML snippet -->
    <div id="checkboxes">
        <input type="checkbox" />
        <input type="checkbox" checked />
        <input type="checkbox" />
        <input type="checkbox" checked />
        <input type="checkbox" />
    </div>

/* CSS snippet */
#checkboxes :checked {
    border: 1px solid red;
}

::之前,::之后,和内容

content样式属性结合使用的::before::after 伪元素,用于在选择器的目标元素之前或之后注入一些内容。首先,您可能会想知道这个功能在 CSS 中是干什么的。毕竟,这看起来像是应该由 HTML 和/或 JavaScript 处理的内容问题。

然而,在有些情况下,内容的注入实际上更像是一个样式问题。假设您想要在悬停的段落旁边呈现一个小的补充字形,以向用户指示哪一个是焦点。或者,您可能希望在用户已经访问过的链接旁边显示一个复选标记。如果没有::before::after伪元素和content属性,这两者都是不可能的。

关于content属性,至少还有一件有趣的事情。它允许使用一个attr()函数来引用上下文中元素的一个属性值。例如,看一下清单 3-30 ,当你悬停在它上面时,它会在每个链接旁边呈现一个弹出工具提示,包括链接的标题。

清单 3-30 。::. after 伪元素,用于呈现悬停链接的工具提示

/* CSS snippet */
a:hover::after {
    content: attr(title);
    background-color: lightyellow;
    border: 1px solid black;
    color: black;
    position: relative;
    left: 10px;
    white-space: nowrap;
    padding: 5px;
}

:必需的

这个:required伪类被大量用于向标记为必需的字段添加样式。清单 3-31 显示了一个例子。通常,web 开发人员会用红色边框、可选背景色或附赠符号来修饰必填字段。如果与我们刚刚学习的::before::after伪元素以及content属性结合使用,可以将符号添加到必填字段,而不需要任何额外的标记。

清单 3-31 。 一个 HTML 元素,有一个必需的属性和一个 CSS 规则来设置它的样式

<!-- HTML snippet -->
<input type="text" required />

/* CSS snippet */
input:required {
    border: 1px solid red;
    color: red;
}

::-ms-expand

::-ms-expand伪元素表示呈现在选择控件上的小下拉箭头。

::-ms-浏览

通过使用::-ms-browse属性,可以将使用文件输入控件时呈现的浏览按钮作为目标。能够像这样瞄准子元素,可以对应用程序设计的细节进行非常精细的控制。

位置伪类

许多伪类都与元素在 DOM 中的位置有关。他们是:first-child, :first-of-type, :last-child, :last-of-type, :nth-child(), :nth-last-child(), :nth-last-of-type(), :nth-of-type(), :only-child,:only-of-type

这些位置伪类 非常有助于选择正确的元素,而不必用 id 或 class 属性来修饰 HTML 元素。你可以用它们来指代

  • 页面上的第一个 div
  • 每隔一行表格
  • 一节中的最后一段
  • 奇怪的菜单项
  • 每三个列表项目
  • 更多

您会注意到,这些伪类中大约有一半以-child为后缀,另一半以-of-type为后缀。-child不受限于元素类型,而-of-type则受限于元素类型。考虑清单 3-32 和清单 3-33 之间的细微差别。

清单 3-32 。 带有-child 后缀的位置伪类之一

/* CSS snippet */
div p:first-child {
    font-weight: bold;
}

清单 3-33 。 带有-of-type 后缀的位置伪类之一

/* CSS snippet */
div p:first-of-type {
    font-weight: bold;
}

清单 3-32 将选择“其同级中第一个子段的每个段落”,而清单 3-33 将选择“其同级中第一个段落的每个段落”。当然,这两个匹配的段落也必须是一个div元素的后代,因为选择器是以div开始的。

那么,你已经看到了first-childfirst-of-type伪类做了什么,并且你可能猜到了last-childlast-of-type做了什么。现在看看:nth-child():nth-of-type():nth-last-child():nth-last-of-type()。这些伪类是功能性的,这就是它们有括号的原因。

为了说明所有这些位置伪类,考虑清单 3-34 中的表格。

清单 3-34 。 一张桌子有五排

<!-- HTML snippet -->
<table id="myTable">
    <tr><td>Beans</td><td>2.7</td><td>67%</td></tr>
    <tr><td>Corn</td><td>1.7</td><td>40%</td></tr>
    <tr><td>Beets</td><td>2.9</td><td>77%</td></tr>
    <tr><td>Carrots</td><td>7.0</td><td>62%</td></tr>
    <tr><td>Celery</td><td>12.3</td><td>50%</td></tr>
</table>

如果我们的目标是选择所有的表格行并将它们的背景涂成灰色,我们可以使用。。。

/* CSS snippet */
#myTable tr {
    background-color: gray;
}

另一方面,如果我们的目标是只选择某些行,我们将使用first-last-nth-中的一个。

为了选择表格的第一行,我们将使用清单 3-35 中的。

清单 3-35 。 这个:第一个子伪类用来给表格中的第一行着色

/* CSS snippet */
#myTable tr:first-child {
    background-color: gray;
}

为了选择最后一个表格,我们将使用清单 3-36 中的。

清单 3-36 。 这个:last-child 伪类用来给表格中的最后一行着色

/* CSS snippet */
#myTable tr:last-child {
    background-color: gray;
}

为了每隔一行选择一行,我们可以使用清单 3-37 中的。

清单 3-37 。??:第 n 个子伪类,用于对表格中的每隔一行进行着色

/* CSS snippet */
#myTable tr:nth-child(2n) {
    background-color: gray;
}

现在,这个需要解释一下。:nth-child伪类的行为就像一个函数,您可以传入一个形式为an + b的公式。CSS 引擎会将一组以0开始的正整数插入到n的公式中。结果将是一组整数。CSS 将忽略负值和零值,并使用产生的正整数来确定哪些项目应该匹配。

在清单 3-37 中,我只提供了2n,它相当于2n + 0并计算为[0,2,4,6,8,...]0被省略,我们实质上是高亮显示每一个偶数行。这是一个常见的场景,所以 CSS 也定义了even关键字,并允许我们简单地使用#myTable tr:nth-child(even)来创建相同的效果。

然后考虑公式2n-1,它将计算出类似于[−1,1,3,5,7,9,...]的整数集。然后 CSS 会忽略1(因为它小于零),并将该样式应用于元素 1、3 和 5。就像even一样,CSS 定义了odd关键字来简化问题。

您传递给一个nth-伪类的公式可能会非常有趣。这里有一些例子。。。

第 n 个伪类的示例公式

2n /* every other (starting with the second) */
2n - 1 /* every other (starting with the first) */
3n /* every third */
3n + 3 /* every third starting with 3 */
-n + 3 /* only the first three */
n + 3 /* all, starting with the third */

最后,我们来看看:only-child:only-of-type伪类。清单 3-39 使用了它们中的每一个。

清单 3-39 。 示例使用:独生子女和:唯类型伪类

/* CSS snippet */
div p:only-child {
    font-size: larger;
}

div p:only-of-type {
    font-size: larger;
}

第一个规则将选择“div 中的段落,只要它是该 div 下的唯一元素”,第二个规则将选择“div 中的段落,只要它是该 div 下的唯一段落元素”。

:目标

您可以使用:target伪类向您的应用程序添加一些相当有趣的交互,而无需引入任何 JavaScript。

它是这样工作的。如果文档中有一个元素的id值与当前 URL 中的片段标识符相匹配,那么该元素将与:target伪类相匹配。

例如,如果您的 URL 是http://www.mysite.com/#image01,那么带有image01id的元素将匹配:target伪类。

片段标识符用于在单个文档中导航用户,所以通过使用这个伪类,您可以添加样式,让用户知道他的焦点在哪里或者他在做什么。

:not( )

最后要给大家看的是:非伪类 。这个是有用的,因为它颠倒了括号内的内容。在前面的例子中,我向您展示了如何使用。。。

#checkboxes :checked {
    border: 1px solid red;
}

我们可以颠倒这个逻辑,使用。。。

#checkboxes :not(:checked) {
    border: 1px solid red;
}

组合子

我们现在知道如何使用以下方法选择一个柠檬或一组元素:

  • 元素的类型
  • 元素的一个或多个类
  • 元素的唯一 ID
  • 匹配元素属性的存在或值的表达式
  • 一个或多个伪类
  • 一个或多个伪元素

这涵盖了很多情况,但这不是我们用选择器能做的全部。选择者可以在团队中工作。可以使用组合子将它们组合在一起,以指定元素之间的层次结构和可能复杂的逻辑关系,从而到达目标元素。

我们可以很容易地选择页面上的所有div元素,并在它们上面添加一些空间,方法是:

div {
    margin-top: 6px;
}

然而,如果我们实际上只想在一个节中的 div 元素之前添加空格,那么我们就必须使用一个组合符。我们实际上必须使用下降组合子,它看起来像清单 3-40 。

清单 3-40 。 两个简单样式的选择器被一个后代组合符(一个空格)隔开

/* CSS snippet */
section div {
    margin-top: 6px;
}

清单 3-40 中的选择器中有一个空格。它将sectiondiv分开,这在两者之间建立了一个层次关系。它表示只有作为section的子节点的div应该匹配。这个空间被称为派生组合子,我们将更深入地研究它和其他三个。

下降的

使用一个空格(˽)来表示一个后代组合符。

在两个选择器之间使用这个空格意味着:“选择匹配第二个选择器的所有元素,其中该元素是匹配第一个选择器的元素的后代”。听起来好像满嘴都是,但是看着也不难。一个例子将澄清这一点。

div p {
    padding: 2px;
}

前面的代码将在div内的所有段落标签周围添加一些填充。这意味着它们可以嵌套任意多级,并且可以位于任何div的内部。这是一个非常自由的选择器。

子组合子是一个大于号(>)。

子组合子类似于后代组合子,只是它更加具体。只有当右边选择器匹配的元素是左边选择器匹配的元素的直接后代时,子组合子才匹配。

当你忘记了子组合子,并且懒散地在你的选择器之间留出一个空间时,你可能会遇到麻烦。例如,div p的选择器将匹配任何一个div的子段落,当你的div下只有一层子段落时,这很好。然而,它也将匹配div的远祖p元素,这可能是也可能不是你想要的。

这就是子组合子的用武之地。它要求右边的元素是左边元素的直接子元素。

一般同胞

后代和子组合符表示元素之间的垂直父子关系,但是兄弟组合符 ( )表示同一父元素的子元素之间的水平关系。

一般的兄弟姐妹意味着兄弟姐妹的顺序,但没有说兄弟姐妹之间有多近或多远。给定A ∼ B,AB必须有相同的父元素,并且B必须在A之后,但是中间可能有许多元素。

相邻的兄弟姐妹

相邻兄弟组合子 ( +)类似于一般的兄弟组合子,只是更具体一些。相邻的兄弟元素表明,与任一侧的选择器匹配的元素不仅是兄弟元素,而且它们彼此紧挨着,并按照指定的顺序排列。

你可能不会像其他人一样经常使用相邻兄弟组合符,但是当你需要它的时候,它是一种很好的资产。清单 3-41 显示了一个这样的例子,如果一个H1元素紧跟在另一个H1元素之后,那么这个例子的意图就是删除这个H1元素上面的空格。

清单 3-41 。 相邻兄弟组合子的绝佳用法

/* CSS snippet */
H1 + H1 {
    margin-top: -10px;
}

选择器组

我们已经看到了在类型、类、ID、属性等方面匹配的简单选择器。我们已经看到了使用组合子将简单的选择器串在一起形成层次结构。选择器还有一个非常重要的特性,那就是我们对它们进行分组的能力。希望用相同的样式属性来描述多个目标元素集是很常见的。例如,也许我们希望确保每个标题标签上方有 10 个像素的空间,无论它是h1h2还是其他。

这可以用单独的样式规则来描述,但是这是非常多余的,所以应该使用分组语法。分组就是简单地在选择器之间添加一个逗号。清单 3-42 展示了我们如何给所有的标题标签增加空间。

清单 3-42 。 一种样式规则,使用选择器链接来应用于五个级别的标题标签

/* CSS snippet */
h1, h2, h3, h4, h5 {
    margin-top: 10px;
}

重要的是要注意,当使用更复杂的选择器时,逗号表示选择链的完全重置。你可能会尝试类似于清单 3-43 中的代码,例如,对某个div下的所有标题标签应用相同的样式,但是结果并不是你想要的。因为逗号中断了选择链,所以h1将正确工作,但是h2h3h4选择器实际上将匹配文档中的每个h2h3h4

清单 3-43 。

/* CSS snippet */
#topDiv #childDiv h1, h2, h3, h4 {
    margin-top: 10px;
}

这个难题的解决方案是在链中的每个术语上包含完全限定的样式选择器。清单 3-44 展示了这个相当冗长但精确的解决方案。

清单 3-44 。 修正了选择器链,为每个术语添加了完整的层次结构

/* CSS snippet */
#topDiv #childDiv h1, #topDiv #childDiv h2, #topDiv #childDiv h3, #topDiv #childDiv h4 {
    Margin-top: 10px;
}

样式定义

记住,样式规则是由它的选择器和定义(也就是声明块)组成的。还要记住一个简单的概念,即在定义要修改的内容之前,必须先选择一些内容。我们已经详细讨论了如何选择你想要的东西,现在是时候实际做些什么了!

样式规则的定义非常容易理解,因为它只是一个键值对形式的样式属性集合。键和值总是用冒号(:)分隔,对之间用分号(;)分隔。

键是您将很快了解的许多 CSS 样式属性之一,值是该属性的有效值之一。

例如,颜色是 CSS 样式属性,红色是它的许多有效值之一。定义一个元素的文本颜色需要用一个选择器来定位该元素,然后为颜色属性键提供一个红色值。

清单 3-45 中的规则将使所有段落文本变成红色。

清单 3-45 。 将所有段落文本设置为红色

/* CSS snippet */
p {
    color: red;
}

你现在应该能认出选择器了。p是选择器。这是一个类型选择器,它将匹配文档中的每个段落标签。该定义由一个键值对组成。键是color,值是red

这是样式规则最简单的部分,但是它们也不会变得更复杂。选择器可能更长、更具体,这里多个样式属性对替换了单个属性对。

学习 CSS 主要涉及两件事:

  1. 学习如何使用选择器和
  2. 了解可供您使用的所有可能的样式属性

查看可用样式属性的最简单方法是在 Visual Studio 中使用 IntelliSense。当您在样式定义中调用智能感知时,您将看到所有可能的样式定义的完整列表(图 3-5 )。IntelliSense 不会根据哪些属性适用于您选择的元素类型来筛选列表。

9781430249832_Fig03-05.jpg

图 3-5 。应用于 CSS 样式属性的 Visual Studio 智能感知

有数百个属性,我将在后续章节中介绍其中的许多属性。现在,我只是想确保你理解一些使用它们的基本原则。

正如我所说的,定义一个样式属性非常简单,只需键入一个冒号(😃,键入值,然后用分号(;).

清单 3-46 显示了在一个样式规则中定义的几个样式属性,该规则将应用于所有的p元素,并将指示其中的文本为蓝色和粗体。

清单 3-46 。 具有两个属性的样式规则

/* CSS snippet */
p {
    color: blue;
    font-weight: bold;
}

速记属性

您的样式规则可能很短,只有几个属性,也可能很长,有很多属性。这完全取决于需要对目标元素应用多少样式。

现在考虑下面的大尺寸样式规则,它将应用于所有的div元素,并指示它有一个 2px 宽、虚线和红色的边框。

div {
    color: blue;
    border-left-color: red;
    border-left-style: dashed;
    border-left-width: 2px;
    border-top-color: red;
    border-top-style: dashed;
    border-top-width: 2px;
    border-right-color: red;
    border-right-style: dashed;
    border-right-width: 2px;
    border-bottom-color: red;
    border-bottom-style: dashed;
    border-bottom-width: 2px;
}

令人生畏,不是吗?对于像在元素周围添加红色虚线边框这样简单的事情来说,这显然不是一种优雅的方式,这也是速记属性存在的原因。速记属性允许您一次定义多个样式属性。让我们利用简写属性可以节省的空间和时间重写最后一个例子。

div {
    color: blue;
    border: 2px dashed red;
}

那好多了。一处房产取代了 12 处!border属性是一个速记属性。它以空格分隔的特定顺序获取属性值列表,并将它们应用于更长的实际属性列表。

同样,Visual Studio 是您的朋友。当您输入一个简写属性时,您会得到一个工具提示,向您显示它所期望的以空格分隔的属性列表(图 3-6 )。

9781430249832_Fig03-06.jpg

图 3-6 。Visual Studio 的工具提示建议速记属性的值

看看这些常见的简写属性。。。

border:3px groove black;
font:bold 12px arial;
margin:4px 7px 2px 4px; /* sets the top, right, bottom, and left margins */
padding:10px 2px 7px 1px; /* sets the top, right, bottom, and left padding values */
background:url(image.png) no-repeat gray;

边距和填充属性(我们将在第三章中详细讨论)甚至有一些替代形式。

margin:4px 7px; /* sets the top/bottom margins to 4px, left/right margins to 7px */
padding:10px; /* sets padding to 10px on all sides */

属性值

有效属性值(冒号后面和分号前面的值)因样式属性而异,但它们都遵循一些基本规则。所有属性共享一个公共的有效值,即inherit。值inherit将指示属性应该从其父元素获取其值。

长度值必须是数字,后面紧跟下表中的一个标准 CSS 度量单位。

单位 描述
em 计算出的字体大小
Ex 小写“x”的高度
Px 像素,相对于观察设备
rem 根元素的字体大小
vw 视口宽度
vh 视口高度
vm 视口宽度或高度的较小值
ch 零宽度(呈现字体中零字符的宽度)
in 英寸
cm 厘米
mm 毫米
pt 点数(1 点= 1/72 英寸)
pc 十二点活字(1 点活字= 12 点)

除了这些测量单位之外,您还可以指定一个后跟百分号(%)的值来计算您相对于其容器的长度。例如,宽度为50%的表格单元格将(至少试图)占据表格宽度的一半。

CSS3 引入了一些函数,可以用来给单元添加一些额外的魅力。即使涉及不同的单元,函数也会为你做一些计算。下面的代码将div的宽度设置为比其父容器的全宽小 20 个像素,这在页面上放置对象时可能非常有用。

div {
    width:calc(100% - 20px);
}

min()max()将接受任意数量的参数,并分别返回较小的或最大的。要将左填充设置为 10%和 20 像素中较小的一个,您可以使用以下内容:

div {
    padding-left:min(10%,20px);
}

级联顺序、特异性和范围

使用 CSS 的最终目标是获得应用于文档元素的属性。因此,重要的是要考虑一些会影响哪些应用,哪些不应用的细节。

层叠

CSS 中的 C 代表级联,所以你可能会猜测它是这项技术的旗舰特性之一,你可能是对的。

级联是指将样式规则应用于目标元素的方式。一个样式规则可能只应用于一个元素,而被另一个更重要的样式规则覆盖。

您的浏览器(遵循 CSS 标准)决定这些规则中哪一个更重要的方式是一件相当复杂的事情。我会尽量保持简单,但你应该明白这里发生了什么,所以你不会发现自己抓耳挠腮,想知道为什么你的文本是绿色的,而你的意思是它是黑色的。

样式规则确实是按顺序应用的。但是那个顺序是什么?还记得将样式属性应用于页面元素的三种方法吗?内嵌样式、嵌入样式表和外部样式表。嵌入样式中的样式规则会覆盖外部样式表中的样式规则,内联属性会覆盖这两种规则。

查看清单 3-47 中的 HTML 页面和外部样式表,您能确定div中的Hello, World!文本是否要加粗吗?

清单 3-47 。CSS 样式规则的层叠性质

<!-- default.html -->
<!DOCTYPE html>
<html>
    <head>
        <title></title>
        <link href="default.css" rel="stylesheet" />
    </head>
    <body>
        <div id="myDiv"style="font-weight:normal;" >
            Hello, World!
        </div>
    </body>
</html>

/* default.css */
#myDiv {
    font-weight: bold;
}

答案是不会大胆。外部样式表指示它应该是,但是内联样式用font-weight: normal属性覆盖了它。

如果您不确定哪些规则将被应用,哪些规则将被覆盖,请使用样式跟踪工具。大多数现代浏览器在开发人员工具中包含这样一个功能,当使用 Windows 8 应用程序时,Visual Studio 中也提供了同样的功能。

特异性

特殊性在这件事上也起了作用。特异性是一个奇特的词,用来描述计算机对你的选择器有多具体的最佳猜测,从而在与其他规则竞争时应该给它多少权重。如果两个样式规则以同一元素为目标,但其中一个以更具体的方式这样做,那么它被确定为具有更高的特异性,它获胜,并且应用该样式规则的属性而不是另一个。

例如,ID 选择器就非常具体。如果某个div元素是两个样式规则的目标,每个都试图给它一个不同的颜色边框,但是一个通过它的id引用它,另一个通过它的类型引用它,那么前者将获胜。下例中的div将会有一个红色的边框,尽管我们已经给了它一个蓝色的类。

<!-- HTML snippet -->
        <divid="myDiv" class="blue" >
            Hello, World!
        </div>

/* CSS snippet */
#myDiv {
    border: 1px solid red;
}

.blue {
    border: 1px solid blue;
}

为了更深入地了解 CSS 特异性,我推荐 David Powers 的书 Beginning CSS3

范围

当您学习 JavaScript 时,您将学习一种称为模块化模式的模式,这种模式旨在为某些 JavaScript 块提供范围,否则这些块将完全暴露给全局名称空间,从而暴露给应用程序中的所有其他代码。

CSS 没有这种东西。CSS 没有任何作用域!这意味着,如果在任何给定的 HTML 页面上引用了 100 个样式表,那么所有的 CSS 样式对于所有的页面都是可用的。

然而,Windows 8 应用程序往往遵循一种导航模式,即让用户停留在单个页面上(default.html),并加载用户导航到的页面的 HTML、CSS 和 JavaScript,而不是实际改变范围。这对开发者和用户来说都是一个非常好的实践。这实际上也是现代网站应用程序中一个非常流行的趋势,但是如果你在一个单页应用程序的一个页面上定义了一个 CSS 样式规则,你可能会不小心让这个规则影响到其他页面。因此,建议您在 CSS 样式的规则选择器前面加上 Windows 8 赋予所有页面片段的类,这只是页面的名称。

如果你在 Visual studio 的 Windows 8 项目中创建了一个名为orders.html的新项目,那么默认情况下你的页面的 HTML 将包含一个orders类的div。如果您在所有的选择器前面加上.orders和一个空格(记住,那是后代组合符),那么您的样式规则将只匹配页面中存在的元素。这是一个很好的实践。

媒体查询

CSS3 的前身 CSS2 有媒体类型。根据指定的媒体值,媒体类型允许应用不同的样式表。两个常见的媒体值是screenprint。这意味着当用户代理(通常是浏览器)将文档呈现到屏幕上时,开发人员可以指定一个应用于文档的样式表,而另一个样式表用于将文档呈现到打印机上。

CSS3 随着媒体查询的引入得到了很好的发展,极大地增加了功能。

有了媒体查询,我们不再需要引用完全不同的样式表,而是能够更精确地指定应用于各种媒体相关特征的 CSS 样式规则块。

媒体查询可以确定应该应用单个样式规则(比整个表单更细粒度),而不是仅仅确定在给定的情况下应该应用样式表(尽管这仍然是可能的)。

通过媒体查询,不仅可以区分各种媒体类型(如screenprint以及handheldprojectiontv),还可以区分方向(纵向和横向)以及各种大小、分辨率和纵横比。

将不同风格应用于不同媒体配置的能力对于 Windows 8 开发来说绝对至关重要。Windows 8 可以安装在大量的系统上;这些系统中的大多数都有不同的尺寸和分辨率屏幕——有些可以旋转——最重要的是,Windows 8 提供了捕捉应用程序的能力,将其设置为 320 像素宽,并将其放在屏幕上另一个应用程序的旁边。这引入了更多可能的渲染配置。

谢天谢地,媒体的询问让我们作为开发者的工作变得容易多了。

媒体查询以关键字@media开始,接着是媒体类型和一些特征,最后得到真或假的值。只有当类型和所有特征都匹配时,媒体查询才为真。

因此,有效的媒体查询语法如下所示:

@media type and feature1 and feature2 and featureN {
    styleRule1;
    styleRule2;
    styleRuleN;
}

类型的有效值为:screenprintbraillehandheldprintprojectionttytvembossedspeechall,特征的有效值为:widthheightdevice-widthdevice-heightorientationaspect-ratiodevice-aspect-ratiocolorcolor-indexmonochromeresolutionscangrid。在使用 Windows 8 应用时,-ms-view-state将会是你最好的朋友。这是您确定应用程序处于哪个视图状态的方法。它可以是全屏横向、全屏纵向、快照或填充(当一个应用程序被快照到你的应用程序旁边时)。

上面列举的特征值的子集可以以min-max-为前缀。例如,min-device-width: 800px的值将是有效的,并且有助于指定仅在所使用的设备至少 800 像素宽时才匹配的媒体查询。此外,如果设备支持 256 色或更少的颜色,max-color-index: 256的值将是有效的,并且将匹配。

可以通过用逗号分隔多个媒体查询来构建媒体查询列表。这只是在多种情况下应用同一套样式规则。在清单 3-49 中,当输出介质在屏幕或彩色打印机上时,颜色样式属性将应用于所有段落。

清单 3-49 。 针对屏幕和彩色打印机的媒体查询

/* CSS snippet */
@media screen, print and (color) {
    p {
        color: red;
    }
}

除了指定介质查询应适用的介质类型,您还可以指定介质查询应排除的介质类型。您可以使用以下命令进行媒体查询,该查询适用于除打印机之外的所有内容。。。

@media not print {
    p {
        color: red;
    }
}

这将在除打印之外的所有情况下应用颜色,在打印中可能希望节省彩色墨水。

如果你正在创建一个 Windows 8 应用程序,你肯定会想要处理至少两个视图状态——全屏景观和快照视图,如果没有其他的,媒体查询是可行的。

摘要

我们已经知道 CSS 的目的是用样式影响 HTML,而用样式影响 HTML 是一个双重过程,首先,选择一个或多个 HTML 元素,其次,定义应用于该选择的样式。

我们详细研究了选择过程——使用多种选择器类型和使用多种组合器类型将它们放在一起,这样我们的选择可以更加具体。

我们详细讨论了样式定义以及如何在样式定义中定义多个属性。我们还看到了速记属性的作用,使我们的样式属性更容易编写。

我们看到了样式规则如何级联、生效以及可能相互覆盖,我们还看到了如何使用媒体查询使样式规则仅在与设备、设备大小和视图状态相关的特定情况下生效。

接下来,我们将看看真实的、实时的样式属性,我们将从与样式文本相关的属性开始。

四、文本属性

image 注意伟大的排版有一些非常引人注目的地方,会让用户喜欢你的应用。

如果你已经阅读了第一章,那么你知道如何选择元素,你知道如何为这些元素定义样式规则,现在,你已经准备好把单纯的结构变成既美观又实用的东西了。

我们将详细了解许多样式属性中的一些。在这一章中,我们将介绍与排版相关的属性——与应用程序中的文本相关的属性。我们将查看文本颜色和字体属性,以决定文本呈现的字体、大小和粗细。我们将探索在 Windows 8 应用中比在传统网页设计中更受欢迎的多栏布局,我们将学习如何控制文本栏中的断字。

让我花点时间让你相信在你的 Windows 8 应用中高质量排版的重要性。排版不仅仅是让文本看起来很花哨——至少在 Windows 8 应用中是这样。在 Windows 8 中,排版用于传达风格,但也用于传达结构和层次。它帮助用户的眼睛和大脑快速判断屏幕上内容的意义。它用于帮助他们区分应用程序的标题、部分的类别以及组成应用程序内容的文本正文。

因此,花时间学习如何更好地控制应用程序中的文本显示,然后考虑微软设计风格原则的第一条——对工艺表现出自豪。这意味着你应该花时间来确保你的应用程序是完美的,有意识地让你的字体美观是一种完美应用程序的重要方式。

文本

页面上的文本有一个传递信息的基本功能,但小心选择字体和文本布局可以区分一个看起来像字典的应用程序和一个真正吸引用户的应用程序。

在这一部分,我们将会看到一些基本的文本属性,这些属性可以让我们控制文本的颜色、不透明度、粗细、大小和字体。先说颜色和不透明度。

颜色和不透明度

颜色属性 决定了选中元素的前景色。它主要影响元素包含的文本。

定义颜色有三个常见的地方:color属性、background-color属性(参见第三章)和border-color属性(同样,参见第三章),颜色总是以相同的方式定义——实际上是以方式定义——有四种常见的方式来定义颜色:

  • 按颜色名称
  • 按十六进制值
  • 通过 rgb()或 rgba()函数
  • 通过 hsl()或 hsla()函数

命名颜色

对大多数人来说,用颜色的名字来定义颜色可能是最直观的,所以如果有一种已命名的颜色符合你的要求,那就一定要使用它。您可以通过名称定义颜色,只需将颜色名称作为值提供给color属性。在 HTML/CSS 标准中有 147 种命名颜色,你可以在http://www.w3.org/TR/css3-color/#svg-color看到它们的列表。其中一些是基本的像:RedGreenBlue。其中一些是像DarkRedLightGreenDarkSlateBlue这样的基础的派生。其中一些完全是深奥的,如DarkGoldenRodLemonChiffonPapayaWhip。命名的颜色不区分大小写,所以papayawhipPapayaWhip一样有效。

十六进制颜色

使用十六进制值来定义颜色,你可以拥有远不止 147 种颜色——实际上更像是 1680 万种颜色。也许您还记得早期 web 开发中的建议,即您应该坚持使用窄范围的“web 安全色”,但是在当今时代,浏览器只能使用窄范围的调色板的情况少之又少,这个建议几乎没有意义。如果您正在创建一个 Windows 8 应用程序,那么您知道您正在使用 Internet Explorer 浏览器引擎的最新 web 标准,您当然不需要担心像这样的遗留兼容性问题。

十六进制颜色值是一个六位数的十六进制值,前面有一个哈希(#)。例如,#FFEFD5将给出等同于PapayaWhip的值,十六进制值也不区分大小写,所以#ffefd5也能很好地工作。第一对十六进制数字代表红色级别,第二对代表绿色级别,第三对代表蓝色级别。

表 4-1。一些十六进制值和它们的颜色等价物

十六进制值 颜色
#000000 黑色
#ffffff 白色
#ff0000 红色
#00ff00 绿色的
#0000ff 蓝色
#ffff00 黄色
#00ffff 蓝绿色
#ff00ff 品红

即使是我们当中更狂热的数字骑师也需要一些时间来转换十进制和十六进制,因此任何现代图形软件包都应该提供您选择的任何颜色的十六进制等价物,这很方便。用十六进制值来指定颜色并不能提供不透明度,所以如果你需要某种程度的透明度,那就继续读下去。

对于带有重复对的十六进制值,如#000000#ffffff,甚至是#aa33dd,有一种指定十六进制值的简写方法。如果您指定一个只有三个字符的十六进制值,那么它将成对重复这三个值。用这个速记技巧,用#000代替#000000 ( black),#fff代替#ffffff ( white),或者#ff0代替#ffff00 ( yellow)。

颜色

rgb()rgba()函数定义颜色非常简单。rgb()函数的工作方式与十六进制值类似,只是三个颜色分量作为十进制参数传递给函数,而不是三个十六进制对。颜色值rgb(0,0,0)将产生黑色,就像十六进制值#000000一样。rgba()函数在接受 alpha 值方面更进了一步。alpha 值定义颜色的不透明度;也就是说,它决定了颜色出现的程度,而不是让它的背景透过。这是透明的反面。完全不透明(零透明度)的蓝色是蓝色,并完全覆盖其背景,而一半不透明(半透明度)的蓝色让一些背景通过,就好像它是蓝色的玻璃。作为一个例子,rgb(0,0,255)将完全是蓝色的,而rgba(0,0,255,0.5)将产生类似蓝色玻璃的东西。

红色、绿色和蓝色的值可以是从 0 到 255 的整数值,也可以是百分比值(后跟一个%符号)。alpha 值应该始终是从 0.0(完全透明)到 1.0(完全不透明)的十进制值。

HSL 颜色

rgb()rgba()类似,您可以使用hsl()hsla()功能根据色调、饱和度和亮度值指定颜色。HSL 是表示独特颜色的另一种方法。色调代表颜色在彩虹中的位置,饱和度大致对应于颜色的丰富程度,明度代表颜色的明暗程度。使用 HSL 值代替 RGB 值的好处是它们相对直观。一旦你有了正确的色调,你就可以调整它的饱和度和明度值来对颜色进行明智的调整,这对于 RGB 来说就不可说了。任何 RGB 值的微小变化都会产生完全不同色调的颜色。这在创建色带时特别有用,在色带中,选择一种色调作为主题颜色,并使用不同的饱和度或亮度值来创建应用程序的视觉资产。

这些函数将它们的第一个参数(色调)作为一个从 0 到 255 的整数,将它们的第二个和第三个参数(饱和度和亮度)作为一个百分比,hsla()将第四个参数(alpha)作为一个从0.0(完全透明)到1.0(完全不透明)的小数。

不透明

opacity属性是相关的。文本的不透明度决定了允许透过文本显示多少背景。显式设置文本的不透明度与在rgba()hsla()函数中使用 alpha 值是一样的(并且可以结合使用)。不透明度实际上与透明度相反,因此最大值1.0将导致文本不允许任何背景通过(100%不透明度或完全不透明),最小值0.0将允许背景完全通过(0%不透明度或完全透明)。

当我们开始制作动画时,不透明度变成了一个更加有用和相关的话题。通常,不透明度从 0 到 1 的动画会使其淡入,从 1 到 0 的动画会使其淡出。

字体和文本样式

大多数与版式相关的属性(前面的例子除外)都以text-font-开头,现在你会看到其中的许多属性。字体属性非常多,所以有一个速记属性,非常有用。在谈论速记语法之前,我将介绍一下各个属性。在接下来的章节中,我们将对字体的可能性进行更深入的探讨。

字体风格〔??〕

属性主要用于斜体显示你的文本。这些值(除了所有属性共享的继承值之外)实际上是normalitalicoblique。使用italic将选择正在使用的斜体版本。然而,使用oblique非常相似,你可能会挠头想知道它们之间的区别。不同的是,italic选择斜体版本的字体,oblique只倾斜普通版本的字体。通常它们看起来非常相似,但有时它们实际上非常不同。

大多数印刷工人不喜欢使用oblique而不是利用合适的斜体,但它在紧要关头肯定会起作用。如果你不太在乎完美的排版,那么这将省去你嵌入额外的斜体子集字体的麻烦。清单 4-1 中的代码定义了字体样式的三个值,图 4-1 描绘了结果。

9781430249832_Fig04-01.jpg

图 4-1 。在大多数情况下,斜体字看起来和斜体一样,但是用斜体字代替真正的斜体字体可能会被严肃的印刷者所反对

清单 4-1 。使用 font-style 属性使文本倾斜

<!-- HTML snippet -->
<div class="normal">Lorem ipsum dolor sit amet</div>
<div class="italic">Aenean cursus vehicula purus id sagittis</div>
<div class="oblique">Morbi tincidunt suscipit dignissim</div>

/* CSS snippet */
.normal { font-style: normal; }
.italic { font-style: italic; }
.oblique { font-style: oblique; }

字体变体

font-variant非常简单,只有normalsmallcaps表示值。小型股可能是标准资本化的一个有趣而有效的变体。在小型大写字母文本中,所有字母都是大写的,但只比小写字母稍微高一点。你可以在清单 4-2 和图 4-2 中看到小型大写字母变体的例子。

9781430249832_Fig04-02.jpg

图 4-2 。请注意,第二行中的所有字母都是大写的,但是实际的大写字母只比小写字母高一点点

清单 4-2 。使用 font-variant 属性将文本转换为小型大写字母

<!-- HTML snippet -->
<div class="normal">Lorem ipsum dolor sit amet</div>
<div class="smallCaps">Aenean Cursus Vehicula Purus ID Sagittis</div>

/* CSS snippet */
.normal { font-variant: normal; }
.smallCaps { font-variant: small-caps; }

字体粗细

您可能会经常使用font-weight属性来决定您的文本是否应该加粗。一个normal值将是正常的,一个bold值将是粗体的。字体的粗细可以通过指定从100-900100的任意增量进行数值设置。400的值与normal相同,而700的值与bold相同。您也可以使用bolderlighter使文本比它所继承的值逐渐变粗或变亮。清单 4-3 和图 4-3 提供了一个例子。

9781430249832_Fig04-03.jpg

图 4-3 。第二行(粗体)明显比第一行(正常)要粗,第三行(100)明显要浅,最后一行虽然是最大的字体粗细,但几乎看不出来

清单 4-3 。使用 font-weight 属性使文本加粗或不加粗

<!-- HTML snippet -->
<div class="normal">Lorem ipsum dolor sit amet</div>
<div class="bold">Lorem ipsum dolor sit amet</div>
<div class="_100">Lorem ipsum dolor sit amet</div>
<div class="_900">Lorem ipsum dolor sit amet</div>

/* CSS snippet */
.normal { font-weight: normal; }
.bold { font-weight: bold; }
._100 { font-weight: 100; }
._900 { font-weight: 900; }

字体大小

您可以选择指定字体大小。

  • absolute sizes可以使用绝对尺寸:xx-smallx-smallsmallmediumlargex-largexx-large。指定绝对大小让用户代理决定实际大小。
  • relative sizes您可以指定largersmaller的字体大小,这将使字体大小比其继承的值大或小一个增量。
  • length. 你可以使用一个长度值作为字体大小,这将设置字体大小绝对不考虑用户代理。
  • percentage如果你使用一个字体大小的百分比,它将设置相对于其父字体大小的字体大小。

我们将主要关注 Windows 8 应用程序的像素——至少是要出现在屏幕上的元素。请记住,Windows 8 会自动放大以获得更高分辨率的显示器,因此开发人员不需要动态修改字体大小。清单 4-4 和图 4-4 显示了使用中的各种字体大小。

9781430249832_Fig04-04.jpg

图 4-4 。字体大小的差异不言自明

清单 4-4 。使用 font-size 属性放大或缩小文本

<!-- HTML snippet -->
<div class="pt">Lorem ipsum dolor sit amet</div>
<div class="in">.Lorem ipsum dolor sit amet</div>
<div class="px1">Lorem ipsum dolor sit amet</div>
<div class="px2">Lorem ipsum dolor sit amet</div>

/* CSS snippet */
.pt { font-size: 12pt; }
.in { font-size: .5in; }
.px1 { font-size: 10px; }
.px2 { font-size: 30px; }

行高

font-size相似和相关的是line-height (也称为前导),它控制每行文本上下的空白。line-height的值也是长度值,两倍于font-sizeline-height将有效地产生双倍行距文本。您也可以使用百分比值。清单 4-5 和图 4-5 显示了行高示例。

9781430249832_Fig04-05.jpg

图 4-5 。通过将行高指定为 200%,第二段被赋予双倍行距

清单 4-5 。使用 line-height 属性设置一行文本的垂直间距

<!-- HTML snippet -->
<div>Lorem ipsum dolor sit amet...</div>
<div class="doubleHeight">Lorem ipsum dolor sit amet...</div>

/* CSS snippet */
.doubleHeight { line-height:200%; }

字体系列

font-family的值决定了使用哪种字体来呈现目标元素的文本。这是一个逗号分隔的系统字体列表,通常被称为字体堆栈。用户代理将继续尝试应用字体堆栈中的字体,直到成功找到用户系统上实际安装的字体。

除了实际的字体名称,您还可以指定五个通用字体关键字中的任意一个:serifsans-serifmonospacecursivefantasy。如果用户代理找到了字体堆栈中的通用字体,它将从安装在设备上的通用类型中选择一种字体。一些样本字体系列值在列表 4-6 和图 4-6 中说明。

9781430249832_Fig04-06.jpg

图 4-6 。呈现系统上可用的第一种字体。请注意 serif 和 sans-serif 后退,并注意最后一种字体“垃圾”在系统中找不到,因此使用默认字体

清单 4-6 。使用 font-family 属性选择字体

<!-- HTML snippet -->
<div class="f1">Lorem ipsum dolor sit amet</div>
<div class="f2">Consectetur adipiscing elit</div>
<div class="f3">Maecenas scelerisque tempor tincidunt</div>
<div class="f4">Aliquam fringilla</div>
<div class="f5">Aliquet tincidunt donec lacinia</div>

/* CSS snippet */
.f1 { font-family: arial, sans-serif; }
.f2 { font-family: 'times new roman', serif; }
.f3 { font-family: trebuchet, helvetica, sans-serif; }
.f4 { font-family: wingdings; }
.f5 { font-family: garbage; }

字体速记属性

所有上述字体相关属性都可以封装在字体速记属性中。font 属性需要很多值,所以语法正确很重要。最重要的是它们以正确的顺序提供。

提供属性的顺序是:font-stylefont-variantfont-weightfont-sizeline-height,然后是font-family。不过也有一些警告。

  • font-stylefont-variantfont-weight是可选的,如果它们顺序不对也可以工作,但是它们必须在font-sizefont-family之前。
  • font-sizeline-height应该用斜线(/)而不是空格分隔。
  • line-height是可选的,显然,如果你不包含斜线,就不应该包含它。
  • font-sizefont-family是强制性的。没有任何一项,整行都将被忽略。

清单 4-7 展示了几个字体速记属性值,图 4-7 展示了结果。

9781430249832_Fig04-07.jpg

图 4-7 。如果属性值的顺序正确,它们都将生效

清单 4-7 。使用字体速记属性一次设置多个字体属性

<!-- HTML snippet -->
<div class="f1">Lorem ipsum dolor sit amet</div>
<div class="f2">Consectetur adipiscing elit</div>

/* CSS snippet */
.f1 { font: bold 36pt trebuchet; }
.f2 { font: italic small-caps bold 24pt/48pt 'times new roman'; }

文本转换

属性用来控制文本的大小写。capitalize值将每个单词的第一个字母大写,uppercase值将整个单词大写,lowercase值将所有字符转换成小写。文本转换属性在清单 4-8 和图 4-8 中进行了说明。

9781430249832_Fig04-08.jpg

图 4-8 。第一行被转换为小写,第二行被转换为大写,第三行被转换为大写,每个单词的第一个字母大写

清单 4-8 。使用 text-transform 属性更改文本的大小写

<!-- HTML snippet -->
<div class="lowerCase">LOREM IPSUM DOLOR SIT AMET</div>
<div class="upperCase">lorem ipsum dolor sit amet</div>
<div class="capitalize">lorem ipsum dolor sit amet</div>

/* CSS snippet */
.lowerCase { text-transform: lowercase; }
.upperCase { text-transform: uppercase; }
.capitalize { text-transform: capitalize; }

image 全部大写!你会注意到微软在 Windows 8 的很多地方都选择了全大写——菜单就是其中之一。与人们的设想相反,使用全部大写是降低文本相对重要性的有效方法。人类的大脑被训练来阅读小写或标题大小写的文本,但是大写的文本在视觉上更像是一种补充元素,更倾向于被引用而不是被阅读。请记住,这并不适用于全大写的段落文本。大写的段落文字会吸引读者的注意力,但方式不恰当。微软风格手册上说“不要全部用大写来强调”。用句子结构来强调。最好避免为了强调而格式化,但是如果一定要为了强调而使用格式化,那就用斜体格式,不要全大写。”(微软出版社,2012 年)

文本装饰

您可以使用text-decoration属性更改出现在文本上方、下方和中间的线条。实际上,CSS3 调用了对text-decoration属性的扩展,以允许伴随文本的线条的颜色和样式。新标准将text-decoration属性本身变成了text-decoration-line-color-style的简写属性。不幸的是,在撰写本文时,这个新标准还没有被任何现代浏览器实现。这包括支持 Internet Explorer 10 和 Windows 8 应用程序的 Trident 引擎。

在标准实现之前,只需使用值为overlineline-throughunderlinetext-decoration属性,在文本之上、之中或之下添加分数,如清单 4-9 和图 4-9 所示。

9781430249832_Fig04-09.jpg

图 4-9 。导致文本上方、中间和下方出现线条

清单 4-9 。设置为上划线、划线和下划线的文本装饰值

<!-- HTML snippet -->
<div id="style1">Lorem ipsum dolor sit amet</div>
<div id="style2">Consectetur adipiscing elit</div>
<div id="style3">Maecenas scelerisque tempor tincidunt</div>

/* CSS snippet */
#style1 { text-decoration: overline; }
#style2 { text-decoration: line-through; }
#style3 { text-decoration: underline; }

字体-面

我们已经讨论了如何指定颜色、大小,甚至字体系列,但是有一个相当明显的警告。如果你指定了font-family: pickle;,那么用户必须在他的机器上安装 pickle 字体才能正确看到它。

无论你的目标是网络还是 Windows 8,这都是一个重大问题。有几个字体,你可以很好地打赌,将在您的用户系统,但没有一个可以保证。你可以使用通用的字体类型,但是这并不能保证你的排版看起来和你设计的一样。你需要的是确定用户能够通过提供字体来查看你的页面。这就是@font-face的用武之地。@font-face是自定义字体定义。@语法表明它不是一个属性或值,而是一个特殊的 CSS 实体,很像@media关键字。

@font-face关键字允许我们定义自定义字体名称和字体源的 URL。很可能你的字体会出现在你的项目中,我建议你按照惯例把你的字体放到一个名为type的文件夹中。在这种情况下,您可以简单地提供一个根级别的 URL,如/type/myfont.ttf

清单 4-10 展示了一个简单的@font-face定义以及该字体在 HTML 中的实现。注意,font-family值在@font-face块中定义,然后在.nifty样式规则中引用。这个名称是您自己选择的,但是引用必须与定义相匹配。

清单 4-10 。引用存储在项目类型文件夹中的字体文件的字体定义

<!-- HTML snippet -->
<span class="nifty">This is my nifty font!</span>

/* CSS snippet */
@font-face {
    font-family: "niftyfont";
    src: url("/type/nifty_n.woff") format(woff)
}

.nifty {
    font-family: niftyfont;
}

你可以在src属性中指定多个值来创建一个兼容性堆栈,但是当你在 Windows 8 应用上工作时,你的优势是不必担心其他浏览器作为目标。示例中的格式是 WOFF,但是您可能没有使用过这种字体格式。

WOFF (Web 开放字体格式)是由 Opera、微软和 Mozilla 共同提出的开放格式标准。WOFF 格式实际上不是一种新的字体格式,而只是现有的 EOT,TTF,OTF 字体的包装。包装 WOFF 的现有字体以用于您的应用程序有几个强大的优势:

  • WOFF 被压缩,因此换行字体比其相关的原始字体小。
  • 元数据可以包含在字体包中,以传递关于字体来源的信息,甚至是它的许可信息。

不过,无论你使用传统字体还是 WOFF 字体,如果你希望你的应用程序具有可访问性、适应性和可搜索性,添加良好的排版是一个好主意,定义字体(而不是仅仅在位图中嵌入你的自定义类型)是一个好主意。

Microsoft 供应商特定的文本属性

微软提供了一些供应商特定的、文本相关的 CSS 属性,即:-ms-text-autospace-ms-text-align-last-ms-text-justify-ms-text-kashida-space-ms-text-overflow-ms-text-underline-position。这些属性不会被广泛使用,但在某些情况下,它们实际上是至关重要的。

属性允许处理表意文字——传达思想的亚洲字符——周围的间距。

属性决定了如何处理一个段落的最后一行的间距。

属性控制 kashida 间距,这是阿拉伯书写系统中某些字符的扩展。

最后,-ms-text-underline-position属性可以决定文本的下划线(如果应用的话)是显示在字符的下面还是上面。值为abovebelow,默认为auto

对齐和对齐

对齐是文本布局的一个重要部分。对齐方式控制渲染引擎处理文本前后左右空间的方式。很可能,每个人都有在文字处理器中左对齐、居中、右对齐和对齐文本的经验。这正是text-align属性的工作。

您可以将text-align属性设置为leftrightcenterjustify的值。

除了设置文本的水平对齐,你还可以做很多事情来控制它的垂直布局。vertical-align属性取值如下:autobaselinesubsupertopmiddlebottomtext-toptext-bottom。默认值baseline将文本行的底部(不包括下行)与其父行的基线对齐。vertical-align属性也将接受一个长度或百分比值作为偏移量。

vertical-align属性并不总是如您所料。事实上,根据它所应用到的元素的类型,它的行为略有不同。

当您在图像标签上使用vertical-align时,它的行为类似于旧的valign属性,并确定图像相对于包含它的文本的位置。

当您在表格的单元格中使用vertical-align属性时,它会影响单元格中文本(或其他元素)的垂直对齐,如清单 4-11 中的所示。

9781430249832_Fig04-10.jpg

图 4-10 。单元格文本按照定义正确垂直对齐

清单 4-11 。简单表格中的垂直对齐

<!-- HTML snippet -->
<table>
    <tr>
        <td colspan="3">I'm a table</td>
    </tr>
    <tr>
        <td>top</td>
        <td>middle</td>
        <td>bottom</td>
    </tr>
</table>

/* CSS snippet */
table { width: 300px; border-collapse:collapse; }
table tr:nth-of-type(2)  { height: 80px; }
table td { border: 1px solid; }
table tr:nth-of-type(2) td:nth-of-type(1) { vertical-align: top; }
table tr:nth-of-type(2) td:nth-of-type(2) { vertical-align: middle; }
table tr:nth-of-type(2) td:nth-of-type(3) { vertical-align: bottom; }

关于清单 4-11,我想指出一些与主题无关的事情:

  • 边界-崩溃:崩溃;属性用于移除表中单元格之间固有的间距
  • n-of-type()选择器用于根据位置选择表格行和单元格

在图 4-10 的表格单元格中,可以看到文本已经垂直居中。

然而,当你试图在div中使用vertical-align时,你可能会大吃一惊。注意在清单 4-12 中,我们是如何试图指示被称为vadiv将其文本水平居中和垂直居中对齐的。

9781430249832_Fig04-11.jpg

图 4-11。我们提供的文本没有垂直居中

清单 4-12 。关于使用垂直对齐的一个常见错误

<!-- HTML snippet -->
<div id="va">
    middle?
</div>

/* CSS snippet */
div#va {
    height: 100px;
    width: 200px;
    vertical-align: middle;
    text-align: center;
    border: 1px solid black;
}

它在水平方向上到达了中间,但在垂直方向上显然不在中间。为什么这个属性被忽略了?属性不只是在 CSS 中奇怪地实现。对于表格单元格,它实际上是垂直对齐的,但是如果我们的 div 包含行内元素(行内元素像文本一样流动并换行),那么vertical-align会做一些完全不同的事情。它相当于旧的valign HTML 属性,调整元素相对于基线的垂直位置。这丝毫不会影响它们在父容器中的位置。

这个问题似乎已经困扰网页设计师几十年了,我知道这也是我自己一些不眠之夜的原因。网页从来没有真正被设计来指定垂直行为。HTML 的本质是水平伸缩,内容从上到下垂直流动。这些标准已经发展并取得了进步,但是仍然存在过去的残余。

有一些变通方法可以帮助解决这个问题,但是没有一个是好的,这就是为什么微软 CSS3 实现中的新网格非常受欢迎。第六章将带您深入了解网格的实现,以解决这个重大问题。

vertical-align的另一个重要用途是将文本设置为上标或下标。subsuper值将为您执行此校准。您经常会在span元素中找到这个。例如,下面可以用来构造等式y = x 2

<div>y = x<span style="vertical-align:super;">2</span></div>

许多现代 HTML 应用和网站严重依赖大量文本,CSS3 通过引入多栏文本管理将这些应用带入了可行性领域。对多列的支持也不仅仅是微弱的支持。它功能强大,功能齐全。

在引入多栏之前,开发人员被迫手动或以编程方式布置栏。新的多列 CSS 属性真的让任何类型的阅读器应用程序不仅成为可能,而且富有表现力和趣味性。

多列支持对 Windows 8 来说尤其重要,因为设计语言是水平布局而不是垂直布局。经常听到有人质疑这是为什么,那么下面是几个原因。。。

  • 首先,水平平移是独特的,是差异化的。这是 Windows 8 重塑用户体验的另一种方式,也是它在竞争中脱颖而出的另一种方式。
  • 世界上大多数语言都是从左向右阅读的,因此从左向右平移对人类来说是很自然的。
  • 大量文本水平排列的应用程序读起来就像一本杂志,新内容从右边显示出来。这再次使它成为一个熟悉的范例。
  • 人体手臂和手的解剖结构水平移动比垂直移动更容易。试着左右滑动,然后上下滑动,你就会看到了。
  • 最后,屏幕传统上是横向的,随着屏幕纵横比从 4:3 变为 16:9,屏幕变得更加夸张。

对于垂直布局,完全有可能有一个与屏幕宽度相同的列,但对于从右侧显示新内容的水平布局,列就成了必需的。

请注意,从垂直滚动改为水平滚动的一个含义是,文本将不再逐行显示,而是逐列显示(图 4-12 )。在第六章中,我们将学习捕捉点。对齐点有助于让用户停留在文本的每一列。其结果很像翻页——电子书阅读器已经使用了多年,纸质书已经使用了几个世纪。

9781430249832_Fig04-12.jpg

图 4-12 。垂直文本布局与水平文本布局

垂直滚动和水平滚动之间的差异相当于布局的巨大变化。查看图 4-13 和图 4-14 以查看每个版面中相同内容的示例——一篇关于巴黎的文章。

9781430249832_Fig04-13.jpg

图 4-13 。使用垂直滚动文本在屏幕底部运行的网络文章

9781430249832_Fig04-14.jpg

图 4-14 。同一篇文章在一个 Windows 8 应用中水平放置,邀请用户向右平移

列宽和列数

您可以通过选择列计数或列宽将您的文本块转换成多列文本块——只需要设置一个,另一个将被计算出来。如果在 1366 像素宽的设备上设置列数2,那么列数将按 683 像素计算(减去页边距和装订线的宽度)。当您确切知道要在给定容器中呈现多少列时,请使用列计数。想象一下你的用户将你的文本移动到大屏幕格式。如果你的用户在他们的小屏幕上有两列文本,然后把你的应用程序移到大屏幕上,你还想要两列(现在很宽)的文本吗?可能不会。更有可能的是,您想要设置列宽,并让屏幕上生成尽可能多的列。看看图 4-15 就明白我的意思了。更大的屏幕需要更多的文本栏。

9781430249832_Fig04-15.jpg

图 4-15 。设置列宽允许在更大的屏幕上显示更多的列

属性本身是column-widthcolumn-count,还有一个简写属性columns。有趣的是,你很少需要同时使用column-widthcolumn-count,所以你可能会在大部分时间或所有时间里只使用一种速记方法。

速记属性叫做columns。它需要宽度和/或计数。正如我所说的,你可能会选择其中之一,所以通常你的列属性看起来就像清单 4-13 中的那些。

清单 4-13 。确定多列的典型属性

/* CSS snippet */
.columns {
    columns: 4;
}

.columns {
    columns: 200px;
}

.columns {
    columns: 400px;
}

列间距和列规则

column-gapcolumn-rule属性让您可以控制列之间的空间发生了什么。

column-gap接受一个长度值,并确定列之间的间距。Windows 8 设计指南没有对多列文本之间的间距宽度给出建议,但它建议列表中的列之间的间距为 40 像素,这也是我对文本列的建议。

column-rule实际上是一个简写的属性,它一次轻松地封装了column-rule-widthcolumn-rule-stylecolumn-rule-color。规则只是分隔文本列的一行。它在美学上是有帮助的,但也可能有点花哨,所以要谨慎使用列规则。当两列文本彼此不相关时,为了清楚起见,在它们之间添加一条规则可能会有所帮助。

列填充

对于列中的文本流动,呈现引擎可以选择两种可能的行为。如果您有三列,但没有足够的文本填满所有三列,引擎可以填充第一和第二列,让第三列变短,或者它可以平衡所有三列中的内容量(垂直)。

属性是你如何控制这些行为中的哪一个被使用。autobalance值的结果行为显示在图 4-16 中。

9781430249832_Fig04-16.jpg

图 4-16 。自动和平衡列填充的区别

多列样品

我们将会看到许多不同的多栏配置,对于每一个,我们将会使用你在清单 4-14 中看到的相同的 HTML。

清单 4-14 。一个包含五段大小合适的文本的 div

<!-- HTML snippet -->
<div id="columns">
    <p>Lorem ipsum dolor sit amet ... </p>
    <p>Sed rhoncus, erat in eleifend ... </p>
    <p>Nam mollis iaculis neque ut ... </p>
    <p>In eleifend purus et leo ... </p>
    <p>Sed quis sapien vitae elit ... </p>
</div>

ID 为 columns 的div包含五段文本。为了简洁起见,我对实际的段落文本进行了裁剪,但实际上每个段落都是典型的段落大小。让我们看看一些可能的 CSS 样式规则,以各种方式将这五个段落格式化成列。

列表 4-15 和图 4-17 显示了自动列填充的结果。另一方面,清单 4-16 和图 4-18 展示了平衡的列填充。

9781430249832_Fig04-17.jpg

图 4-17 。两个 400 像素的列适合,列填充的自动值导致每个列在溢出到下一列之前被填充到底部

9781430249832_Fig04-18.jpg

图 4-18 。三列适合空间,文本溢出,以便所有具有相同数量的文本

清单 4-15 。列宽度为 400 像素,自动填充列

/* CSS snippet */
#columns {
    columns: 400px;
    column-fill: auto;
    height: 600px;
}

让我们看看,当我们有多列时,我们可以做些什么来垂直平衡文本。

清单 4-16 。三列,列填充值为 balance

/* CSS snippet */
#columns {
    columns: 3;
    column-fill: balance;
    height: 600px;
}

某些类型的内容适合窄栏。我可以想象一个类似字典的东西,它主要是一系列简短的内容。当您考虑您的设计时,您可能会发现一些列间距和列之间的一些规则会有所帮助。清单 4-17 展示了一些窄列,列之间有一点间隙和浅灰色的标尺。

9781430249832_Fig04-19.jpg

图 4-19。有规则的窄栏看起来很优雅

清单 4-17 。尽可能多的 80 像素列,以适合 40 像素的间隙和其间的浅灰色嵌线

/* columns.css */
#columns {
    columns: 80px;
    column-gap: 40px;
    column-rule: 1px solid #ddd;
}

image 孤儿寡母当个别单词或行滞留在一列的开头或结尾时,它们往往会显得格格不入,而且是无意的。我们称这些人为寡妇和孤儿。避免寡妇和孤儿使用 CSS3 的唯一方法是在段落元素中添加break-inside:avoid。这将导致一个段落试图保持在一起,而不是分裂自己的列。这不是一个理想的解决方案,因为它要么全有,要么全无。如果一个段落连一行都放不下,那么整个段落就会跳到下一列,可能会留下一个大洞,看起来比小寡妇还要无意。

列表样式

有两种列表:有序列表和无序列表。有序列表通常用数字或字母标记,无序列表通常用项目符号标记。您可以使用list-style属性:list-style-typelist-style-positionlist-style-image和速记属性list-style来控制它们。

list-style-type属性很有趣,因为不管标记是什么,它的值都可以将列表转换成有序列表或无序列表。因此,如果 HTML 指定了一个ul(无序列表)标签,您可以简单地应用一个list-style-type,它将呈现为一个有序列表,就像您使用了ol标签一样。

list-style-type属性的有效值为:nonecirclediscsquareArmeniandecimaldecimal-leading-zeroGeorgianlower-alphalower-greeklower-latinlower-romanupper-alphaupper-latinupper-roman

当您试图让列表正确对齐时,list-style-position属性会非常方便。值outside确定项目符号在文本之外,并且换行文本缩进,因此项目符号保持悬挂在左侧。另一方面,inside的值确定项目符号在文本内,并且换行文本与项目符号对齐。

属性允许你指定一个自定义的图片用于你的列表的项目符号。该值应该采用url('folder/image.png')的形式。

清单 4-18 和图 4-20 展示了一个没有应用 CSS 修改的标准无序水果列表。

9781430249832_Fig04-20.jpg

图 4-20 。创建简单的项目符号列表并不令人惊讶

清单 4-18 。没有应用样式的标准无序列表

<!-- HTML snippet -->
<ul>
    <li>lorem</li>
    <li>ipsum</li>
    <li>dolor</li>
    <li>sit amet</li>
    <li>tempor</li>
</ul>

清单 4-19 向列表中添加了一些样式,将无序列表(通常使用项目符号)转换成有序列表(使用十进制数字),并确保数字在列表中。我将列表宽度限制为 200 像素,并重复了单词 mangoes,只是为了说明这个list-style-position属性。你可以在图 4-21 中看到结果。

9781430249832_Fig04-21.jpg

图 4-21 。即使标记将其定义为无序列表,也会创建有序列表(编号值为 1–5)。此外,请注意第 5 项一直换行到数字级别的方式

清单 4-19 。标准无序列表现在应用了样式

<!-- HTML snippet -->
<ul>
    <li>lorem</li>
    <li>ipsum</li>
    <li>dolor</li>
    <li>sit amet</li>
    <li>Maecenas scelerisque tempor tincidunt</li>
</ul>

/* CSS snippet */
ul {
    list-style-type: decimal;
    list-style-position: inside;
    width: 200px;
}

使用 CSS 来设计列表的样式是非常容易的,所以列表是一种很好的、语义化的方式来指示 UI 中任何重复的项目或实体列表。例如,您可以将一个项目列表转换成一个列表、一个菜单或一堆小块。

用连字符号连接

断字是在文本块换行时断开单词,以提高对齐效果。如果渲染引擎被迫将整个单词放在一起,它可能会在文本列中造成一些令人尴尬的大范围空白。但是,如果允许引擎在中间断开一个单词,并在下一行继续,并用连字符表示这一点,那么它可以对文本的换行进行更多的控制。

直到最近,网页中还没有断字。CSS3 已经定义了连字符属性,但是还没有被很多浏览器实现。IE10 和 Windows 8 实际上不识别标准的hyphens属性,但是它们也支持同一属性的供应商特定版本。除了特定于供应商的前缀之外,语法也是一样的。然后,属性是-ms-hyphens.。像所有其他供应商特定的属性和属性值一样,微软致力于 CSS 标准,并且微软将通过去掉供应商前缀尽快切换到标准属性。今天你使用-ms-hyphens,但最终,你只会使用hyphens

-连字符

指示一个文本块进行断字非常简单。清单 4-20 用值为auto的单个-ms-hyphens属性实现了这一点。-ms-hyphens 属性可以有三个有效值之一(除了inherit ): noneautomanual。一个none值显然不允许任何断字操作。manual值将只允许在内容中明确建议了断点的地方使用断字符-通过出现硬连字符()或软连字符(&shy;)来指示。软连字符表示单词可以在给定点断开,但是除非必须断开,否则连字符不会出现。另一方面,一个auto值将允许渲染引擎在那些建议的点以及没有建议的单词中间中断。

9781430249832_Fig04-22.jpg

图 4-22。连字符用于减少文本右边距的锯齿

清单 4-20 。使用-ms-hyphens: auto 打开断字功能

<!-- HTML snippet -->
<p>Lorem ipsum dolor sit amet...</p>

/* CSS snippet */
p {
    width: 500px;
    -ms-hyphens: auto;
}

在大多数情况下,这就是你所需要做的,但是如果你需要对断字的呈现方式有更多的控制,那么你需要查看其他属性。

-ms-连字符-限制-区域

属性决定了行尾可以保留的最小空白量。如果你指定了一个区域50px,并且在对齐发生之前,在一行中将会有超过 50 个像素的空白,那么下一行的单词将会被连字符连接并向上拉以填充其中的一些空间。数字越小,允许的空白就越少,因此需要更多的连字符。如果区域太大,则根本不会出现断字。

如果清单 4-21 和图 4-23 中的 CSS 被应用于清单 4-20 中的同一个 HTML,那么我们将以一个连字符行结束——以 adipis 结尾的那一行。如果单词ADI pissing的那一部分没有被提取出来并使用连字符连接,那么单词 dignissim 后面的空格将会超过 50 个像素。

9781430249832_Fig04-23.jpg

图 4-23 。如果将此图与图 4-22 进行比较,可以看出连字符的行为受到了影响

清单 4-21 。用-ms-hyphenate-limit-zone 设置断字区

/* CSS snippet */
p {
    width: 500px;
    -ms-hyphens: auto;
    -ms-hyphenate-limit-zone: 50px;
}

-ms-连字符-限制-字符

下一个-ms-hyphenate属性是-ms-hyphenate-limit-chars。该属性设置可以断字的单词的最小大小。您可以为所讨论的整个单词指定一个最小大小,为连字符之前的部分指定一个最小大小,为连字符之后的部分指定一个最小大小。这三个值由空格分隔。值5 2 2将指示带连字符的单词必须至少有 5 个字符,并且在带连字符后,连字符前至少有 2 个字符,连字符后至少有 2 个字符。

您可以使用关键字auto来代替这三个值中的任何一个。auto 的默认值是5 2 2,所以如果auto替换了第一个值,那么它将再次代表一个5,第二个2,第三个2。值8 4 4将非常保守,仅允许大的单词断开,并且将要求至少 4 个字符保留在一行中,并且至少 4 个字符向下移动到下一行。

清单 4-22 中的 CSS 使用了一个值7 3 3作为例子,注意在图 4-24 中,单词 sollicitudin 被允许断开,因为它超过了 7 个字符(其中至少有 3 个字符可以保留在连字符之前),但是 Curae 不允许断开,因为它只包含 5 个字符。

9781430249832_Fig04-24.jpg

图 4-24 。同样,断字行为与前面的示例有所不同

清单 4-22 。设置一个单词必须有多大才能断字和断字

/* CSS snippet */
p {
    width: 500px;
    -ms-hyphens: auto;
    -ms-hyphenate-limit-chars: 7 3 3;
}

-ms-连字符-限制-行

-ms-hyphenate家族的最后一名成员是-ms-hyphenate-limit-lines。该属性将确保连续的连字符行不超过指定的数量。在清单 4-23 中,-ms-hyphenate-limit-zone被设置为3px,这将导致大量的断字,但是-ms-hyphenate-limit-lines被设置为2,这意味着一行中不超过 2 行可以被断字。如果你查看图 4-25 中的结果,你会注意到以 Donec 结尾的行是一个在行尾之前至少有 3 个像素空白的断字候选行,但是前面两行(以 Vestibu-Pel- 结尾)已经被断字,所以它不能被断字。

9781430249832_Fig04-25.jpg

图 4-25 。-ms-hyphenate-limit-lines 限制了连续连字符的行数,可以防止您的文本看起来过于支离破碎

清单 4-23 。使用-ms-hyphenate-limit-lines 设置允许多少个连续的连字符行

/* CSS snippet */
p {
    width: 500px;
    -ms-hyphens: auto;
    -ms-hyphenate-limit-zone: 3px;
    -ms-hyphenate-limit-lines: 2;
}

摘要

在这一章中,我们已经讨论了很多与文本样式相关的内容。我们已经介绍了可以用来定制文本颜色和不透明度的 CSS 属性,影响字体的各种方法,我们还介绍了一些强大的文本布局控件的多列和断字,这对 CSS 世界来说是相当新的。

在你的应用中,清晰、干净、有目的的字体设计的重要性怎么强调都不为过。它是一个重要的工具,可以让 Windows 8 应用程序的设计足够引人注目,让用户喜欢它,与朋友谈论它,并给你的应用程序带来高评分。

五、盒子属性

image 注意HTML 中的一切都适合一个盒子,知道如何控制这些盒子的格式和布局对于扎实理解 CSS 至关重要。

框属性是 HTML 元素的属性,描述了它的大小、填充、边框和间距等内容。

`我们将研究元素大小和间距,以及使这些元素看起来更好的所有填充和边框颜色,并使它们在视图状态改变时发挥应有的作用。让单一视图工作正常是一回事,但是当用户将设备旋转到纵向时会发生什么呢?如果一个元素的框属性设置不正确,那么整个布局会受到不利影响。我们将在本章中探讨的属性对于让您的 Windows 8 应用达到应有的效果至关重要。图 5-1 展示了一些按照微软设计原则设计的 Windows 8 应用。

9781430249832_Fig05-01.jpg

图 5-1 。一些根据微软设计原则设计的 Windows 8 应用程序

指导 Windows 8 外观和感觉的设计语言融入了大量的设计、思考和测试。从触摸目标的大小到它们之间的间距,一切都在设计原则中占有一席之地。在图 5-1 中,注意内容上方、下方和左侧的一致空间。请注意,内容没有被压缩在屏幕上,而是被给予了一点喘息的空间。请注意,除了内容本身之外,用于交互或导航的视觉元素非常少。所有这些都是设计语言的一部分,甚至更多。

要实现这些原则,你必须非常依赖于盒子模型和各种布局技术。本章介绍盒子模型,第七章将介绍布局。为了彻底理解盒子模型,你必须理解大小、边距、边框和填充。我们将花更多的时间来覆盖渐变和阴影,因为它们有时可以补充你的核心图形,为你的设计增加价值。

盒子模型

如果您想单独理解边距、边框、填充和背景属性,您必须理解框模型。确保图 5-2 中显示的术语已被记忆。

9781430249832_Fig05-02.jpg

图 5-2 。HTML/CSS 盒子模型

标记为边距 的空间是该元素与其周围所有东西之间的空间。边框 在这里显示为灰色线条,但它实际上可以是多种样式、颜色和粗细中的任何一种。边框和框内实际内容之间的空间称为填充

这里的内容表示为文本,但是请记住,您的内容可能是文本,也可能是更多嵌套为子元素的元素。

我需要定义更多基于这个盒子模型的术语。边框是图表中包括边框区域、填充区域和内容,但不包括边距的区域。填充框是包括填充区域和内容区域(不包括边距和边框)的矩形区域。而内容框就是内容区。一会儿你就会明白为什么这些术语很重要。

尺寸

HTML 容器元素可以在没有指定大小信息的情况下定义,并根据其父元素的大小或其内容的大小做出反应,但您也可以显式声明它们应该是什么大小。在 Windows 8 应用程序中指定大小特别有帮助,因为视觉伪像会出现在各种设备上,一些设备实际上会应用系统级缩放,以保持一致和可触摸的用户体验。换句话说,与现有的 Windows 设计环境不同,像按钮这样的东西不会随着用户分辨率的提高而变小。

大多数 HTML 元素可以指定宽度和高度属性来确定它们的显式大小。除了这个绝对值之外,还可以为每个值指定最小值和最大值。以下是可用属性的列表:widthmin-widthmax-widthheightmin-heightmax-height

最小值和最大值属性对于创建良好的自适应布局非常有帮助。80pxmin-width值将允许目标元素的宽度增长到大于 80 像素(例如,如果用户正在向元素中键入内容),但不会变窄。

在设置一个元素的宽度和高度之前,一定要问自己是应该使用最小值还是最大值属性。你的元素应该是一个绝对的大小,还是应该能够适应,在某些情况下变得更小或更大?正如我们已经讨论过的,这两个属性的值应该是标准的长度单位。清单 5-1 展示了如何用各种单位来指定元素的大小。

清单 5-1。 一个 div,包含五个宽度不同的子元素

<!-- HTML snippet -->
<div id="parent">
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
</div>

/* CSS snippet */
#parent { width:200px; border: 1px solid black;}
    #parent > div { background:green; height: 20px; margin:2px; color:white; }
    #parent div:nth-of-type(1) { width: 10px; }
    #parent div:nth-of-type(2) { width: 50%; }
    #parent div:nth-of-type(3) { width: 100px; }
    #parent div:nth-of-type(4) { width: calc(100% - 10px); }
    #parent div:nth-of-type(5) { width: 1in; }

关于清单 5-1 中的,你应该注意一些事情。

  • 请注意除了第一行之外的所有行都缩进了。这是前面提到的表示层次结构的约定的一个例子。
  • 注意第四个子元素的宽度使用了calc()函数来表示宽度应该是“100%差 10 个像素”。
  • 注意:nth-of-type()伪类的使用。这是按顺序为一系列相似元素定义样式规则的好方法。因为使用了:nth-of-type()而不是:n-child,所以我们可以稍后在div元素的序列上方添加一个标题,并且不会对它们的样式产生任何影响。

清单 5-1 产生的 HTML 看起来像图 5-3 。你可以看到彼此之间不同的宽度。

9781430249832_Fig05-03.jpg

图 5-3 。各种大小的矩形说明了宽度属性

在 CSS3 之前,当设置了对象的宽度和高度时,指定的值会影响内容区域的宽度和高度(参见框模型)。在某些情况下,这可能是一种直观的方法,但也会引起一些恐慌。举个例子,将div的宽度设置为100%会导致它采用其父项的宽度,但是添加一些填充和边框会导致它看起来比其父项更宽。所以即使这种测量盒子的方法经常令人沮丧,它仍然是 W3C 标准。

CSS3 还引入了两个增强功能,极大地改善了我们的规模场景。第一个我们已经讨论过了,它是用于指定宽度的calc()方法 ,比如calc(100% - 20px)。第二个是box-sizing房产 。box-sizing属性取值为border-boxcontent-box(默认)。指定border-box表示widthheight应该被转换为整个框的宽度和高度(包括填充和边框),而不仅仅是内容。

让我们看看清单 5-2 中的例子,以确保我们清楚这种行为。

9781430249832_Fig05-04.jpg

图 5-4。一个具有 50 像素边框的 div,其整体宽度明显超过 200 像素

清单 5-2 。使用默认内容框大小调整方法 的 200 像素框

<!-- HTML snipppet -->
<div id="box"></div>

/* CSS snippet */
#box {
    margin: 50px;
    padding: 50px;
    border: 50px solid gray;
    width: 200px;
    height: 200px;
}

现在,如果我告诉你,结果框中的边框是 50 像素厚,你会相信整个框是 200 像素宽 200 像素高吗?这似乎意味着更多。使用标准的大小调整方法,内容为 200 像素宽,填充和边框各为 50 像素,将整个框的外观宽度增加到 400 像素!

清单 5-3 重复了相同的 CSS,但是引入了一个新的属性,看看这显著不同的结果。

清单 5-3。 一个 200 像素的盒子使用了边框大小调整方法

<!-- HTML snipppet -->
<div id="box"></div>

/* CSS snippet */
#box {
    margin: 50px;
    padding: 50px;
    border: 50px solid gray;
    width: 100px;
    height: 100px;
    box-sizing: border-box;
}

9781430249832_Fig05-05.jpg

图 5-5。一个 div 已经通过框大小调整设置了它的大小调整方法,现在看起来真正的整体宽度是 200 像素

溢出

盒子模型及其大小、边距和填充让您可以很好地控制页面上所有容器的形状。页面上这些容器的全部意义在于包含你的应用程序的内容,通常这些内容会太大或太长而不适合。在这种情况下,容器的行为就是我们所说的溢出。

例如,如果一个div元素的高度足以显示 10 行文本,并且其中有超过 10 行的文本,那么我们就有一些溢出。

您可以使用overflow属性来控制这种行为。有效值为visible(该值为默认值)、hiddenscrollauto

  • visible 决定了溢出的内容应该可见。即使它是可见的,也不会影响它可能碰撞到的任何元素的布局。
  • hidden 使任何溢出的内容不可见。
  • scroll 给元素添加滚动条,这样用户可以选择滚动到溢出的内容。
  • 只有在有理由的情况下才添加滚动条。

overflow属性实际上是overflow-xoverflow-y的简写属性,因此您可以独立地控制容器在两个轴上的行为方式。

清单 5-4 展示了我提到的例子,一个div的大小可以显示 10 行文本,但包含更多。还没有设置overflow属性,所以使用默认值visible,事实上,额外的文本在结果中清晰可见。这种溢出的文本的一个有趣的行为是,它不会将其他内容推来推去,这种行为对您来说可能直观,也可能不直观。包含的div作为一个块级元素,将后续内容向下推,但是它溢出的文本对页面上其他任何内容的位置没有进一步的影响。

清单 5-4。 一个内容溢出的 div 不指定其溢出

<!-- HTML snippet -->
<div id="container">
    <p>Lorem ipsum dolor sit amet ... </p>
</div>

/* CSS snippet */
#container {
    height: 220px;
    width: 300px;
    border: 1px solid gray;
    margin: 10px;
    padding: 10px;
}

9781430249832_Fig05-06.jpg

图 5-6。如你所见,默认溢出是可见的

让我们来看看应用了不同的overflow值的同一个 HTML。在清单 5-5 的中,你可以看到我们可以通过将overflow属性设置为hidden来消除这个可见的溢出。

清单 5-5 。溢出值“隐藏”应该会改变行为

<!-- HTML snippet -->
<div id="container">
    <p>Lorem ipsum dolor sit amet ... </p>
</div>

/* CSS snippet */
#container {
    height: 220px;
    width: 300px;
    border: 1px solid gray;
    margin: 10px;
    padding: 10px;
    overflow: hidden;
}

9781430249832_Fig05-07.jpg

图 5-7。溢出的内容现在是隐藏的,但没有办法访问它

不过,这让我们无法查看隐藏的文本。清单 5-6 通过将overflow-y设置为auto对此进行了改进,这导致div保持相同的大小,但是突然具有了滚动的能力。你可能想知道为什么我不选择scroll值而不是autoscroll值 强制容器总是处于滚动模式,但是auto值足够智能,只在确实有溢出的内容需要滚动时才呈现滚动条。请注意,在 Windows 8 中,系统会确定用户当前使用的是鼠标还是触摸,并相应地呈现滚动条。当使用鼠标时,用户将看到传统的滚动条,但当使用触摸时,用户将看到更干净、更小的滚动位置指示器,该指示器提供信息,而不必是交互式的。

清单 5-6。 设置溢出为“自动”使内容可滚动

<!-- HTML snippet -->
<div id="container">
    <p>Lorem ipsum dolor sit amet ... </p>
</div>

/* CSS snippet */
#container {
    height: 220px;
    width: 300px;
    border: 1px solid gray;
    margin: 10px;
    padding: 10px;
    overflow-y: auto;
}

添加一个属性overflow-y: auto;会导致容器在其内容溢出时呈现垂直滚动条。图 5-8 说明了这一点,并强调了用户使用鼠标和触摸时滚动条外观的不同。

9781430249832_Fig05-08.jpg

图 5-8 。滚动条在使用触摸和鼠标时呈现不同

能见度

可以修改元素的可见性来确定该元素是否出现。

有两种不同的样式属性会影响元素的可见性:visibilitydisplay

visibility属性可以设置为visiblehiddencollapse。默认值是visible,显然设置了一个元素出现。hidden值使元素完全不可见。即使一个元素被隐藏,它仍然会影响布局,所以如果你设置它为 100 像素高,那么它仍然会占用 100 像素的垂直空间。最后,collapse值的工作方式与hidden值非常相似,除了对于表格的行和列,它还折叠行或列,因此它不会占用空间。

属性可以设置成许多不同的东西,但是大多数与目标元素的布局有关,而不是它的可见性。然而,一个可能的值是none。将display设置为none不仅会隐藏目标元素,还会有效地将它从 DOM 中取出,使其不再影响布局。

jQuery 等一些库提供了一些函数(在 jQuery 中是show()hide()),使得修改这些属性以及显示或隐藏元素变得更加容易。在附录 A 中,我将讨论一些 CSS 相关的库,以及如何在你的 Windows 8 应用中使用它们。

边距

元素的边距是元素外部和周围的空间量。元素是矩形的,每边都有属性,所以我们有以下属性:margin-topmargin-rightmargin-bottommargin-left

为了说明边距,让我们创建三个元素,一个在另一个之上,然后对它们应用一些边距来查看效果。清单 5-7 包含了 HTML 和初始 CSS,没有任何与边距相关的样式。

清单 5-7。 一些没有应用边距的 div 元素

<!-- HTML snippet -->
<div id="parent">
    <div></div>
    <div></div>
    <div></div>
</div>

/* CSS snippet */
#parent > div {
    width: 200px;
    height: 80px;
    border: 1px solid black;
}

这三个div元素被一个接一个地放置,导致图 5-9 中的三个盒子也被一个接一个地放置。

9781430249832_Fig05-09.jpg

图 5-9 。div 元素相互对接,看起来可能很拥挤

您可以看到div元素紧挨着彼此。事实上,他们甚至似乎共享同一边界。现在让我们在这些元素之间留出一点空间。要做到这一点,我们可以在每个页面的底部、顶部或两者都增加一些边距。根据 Windows 8 的设计原则,我们应该在磁贴之间留出 10 个像素的空间,让我们在每个元素周围添加 5 个像素的空白。清单 5-8 展示了如何使用 CSS 来完成这个任务。HTML 将与前面的清单相同。

清单 5-8 。一些应用了 5 像素边距 的 div 元素

/* default.css */
#parent > div {
    width: 200px;
    height: 80px;
    border: 1px solid black;
    margin: 5px;
}

9781430249832_Fig05-10.jpg

图 5-10。div 元素之间现在有了一点空间

这就是元素之间 10 个像素的样子。这没什么难的。为了好玩,清单 5-9 给这些元素增加了不同级别的左边距。

清单 5-9 。仍然指定相同的 5 像素边距,现在增加了一些额外的左边距

/* default.css */
#parent > div {
    width: 200px;
    height: 80px;
    border: 1px solid black;
    margin: 5px;
}

    #parent > div:nth-child(1) {
        margin-left: 5px;
    }

    #parent > div:nth-child(2) {
        margin-left: 50px;
    }

    #parent > div:nth-child(3) {
        margin-left: 100px;
    }

9781430249832_Fig05-11.jpg

图 5-11。元素之间的边距仍然可见;额外的左边距也是如此

然而,margin速记属性使得同时设置这四个属性变得更加容易。简写属性 将采用以下任何一种形式。

  • 所有四边的页边距都可以通过依次指定顶部、右侧、底部和左侧页边距来表示,并由空格分隔,如margin: 10px 8px 7px 2px;所示
  • 水平和垂直对称的页边距可以采用一个值来表示顶部和底部,另一个值表示左侧和右侧,如margin: 20px 5px;所示,在这种情况下,顶部和底部页边距将被设置为 20px,左侧和右侧被设置为 5px。
  • 最后,四周相同的边距可以取一个值,如margin: 10px;所示,这将在四边创建 10px 的空间。

属性对于所有类型的间距都是非常有用的。它用于控制文本块中段落上方或下方的间距,以及列表中图块组之间的间距。它还用于遵循 Windows 8 的设计原则,并在每个视图的左侧添加 120 像素的空间。事实上,为了适应这种左边距,如果你使用导航应用项目模板来创建你的项目,那么每次你创建一个新的页面,你的 CSS 文件看起来就像清单 5-10 中的样式规则。

清单 5-10 。Visual Studio 中新页面控件的默认样式表

/* CSS snippet */
.pageTitle p {
    margin-left: 120px;
}

此规则为页面默认获得的“内容在此处”段落设置 120 像素的左边距。我不觉得这有什么帮助,因为我很快替换了股票段落,很少在我的主要部分出现一个标签。因此,通过为我在主要部分(也就是角色为main的部分)放置的任何东西设置左边距,用下面的规则替换这条规则效果很好,如清单 5-11 所示。

清单 5-11。 更好的设置标准左边距的方式来影响主节中的一切

/* CSS snippet */
.pageTitle section[role=main] > * {
    margin-left: 120px;
}

如果你使用网格项目模板创建一个新的 Windows 8 项目,你的应用程序看起来马上就像图 5-12 ,你已经可以看到左边距的特征 120 像素已经被编程到该项目模板中。

9781430249832_Fig05-12.jpg

图 5-12 。Windows 8 应用程序典型的 120 像素左边距在 grip 项目模板中显而易见。推荐的标准页边距还描述了列表项和组

边框

边框是围绕元素的线条。同样,这次我们得到了一个简写属性border—,这样我们就不用每次都键入border-widthborder-styleborder-color了。清单 5-12 展示了如何在div周围创建一个细的黑色边框。

清单 5-12 。div 周围的细黑边框

/* CSS snippet */
div {
    border: 1px solid black;
}

您可以为border-width使用任何长度单位。border-style的值有dasheddotteddoublegroovehiddeninsetnoneoutsetridgesolid。我在第三章中介绍了border-color可以用无数种方式定义颜色。

清单 5-13 显示了更多的边框属性组合。

清单 5-13 。一些边界属性组合

/* CSS snippet */
.border1 { border: 1px solid red; } /* thin red */
.border2 { border: 8px solid rgb(0,255,0,.5) /* partially transparent, thick, green */
.border3 { border: 3px double black; }

填充

就像margin决定元素外的空间一样,padding决定元素内的空间(但在其内容外)。就像margin属性一样,padding是一个简写属性,它比每次都使用padding-toppadding-rightpadding-bottompadding-left更容易,并且设置它的工作方式也与margin完全相同。

清单 5-14 显示了一个简单的div,带有黑色边框,包含一段文本。divpadding被设置为50px,你可以在结果中看到这么多填充的效果。

清单 5-14 。50 像素的填充为文本在其边框内提供了一些空间

<!-- default.html -->
<!DOCTYPE html>

<html lang="en" FontName2">http://www.w3.org/1999/xhtml ">
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <div>
        <p>Lorem ipsum dolor sit amet...</p>
    </div>
</body>
</html>

/* default.css */
div {
    border: 1px solid black;
    padding: 50px;
    width:50%;
}

9781430249832_Fig05-13.jpg

图 5-13。在一段文字周围增加了一些夸张的呼吸空间

背景

当您想要用颜色或图像影响元素的背景时,可以使用背景属性。分别是background-colorbackground-imagebackground-sizebackground-repeatbackground-position-xbackground-position-ybackground-originbackground-clipbackground-attachment

背景属性可用作背景图像、背景重复和背景颜色的速记属性,背景位置可用作背景属性 x 和背景属性 y 的速记属性。

背景色

您可以使用我们讨论过的所有标准颜色属性值来设置颜色。如果你回头看看我们讨论过的盒子模型,background-color(以及background-image)将填充内容、填充和边界区域。它呈现在内容、填充和边框之后。如果你设置你的边框有点透明,你会看到背景显示通过。

背景图像

background-image的值应该是到一个或多个图像的绝对或相对链接,每个都用url()包裹,并用逗号分隔。包含多个图像会导致图像堆叠在一起,首先声明的那些图像会放在最上面。

清单 5-15 展示了一个 Windows 8 徽标被用作div元素后的背景图像的例子。

清单 5-15 。指定为 div 背景图像的图像(Windows 8 徽标)

<!—HTML snippet -->
<div id="container"></div>

/* CSS snippet */
div#container {
    width:600px;
    height:400px;
    border:1px solid gray;
    background-image: urlimg/win8logo.png');
}

9781430249832_Fig05-14.jpg

图 5-14 。图像出现在 div 元素的背景中,但是您可以看出它太大了

在图 5-14 的中,我们设置为 dev 元素背景的 Windows 8 logo 需要调整大小以适合 div,这个工作属于background-size属性。

background-size

background-size属性值可以是一对代表宽度和高度的长度或百分比。可以使用关键字auto代替宽度或高度值,让长度值根据图像的长宽比和其他维度来确定,为宽度和高度指定auto等同于根本不设置该属性。

除了明确指定背景图像的大小之外,covercontain值可以用来以一种巧妙而有用的方式调整图像的大小。cover值缩小图像以完全适合容器,contain值放大图像以适合容器。在这两种情况下,都会调整图像的大小,但会保持其纵横比。

如果存在一个以上的背景图像,那么大小对它们的影响是一样的。让我们使用清单 5-16 中的这个属性来调整我们的 Windows 8 徽标的大小,以更好地适应我们的div元素。

清单 5-16 。添加了一个背景大小属性来控制背景图像的大小

<!—HTML snippet -->
<div id="container"></div>

/* CSS snippet */
div#container {
    width:600px;
    height:400px;
    border:1px solid gray;
    background-image: urlimg/win8logo.png");
    background-size: auto 100px;
}

图 5-15 显示了添加background-size属性的效果。

9781430249832_Fig05-15.jpg

图 5-15 。背景图像的大小已经被适当地调整了,但是这并不是我们想要的效果

图 5-15 中的图像是我们指定的 100 像素高,但是它重复并平铺整个背景。这是背景图像的默认行为,要覆盖它,我们需要使用background-repeat属性。

image 提示covercontain值提供的使背景图像适合其容器的功能在一个名为 ViewBox 的 WinJS 控件中扩展。视图框影响任何单个元素,即使该元素包含复杂的内容层次结构。这是一个非常强大的工具,可以根据当前屏幕的大小和形状动态调整内容。你会在第七章中读到更多关于视图框控件的内容。

背景-重复

默认情况下重复背景是有意义的,考虑到它们最初和主要的意图是用连续的图像贴墙纸。然而,这种行为是非常可定制的。属性提供了一些非常巧妙的功能。可以取值为no-repeatrepeatrepeat-xrepeat-yroundspace

默认值是repeat,要完全关闭该行为,可以使用no-repeat。如果你想让图像只在 x 方向重复,那么使用repeat-x,同样使用repeat-yround值实际上会在两个方向上重复图像,但如果元素大小不是大小的整数倍,则不允许裁剪。适合的图像随后被放大以适合元素。space值与此类似,但它不是放大结果图像,而是在它们之间添加空间来填充空间。

清单 5-17。 添加一个背景重复属性,其值为不重复

<!—HTML snippet -->
<div id="container"></div>

/* CSS snippet */
div#container {
    width:600px;
    height:400px;
    border:1px solid gray;
    background-image: urlimg/win8logo.png');
    background-size: auto 100px;
    background-repeat: no-repeat;
}

9781430249832_Fig05-16.jpg

图 5-16。背景图像不再重复

背景-位置

CSS 还让我们可以控制背景图像应该呈现在包含元素中的什么位置。如果您关闭了重复,并且只有一个图像,那么您可以使用带有类似于left topright centercenter bottom的值组合的background-position属性。您也可以使用一对长度单位或百分比来分别表示 x 和 y 方向上的位置。

使用这种技术来设置一个相对暗淡的图像,使其出现在视图的右下角,这是一个添加你的品牌的好方法。无论用户如何滚动内容,图像都将保留在那个角落。

清单 5-18 将background-position属性添加到我们的容器中。

清单 5-18 。添加一个值为“不重复”的背景重复属性

<!—HTML snippet -->
<div id="container"></div>

/* CSS snippet */
div#container {
    width:600px;
    height:400px;
    border:1px solid gray;
    background-image: urlimg/win8logo.png');
    background-size: auto 100px;
    background-repeat: no-repeat;
    background-position: center center;
}

你在图 5-17 中看到的结果开始看起来像是我们想要做的事情!

9781430249832_Fig05-17.jpg

图 5-17 。背景图像水平和垂直居中

在这里,我们选择了使用关键字center将图像居中,但是我们也可以使用类似于background-position: 100px 100px的东西将图像绝对放置,这样就可以将 logo 设置为距离容器顶部和左侧 100 个像素。

背景-原点

background-origin用于确定background-position坐标的起点。默认值是padding-box,因此0% 0%background-position0px 0pxleft top将意味着背景图像从填充的顶部和左侧开始对齐(即使默认情况下背景图像实际上也延伸到边框之下)。

background-clip

我前面提到过元素的背景会影响内容下面的区域、填充和边框,但是background-clip属性给了我们一点控制权。该属性取值与background-origin : border-boxpadding-boxcontent-box相同。省略它将意味着它使用其默认值border-box。裁剪到边框意味着背景位于内容、填充和边框下的整个区域之下。然而,将background-clip属性设置为值padding-box将有效地剪切边框下的部分,这样背景仅在填充和内容下可见。最后,content-box的值甚至会将它裁剪到填充区内,使它仅位于内容下方。

9781430249832_Fig05-18.jpg

图 5-18。背景图像被剪切到盒子模型的特定区域

背景-附件

属性决定了当前景内容滚动时背景图像的行为。

fixed 将背景图像附加到视口(通常是整个窗口),其效果是将其固定在页面上。

scroll 将背景图像附加到文档上,因此如果正文的全部内容溢出页面并强制滚动,那么背景图像将随之滚动。

CSS3 中引入了第三个值local ,它将元素的背景图像附加到该元素的内容中。只有引入这个值,我们才能够用滚动的内容来滚动背景图像div

作为一个例子,请看清单 5-19 。一个包含三个相当大的文本段落的div被分配了一个 CSS 样式规则,除了别的以外,该规则将它设置为在其中心呈现一个小的背景图像。overflow-y使它可以滚动,但是我们很快会讨论这个属性。需要注意的属性是localbackground-attachment。这将导致背景图像与其余内容一起滚动,正如您在结果中看到的那样。

清单 5-19 。local 的背景附件值用于将背景图像粘贴到它所在的滚动容器中

<!—HTML snippet -->
<div>
    <p>Lorem ipsum dolor sit amet... </p>
    <p>Sed rhoncus, erat in eleifend... </p>
    <p>Nam mollis iaculis neque ut... </p>
    <p>In eleifend purus et leo... </p>
    <p>Sed quis sapien vitae elit... </p>
</div>

/* CSS snippet */
div {
    padding: 10px;
    border: 1px solid gray;
    width: 500px;
    height: 500px;
    background: urlimg/win8logo.png') no-repeat;
    background-position: center center;
    background-attachment: local;
    background-size: 80px 80px;
    overflow-y: scroll;
}

将这个属性添加到我们的目标元素实际上将背景图像固定到包含元素的滚动部分。

9781430249832_Fig05-19.jpg

图 5-19。带有本地附加背景图像的可滚动 div 将滚动图像及其内容

渐变

如果你有 web 开发的背景,你会非常清楚如何给文档的宽度或高度添加颜色渐变。我所指的 hack 是一个包含所需梯度的图像,在垂直于梯度的方向上只有一个像素。然后,该图像被设置为背景图像,并允许在页面上重复显示。它可以工作,但是不够优雅,而且有一些缺点。我想到的一种情况是,用户滚动到渐变的边缘,或者升级到更高分辨率的屏幕,然后出现渐变的突变边缘。这让网页设计师很尴尬,但是随着 CSS3 渐变的出现,这个问题已经解决了。

我想在这里加入一个软警告,不要不加考虑地使用渐变。渐变是有帮助的,但是微妙是关键。多余的图形工件通常会违反微软的设计原则,即使用颜色和形状作为信息,而不仅仅是为了吸引眼球。也就是说,在某些情况下,微妙的渐变背景可能正好合适。

渐变是在background-image属性中实现的,使用linear-gradient()函数实现线性渐变,或者使用radial-gradient()函数实现径向渐变。

linear-gradient()函数 接受渐变方向,然后至少两个颜色停止。清单 5-20 显示了从左到右从黑到白的渐变。

清单 5-20 。定义为 div 背景的线性渐变

<!-- HTML snippet -->
<div class="gradient"></div>

/* CSS snippet */
.gradient {
    width:400px;
    height:400px;
    background-image: linear-gradient(to right, black 0%, white 100%)
}

9781430249832_Fig05-20.jpg

图 5-20。平滑渐变 由黑变白

添加色标也很容易。清单 5-21 每 20%黑白交替一次。

清单 5-21 。带有交替色标的线性渐变

<!-- HTML snippet -->
<div class="gradient"></div>

/* CSS snippet */
.gradient {
    width:400px;
    height:400px;
    background-image: linear-gradient(to right, black 0%, white 20%, black 40%,
        white 60%, black 80%, white 100%);
}

9781430249832_Fig05-21.jpg

图 5-21。更复杂的线性渐变

指示方向的第一个参数可以接受一个边(leftrighttopbottom)、一个角(top lefttop rightbottom leftbottom right),或者像45deg320deg这样的自定义度数。

你可以在微软的网站上找到更多关于线性渐变实现的信息。

创建径向渐变的函数有点复杂,我只展示一个例子,并再次鼓励你在http://msdn.microsoft.com/en-us/library/windows/apps/hh453718(v=vs.85).aspx访问微软关于这个主题的文档,当你准备好自己实现它的时候。清单 5-22 将创建一个渐变,从中间的黑色开始,以白色向外辐射到最远的角落。

清单 5-22 。从中心到最远角的径向渐变

<!-- HTML snippet -->
<div class="gradient"></div>

/* CSS snippet */
.gradient {
    width:400px;
    height:400px;
    background-image: radial-gradient(circle at 50% 50%, black 0%, white 100%);
}

9781430249832_Fig05-22.jpg

图 5-22。从中间的黑色到外部的白色的径向渐变

影子

多年来,网页设计者想出了各种各样的技巧来创建阴影,因为直到现在,标准中还没有相关的规定。现在实现投影很容易,但更好的是它是标准的。

我会像对待渐变一样,对阴影的使用发出同样的警告。这是一种视觉装饰。阴影是一种 skeuemorphic 人工制品,因为它试图使你的应用程序中的一些元素看起来像在现实世界中一样。但这并不在现实世界中,不是吗?这是在数字世界中,Windows 8 的设计原则建议我们将我们的应用程序设计成真正的数字,而不要考虑现实世界效果可能带来的所有依赖和限制。

推荐与否,框阴影非常容易实现。清单 5-23 显示了一个带有简单阴影的小白框。这些值表示水平偏移、垂直偏移、模糊半径、扩散距离和阴影颜色。

清单 5-23 。div 元素上的方框阴影定义

<!-- HTML snippet -->
<div class="shadow"></div>

/* CSS snippet */
.shadow {
    width: 100px;
    height: 100px;
    background-color: white;
    border: 1px solid black;
    box-shadow: 10px 10px 10px 0px hsla(0,0%,0%,.5);
}

9781430249832_Fig05-23.jpg

图 5-23。白色方框 下的简单方框阴影

关于阴影的一个有趣的事情是,它们不仅仅可以用来创造阴影。清单 5-24 显示了两个例子。在第一个示例中,添加了一个没有水平或垂直偏移的阴影来创建发光效果。在第二个例子中,一个实心的阴影被用来明显地将盒子向外延伸,并且在一个虚线的边框之下,使得元素看起来像是在它的周围缝了针。

清单 5-24 。框阴影属性 的两种可选用法

<!-- HTML snippet -->
<div class="flex">
    <div class="glowBox"></div>
    <div class="stitches"></div>
</div>

/* CSS snippet */
.flex {
    display:-ms-flexbox;
    -ms-flex-pack:distribute;
    -ms-flex-align:center;
    height:200px;
    width:600px;
}

.glowBox {
    border:1px solid gray;
    width:100px;
    height:100px;
    box-shadow: 0px 0px 40px 10px hsla(0,0%,0%,.5);
}

.stitches {
    background-color: lightgray;
    border:2px dashed black;
    box-shadow: 0 0 0 4px lightgray;
    color: black;
    padding: 10px;
    border-radius: 4px;
    width:100px;
    height:100px;
}

9781430249832_Fig05-24.jpg

图 5-24。两个 div 元素——一个发光,另一个明显是缝合在一起的

摘要

在这一章中,我们已经了解了影响任何 HTML 元素的矩形模型的所有属性(我们称之为box)。我们定义影响大小(宽度和高度)、边距(外部空间)、边框、填充(内部空间)等的 CSS 属性。我们学习了设置内容的宽度和高度的相对值或绝对值,以确定其呈现的大小。我们还学习了如何处理对于它们的空间来说太大和溢出的元素内容。我们学习了单独或一起定义元素的左、右、上、下边距。我们学习了如何使用线条样式、大小和颜色来确定元素的边框。我们学习了如何定义填充,让内容在框架内有一点喘息的空间。

我们还研究了元素中的背景设置,可以是纯色、图像,甚至是线性或径向渐变。最后,我们学习了盒子阴影,并使用它们给我们的元素一些明显的深度和一点点创造性的兴趣。在理解后续概念之前,理解这些概念是很重要的,尤其是第七章中的高级布局概念。

现在我们对盒子属性有了基本的了解,我们将继续元素的转换和动画。我们不是确定元素的静态外观,而是确定它们的过渡和动画,真正让我们的 Windows 8 应用变得生动起来。`

六、变换、过渡和动画属性

image 注意动画在一个成功的 Windows 8 应用中远不止是点缀;相反,它通过吸引用户并使应用程序变得生动而变得重要。而一个引人入胜、生动活泼的 app 才是成功的 app。

一个没有足够动画的 Windows 8 应用程序至少会给用户留下一种潜意识的感觉,即缺少了什么或者什么东西死了。太多的动画可能会分散注意力,分散注意力,甚至可能会令人厌烦。所以动画应该足以让你的应用程序的视觉效果栩栩如生,仅此而已。

适量的动画是 Windows 8 设计的核心原则。涵盖 Windows 8 设计的原则之一是真正的数字化,也就是说,拥抱现代数字系统的功能和自由,摆脱物理系统的束缚。强调用户任务的动画和过渡符合这个原则。

与这个主题相关的还有另一个原则——你的 UI 应该快速流畅。你的界面不仅应该在性能上或与用户的接触上没有停顿,而且应该从一个屏幕流到下一个屏幕。有很好的理由让 UI 动画化,甚至有更好的理由不要动画化太多。

在动画方面,微软再次采用并实现了 W3C 标准,并在必要的地方对其进行了扩展,以满足现代应用程序的需求。由于您的动画在 Windows 8 中运行,它们将拥有操作系统硬件加速的巨大优势。您不必接受使用 web 堆栈带来的性能损失。

在这一章中,我们将讨论所有与 CSS 和动画相关的东西。我们将涵盖二维和三维的变换、过渡和动画。比起我们现在的时间和空间,更多的页面可以很容易地用于这些广泛的主题,但我希望传达这个概念,并让你对可能的事情感到兴奋。

变换

转换只是简单的变化。它可能是宽度的改变、颜色的改变或一些其他的属性。但是,您已经知道,这些属性的更改可以通过简单地修改属性值来完成,那么,当您可以简单地调整元素的位置属性并看到相同的效果时,为什么您要添加一个转换,例如,将元素向右移动一些像素呢?或者,当您可以增加其宽度和高度属性时,为什么要应用缩放变换来使某些东西增长呢?

答案是转换很容易应用和移除(甚至是通过编程)。它会在目标元素呈现后影响它,并且会影响整个目标元素,包括子元素。

此外,转换允许您以单独使用属性无法实现的方式影响元素。例如,如果没有变换,就不能给元素添加旋转。事实上,有一个完整的 CSS 转换子集可以用类似 3D 的效果来影响元素,而这些效果肯定不可能用标准属性来实现。

有许多可用的变换效果,然而只有大约六个 CSS 属性需要记住,它们是perspectiveperspective-originbackface-visibilitytransform-origintransform-styletransform。还有更多关于这些属性的细节。在http://www.w3.org/TR/css3-transforms/#transform-property可以看到完整的列表。

变换函数

到目前为止,最有趣的属性是transform属性。对于它的值,它期望一个或多个转换函数的列表。我将首先描述每个与转换相关的属性,然后非常详细地关注转换属性。

perspective

不要混淆这个属性值和马上要讨论的perspective()函数的区别。它们有相同的视觉效果,但是这个(transform函数的perspective值)影响它所有子元素的变换,而perspective()函数只影响它所在元素的变换。我将等到下一节来描述perspective()函数的实际效果。perspective属性值仅影响 3D 变换元素。

perspective-origin

perspective-origin属性是虚拟摄像机从目标元素正前方的偏差(使用 x 和 y 坐标)。如果你看着一个正方形建筑的墙,并且你向右走得足够远,从你的角度来看,墙会改变形状,这正是你在修改这个属性时应该想到的。与perspective一样,perspective-origin影响其子变换的变换,但不影响自己的变换。

backface-visibility

属性决定了当我们旋转一个元素并看到它的背面时,它的内容会发生什么。如果在这种情况下您应该看到内容的后面,请将其设置为visible,如果不应该,请将其设置为hidden

transform-origin

如果你在正方形的一个角附近插上一根针并旋转它,你会得到一个不同于把针插在正方形中心的结果。这就是transform-origin属性为您做的事情。它指示转换应该从元素的哪个部分开始。根据转换的不同,它的行为会有所不同。

transform-style

您可以指定flatpreserve-3d作为transform-style属性的值。如果您选择preserve-3d,那么嵌套元素将保持它们在 3D 空间中的相对位置。但是,如果您选择flat,那么所有元素都会折叠到一个共享平面。

transform

transform函数是完成大部分繁重工作的函数。它的值可能很大,因为它由一个或多个可用的转换函数组成(用空格分隔)。transform 属性及其众多函数都非常复杂。欢迎你去 w3c.org,得到你所需要的公式和理论,但是我会保持我所有的描述和例子简单实用。我将更多地谈论你可能选择在你的应用程序中使用转换的原因,而不是背后的数学和理论。

2D 变换函数

有许多变换函数可供选择。您可以使用以下任何二维变换来影响您的目标元素:rotate()scale()scaleX()scaleY()skew()skewX()skewY()translate()translateX()translateY()matrix()

rotate()

rotate 函数的目的一点也不含糊。它将目标元素顺时针旋转指定的度数。度数单位为deg,遵循从负无穷大到正的值。负值将逆时针旋转目标元素,正值将顺时针旋转目标元素。

rotate()函数完全愿意接受大于 360 度旋转的数字,它将继续旋转。因此,440deg的值将顺时针方向旋转目标一次半,最终将等于180deg的值。

清单 6-1 展示了具有三个不同旋转值的相同div元素。

image 注意在本章的代码清单中,您可能会注意到父容器的display值可能被设置为-ms-flexbox-ms-grid。目前,只知道这些是布局技术。flexbox 非常适合将它的孩子一个接一个地排列起来,在这种情况下,网格用于将他们堆叠在一起。我们将在第七章中详细讨论这些和其他布局技术。

清单 6-1 。 分别为 5 度、-45 度和 180 度的文本块

<!-- HTML snippet -->
<div class="parent">
    <div>Lorem ipsum dolor sit amet...</div>
    <div>Lorem ipsum dolor sit amet...</div>
    <div>Lorem ipsum dolor sit amet...</div>
</div>

/* CSS snippet */
.parent {
    display: -ms-flexbox;
}

.parent div {
    border: 2px solid gray;
    width: 200px;
    height: 200px;
    padding: 5px;
    margin: 5px;
}

.parent div:nth-of-type(1) {
    transform: rotate(5deg);
}

.parent div:nth-of-type(2) {
    transform: rotate(-45deg);
}

.parent div:nth-of-type(3) {
    transform: rotate(180deg);
}

9781430249832_Fig06-01.jpg

图 6-1。具有不同旋转级别的变换元素

scale()

缩放是调整目标元素及其所有内容的大小,它在呈现目标后生效。这意味着元素的子内容不会根据其父元素的大小进行布局,而是随着父元素一起缩放。

scale()函数要求一个值的乘数。值0.5会将对象缩放到其原始大小的一半,1会保持其大小不变,2会使其翻倍。清单 6-2 经历了完全相同的场景。

清单 6-2 。 将对象缩放到原来的一半再到两倍

<!-- HTML snippet -->
<div class="parent">
    <div>Lorem ipsum dolor sit amet...</div>
    <div>Lorem ipsum dolor sit amet...</div>
    <div>Lorem ipsum dolor sit amet...</div>
</div>

/* CSS snippet */
.parent {
    display: -ms-flexbox;
}

.parent div {
    border: 2px solid gray;
    width: 200px;
    height: 200px;
    padding: 5px;
    margin: 5px;
}

.parent div:nth-of-type(1) {
    transform: scale(1);
}

.parent div:nth-of-type(2) {
    transform: scale(0.5);
}

.parent div:nth-of-type(3) {
    transform: scale(2);
}

9781430249832_Fig06-02.jpg

图 6-2 。三个级别的规模

注意缩放后,图 6-2 中的 div 实际上是重叠的。仅仅基于传统的 HTML 经验,这似乎是不可能的。缩放仅在目标元素完全呈现后发生,这意味着这与简单地为宽度和高度属性指定替代值有很大的不同。

考虑清单 6-3 中的元素。它的内容有一个段落,其尺寸为 400 像素宽,200 像素高。

清单 6-3 。 一个 400 像素乘 200 像素的带文本内容的 div 元素

<!-- HTML snippet -->
<div id="textDiv">
    <p>Lorem ipsum dolor sit amet...</p>
</div>

/* CSS snippet */
#textDiv {
    width: 400px;
    height: 200px;
}

现在让我们看看清单 6-4 中的,其中我们使用宽度和高度属性来调整div的大小。您会注意到段落文本已经对其父文本的大小变化做出了反应。

清单 6-4 。 通过操作样式属性将 div 的大小调整为 800 像素乘 400 像素

/* CSS snippet */
#textDiv {
    width: 800px;
    height: 400px;
}

9781430249832_Fig06-04.jpg

图 6-4。div 文本的布局适合其更大的容器

现在,相反地,清单 6-5 缩放元素。您会注意到,段落文本没有改变其布局,但形状保持不变,尽管大小是原来的两倍。

清单 6-5 。 使用变换而不是缩放来放大 div

/* CSS snippet */
#textDiv {
    width: 200px;
    height: 100px;
    transform: scale(2,2);
}

9781430249832_Fig06-05.jpg

图 6-5。文本完全按照它在较小容器中的样子进行布局,只是后来进行了缩放

可以在 x 轴和 y 轴上统一进行缩放,也可以独立于其中一个轴进行缩放。函数scaleX()和 scaleY()存在并将只变换适当的轴,但是同样的事情可以通过简单地向 scale 方法提供两个值并确保将其中一个设置为值1来完成。那么,清单 6-6 中的两个表达式是相同的。

清单 6-6 。 两种交替方式将目标元素的宽度增加一倍

/* CSS snippet */
div {
    transform: scale(2,1);
}

div {
    transform: scaleX(2);
}

skew()

skew()函数向相反的方向平移目标元素的对边,扭曲元素的形状及其内容。它可以用来创造各种各样的效果。

倾斜可以用来显示透视效果。此外,如果您在屏幕上制作元素动画,可以使用倾斜来显示元素的加速和减速,但那只是动画。倾斜变换本身是静态的。

清单 6-7 显示了一个基本的倾斜变换,这次再次影响了一段文本。

清单 6-7 。 文本沿单轴的基本倾斜

<!-- HTML snippet -->
<div class="parent">
    <div>Lorem ipsum dolor sit amet...</div>
    <div>Lorem ipsum dolor sit amet...</div>
    <div>Lorem ipsum dolor sit amet...</div>
</div>

/* CSS snippet */
.parent {
    display: -ms-flexbox;
}

.parent div {
    border: 2px solid gray;
    width: 200px;
    height: 200px;
    padding: 5px;
    margin: 5px;
}
.parent div:nth-of-type(1) {
    transform: skew(0deg,10deg);
}

.parent div:nth-of-type(2) {
    transform: skew(-5deg,0deg);
}

.parent div:nth-of-type(3) {
    transform: skew(15deg,15deg);
}

9781430249832_Fig06-06.jpg

图 6-6。垂直倾斜、水平倾斜以及两者的组合

像缩放变换一样,倾斜变换也有skewX()skewY()两种,但是我个人认为不值得使用它们,因为使用skew(n,n)和传入两个值一样容易。

translate()

像它的表亲 scale 和 skew 一样,translate 也有独立的轴版本,正如你可能猜到的那样,它们是translateX()translateY()。我有没有提到我个人认为它们不值得?

平移变换是一个非常重要的变换。翻译就是简单地移动。translateX()功能沿 x 轴左右移动目标,translateY()功能沿 y 轴上下移动目标。然后translate()函数取两个值,并可以沿着 xy 平面移动它的目标。你可以想象这样一个函数有多少种用途。

清单 6-8 使用 flexbox 将 5 个div元素并排放置。它们都有一个预先确定 75 像素的正方形,一些空白,和一个白色的边界来区分。然后,突出显示的块挑选出第 5 个div,并应用变换将其向右下方移动 20 个像素。

清单 6-8 。rotate()、scale()和 translate()的组合赋予第一个字母一些活力

<!-- HTML snippet -->
<div class="parent">
    <div>Lorem ipsum dolor sit amet...</div>
    <div>Lorem ipsum dolor sit amet...</div>
    <div>Lorem ipsum dolor sit amet...</div>
</div>

/* CSS snippet */
.parent {
    display: -ms-flexbox;
}

.parent div {
    border: 2px solid gray;
    width: 200px;
    height: 200px;
    padding: 5px;
    margin: 5px;
}

.parent div:nth-of-type(1){
    transform: translate(0,0)
}

.parent div:nth-of-type(2){
    transform: translate(20px,20px);
}

.parent div:nth-of-type(3){
    transform: translate(0,0);
}

9781430249832_Fig06-07.jpg

图 6-7。中间的 div 从其原始位置向右移动了 20 个像素,向下移动了 20 个像素

matrix()

我们已经讨论过的函数背后的所有功能以及更多功能都可以通过matrix函数实现。matrix功能不适合心脏虚弱的人。这很乏味也很复杂,但是当你需要深入了解并使你的变换看起来如此时,这个函数(以及对你书架上的线性代数书的一点回顾)将带你去那里。

3D 变换功能

首先,3D 这个术语有点误导。这些所谓的三维变换并不比二维计算机屏幕上发生的任何事情更三维,但定义计算机上 3D 技术的东西是对象在投影到二维屏幕之前呈现的 3D 空间。这种效果充其量只能产生 3D 效果。

有许多可用的三维变换函数。可以选择perspective()perspective-origin()rotate3d()rotateX()rotateY()rotateZ()scale3d()scaleZ()translate3d()translateZ()matrix3d()

对于所有的 3D 变换,您应该想象您的平面的、渲染的元素在 3D 空间中站立,虚拟相机指向它们。一些属性将操作这个虚拟摄像机的位置,其他的将操作渲染元素的位置。

perspective()

perspective()函数操纵被转换元素的虚拟摄像机。它采用具有较高值的长度值,这意味着虚拟相机离对象更远,并且元素将看起来更平,而较低值,这意味着虚拟相机离对象更近,并且元素将看起来更有深度。

很容易注意到引入的戏剧性深度,因为perspective()功能被配置为使相机更靠近对象。清单 6-9 展示了缩小视角的视觉效果。

清单 6-9 。 三个 div 都以相同的角度旋转,透视值分别为无、400 像素和 200 像素

<!-- HTML snippet -->
<div class="parent">
    <div>Lorem ipsum dolor sit amet...</div>
    <div>Lorem ipsum dolor sit amet...</div>
    <div>Lorem ipsum dolor sit amet...</div>
</div>

/* CSS snippet */
.parent {
    display: -ms-flexbox;
}

.parent div {
    border: 2px solid gray;
    width: 200px;
    height: 200px;
    padding: 5px;
    margin: 5px;
}

.parent div:nth-of-type(1) {
    transform: rotate3d(1,1,1,45deg);
}

.parent div:nth-of-type(2) {
    transform: perspective(400px) rotate3d(1,1,1,45deg);
}

.parent div:nth-of-type(3) {
    transform: perspective(200px) rotate3d(1,1,1,45deg);
}

9781430249832_Fig06-08.jpg

图 6-8。减少透视值会导致表观深度增加

正如我前面提到的,您应该小心不要将这个perspective()函数与perspective属性混淆。同样,该函数会影响它所应用到的元素的变换。还要记住,perspective对没有应用任何其他 3D 变换功能的对象没有任何效果。

rotate3d()

当然,当我们看 2D 变换时,旋转也是可用的,但是你可能期望在 3D 环境中有一些额外的功能,你是对的。在我们的新空间中,我们将能够使用rotate3d()rotateX()rotateY()rotateZ()围绕所有三个轴旋转。后三种功能没有什么是前一种功能做不到的。rotate3d()函数有四个参数。前三个是向量的 x、y 和 z 坐标,将被规范化为单位向量,第四个是元素围绕该向量旋转的度数(顺时针)。

然后,调用rotate3d(1,0,0,45deg),将围绕 x 轴顺时针旋转元素 45 度;rotate3d(0,1,0,45deg)将绕 y 轴顺时针旋转 45 度;而rotate3d(1,1,0,45deg)将围绕一个向量旋转它 45 度,这个向量从原点开始,直接投影在 xy 平面上的 x 轴和 y 轴之间。

在任何旋转函数中,正的角度值将围绕轴或单位向量顺时针旋转元素,负的角度值将逆时针旋转元素。

清单 6-10 旋转三维平面元素。

清单 6-10 。rotate 3d()函数用于围绕 x 轴、y 轴以及两者的组合旋转一个 div

<!-- HTML snippet -->
<div class="parent">
    <div>Lorem ipsum dolor sit amet...</div>
    <div>Lorem ipsum dolor sit amet...</div>
    <div>Lorem ipsum dolor sit amet...</div>
</div>

/* CSS snippet */
.parent {
    display: -ms-flexbox;
}

.parent div {
    border: 2px solid gray;
    width: 200px;
    height: 200px;
    padding: 5px;
    margin: 5px;
}

.parent div:nth-of-type(1) {
    transform: rotate3d(1,0,0,45deg);
}

.parent div:nth-of-type(2) {
    transform: rotate3d(0,1,0,45deg);
}

.parent div:nth-of-type(3) {
    transform: rotate3d(1,1,0,45deg);
}

9781430249832_Fig06-09.jpg

图 6-9。围绕 x 轴的旋转看起来就像元素被垂直挤压,围绕 y 轴的旋转看起来就像被水平挤压,结合起来看起来就像是 3D 的

image 注意从技术上讲,上述三种变换中的任何一种都可以使用正确的二维变换函数来实现,但这需要一个坐标系映射。换句话说,让我们想象你正试图让一个正方形的视觉元素看起来像是朝着用户打开的,就像一扇客厅的门。要使用二维变换做到这一点,您需要弄清楚什么样的倾斜、缩放和平移组合会产生这种效果。然而,使用三维变换只需要一个简单的rotateY()函数调用,几乎不需要思考!

scale3d()

一旦你把你的元素放入三维空间,所有主要的变换功能——旋转、缩放和平移——都必须实现。像它的对应物一样,scale3d()函数也伴随着scaleX()scaleY()scaleZ()函数,同样,scale3d()函数可以执行其他函数可以执行的任何函数。你发现一种模式了吗?

scale3d()函数要求我们提供三个参数,它们是沿 x、y 和 z 轴的比例因子。清单 6-11 使用了三个后续的scale3d()函数调用来最好地说明区别。

?? 列表 6-11 。 一个 div 配置为不按比例缩放(1,1,1),第二个 div 配置为按比例缩小(0.8,0.8,0.8),第三个 div 在 y 和 z 轴按比例放大,在 x 轴按比例缩小

<!-- HTML snippet -->
<div class="parent">
    <div>Lorem ipsum dolor sit amet...</div>
    <div>Lorem ipsum dolor sit amet...</div>
    <div>Lorem ipsum dolor sit amet...</div>
</div>

/* CSS snippet */
.parent {
    display: -ms-flexbox;
    perspective: 800px;
}

.parent div {
    border: 2px solid gray;
    width: 200px;
    height: 200px;
    padding: 5px;
    margin: 5px;
}

.parent div:nth-of-type(1) {
    transform: rotate3d(1,1,0,45deg) scale3d(1,1,1)
}

.parent div:nth-of-type(2) {
    transform: rotate3d(1,1,0,45deg) scale3d(0.8,0.8,0.8)
}

.parent div:nth-of-type(3) {
    transform: rotate3d(1,1,0,45deg) scale3d(0.5,1.2,1.2);
}

9781430249832_Fig06-10.jpg

图 6-10。三维缩放的结果

translate3d()

当涉及到在二维空间移动元素时,translateX()translateY()函数覆盖了基础,但随着第三维空间的到来,需要一个translateZ(),事实就是如此。translateZ()功能将物体移近或移离观看者(假设观看者仍在他或她的原始位置perspective-origin: 50% 50%)。

清单 6-12 显示了一个例子,三个不透明的元素直接堆叠在一起。使用沿 z 轴的 3D 平移在堆叠的元素之间增加了一些距离。整个场景已被旋转以显示隐藏的元素。

清单 6-12 。 样式规则旋转和平移三个元素。那个。覆盖父规则以使用网格使元素堆叠在彼此之上

<!-- HTML snippet -->
<div class="parent">
    <div>Lorem ipsum dolor sit amet...</div>
    <div>Lorem ipsum dolor sit amet...</div>
    <div>Lorem ipsum dolor sit amet...</div>
</div>

/* CSS snippet */
.parent {
    display: -ms-grid;
}

.parent div {
    border: 2px solid gray;
    background-color: white;
    width: 200px;
    height: 200px;
    padding: 5px;
    margin: 5px;
}

.parent div:nth-of-type(1) {
    transform: rotate3d(1, -1, 0, 45deg) translate3d(0, 0, 0);
}

.parent div:nth-of-type(2) {
    transform: rotate3d(1, -1, 0, 45deg) translate3d(0, 0, 60px);
}

.parent div:nth-of-type(3) {
    transform: rotate3d(1, -1, 0, 45deg) translate3d(0, 0, 120px);
}

9781430249832_Fig06-11.jpg

图 6-11。场景的旋转和每个元素的不同平移级别使我们能够看到每个元素,即使它们是堆叠的

matrix3d()

matrix3d()函数是所有其他转换函数的母体。所有其他的都是基于它的,所以它能做其他函数能做的一切,甚至更多。唯一的缺点是,除非你有高等数学学位,否则很难弄清楚如何使用它。

我只辅修过数学,所以清单 6-13 借用了 MSDN 网站上matrix3d函数的一个例子(http://msdn.microsoft.com/en-us/library/windows/apps/jj193609.aspx)。

清单 6-13 。matrix 3d()函数的十六个值导致目标元素发生组合转换

<!-- HTML snippet -->
<div class="parent">
    <div>Lorem ipsum dolor sit amet...</div>
</div>

/* CSS snippet */
.parent {
    display: -ms-flexbox;
}

.parent div {
    border: 2px solid gray;
    width: 200px;
    height: 200px;
    padding: 5px;
    margin: 5px;
}

.parent div:nth-of-type(1) {
    transform: matrix3d(
         0.359127,  -0.469472, 0.806613, 0,
         0.190951,   0.882948, 0.428884, 0,
        -0.913545,   0,        0.406737, 0,
         0,          0,        0,        1
    );
}

9781430249832_Fig06-12.jpg

图 6-12。使用 matrix3d()函数完成的转换组合

转换是一种变化,但它是静态的,在应用程序的视图渲染时应用,并在视图渲染时保持不变。我们真正需要做的是让事情向前发展。例如,当从一个页面切换到下一个页面时,我们可能需要最微妙的动画来指示一个页面飞入,另一个页面飞出。当我们将字体从正常变为粗体时,我们可能希望文本加粗,而不仅仅是在看不清的瞬间加粗。

为了让事情进展顺利,我们需要使用过渡。

过渡

你可能认为动画是一个脚本函数,想知道它们在 CSS 而不是 JavaScript 中做什么。不过,过渡和动画都与风格有关。唯一的区别是,它们不仅考虑了静态风格,还考虑了动态风格。

过渡为介于两种风格之间的风格提供了一种实现。它们是从这里到那里应该发生的事情的描述。当一个元素突然被添加到屏幕上时,它应该如何到达那里?当它被移除时,它应该如何离开?

就像变换一样,没有大量的 CSS 属性适用于过渡——实际上只有四个,外加一个简写。您可以从transition-propertytransition-durationtransition-timing-functiontransition-delay中选择,或者您可以跳过长的属性名称,将它们全部组合成transition速记法。这是更常见的方法。

一旦某个元素成为样式规则的目标,过渡就会生效,它们的动画也会开始。在下面的例子中,您将看到我们的转换在一个样式规则中,选择器中有一个:hover 伪类。这意味着用户将悬停在一个元素上,此时将成为包含转换的样式规则的目标;因此,它们将被转换,而不是简单地将样式属性从原来的样子更改为新规则要求的样子。

如果一个转换介于两个状态之间,那么我们显然是在处理某个开始状态和某个结束状态。转换总是由结束状态定义的。如果我们将一个div从矮变高,那么这个变化是由定义高状态的样式规则定义的。

清单 6-14 和图 6-13 显示了一个非常简单的过渡。一个div元素以每边 100 像素的大小开始。它有一个白色的背景和灰色的能见度边界。定义了第二个样式规则,该规则应用于处于悬停状态的框——也就是说,当用户将鼠标悬停在 div 上时。第二帧代表用户悬停的时刻。

9781430249832_Fig06-13.jpg

图 6-13 。过渡的五个帧试图说明产生的动画

在第二个规则中,我们指定了两个属性,然后指定了一个非常简单的转换属性,该属性简单地表明该样式规则中的所有属性需要两秒钟才能生效。这意味着五帧代表两秒钟,这是白盒变成灰色和方形边缘获得边框半径所需的时间。

清单 6-14 。 一个非常简单的转换,配置为花费两秒钟,当目标元素悬停时应用于背景颜色和边框半径属性

<!-- HTML snippet -->
<div id="box">
</div>

/* CSS snippet */
#box {
    width: 100px;
    height: 100px;
    border: 2px solid hsl(0,0%,70%);
}

#box:hover {
    background-color: gray;
    border-radius: 25px;
    transition: 2s;
}

在最后一个例子中,我们创建了一个省略了所有属性的转换,并将其应用于所有属性。如果要限制过渡实际应用到哪些属性,必须指定它们。清单 6-15 显示了前一个例子中#box:hover样式规则的另一个版本,图 6-14 显示了结果。当然,不同之处在于,虽然边框半径看起来像以前一样有动画效果,但是过渡属性不再考虑背景色,因为存在一个明确的属性列表,并且没有邀请背景色。因此,当用户将鼠标悬停在上面时,div会变成灰色,在我们的图 6-14 中,第二帧和随后的帧描述的正是这种情况。

9781430249832_Fig06-14.jpg

图 6-14 。导致背景色从翻译属性中排除,并且在第二帧中明显立即应用背景色

清单 6-15 。 边界半径属性明确包含在平移属性中

#box:hover {
    background-color: gray;
    border-radius: 25px;
    transition: border-radius 2s;
}

指定过渡应该花费两秒钟是假设对象的渐进变化应该根据缓动功能发生。“减缓”功能的作用方式更像我们世界中受重力约束的真实对象,因为它开始时慢一点,结束时也慢一点。放松功能对眼睛来说很容易,因为即使它是潜意识的,它看起来也像我们人类大脑习惯看到的东西。

不过,我们不必遵循这个函数。在清单 6-16 中,注意速记属性仍然在使用,但是我们现在已经指定应该使用线性函数。在我的 5 帧动画中,动画的变化太细微了,无法察觉,所以你只能自己尝试了。定时功能的有效值为lineareaseease-inease-outease-in-outcubic-bezier()step-startstep-endsteps()

清单 6-16 。??【线性计时功能】增加了转场速记属性

/* CSS snippet */
#box:hover {
    background-color: gray;
    border-radius: 25px;
    transition: border-radius linear 2s;
}

lineareaseease-inease-outease-in-out的值很方便,而cubic-bezier()允许你指定任何你想要的连续函数。step-startstep-end值和steps()功能允许非连续计时,以便过渡以阶梯方式生效。

最后,如果你没有为transition-delay属性指定一个值,那么就不会有延迟,CSS 样式规则生效的时候转换就开始了。增加延迟值2s将在状态改变整整两秒后开始转换。

您也可以应用多个过渡属性,但不是通过添加多个样式属性来实现。相反,您将多个属性放入同一个transition属性中,并用逗号分隔它们。

清单 6-17 将为我们的div制作背景色和边框半径的动画,但是背景色在 2 秒内不会开始,需要 10 秒才能完成。希望这个例子能激发你对 CSS 转换可能性的想象。

清单 6-17 。 为两个属性指定不同的时序

/* CSS snippet */
#box:hover {
    background-color: gray;
    border-radius: 25px;
    transition: border-radius 2s, background-color 10s;
}

不是每一个 CSS 属性都适合转换,但差不多如此。根据 W3C,可转换属性的实际列表包含background-imagebackground-positionborder-colorborder-widthborder-spacingbottomcolorcropfont-sizefont-weightgrid-*heightleftletter-spacingline-heightmarginmax-heightmax-widthmin-heightmin-widthopacityoutline-coloroutline-offsetoutline-width

转换让我们可以很好地控制元素从一种状态到另一种状态的变化。但是,如果我们真的要掌握应用程序中的东西是如何移动的,我们就需要深入研究适当的 CSS 动画这个主题。

动画

CSS 动画比过渡给了我们更多的对目标元素的控制。过渡允许我们定义从开始状态到结束状态的单个过渡,但是动画允许定义任意数量的关键帧,整个动画可以按顺序通过这些关键帧。有了转场,我可以把我的div从白色变成灰色,但是有了动画,我可以把它从白色变成红色,变成绿色,变成紫色,然后变成灰色,而且我可以控制每一步的时间。

动画比过渡稍微复杂一点,但是像大多数 CSS 概念一样,肯定不会太难理解。动画分两步完成。

第一步是定义动画本身,除了它最终可能应用的任何目标元素。这是使用与媒体查询定义和字体定义类似的语法来完成的。一个动画定义看起来像清单 6-18 中的。

清单 6-18 。??【一个动画定义的基本结构】

@keyframes myAnimation {
    {keyframe1} {
        {properties}
    }
    {keyframe1} {
        {properties}
    }
    ...
    {keyframeN} {
        {properties}
    }
}

动画可以有任意数量的关键帧。每个关键帧的标识符可以是fromto或数字百分比值。from值相当于0%to值相当于100%。您不必指定开始关键帧(from)或结束关键帧(to)。如果您关闭其中任何一个,它会将目标元素的属性值恢复到应用动画之前的位置。

清单 6-19 定义了一个动画,它将把它的目标元素从原来的颜色变成灰色,然后再变回灰色。

清单 6-19 。 单个中点关键帧(开始和结束关键帧将被隐式假定)

/* CSS snippet */
@keyframes toGrayAndBack {
    50% {
        background-color: gray;
    }
}

第二步是将动画应用到元素上。这可以简单地通过将animation-name属性添加到元素样式集合中来完成(或者将一个类应用到包含animation-name属性的元素中)。

像过渡一样,动画在应用程序生效时开始。如果一个元素是用 animation-name 属性定义的,那么动画将在页面加载时开始。要使用 JavaScript 触发动画,您可以将动画应用到某个样式,然后以编程方式将该样式添加到您希望制作动画的元素中。

动画属性有animation-nameanimation-durationanimation-timing-functionanimation-iteration-countanimation-directionanimation-play-stateanimation-delay, animation-fill-modeanimation速记属性。我推荐使用animation速记属性,因为它使得代码更加可读。

一个动画应用程序可能看起来像清单 6-20 中的,它将确定名为myAnimation 的动画等待一秒钟的延迟,然后执行一个缓时功能五秒钟,并重复三次。

清单 6-20 。my animation 动画对 myElement 元素的应用

#myElement {
    animation: myAnimation 5s ease 1s 3;
}

注意清单 6-19 的中定义名称myAnimation的方式,以及在清单 6-20 的中使用了完全相同的动画名称的事实。如果这些名称不完全匹配,则动画将不适用。

在清单 6-21 中,我们将把定义和应用放在一起。代码获取我们创建的toGrayAndBack动画,并将其应用到一个div元素。你可以看到图 6-15 中的div花了四秒钟从白色变成灰色,然后又变回灰色。注意,第一个(from)和最后一个(to)状态在这个动画中没有定义。只定义了中间(50%)。因此div是白色的,因为没有其他背景色应用于它,这是动画默认的状态。

9781430249832_Fig06-15.jpg

图 6-15 。显示元素背景色从白色到灰色再变回白色的动画的五帧

清单 6-21 。 将动画定义和应用结合在一起

<!-- HTML snippet -->
<div id="box"></div>

/* CSS snippet */
@keyframes toGrayAndBack {
    50% {
        background-color: gray;
    }
}
#box { animation: toGrayAndBack 4s; }

现在,我们将通过定义多个关键帧来进行一些改进。清单 6-22 包括三个关键帧,其中第一个和最后一个不是从对象中假定的,而是明确指定的。请记住,如果您的第一个和最后一个关键帧不相同,那么您将很难在不引入不和谐过渡的情况下创建循环动画。

清单 6-22 。 明确包含开始和结束关键帧

<!-- HTML snippet -->
<div id="box">
</div>

/* CSS snippet */
@keyframes toBlackToGray {
    start {
        background-color: white;
    }
    50% {
        background-color: black;
    }
    end {
        background-color: gray;
    }
}

#box {
    animation: toBlackToGray 4s;
}

9781430249832_Fig06-16.jpg

图 6-16。五帧说明背景从白色到黑色再到灰色的动画

我应该在这里记下一条方便的捷径。如果块定义相同,关键帧名称如start50%end可以组合,并用逗号分隔。很多时候你会以相同的属性集开始和结束,你可以用这种方法简化它。清单 6-23 提供了一个将目标元素从白色变成黑色再变回白色的技术例子。

清单 6-23 。?? 两个关键帧被定义在一起。不按顺序定义关键帧没有问题。

/* CSS snippet */
@keyframes toBlackAndBack {
    start, end {
        background-color: white;
    }
    50% {
        background-color: black;
    }
}

最后,清单 6-24 通过使用三个中间关键帧在轨道上移动一个div绕着另一个div更进一步。

清单 6-24 。 HTML 和 CSS 创建两个 div 元素和动画,一个环绕另一个

<!-- HTML snippet -->
<div id="fixed"></div>
<div id="orbit"></div>

/* CSS snippet */
#fixed {
    background-color: black;
    width: 100px;
    height: 100px;
    position: relative;
    left: 150px;
    top: 250px;
}

#orbit {
    background-color: gray;
    width: 100px;
    height: 100px;
    position: relative;
    left: 0px;
    top: 0px;
    animation: orbit 10s linear infinite;
}

@keyframes orbit {
    25% {
            left: 300px;
            top: 0px;
        }
    50% {
            left: 300px;
            top: 300px;
        }
    75% {
            left: 0px;
            top: 300px;
        }
}

9781430249832_Fig06-17.jpg

图 6-17。灰色 div 遵循黑色 div 周围的线性时序路径

除了应该生效的属性列表之外,关键帧还可以指定计时功能。这意味着,您可以通过将计时功能放入目标元素的动画应用程序中,为所有关键帧指定一个计时功能,或者可以在每个关键帧中指定多个计时功能,并让它们按照您喜欢的方式工作,即使它们互不相同。

令人印象深刻的是,这么多的动画可以单独用声明式 CSS 完成,但很可能您需要将一些逻辑合并到您的视觉资产的动画中,并且您需要为此使用 JavaScript。例如,在一个纸牌游戏中,在用户做出选择之前,你不知道他/她会做出什么样的选择,你需要做出相应的反应和动画。

CSS 动画的首要特性之一是捕捉基于动画的事件的能力。可用的事件有animationstartanimationiterationanimationend,每个事件的功能从其名称就很明显,所以我就不解释了。这些事件是在目标元素上触发的,所以你可以像清单 6-25 中的那样连接你的事件处理程序。

清单 6-25 。 为 animationstart 事件实现一个事件处理程序

ready: function (element, options) {
    document.querySelector("#myElement").addEventListener("animationstart", function(e) {
        // do something
    });
}

当您需要对某个元素的动画状态做出逻辑反应时,动画事件当然非常有用。例如,您可能希望在一个动画结束时触发另一个动画的开始。该代码可能看起来像清单 6-26 ,其中当elementA完成其动画时,一个类被添加到elementB

清单 6-26 。 给一个元素添加一个类,当另一个结束时触发一个动画

ready: function (element, options) {
    var elementA = document.querySelector("#elementA");
    var elementB = document.querySelector("#elementB");
    elementA.addEventListener(“animationend”, function(e) {
        elementB.classList.add(“flash”);
    });
}

MSCSSMatrix 对象

我们已经讨论了无数种应用 CSS 属性来改进带有转换的应用程序的方法,但是如果没有 MSCSSMatrix 对象的介绍,这个讨论将是不完整的。除了您在 CSS 文件中指定的所有这些属性之外,MSCSSMatrix JavaScript 对象允许您在脚本中应用我们到目前为止讨论过的所有转换。这意味着你可以添加逻辑和更复杂的时间。

一个简单的例子就能说明问题。清单 6-27 根本不包含 CSS,只包含 HTML 和 JavaScript。假设 JavaScript 存在于一个 ready 方法中,该方法在 DOM 就绪时触发。

清单 6-27 。 一个简单的 div,其文本由 JavaScript 指示绕 z 轴逆时针旋转 10 度

<!-- HTML snippet -->
<div class="parent">
    Hello world
</div>

// JavaScript snippet
var d = document.querySelector(".parent");
d.style.transform = new MSCSSMatrix(d.style.transform).rotate(0, 0, -10);

9781430249832_Fig06-18.jpg

图 6-18。结果,旋转的文本,完成没有显式的 CSS 编写

在这个例子中,我们使用了rotate函数,但是所有其他的转换函数都是可用的,当然,你也可以手动设置你的矩阵值。

想象一下用 JavaScript 中 CSS 转换的力量可以做些什么是令人兴奋的。你可以在微软 MSDN 网站http://msdn.microsoft.com/en-us/library/windows/apps/hh453593.aspx上找到更多关于 MSCSSMatrix 对象的信息。

CSS3 中变换、过渡和动画的引入只是 web stack 成为世界上最强大的(也是无处不在的)用户界面技术的部分原因。

摘要

到目前为止,我们一直在看只包含少量基本元素的例子。在一个真实的应用程序场景中,你需要在屏幕上获得更多的视觉资产,并以一种有意义和实现良好设计的方式来布局它们。

你将在 CSS 中得到的布局元素和技术,以及来自 WinJS 库的一些帮助,将会给你把你的设计变成一个工作模型所需要的能力,我们将在下一章中研究那些布局技术。

七、布局属性

image CSS 能够驱动布局而不把开发者逼疯的能力由来已久。这几乎一直是一个痛处。

在我看来,web stack 长期以来对布局的支持并不多,这是由几个原因造成的。首先,我们有桌子,桌子在某种程度上已经足够了。当然,它们从来都不是定义你的布局的正确位置,但是已经足够了。第二,web 开发面向各种设备上的许多浏览器,并试图定义一个适应其显示的文档。相比之下,在印刷世界中,一张标准的纸永远不会改变大小,视觉伪像可以放在任何地方。

布局在 HTML 中仍然具有挑战性,但是开发人员哀叹缺少选择的日子已经一去不复返了。今天,我们看到越来越多的开发人员就众多选项中的哪一个在特定场景中最有效展开建设性的争论。

传统布局

表格是 HTML 中非常早期的元素。很有可能你对 HTML 表格有过一些经验,但是如果你没有,那么只需要知道它们不仅仅用于表格数据。它们也被用于布局。

我认为唯一和表格一样基本的 HTML 实现(或者更基本)是框架集。网站的布局有导航窗格、页眉和页脚窗格,框架集通过在每个窗格中加载不同的 HTML 文件来实现它们。框架集给开发人员和用户都带来了一系列的麻烦,所以推荐使用在一个 HTML 文件中定义了所有内容的表格。

除了表格之外,遗留布局的主题还应该包括使用带有显示和位置属性的普通旧div元素,以便将它们放置到位。

表格布局

表格布局实际上非常灵活和有效,我可以想象许多网站设计者今天仍然依赖它。我还敢打赌,这些设计师对他们的同行隐瞒了这个事实,因为表格绝不是当前的推荐。不推荐使用它们,因为它们与保持关注点分离的目的不一致,并且不具有适应性——这是 web 设计的两个核心原则。

还记得 HTML 定义结构,CSS 定义样式吗?HTML 允许你定义使用中的元素和它们之间的层次关系,但是不应该说这些元素的位置。更确切地说,布局是一种风格,它属于 CSS。将你的布局以表格的形式放入你的 HTML 也许能完成任务,但是它会导致一个网站不能像现代应用程序和设备所需要的那样具有适应性。

用表格定义布局会产生某些断言,如“此内容必须在此内容之下”或“此内容必须包含在此区域中”,他们会将站点锁定在这些断言中。

清单 7-1 显示了一个可以很好定义文档布局的表格。

清单 7-1 。用作布局的表格元素

<!-- HTML snippet -->
<table>
    <tr><td rowspan="3" id="nav"></td><td rowspan="3"></td><td></td></tr>
    <tr><td></td></tr>
    <tr><td></td></tr>
    <tr><td colspan="3" id="footer"></td></tr>
</table>

/* CSS snippet */
table{
    width:1100px;
    height: 560px;
    border-collapse: collapse;
}

table td {
    border: 2px solid black;
    padding: 5px;
}

#nav {
    width:180px;
}

#footer {
    height:80px;

}

9781430249832_Fig07-01.jpg

图 7-1。这个示例布局看起来很不错,但是使用表格来定义布局从来都不是一个好主意

显示和定位

如果表格是布局上的禁忌,那怎么办?下一个更好的解决方案是使用带有显示和位置属性的div元素,手动将它们发送到屏幕上应该出现的位置。一个div(division 的缩写)是一个抽象元素,通常用来将内容的各个部分分开。它以块模式呈现,这意味着前面和后面的元素呈现在它的上面和下面。这与行内元素形成对比,行内元素像文字处理器中的文本一样流动。

这些div元素可以用样式属性进行格式化和定位,这使它们成为布局中表格的良好替代品。今天,它们在 web 上无处不在,但这并不是说开发人员乐于处理它们!使用div元素作为布局的技术带来了自己的痛苦,这就是为什么各地的 web 开发人员对已经出现或即将出现的新格式功能感到兴奋。然而,我们将在这里详细讨论经典的div定位,因为它是基础性的,因为你肯定会在“野外”看到它

display属性决定了它的目标将如何显示,成为布局主题中非常重要的属性。display属性的有效值(包括特定于微软供应商的值)为inlineblocklist-itemrun-ininline-blocktableinline-tabletable-row-grouptable-header-grouptable-footer-grouptable-rowtable-column-grouptable-columntable-celltable-caption-ms-flexbox-ms-inline-flexbox-ms-grid-ms-inline-gridnone

显示属性

有很多值,所以我认为我们至少需要粗略地介绍一些显示属性,以便对布局有一个全面的了解。

在一条直线上的

当一个元素为inline时,它的行为就像文字处理器中的文本——也就是说,它相对于其前一个兄弟元素水平呈现,从一行流到下一行,并影响其自身的行高。尝试使用widthheight调整内联元素的大小不会影响它们。

街区

块元素占用尽可能多的宽度,这迫使它们在自己的行上。指定多个块元素会导致元素在渲染时垂直堆叠。可以调整块元素的大小。

内嵌块

从父元素的角度来看,设置为显示为inline-block的元素的行为类似于内联元素,但是它们将自身及其子元素呈现为块元素。这是创建多个div元素的有效方法,这些元素像文本一样一个挨着一个显示。

列表项目

设置为display: list-item的元素就像列表中的一个项目一样呈现。为元素创建了一个标记框和一个块框,给它一个你所期望的项目符号和左边距。

试车

run-in值是一个有趣且颇具艺术性的单次使用值,通常用于标题。如果一个标题被设置为显示为run-in,那么它将和它下面的一段文字显示在同一行,并迫使文字移动以腾出空间。

内嵌表格和表格*

表格中各种组件(行、单元格等)的所有行为。)可以通过将元素显示设置为各种表格属性来模拟。你会记得,在 HTML 中使用表格是不好的做法,因为布局不是 HTML 的工作。但是,如果您使用这些样式属性实现您的表格,那么您将获得表格布局的所有优点,而没有缺点。不过我还是不推荐。

-ms-flexbox 和-ms-inline-flexbox

flexbox 是布局内容的一个非常强大的元素。Flexboxes 能够智能地处理其子对象的渲染方式,并为您提供流动、包裹、拉伸、对齐以及更多功能。接下来会有一整节专门介绍 flexboxes。使用-ms-flexbox值将创建一个块样式元素,使用-ms-inline-flexbox将创建一个内嵌 flexbox。

-ms 网格和-ms 内嵌网格

网格还可以让您很好地控制子元素的布局。网格允许内容的特定放置方式仍然可以适应大小和位置的变化。就像 flexbox 一样,有一个块和一个内嵌版本。

一旦为元素选择了合适的显示值,就应该知道定位它的选项。

位置属性

通过position属性进行定位,值为staticabsoluterelativefixedstatic的值为默认值。位置值的好处将在以下章节中介绍。

静电

默认的static值不做任何额外的工作来定位对象。无论是内联元素还是块元素,它的位置都是由它在 HTML 文件中的位置决定的。

绝对的

一个absolute值设置一个元素,如果它的父元素被定位,它将被放置在相对于它的父元素的任何地方。如果它的父级没有被定位,那么它将相对于文档体。绝对定位的元素被从文档流中取出并停止影响它。他们不再把其他元素推来推去,也不再被它们推来推去。

亲戚

absolute值一样,relative值设置您显式地放置您的元素,但是放置值被认为是相对于元素在它是静态的情况下呈现的位置。这是将元素向左移动一点的好方法。

固定的;不变的

fixed值的工作方式类似于absolute,但是它考虑到了它所在的视口,所以如果它在滚动的div中被渲染,它可以相对于div被放置。

除了 static 之外的每个位置值都决定了元素的位置,通过指定 left、right、top 和 bottom 的值可以做到这一点。对于绝对位置和固定位置,这些属性决定了元素的四个边相对于另一个元素的位置,但是对于相对位置,它们决定了该边相对于静态元素呈现位置的位置。

清单 7-2 展示了一个简单的正方形div元素行的例子(使用 flexbox 布局,我们很快会讨论到)。第三个元素的位置值为 relative(与默认的 static 相反),然后向右下方移动 15 个像素。效果是将盒子从原来的位置移开,如图 7-2 中的所示。

清单 7-2 。将元素的位置设置为相对位置,将其左侧和顶部设置为 15 像素

<!-- HTML snippet -->
<div id="dpFlexbox">
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
</div>

/* CSS snippet */
#dpFlexbox {
    display: -ms-flexbox;
}

#dpFlexbox > div {
    border: 1px solid black;
    width: 100px;
    height: 100px;
    margin: 5px;
}

#dpFlexbox > div:nth-of-type(3) {
    position: relative;
    background-color: gray;
    left: 15px;
    top: 15px;

}

9781430249832_Fig07-02.jpg

图 7-2 。该元素从它本应占据的空间偏移

清单 7-3 显示了同样的五个盒子,第三个盒子的左边和上边同样被赋予了 15 像素的值,但是这次position被设置为absolute。正如您所看到的,灰色框现在已经从文档的左上角移动到了 15 个像素。在图 7-3 的中也要注意,其他div元素已经一起折叠,因为第三个元素已经从流程中取出。

清单 7-3 。这次将位置设置为绝对位置

<!-- HTML snippet -->
<div id="dpFlexbox">
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
</div>

/* CSS snippet */
#dpFlexbox {
    display: -ms-flexbox;
}

#dpFlexbox > div {
    border: 1px solid black;
    width: 100px;
    height: 100px;
    margin: 5px;
}

#dpFlexbox > div:nth-of-type(3) {
    position: absolute;
    background-color: gray;
    left: 15px;
    top: 15px;
}

9781430249832_Fig07-03.jpg

图 7-3 。该元素位于文档的左上角,并失去了它在 div 元素列表中的位置

我们已经讨论了边距、边框和填充属性,但是我想提醒您,它们在内容布局中确实起着很大的作用。Windows 8 设计的核心原则之一是少即是多。这意味着我们没有尽力在屏幕上显示尽可能多的信息。我们并不试图让用户一次点击就能完成所有事情,这似乎是网站设计的趋势。所以我们不怕给我们的内容布局增加一些有意的、有目的的呼吸空间。这通常采用边距或填充的形式,所以要准备好使用它们。

浮动

可以使用float属性告诉块级元素浮动。浮动元素允许后续的内联内容呈现在自身旁边(而不是下面)。您可以提供leftright的值来决定应该填充块级元素内容的哪一侧。

当按照预期使用时,浮动工作得很好,但是 web 开发人员已经使用它一段时间来获得块级元素以方便布局。

使用float进行布局会提醒你,它的强制作用并不那么舒服。

现代布局

幸运的是,我们不局限于旧的内容布局方式。我们现在有了更多的现代化设备,它们比预期的更简单,同时也更强大。web 开发中特别痛苦的部分有办法激发对新事物的足够需求。

flex box(flex box)的缩写形式

在 flexboxes 的标准建议和实现中仍然有一些混乱,但是 IE10 和 Windows 8 已经很好地实现了这种非常有用的布局技术。

Flexboxes 解决了一个老问题。我们长期以来用于布局内容的抽象元素被设计成块元素。现代用户界面,尤其是 Windows 8 中的界面充满了流动的产品列表、朋友的图片和图片。

虽然使用经典的div定位和浮动来实现必要的布局几乎是不可能的,但这通常是痛苦的。Flexboxes 减轻了大部分或全部痛苦。

指导元素列表作为 flexbox 进行布局就像在父元素上设置display: -ms-flexbox;一样简单。默认的布局方向是水平的,所以一旦添加了这个属性,子元素将开始从左向右流动。这本身是有帮助的,但这仅仅是开始。Flexboxes 还使您能够沿布局轴以及垂直于布局轴调整项目和空间的大小。它们还允许您控制内容的布局方向和顺序,而不管它在 HTML 中是如何指定的。

柔性盒属性

让我向您介绍 flexbox 的其他相关属性和值,并解释它们的功能。以下属性将被添加到父元素(我们添加了display: -ms-flexbox;的元素)。

-ms-flex-方向

确定子元素的流向。您可以使用默认值row从左到右排列项目,或者使用默认值column从上到下排列项目。您也可以使用row-reverse从右到左或column-reverse从下到上。

-ms-flex-align

属性处理垂直于项目排列方向的间距。假设我们有一个方向设置为row的 flexbox,start的值会将子元素沿着行的顶部排列,center会将它们排列在中间,end会将它们沿着底部排列。此外,baseline将对齐前沿,stretch(这是默认设置)将增长每个元素以适应空间。对于方向设置为column的柔性盒,情况正好相反。

-ms-flex 包装

类似于-ms-flex-align处理垂直于方向的间距,-ms-flex-pack处理垂直于方向的间距。使用start值,您可以指示所有子元素打包到左侧(仍然假设 flexbox 的方向为row),使用center打包到中间,使用end打包到右侧,使用justify您可以指示子元素均衡它们的间距,以便它们从头到尾都很好地适合它们的分配空间。

ms-flex-wrap

属性让你控制当子元素到达 flexbox 末尾时会发生什么。包装是布局内容的一种很好的方式。使用wrap打开包装,或将其设为默认值none关闭包装。

-ms-flex-flow

-ms-flex-direction-ms-flex-wrap是公共属性,并且提供了一个简写属性-ms-flex-flow来封装它们。

同样,到目前为止提到的属性应该应用于元素 flexbox 本身。另一方面,以下属性应该添加到子元素中:

-ms-flex

属性是一个相当重要的属性。它最多采用三个值(如您所料,用空格分隔),即正伸缩量负伸缩量首选大小。您也可以使用none的值,它相当于0 0 auto的值。一个项目的正伸缩是它沿着方向轴增长的能力,负伸缩是它收缩的能力。您为 flex 设置的整数值决定了相对(相对于其他 flex 项目)大小变化。如果这还不清楚,那么请看清单 7-7 中的例子。

-ms-flex-订单

最后,-ms-flex-order也是一个整数值,指定子项目所属的组。一开始,设置一个组似乎与排序无关,但是如果你给每个项目分配了自己的组号(0,1,2,3,…),那么 flex order 就会相应地排列你的项目。如果您没有为 flex order 提供值,或者如果您将所有项目放在同一个组中,那么它们的顺序将不会改变 DOM 中指定的顺序。

这是对属性的细分,但让我们来看看一些正在运行的 flexboxes,以便更好地了解我们应该何时以及如何使用它们。

清单 7-4 定义了一个非常简单的 flexbox。这种方法只需要一行 CSS 代码,就能有效地水平布局子元素。

清单 7-4 。将父 div 的显示属性设置为-ms-flexbox

<!-- HTML snippet -->
<div id="flexbox">
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
</div>

/* CSS snippet */
#flexbox {
    display: -ms-flexbox;
}

#flexbox > div {
    border: 1px solid black;
    width: 100px;
    height: 100px;
    margin: 5px;
}

9781430249832_Fig07-04.jpg

图 7-4 。默认情况下,flexbox 是水平的,可以很好地布局 div 元素

flexbox 的价值是轻量级的,易于定义,一旦你开始寻找,你会发现它到处都有很好的用途。

图 7-4 中的柔性盒没有指定尺寸,所以它只是符合其内容的尺寸。注意在清单 7-5 中,当我们向 flexbox 添加widthheight属性(并添加一个border用于可见性)时,内容是如何布局的。

清单 7-5 。向 flexbox 添加一些尺寸和边界

<!-- HTML snippet -->
<div id="flexbox">
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
</div>

/* CSS snippet */
#flexbox {
    display: -ms-flexbox;
    width: 800px;
    height: 300px;
    border: 3px solid lightgray;
}

#flexbox > div {
    border: 1px solid black;
    width: 100px;
    height: 100px;
    margin: 5px;
}

9781430249832_Fig07-05.jpg

图 7-5。默认情况下,所有子元素都靠左上方对齐

内容在顶部和左侧。到目前为止,您看到的所有功能都只涉及到一个与 flexbox 相关的属性:display: -ms-flexbox。布局由许多默认值控制。让我来揭示这些。

首先,direction 属性默认为 row,这也是内容水平布局的原因。此外,“打包”和“对齐”属性默认为“开始”,这会将内容放在 flexbox 的左上角。然而,我们可以对这些属性进行创新,并真正控制这些盒子的行为。

在清单 7-6 中,我们设置了要分发的 flexbox 包。我们还将盒子的宽度和高度最小化,让它们自由生长。“要对齐的项目”属性的默认值是“拉伸”。请记住,pack 是沿布局方向的间距,在本例中为水平方向,align 是垂直于布局方向的间距,在本例中为垂直方向。

清单 7-6 。包装设置为分配,对齐将采用其默认值拉伸

<!-- HTML snippet -->
<div id="flexbox">
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
</div>

/* CSS snippet */
#flexbox {
    display: -ms-flexbox;
    -ms-flex-pack: distribute;
    width: 800px;
    height: 300px;
    border: 3px solid lightgray;
}

#flexbox > div {
    border: 1px solid black;
    min-width: 100px;
    min-height: 100px;
    margin: 5px;
}

9781430249832_Fig07-06.jpg

图 7-6。子元素均匀分布,并垂直拉伸以填充它们的容器

我们从来没有在 HTML 中见过如此简单的项目布局控制,而且它还没有结束。

这些元素都被拉伸到相同的高度,但是如果不是这样,那么我们的 align 属性将能够处理许多不同的配置来排列它们。

除了对齐(-ms-flex-align)和打包(-ms-flex-pack)控制之外,我们对项目如何伸缩也有一些发言权。弯曲沿着布局的方向发生,是一个项目的扩展或收缩,以填充空白空间。到目前为止,我们已经设置了 flexbox 级别的属性,但是 flex 属性作用于项目本身,因为它们可能是不同的值。

在清单 7-7 中,我们从项目中移除了最小宽度值,指示第二个和第四个项目(偶数项目)以相对值 2 伸缩,并告诉第一个、第三个和第五个项目(奇数项目)以相对值 1 伸缩。在图 7-7 的结果中,你可以看到项目已经按照我们的要求完成,并采用它们的相对宽度值来填充 flexbox 中的所有空间。太棒了。

清单 7-7 。指示子项以不同的相对伸缩值“伸缩”

<!-- HTML snippet -->
<div id="flexbox">
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
</div>

/* CSS snippet */
#flexbox {
    display: -ms-flexbox;
    width: 800px;
    height: 300px;
    border: 3px solid lightgray;
}

#flexbox > div {
    border: 1px solid black;
    min-height: 100px;
    margin: 5px;
}

    flexbox > div:nth-child(odd) {
        -ms-flex: 1;
    }

    #flexbox > div:nth-child(even) {
        -ms-flex: 2;
    }

9781430249832_Fig07-07.jpg

图 7-7 。所有项目都在伸缩,但偶数项目占据了两倍的水平空间

在我们继续研究网格之前,还有一个 flexbox 人才您应该看到。

如果 flexbox 的项目有明确的大小,并且它们溢出了 flexbox,flexbox 会像任何其他有溢出的div一样处理它们——默认情况下,它会显示溢出溢出了它的边界。它看起来有点像图 7-8 。

9781430249832_Fig07-08.jpg

图 7-8 。有太多子项目并且没有包装的 flexbox 会溢出它的边界

常规的div元素允许我们隐藏或滚动溢出的内容,但是 flexbox 更进一步,允许你包装内容,正如你在清单 7-8 中看到的。瞧啊。

清单 7-8 。向 flexbox 添加包装

<!-- HTML snippet -->
<div id="flexbox">
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
</div>

/* CSS snippet */
#flexbox {
     display: -ms-flexbox;
     -ms-flex-wrap: wrap;
     width: 800px;
     height: 300px;
     border: 3px solid lightgray;
     -ms-flex-pack: start;
}

#flexbox > div {
    border: 1px solid black;
    width: 100px;
    height: 100px;
    margin: 5px;
}

9781430249832_Fig07-09.jpg

图 7-9。项目会换行到下一行

在 Windows 8 应用中使用 Flexbox

我已经从技术上向您展示了 flexbox 的各种属性和功能,但我认为在 Windows 8 应用程序中查看 flexbox 的运行会有所帮助。

本例的目标是创建一个容器,在该容器中可以看到单个子项,后续的子项从右边窥视,邀请用户滑动以显示它。捕捉点应该帮助用户将滚动位置精确地移动到下一项。Flexboxes 在这种情况下非常出色,所以让我们看看如何实现它。清单 7-9 显示了一个简单有效的解决方案,它利用了我们的朋友 flexbox。

清单 7-9 。创建包含可切换内容的容器的完整 HTML、CSS 和 JavaScript

<!-- HTML snippet -->
<div class="swiper">
    <div>
        <h3>Lorem Ipsum</h3>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing. . .</p>
        <p>Donec dignissim tempor risus, in iaculis odio. . .</p>
    </div>
    <div>
        <h3>Maecenas velit</h3>
        <p>In hac habitasse platea dictumst. Quisque facilisis. . .</p>
        <p>Maecenas velit nisi, accumsan tempor tincidunt vel. . .</p>
    </div>
    <div>
        <h3>Nisl augue</h3>
        <p>Nulla rhoncus, nulla at convallis pretium, nisl augue. . .</p>
        <p>Donec tempor urna venenatis neque ornare congue. . .</p>
    </div>
</div>

/* CSS snippet */
.swiper {
    border:2px solid gray;
    display:-ms-flexbox;
    height:400px;
    width:600px;
    overflow-x:auto;
    -ms-scroll-snap-x:mandatory snapInterval(0%,80%);
}

.swiper > div {
    width: 80%;
    padding-left:40px;
    box-sizing: border-box;
    overflow-y:auto;
    margin-bottom:16px; /* room for vertical scrollbar */
    padding-right:16px; /* room for vertical scrollbar */
}

.swiper > div:last-child {
    width:100%;
}

清单 7-9 中的解决方案确实需要解释。

首先,HTML 定义了一个简单的 div 元素,它有三个子 div 元素,每个元素都包含自己的内容。包含的 div 已被分配了一个类swiper。使用一个类来添加这个功能是一个聪明的方法,因为这意味着通过简单地添加这个类就可以很容易地将这个功能添加到我们想要的任何元素中。

第一个样式规则以那个swiper类的元素为目标。对于这个规则,我们使用最重要的display:-ms-flexbox;属性。仅这个属性就将所有子元素设置为一个接一个地布局。border 和 height 和 width 属性是任意的,只是用于可见性。不过,最后两个属性很重要。

如果元素的内容比容器宽,属性设置元素水平滚动。最后一个属性-ms-scroll-snap-x:mandatory snapInterval(0%,80%);负责以合理的增量停止用户的平移手势,即当一个项目的边缘位于容器的左侧时。注意,属性值中的snapInterval的第二个参数是80%。在看到下一个样式规则中子元素的宽度后,原因就清楚了。

下一个样式规则的选择器是.swiper > div。您将认识到子组合,它使得使用该选择器的样式规则以每个 div 元素为目标,该 div 元素是 swiper 类元素的直接子元素。

flexbox 的子元素应该有 80%宽(并且应该匹配包含元素的-ms-scroll-snap属性的值。这允许下一个子项的 20%的内容“窥视”到框架中,避开即将到来的内容并邀请用户请求它。此外,每个孩子应该在左侧有 40 个像素的填充,以将他们彼此分开,但我们选择的 80%宽度应该使用border-boxbox-sizing值来包括总宽度的填充值。

允许任何子项垂直溢出是不理想的。这样做会让用户在两个不同的滚动方向之间感到困惑。然而,overflow-y: auto;属性将确保如果内容变得太高,至少不会溢出底部,而是呈现一个滚动条。

该规则的最后两个属性提供了底部边距和右填充,以便在不覆盖内容的情况下为滚动条提供呈现空间。

最后一个样式规则很简单,它只声明最后一个子元素的宽度不应该是 80%,而是 100%。如果不将此属性应用于最后一个子元素,强制捕捉点将不允许您导航到最后一个元素。

你可以在图 7-10 中看到这段代码的结果,但更好的是你应该通过查看源代码来探究这个解决方案的行为。

9781430249832_Fig07-10.jpg

图 7-10 。随后的内容显示在右侧,只需展示一个预告片就能让大家知道

flexbox 是 CSS 家族中令人兴奋的新成员,但它的主要功能是将数据排列到分配给它的空间中。为了采取更结构化的方法,请看一下新的 CSS 网格。

格子

网格的用途与 flexbox 非常不同。作为一个例子,flexbox 可以帮助你为一个目录制作一个产品列表,但是网格将会布局你的整个 UI,并极大地提高它的适应性。

通过简单地添加 display 属性和一个值-ms-grid,一个元素就变成了一个网格。看看在清单 7-10 中的div元素栈发生了什么,当我们唯一的改变是将它们的父元素指向一个网格时。

清单 7-10 。将 div 设置为显示值为-ms-grid

<!-- HTML snippet -->
<div id="grid">
    <div id="a">A</div>
    <div id="b">B</div>
    <div id="c">C</div>
    <div id="d">D</div>
</div>

/* CSS snippet */
#grid {
    display: -ms-grid;
    width: 800px;
    height: 300px;
    border: 3px solid lightgray;
}

#grid > div {
    border: 1px solid black;
    width: 100px;
    height: 100px;
    margin: 5px;
}

9781430249832_Fig07-11.jpg

图 7-11。子项目一个堆叠在另一个之上

乍一看,这似乎有点奇怪。所有的物品都堆叠在一起。它看起来还不太像一个布局工具。为了解决这个问题,我们还应该定义一些行和列。清单 7-11 就是这么做的,定义了一个简单的 2 乘 2 的网格,这样我们的每个盒子都可以有自己的空间。然而,除了定义行之外,我们还必须告诉每个元素它属于哪一行和哪一列。网格中未分配行或列的任何子元素都采用默认值 1,并显示在第一行或第一列中。

清单 7-11 。为每个子项添加列和行分配

<!-- HTML snippet -->
<div id="grid">
    <div id="a">A</div>
    <div id="b">B</div>
    <div id="c">C</div>
    <div id="d">D</div>
</div>

/* CSS snippet */
#grid {
    display: -ms-grid;
    -ms-grid-rows: 1fr 1fr;
    -ms-grid-columns: 1fr 1fr;
    width: 800px;
    height: 300px;
    border: 3px solid lightgray;
}

#grid > div {
    border: 1px solid black;
    width: 100px;
    height: 100px;
    margin: 5px;
}

#a { -ms-grid-row: 1; -ms-grid-column: 1; }
#b { -ms-grid-row: 1; -ms-grid-column: 2; }
#c { -ms-grid-row: 2; -ms-grid-column: 1; }
#d { -ms-grid-row: 2; -ms-grid-column: 2; }

9781430249832_Fig07-12.jpg

图 7-12。子项目会移至其指定的储存格

网格的行被定义为 1fr 1fr。这是以空格分隔的所有行的行高列表。在这种情况下,我们使用了代表fractionfr单元标志符。如果所有行都被赋予小数值 1,那么每一行都将被赋予相等的可用网格高度。

也可以将一些行定义为具有绝对高度,在这种情况下,分数值确定总高度减去所有绝对值定义后的每行部分。网格行定义100px 1fr 100px将创建一个网格,在顶部和底部有一个 100 像素的行,剩余的空间给中间的行。

在应用所有绝对长度后,小数部分应用于剩余空间的分数。例如,列值为200px 1fr 3fr的 1000 像素宽的网格将得到一列 200 像素(绝对定义的)加上一列 200 像素(剩余 800 像素的 1/4)加上一列 600 像素(剩余 800 像素的 3/4)。

假设我们现在想要定义一个应用程序布局,它包括一个固定的标题,一些固定的左边空间,然后剩余的空间被平分(垂直)。看看清单 7-12 中的,看看这是如何实现的。

清单 7-12 。使用网格设置应用程序布局

<!-- HTML snippet -->
<div id="grid">
    <div id="header">header</div>
    <div id="left">left</div>
    <div id="firstHalf">first half</div>
    <div id="secondHalf">second half</div>
</div>

/* CSS snippet */
#grid {
    display: -ms-grid;
    -ms-grid-rows: 120px 1fr;
    -ms-grid-columns: 120px 1fr 1fr;
    width: 1000px;
    height: 540px;
    border: 3px solid lightgray;
}

#grid > div {
    border: 1px solid black;
    margin: 5px;
    padding: 10px;
}

#header { -ms-grid-row: 1; -ms-grid-column: 1; }
#left { -ms-grid-row: 2; -ms-grid-column: 1; }
#firstHalf { -ms-grid-row: 2; -ms-grid-column: 2; }
#secondHalf { -ms-grid-row: 2; -ms-grid-column: 3; }

9781430249832_Fig07-13.jpg

图 7-13。布局的实现只有一个小问题

这个布局看起来很好,但是我们有一个小问题:标题项只占第一列。网格与它的旧表祖先的另一个相似之处在于它跨行或跨列的能力。在前面的例子中,我们在主内容会话中添加了一个分割,但是我们可能不希望它影响我们的标题。清单 7-13 中的解决方案简单地定义了我们的#header元素来跨越占据整个布局顶部的三列。这些跨度定义适用于单个网格子元素,因此指示一个元素位于单个单元格中,而另一个元素跨越多个单元格是完全有效的。这是相对于表格的一个相当大的优势。

清单 7-13 。将单元格范围添加到标题子项

<!-- HTML snippet -->
<div id="grid">
    <div id="header">header</div>
    <div id="left">left</div>
    <div id="firstHalf">first half</div>
    <div id="secondHalf">second half</div>
</div>

/* CSS snippet */
#grid {
    display: -ms-grid;
    -ms-grid-rows: 120px 1fr;
    -ms-grid-columns: 120px 1fr 1fr;
    width: 1000px;
    height: 540px;
    border: 3px solid lightgray;
}

#grid > div {
    border: 1px solid black;
    margin: 5px;
    padding: 10px;
}

#header { -ms-grid-row: 1; -ms-grid-column: 1;-ms-grid-column-span: 3; }
#left { -ms-grid-row: 2; -ms-grid-column: 1; }
#firstHalf { -ms-grid-row: 2; -ms-grid-column: 2; }
#secondHalf { -ms-grid-row: 2; -ms-grid-column: 3; }

9781430249832_Fig07-14.jpg

图 7-14。标题横跨三列,占据了应用程序布局的整个顶部

在 Windows 8 应用程序中使用网格

就像我们对 flexbox 所做的一样,我们将看一个在真实的 Windows 8 应用程序中使用网格的例子。

让我们想象一下,我们正在显示一个用户的个人资料,其中包括他的面部照片和一些关于他的信息,这些信息形成了一个布局,就像你在图 7-15 中看到的。

9781430249832_Fig07-15.jpg

图 7-15 。所需轮廓布局的粗略描述

这是 CSS 网格的完美候选。首先,请注意我们的布局已经分成三列。接下来,请注意,虽然我们的内容没有形成漂亮的行,但我们可以使用三行,然后进行一些跨越来达到相同的效果。

9781430249832_Fig07-16.jpg

图 7-16。轮廓布局与一些重叠的线条描绘了我们的网格应该如何设置

现在让我们深入了解解决方案。

。定义一个网格,并相应地放置和跨越各个元素

<!-- HTML snippet -->
<div class="profile">
    <div class="image">image</div>
    <div class="section1">section 1</div>
    <div class="section2">section 2</div>
    <div class="section3">section 3</div>
    <div class="section4">section 4</div>
</div>

/* CSS snippet */
.profile {
    display:-ms-grid;
    width:1025px;
    height:576px;
    -ms-grid-columns: 7fr 14fr 9fr;
    -ms-grid-rows: 120px 2fr 3fr;
}

.profile > div {
    border: 1px solid black;
    margin: 5px;
    padding: 5px;
    font-size:x-large;
}

.image {
    -ms-grid-row-span: 2;
}

.section1 {
    -ms-grid-row: 3;
}

.section2 {
    -ms-grid-column: 2;
    -ms-grid-row-span: 3;
}

.section3 {
    -ms-grid-column: 3;
}

.section4 {
    -ms-grid-column: 3;
    -ms-grid-row: 2;
    -ms-grid-row-span: 2;
}

图 7-17 是结果,大致相当于初始图。我希望这能让你相信,使用 CSS 网格功能可以轻松实现 Windows 8 视图的自定义布局。

9781430249832_Fig07-17.jpg

图 7-17 。最终的结果看起来很像我们想要的配置文件布局

视图框和列表视图

我们已经介绍了 CSS 为我们提供的传统和现代布局实现,但当您在 Windows 8 应用程序上工作时,您可以使用 Windows Library for JavaScript(WinJS ),并且 WinJS 为您提供了一些更现代的布局控制。尽管它们不是 CSS 属性,但在本书的上下文中还是值得一看的。

首先是WinJS.UI.ViewBox。ViewBox 是一个控件,当您希望内容根据应用程序视图状态的变化自动缩放时,可以向其添加内容。例如,如果一个应用程序在平板电脑上运行,它可能会旋转到纵向方向,在这种情况下,可能需要放大或缩小其中的内容。我说的不是基于新的维度重新呈现它的内容。我说的是内容的直接缩放,就像在 ViewBox 介入之前呈现的那样。

这种缩放之所以成为可能,要感谢我们在第六章中已经学过的缩放和平移变换函数。ViewBox 很乐意为您应用这种效果,它唯一要求的是您只给它一个子项目。这并不是一个大问题,因为即使你有多个子项,你所要做的就是把它们像一个简单的 div 一样包装在一个容器中。

如图 7-18 中的所示,查看框适合子项目而不改变其纵横比。在横向方向上,高度将约束孩子的尺寸,而在纵向方向上,宽度将是约束。

9781430249832_Fig07-18.jpg

图 7-18 。在完整(a)、对齐(b)、填充(c)和纵向(d)视图状态下,如何在视图框中呈现简单形状

关于内容布局的另一个 WinJS 条款是非常强大的WinJS.UI.ListView控件。在一个 app 里把事情罗列出来是很常见的。我们习惯于看到朋友列表、股票价格列表和电子邮件列表。ListView 很好地涵盖了这些情况,它能够将项目布局为具有多行和多列的网格,或者垂直或水平的列表。与更原始的 flexboxes 和网格相比,ListView 有一些优势,包括但不限于:

  • 项目选择
  • 同步或异步数据源的数据绑定
  • 不对称的跨单元布局
  • 项目分组
  • 自定义渲染函数

清单 7-15 显示了最基本的 ListView,它被绑定到数字 1 到 11。请注意如何在项目模板中使用网格,以便其内容可以水平和垂直居中。

清单 7-15 。实现 ListView 的 HTML、CSS 和 JavaScript

<!-- HTML snippet -->
<div id="list" data-win-control="WinJS.UI.ListView"></div>
<div id="template" data-win-control="WinJS.Binding.Template">
    <div>
        <img data-win-bind="src:imageUrl" />
        <div data-win-bind="innerText:name"></div>
    </div>
</div>

/* CSS snippet */
.win-listview {
    height: 100%;
}

.win-listview .win-item .grid {
    display:-ms-grid;
    -ms-grid-rows:160px;
    -ms-grid-columns:160px;
    background-color:gray;
    color:white;
}

.win-listview .win-item .grid div {
    font-size: 36px;
    font-weight: bold;
    -ms-grid-column-align: center;
    -ms-grid-row-align: center;
}

// JavaScript snippet
var numbersList = new WinJS.Binding.List();

var list = document.getElementById("list").winControl;
list.itemTemplate = document.getElementById("template");
list.itemDataSource = numbersList.dataSource;

for (var i = 1; i <= 14; i++) {
    numbersList.push(i);

}

您可能想知道 ListView 控件本身是使用 grid 还是 flexbox 来实现,但实际上两者都没有使用。ListView 负责如此多的情况和组合,以至于需要更多的控制,所以它是用自定义定位属性实现的。

在第九章中,我们将讨论更多关于 ListView 的内容,并学习如何使用 CSS 样式规则来定位所有的组件。

在图 7-19 中,你可以看到一个非常基本的列表视图,但是列表视图有很多扩展功能,包括项目分组。在这种情况下,标题呈现在每组项目的上方,开发人员必须提供一个标题模板来定义标题的外观。

9781430249832_Fig07-19.jpg

图 7-19 。实现了 ListView,并为效果选择了单个项目

清单 7-16 从与清单 7-15 相同的 ListView 开始,然后添加头部模板和必要的 JavaScript 代码来进行简单的分组。

清单 7-16 。添加 HTML 和 JavaScript 来实现 ListView 上的分组

<!-- HTML snippet -->
<div id="list" data-win-control="WinJS.UI.ListView"></div>
<div id="template" data-win-control="WinJS.Binding.Template">
    <div class="grid">
        <div data-win-bind="innerText:this"></div>
    </div>
</div>
<div id="headerTemplate" data-win-control="WinJS.Binding.Template">
    <h2 data-win-bind="innerText:this"></h2>
</div>

/* CSS snippet */
.win-listview {
    height: 100%;
}

.win-listview .win-item .grid {
    display:-ms-grid;
    -ms-grid-rows:160px;
    -ms-grid-columns:160px;
    background-color:gray;
    color:white;
}

.win-listview .win-item .grid div {
    font-size: 36px;
    font-weight: bold;
    -ms-grid-column-align: center;
    -ms-grid-row-align: center;
}

// JavaScript snippet
var numbersList = new WinJS.Binding.List().createGrouped(
    function(n) { return (n % 2 == 0 ? "even" : "odd"); },
    function(n) { return (n % 2 == 0 ? "even" : "odd"); }
) ;

var list = document.querySelector("#list").winControl;
list.itemTemplate = document.querySelector("#template");
list.itemDataSource = numbersList.dataSource;
list.itemHeaderTemplate = document.querySelector(“.lst0714 #headerTemplate”);
list.groupDataSource = numbersList.groups.dataSource;

for (var i = 1; i <= 11; i++) {
    numbersList.push(i);
}

正如你在图 7-20 中看到的,偶数和奇数编号的项目被分成不同的组,我们在标题模板中定义的h2元素宣布哪个是哪个。选择了几个项目来产生效果。

9781430249832_Fig07-20.jpg

图 7-20 。ListView 项目按偶数和奇数分组

到目前为止,我们已经看到了两个列表视图,它们的条目大小是对称的,但是也可以允许条目扩展,只要它们的宽度和高度都是最小平铺尺寸的倍数(加上边距)。清单 7-15 中的和清单 7-16 中的的图块宽和高均为 160 像素。一个更大的拼贴(宽度和高度都扩展了)将是 330 个像素,即 160 乘以 2 加上 10 个像素的边距。我们不仅要将一些项目设置为新的大小,还必须在列表的布局中添加一个groupInfo函数。清单 7-17 完全实现了一个不对称的列表视图。

清单 7-17 。ListView 被升级为允许单元格跨越

<!-- HTML snippet -->
<div id="list" data-win-control="WinJS.UI.ListView"></div>
<div id="template" data-win-control="WinJS.Binding.Template">
    <divdata-win-bind="className:size;" >
        <div data-win-bind="innerText:number" class="number"></div>
    </div>
</div>

/* CSS snippet */
.win-listview {
    height: 100%;
}

.normal {
    display:-ms-grid;
    -ms-grid-rows:160px;
    -ms-grid-columns:160px;
    background-color:gray;
    color:white;
}

.oversized {
    display:-ms-grid;
    -ms-grid-rows:330px;
    -ms-grid-columns:330px;
    background-color:gray;
    color:white;
}

.number {
    font-size: 36px;
    font-weight: bold;
    -ms-grid-column-align: center;
    -ms-grid-row-align: center;
}

// JavaScript snippet
var numbersList = new WinJS.Binding.List();

var list = document.querySelector(".lst0715 #list").winControl;
list.itemTemplate = document.querySelector(".lst0715 #template");
list.itemDataSource = numbersList.dataSource;
list.layout.groupInfo = function () {
    return { enableCellSpanning: true, cellWidth: 160, cellHeight: 160 };
};

for (var i = 1; i <= 11; i++) {
    numbersList.push({ number: i, size:calculateSize(i) });
}
function calculateSize(n) { return (n == 1 ? "oversized" : "normal"); }

注意在图 7-21 中,第二个和第三个瓦片被第一个替换了,但是所有的槽都被填满了。如果不同大小的项目以错误的顺序添加到 ListView 中,布局中可能会出现缺口。可能有必要添加一些逻辑来确保根据项目大小添加项目的顺序。

9781430249832_Fig07-21.jpg

图 7-21 。ListView 项目是不对称的,第一个项目比其他项目大

滚动控制

当一个元素的内容溢出该元素的边界,并且该元素的overflow属性被设置为autoscroll时,滚动条将被呈现,用户将能够使用鼠标或触摸来滚动。这是一个解决内容多于屏幕的老办法。

传统上,对容器如何滚动的大部分控制都留给了用户代理,但一些特定于微软供应商的属性给了应用程序作者一些发言权。我们将对滚动限制、轨道、捕捉点以及一个叫做链接的行为有发言权。我们现在来看一下每一项。

限制

容器滚动的距离由scrollLeft DOM 属性控制,可以设置该属性的最小值和最大值,从而通过设置-ms-scroll-limit-*属性来约束容器的滚动。

限制属性

让我们看看一些极限性质。

-ms-scroll-limit-x-min 和-ms-scroll-limit-y-min

为这些属性指定一个长度值,以分别约束容器在水平和垂直方向上的最小滚动距离。例如,100px-ms-scroll-limit-x-min值将导致容器从已经滚动了 100 个像素的地方开始,并且不可能从那里向左滚动。

-ms-scroll-limit-x-max 和-ms-scroll-limit-y-max

最大值决定了用户可以在容器内从内容开始滚动多远。一个500px-ms-scroll-limit-x-max值将使得从容器内容的开始滚动超过 500 像素成为不可能。

-毫秒滚动限制

该速记属性可用于一次性设置-ms-scroll-limit-x-min-ms-scroll-limit-y-min-ms-scroll-limit-x-max-ms-scroll-limit-y-max

我通常建议尽可能使用速记属性,因为 CSS 有时会变得非常长,使用速记属性是一个更简洁的机会。

轨道

滚动属性还有助于控制微软术语中称为轨道的滚动行为。当滚动处于 rails 模式时,内容滚动的方向被锁定在用户开始滚动的第一个轴上。如果用户开始向下滚动,那么 rails 模式将允许沿垂直轴滚动,但不允许沿水平轴滚动。当 rails 模式关闭时,用户可以自由地向任一方向滚动。

rails 模式之所以存在,是因为用户在浏览网页时通常会阅读一长列垂直文本,并希望保持水平滚动位置不变以保持方向不变。rails 行为试图根据用户的初始滚动方向来预测该约束是否应该生效。如果用户垂直滚动,那么滚动将被锁定到 y 轴。如果用户水平滚动,那么滚动将被锁定到 x 轴。如果用户在任一对角线方向上滚动,那么滚动将是自由形式的,并且用户将被允许在任一轴上滚动。

赋予你的内容轨道的唯一属性是-ms-scroll-rails。数值为nonerailed,默认值为railed

捕捉点

有时,内容完全是线性的,应该从头到尾平滑滚动,无论用户在哪里停止,都应该停止,但其他时候,内容是以数字单位或部分排列的,帮助用户着陆更有意义,以便下一部分的开始与容器的开始对齐。这些点称为捕捉点。

微软已经在其浏览器引擎中实现了捕捉点,尽管它们在 CSS 标准中并不存在。这就是相关属性具有特定于供应商的前缀的原因。

可以用两种方法之一定义捕捉点:强制或近似。

将容器定义为使用强制捕捉点意味着它将始终停在最近的捕捉点。它永远不会停在中间的某个地方。然而,将它定义为使用邻近捕捉点意味着,如果它足够接近捕捉点,那么它会找到到达捕捉点的路径,但是如果它不够接近,那么它会让步,在点之间停止。

捕捉点属性

让我们看看一些捕捉点属性。

-ms-scroll-snap-points-x 和-ms-scroll-snap-points-y

使用两个函数之一定义捕捉点:snapInterval()snapList()

snapInterval()功能采用起始长度和捕捉长度。传入0px100px将从 0 开始每 100 个像素创建一个捕捉点。当您的内容重复且有规律,并且您可以预测宽度时,请使用snapInterval

当内容不规则时,snapList()函数是更好的选择。您可以手动将捕捉点定义为参数。因此snapList(0px,200px,500px)将在 0、200 和 500 像素处设置捕捉点,即使这些显然不是规则的间隔。

-ms-scroll-snap-type

ms-scroll-snap-type 属性让开发人员有机会在邻近捕捉点和强制捕捉点之间进行选择。如果用户平移动作将在一个捕捉点附近停止,则值proximity将捕捉到该点,而值mandatory将永远不会在两个捕捉点之间停止。

-ms-scroll-snap-x 和-ms-scroll-snap-y

-ms-scroll-snap-x-ms-scroll-snap-y属性是封装了捕捉点和类型的简写属性。

回头看看清单 7-9 ,注意到-ms-scroll-snap-x 捕捉点被用来同时设置点和类型。那个属性是-ms-scroll-snap-x:mandatory snapInterval(0%,80%),它要求平移仅在 flexbox 宽度的 80%的某个增量处停止。

链接

滚动链接是当子容器到达其内容的末尾时,将用户的滚动手势从子容器向上传递到其父容器的行为。例如,如果您在能够水平平移的页面上放置了一个水平平移列表视图,并且用户触摸该列表视图并水平滑动,则该手势将在列表视图级别被识别,并将平移其内容。然而,一旦 ListView 用尽内容并达到其极限,相同的滑动手势将继续平移整个页面。-ms-scroll-chaining属性是负责这一行为的属性。none的默认值不会将滚动手势传递给父对象,而是简单地显示一个反弹动画来指示内容的边缘。该值必须改为设置为chained以获得链接行为。

总结

我们已经讨论了很多关于布局的内容。我们看了一些在网络上展示内容的传统方法,然后我们看了一些令人兴奋的现代替代方法。

我们看到的第一个遗留方法是通过表格进行布局。表格非常适合显示表格数据,但不适合布局应用程序。它们之所以不合适,是因为在 HTML 标记中定义它们会使它们变得僵化,难以在运行时操作。我们还研究了在布局的标准div元素上使用显示和定位属性。这是一个体面且相当受欢迎的策略,但它有其痛点,而且现在存在更容易和更具表现力的现代选择。

我们讨论的现代布局技术是 CSS flexbox 和 grid,但我们还通过 WinJS JavaScript 库查看了 Windows 8 应用程序中的一些控件,这些控件称为 ViewBox 和 ListView,为我们做了一些非常有用的布局工作。

我喜欢概括说,flexbox 流动它的孩子,网格放置它的孩子。flexbox 允许您轻松地布置一些元素来创建水平或垂直的内容列表。在列表中,单个项目可以按照设计要求进行打包、对齐和调整大小,以处理它们的空间。网格可以让你创建一个框架,在里面放置孩子非常容易。相对于网格结构形成的任何单元,项目可以放置在网格内的任何位置。

ViewBox 和 ListView——我们探索的两个 WinJS 布局实现——是原生的 Windows 8 控件,不可用于(因为它们已经存在)公共网站的开发。视框响应用户视图状态的变化,例如当用户将他们的图形输入板旋转到纵向时。然后,它将 CSS 转换应用于其内容,以翻译和缩放它们。ListView 是展示数据项列表的终极工具。它有助于项目选择、排序、分组等等。

应用程序的布局是其设计和功能的基础,但 web 开发人员传统上很难执行这样一个简单的任务,如将视觉元素放在他们想要的地方。然而,最新 CSS 标准的 Windows 8 实现提供了一些很棒的功能,肯定会减轻这种痛苦。

在第八章、第九章和第十章中,我们将从更高的层面来看内置 WinJS 库中的样式规则集合和我们自己的自制样式表。我们还会将这些规则应用到我们的应用程序中,并覆盖和扩展它们,使它们成为我们自己的规则。

所以和我一起换挡,让我们继续巡航!

八、全局样式

image 注意WinJS 库提供了大量已经为你编写好的 CSS 实际上差不多有 4000 行。这种风格规则的大集合使你的应用在 Windows 8 中看起来就像在家一样。

在我称之为全局样式的这一章中,我们将看看 WinJS 库中影响你的 Windows 8 应用的一般环境和排版的样式。我们还将了解应用程序栏、设置面板、弹出菜单和菜单的样式。WinJS 控件的样式有很多规则,而第九章正是致力于此。

我强烈推荐两种快速全面学习 Windows 8 CSS 的方法。首先,花时间滚动浏览 ui-light.css 文件,你可以在 Visual Studio 的任何 Windows 8 JavaScript 项目的 References 部分找到该文件。你可以在http://msdn.microsoft.com/en-us/library/windows/apps/hh465498.aspx找到一些非常有用的参考资料。其次,学习本章中各种控件和结构固有的类名。例如,一旦您知道包含设置弹出按钮的元素包含一个类值win-settingsflyout,您就可以使用一个简单的类选择器来确定该元素的样式范围并添加您自己的样式。

排版

字体设计在 Windows 8 应用中起着非常重要的作用。有目的地选择字体和大小来传达关于屏幕上各种内容的相对重要性的信息。用户很快就能下意识地识别出定位一致的大标题,而逐渐变小的标题易于定位和关联。

来源

主要的 Windows 字体是 Segoe UI(读作 SEE-go)。这是一个干净的,现代的,无衬线字体,支持拉丁语,西里尔语,希腊语,阿拉伯语,希伯来语,亚美尼亚语和格鲁吉亚语,所以它是超级通用的。Windows 8 设计指南并不严格要求您的应用程序使用 Segoe UI 字体。这是一个很好的选择,因为它给你的应用带来熟悉的感觉,帮助用户感觉它是一个有凝聚力的平台的一部分。然而,如果你的品牌有一个标准的字体,人们可能认为这是你品牌的一个特征,那么这可能是一个更好的选择。

Calibri 字体也是 Windows 8 应用程序的推荐字体。与 Segoe UI 相比,它更像是一种阅读或书写字体,并且支持拉丁语、希腊语和西里尔字母。Calibri 适用于电子邮件和聊天应用。最后,Cambria 字体用于阅读较大的文本块,你可以在图书阅读器或类似设备中找到。在http://msdn.microsoft.com/en-us/library/windows/apps/hh700394.aspx可以找到关于 Windows 8 排版指南的更多内容。

WinJS 样式表做的第一件事就是定义(使用@font-face,就像我们在第四章中学到的那样)许多围绕 Segoe UI 的字体系列值。它们都是相同的字体,但是它们使用不同的字体粗细值。字体粗细为:

  • Segoe UI Light - 200 重量
  • Segoe UI 半轻型- 300 重量
  • Segoe UI - 400(正常)重量
  • Segoe UI Semibold - 600 重量
  • Segoe UI Bold - 700 重量

要使用这些,你只需要指定一个 CSS font-family属性和定义好的字体族名称(比如 Segoe UI Light,Segoe UI 等)。).

类型样式

类型样式是由 WinJS 定义的样式规则,会影响应用程序中的文本。我们在第一章中了解到,type ramp 是一组标准的字体大小,为 Windows 8 应用程序提供了其特有的可识别外观。从最大到最小的类型渐变为 42 点、20 点、11 点和 9 点。这是一个相当宽的字体大小范围,其原因是为了在屏幕上提供文本内容的快速视觉层次。看到 42 磅的标题和 20 磅的子标题会使内容自己说明其结构和相对重要性。

你可以在图 8-1 中看到这种斜坡的一个典型例子。请注意,识别应用程序的标题、区分各个部分以及阅读标题和正文内容是多么容易。还要注意,这是通过文字斜坡和一些空间的战略性使用实现的。我们的布局不需要任何线条或方框来分隔应用程序的各个部分。

9781430249832_Fig08-01.jpg

图 8-1 。Windows 8 的文字渐变使得辨别内容的相对重要性变得容易

图 8-1 中的示例应用程序屏幕使用了标准的 Windows 8 light 主题(ui-light.css)。这个主题是白色背景和黑色文字的原因。与之形成鲜明对比的还有一个暗黑主题(ui-dark.css)。图 8-2 显示了这次使用黑暗主题的相同视图。应用程序开发者可以选择使用哪个主题,这是一个应用程序一个应用程序的决定。它不受操作系统级别设置的控制。黑暗主题中的斜坡字体显然在传达结构方面也同样有效。

9781430249832_Fig08-02.jpg

图 8-2 。与图 8-1 相同的视图,这次使用了 ui-dark 主题

42 点的页面标题相对较大,使用户能够快速轻松地定位当前页面。这可能看起来不重要,但非常重要。当用户从一个应用程序切换到另一个应用程序,抓取旁边的应用程序,并启动新的应用程序时,无意识地瞥一眼屏幕顶部可以缓解应用程序的眩晕。

20 点的部分标题将很快成为用户熟悉的组织指示器,他们会寻找它们来查看应用程序是如何分类或细分的。用户将看到这些部分从屏幕右侧溢出,自然地滑动以查看更多内容。

11 点和 9 点的副标题和正文当然是可读的,但它也明显不如页面标题和章节标题重要。

清单 8-1 定义了所有六个级别的标题文本以及段落文本,图 8-3 显示了渲染结果。请注意,标题 3、标题 4、标题 5 和标准段落文本都是相同的磅值(11),只有粗细不同。

清单 8-1 。 六个表头层次和一个段落

<!-- HTML snippet -->
<h1>Heading 1</h1>
<h2>Heading 2</h2>
<h3>Heading 3</h3>
<h4>Heading 4</h4>
<h5>Heading 5</h5>
<h6>Heading 6</h6>
<p>Paragraph text</p>

9781430249832_Fig08-03.jpg

图 8-3 。根据 WinJS 样式表呈现的结果

只有在 HTML 中定义了标题标签(h1h2h3h4h5h6)时,每种标题样式才适用,但是每种样式都有一个相应的类,可以添加到您想要的任何元素中。

h1的阶级对应是win-type-xx-largeh2的是win-type-x-largeh3的是win-type-largeh4的是win-type-mediumh5的是win-type-smallh6的是win-type-xx-small。还有一个用于win-type-x-small的样式,尽管它没有映射到标题级别。要使用这些样式中的一种,您只需将正确的类添加到您试图影响的任何元素中。

当您想要匹配文字渐变但不明确使用标题标签时,您可以选择使用样式。标题标签有某些你可能不总是想要的行为,最大的是它是一个块级元素。清单 8-2 提供了一个标准按钮(用于比较),后面是一个被赋予了win-type-x-large类的按钮,以有效地赋予它一个h2的外观。图 8-4 中第二个按钮的较大文字很明显。在改变任何默认元素的样式之前,你应该三思而行,只有在为了支持你的应用程序的整体设计而有意改变时,才应该向前推进。

清单 8-2 。 在一个标准的 HTML 按钮上定义了 win-type-x-large 类

<!-- HTML snippet -->
<div><button>Button 1</button></div>
<div><button class="win-type-x-large">Button 2</button></div>

/* CSS snippet */
.lst0802 button {
    margin: 5px;

}

9781430249832_Fig08-04.jpg

图 8-4 。结果是一个样式像 h2 元素的按钮

还有两个赢型风格规则,分别是win-type-ellipsiswin-type-interactive

win-type-ellipsis定义包含值为省略号(“...”)的文本溢出属性),当文本不适合其容器时,它会呈现一个省略号字符。

win-type-interactive样式规则非常有助于使文本呈现为链接,即使它实际上不是链接,这在遵循标准单页应用导航模式的 Windows 8 应用中很常见。

WinJS 也为用户选择的文本提供了标准样式。使用::selection伪元素识别所选文本。例如,在 light 主题中,所选文本的背景颜色是蓝色,文本颜色是白色。

请记住,基于类的样式规则可以与一个元素结合使用,因为可以在一个元素上指定多个类。在清单 8-3 中,段落中间的一段文本被赋予了多个类,以使其像 h3 一样呈现并具有交互性。在图 8-5 中,你可以看到简单地将这个类添加到一个 span 中的效果。

清单 8-3 。 两个类被应用于围绕某些段落文本的跨度

<!-- HTML snippet -->

<p>Lorem ipsum dolor sit amet, <spanclass = " win-type-x-large win-type-interactive "

9781430249832_Fig08-05.jpg

图 8-5 。由于 win-type-interactive 类的缘故,文本的跨度呈现得比其余部分大,并且将提供悬停反馈

抓拍视图

当用户在使用另一个应用程序时,如果他希望保持该应用程序可用(尽管是较小的格式),他会对该应用程序进行快照。对于用户来说,在使用 Windows 8 时获得次要任务的简洁垂直视图是一个很好的功能。对于开发人员来说,这是一个设计考虑,也是一个必要的考虑,因为没有办法配置您的应用程序来禁止捕捉。当你的应用被抓取时,会有很多变化;最值得注意的是,可用的水平像素从至少 1024 降低到只有 320。毫无疑问,每个应用程序都可以被抓拍,所以你必须考虑到这种情况,即使只是通知用户应用程序在被抓拍之前无法正常运行。

至少 WinJS 为您完成了一些为快照视图转换应用程序的工作。WinJS 中的 CSS 文件定义了一个媒体查询,以检测您的应用程序是否处于快照视图中,并设置各种样式属性来保持您的应用程序的可用性和设计。

图 8-6 和 8-7 显示了一个全屏状态和快照状态的样本 app。在这种情况下,没有规定在抓取时调整应用程序,所以它本质上只是显示全屏应用程序的左侧 320 像素。首先,这绝对是您应该避免交付到 Windows 应用商店的行为。

9781430249832_Fig08-06.jpg

图 8-6 。全屏显示的示例应用程序

9781430249832_Fig08-07.jpg

图 8-7 。同一个应用程序捕捉到了屏幕的左侧,说明了全屏视图状态和捕捉视图状态之间的内在风格差异

但是,您可以看到,全视图和快照视图中最左侧的 320 像素之间存在一些差异。应用程序标题更小,因为h1元素和win-type-xx-large类被更改为使用 20 磅的字体,而不是正常的 42 磅。部分标题(第一部分和第二部分)也变小了,因为h2元素和win-type-x-large类的样式规则被改为使用 11 磅的字体,而不是普通的 20 磅。后退按钮也更小。所有这些都是 WinJS 内置的功能。

显然,这个应用程序并不完全支持快照视图,但随着更多的媒体查询和风格属性,它可以。

App bar

Windows 8 应用有可选的应用栏。开发人员可以选择定义一个可以从屏幕底部向上滑动的下部应用程序栏或一个可以从顶部向下滑动的上部应用程序栏。这两个应用程序栏显然也可以定义。用户只需简单的滑动、鼠标右键单击或键盘快捷键(Win + Z)就可以访问这两个选项。

记得我在第一章中提到过,好的 Windows 8 设计的核心原则是让用户沉浸其中,消除干扰。这就是为什么应用程序栏是放置命令的好地方,因为它们不会分散用户对内容的注意力,而且在用户选择之前,它们不需要有价值的设计界面。

图 8-8 显示了必应地图应用 ,显示了下方的应用栏。这个下方的应用程序栏应该用于命令。命令是应用程序用户与应用程序交互或发布命令的一种方式。最常见的命令是按钮,应该符合推荐的设计——带有简单图标的圆形按钮。如果该命令可以用一个默认的 Windows 8 图标来表示,那么它应该是。仅当无法使用内置集合中的图标表示时,才创建自定义图标。

9781430249832_Fig08-08.jpg

图 8-8 。除非用户显示下面的应用程序栏,否则 Bing 地图应用程序只会显示一张地图。这就是用户沉浸在内容中的含义

除了下面的应用程序栏用于命令之外,上面的应用程序栏可供开发者使用,应该主要用于某种形式的导航。在图 8-9 中,可以看到 Internet Explorer 10 应用(Windows Store 版本),可以看到上面的应用栏是用来显示浏览器选项卡缩略图的。

9781430249832_Fig08-09.jpg

图 8-9 。Internet Explorer 实现了一个上层和一个下层的应用程序栏

图 8-10 展示了必应团队打造的预装在 Windows 8 中的运动 app 。在这个应用程序中,下方和上方的应用程序栏都在使用中。上部用于导航到各种可用的运动,而下部的应用程序栏可用于刷新运动数据。

9781430249832_Fig08-10.jpg

图 8-10 。Bing Sports 应用程序的上部应用程序栏会将用户导航到不同的体育赛事

应用程序栏在不同的应用程序中具有统一的外观和功能是很重要的,所以你会发现你通常不需要在应用程序栏中添加样式规则。通常情况下,它们的默认样式就足够了。

在一本关于使用 CSS 样式化应用程序的书中,我们真正想做的是理解应用程序栏的一般结构,以及它是用什么类来修饰的,这样我们就有希望通过添加或覆盖样式来定制它。为了做到这一点,我们将查看实现应用程序栏的完整代码,然后我们将看到生成的 DOM 是什么样子,并突出显示重要的部分(参见图 8-11 )。

})();

9781430249832_Fig08-11.jpg

图 8-11 。应用程序栏仅由声明性标记创建,具有 Windows 8 应用程序栏的所有个性

在清单 8-4 中,我们声明了一个带有命令按钮的应用程序栏(粗体)。这些元素的声明很简单,但它会产生一个健壮的控件,该控件由许多在 DOM 中构建的本地 HTML 元素组成。

清单 8-4 。 在 Windows 8 应用中全面实现应用栏

<!-- lst0804.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Listing 8-4</title>

<!-- WinJS references -->
<link href="//Microsoft.WinJS.1.0/css/ui-light.css" rel="stylesheet" />
<script src="//Microsoft.WinJS.1.0/js/base.js"></script>
<script src="//Microsoft.WinJS.1.0/js/ui.js"></script>

<link href="lst0804.css" rel="stylesheet" />
<script src="lst0804.js"></script>
</head>
<body>
<div class="lst0804 fragment">
<section aria-label="Main content" role="main">

</section>
<div id="myAppBar" data-win-control="WinJS.UI.AppBar">
<button
                data-win-control="WinJS.UI.AppBarCommand"
                data-win-options="{ id:'cmdAdd', label:'Add', icon:'add', section:'global',
                    tooltip:'Add item' }"></button>
<button
                data-win-control="WinJS.UI.AppBarCommand"
                data-win-options="{ id:'cmdDelete', label:'Delete', icon:'delete',
                    section:'global', tooltip:'Delete item' }"></button>
<button
                data-win-control="WinJS.UI.AppBarCommand"
                data-win-options="{ id:'cmdCamera', label:'Camera', icon:'camera',
                    section:'selection', tooltip:'Take a picture' }"></button>
</div>
</div>
</body>
</html>

/* lst0804.css */
(empty)

// lst0804.js
(function () {
    "use strict";

    WinJS.UI.Pages.define("/pages/chapter8/lst0804/lst0804.html", {
        ready: function (element, options) {
            //empty
        }
    });

注意,CSS 和 JavaScript 文件本质上是空的。应用程序栏及其按钮可以完全以声明方式创建。我已经包括了.css.js文件,只是为了让你知道没有奇迹发生。

清单 8-4 的中的lst0804.html文件将定义我们的应用程序栏,但它并不代表应用程序运行时最终出现在 DOM 中的实际 HTML。这是因为 WinJS 处理控件的方式。通过向标准 HTML 元素添加一个data-win-control属性来创建一个控件。然后,当执行WinJS.UI.processAll函数时,所有这些声明的控件都被转换成真实的交易。在这个过程中,类名被添加到每个元素中,在许多情况下是一些内联样式。

为了更好地理解这个过程中发生了什么,让我们在应用程序运行时查看一下 DOM Explorer,看看真实的 DOM。前一个例子中的应用程序栏产生了清单 8-5 中的代码。

清单 8-5 。DOM 来自我们创建的应用程序栏

<!-- HTML snippet from DOM Explorer -->
<divclass="win-overlay win-appbar win-commandlayout win-bottom" id="myAppBar" role="menubar"
style="visibility: hidden; opacity: 0;" aria-label="App Bar"
    data-win-control="WinJS.UI.AppBar" unselectable="on">
<buttonclass="win-command win-global" id="cmdAdd" role="menuitem" aria-label="Add"
        type="button" data-win-options="{ id:'cmdAdd', label:'Add', icon:'add',
        section:'global', tooltip:'Add item' }"
        data-win-control="WinJS.UI.AppBarCommand">
<span tabindex="-1"class="win-commandicon win-commandring" aria-hidden="true">
<span tabindex="-1"class="win-commandimage" aria-hidden="true"> </span>
</span>
<span tabindex="-1"class="win-label" aria-hidden="true">Add</span>
</button>
<buttonclass="win-command win-global" id="cmdDelete" role="menuitem" aria-label="Delete"
        type="button" data-win-options="{ id:'cmdDelete', label:'Delete', icon:'delete',
        section:'global', tooltip:'Delete item' }"
        data-win-control="WinJS.UI.AppBarCommand">
<span tabindex="-1"class="win-commandicon win-commandring" aria-hidden="true">
<span tabindex="-1"class="win-commandimage" aria-hidden="true"></span>
</span>
<span tabindex="-1"class="win-label" aria-hidden="true">Delete</span>
</button>
<buttonclass="win-command win-selection" id="cmdCamera" role="menuitem" aria-label="Camera"
        type="button" data-win-options="{ id:'cmdCamera', label:'Camera', icon:'camera',
        section:'selection', tooltip:'Take a picture' }"
        data-win-control="WinJS.UI.AppBarCommand">
<span tabindex="-1"class="win-commandicon win-commandring" aria-hidden="true">
<span tabindex="-1"class="win-commandimage" aria-hidden="true"> </span>
</span>
<span tabindex="-1"class="win-label" aria-hidden="true">Camera</span>
</button>
</div>

清单 8-5 中粗体显示的内容是已经添加到 DOM 元素中的类属性。这些类名充当我们选择控件不同部分的句柄,并为它们添加或覆盖样式规则。

app bar 本身(清单 8-5 中的最高层div)被赋予了四个类名:win-overlaywin-appbarwin-commandlayoutwin-bottom。这些类中的每一个都将 WinJS CSS 中定义的样式规则应用于应用程序栏,并且每一个类都有不同的用途;这就是为什么有四个类,而不是只有一个。例如,开发人员可以选择将应用程序栏的placemen t 选项设置为top以将其呈现在屏幕顶部,在这种情况下,win-bottom类将被省略并替换为win-top

同样,我们的目标是理解 HTML 的结构,这样我们就可以添加或覆盖影响它的样式。让我们举一个例子,并把它进行到底。我们在应用程序栏上声明的按钮都被赋予了内容,使它们看起来像一个正确的应用程序栏按钮——一个下面有图标和标签的圆圈。这是使用定义了类的 span 元素实现的。环被赋予 win-commandicon 和 win-commandring 类,下面的标签是具有 win-label 类的 span。想象一下,我们的项目需求规定,当用户将鼠标悬停在应用程序栏按钮上时,按钮的边框应该是灰色的,而不是标准的白色。

首先,让我们通过查看 ui-light.css 的第 2511–2599 行来看看按钮是如何获得它们的白色边框的(清单 8-6 )。请特别注意粗体部分。该样式规则影响具有类win-commandring的元素,该类是当前被悬停的按钮的子元素。这正是我们想要推翻的。

清单 8-6 。 从 ui-light.css (第 2511–2599 行)中,我们可以看到当用户将鼠标悬停在命令按钮上时,命令按钮周围的圆圈所获得的样式

/*
Command ring colors.
*/
...
.win-commandring, button:active .win-commandring {
    background-color: transparent;
    border-color: rgb(0, 0, 0);
}
button:hover .win-commandring {
    background-color: rgba(0, 0, 0, 0.13);
    border-color: rgb(0, 0, 0);
}
button:hover:active .win-commandring {
    background-color: rgb(0, 0, 0);
    border-color: rgb(0, 0, 0);
}
button:-ms-keyboard-active .win-commandring {
    background-color: rgb(0, 0, 0);
    border-color: rgb(0, 0, 0);
}
button:disabled .win-commandring,
button:disabled:active .win-commandring {
    background-color: transparent;
    border-color: rgba(0, 0, 0, 0.4);
}
button[aria-checked=true] .win-commandring,
button[aria-checked=true]:active .win-commandring {
    background-color: rgb(0, 0, 0);
    border-color: rgb(0, 0, 0);
}
button[aria-checked=true]:hover .win-commandring {
    background-color: rgb(33, 33, 33);
    border-color: rgb(33, 33, 33);
}
button[aria-checked=true]:hover:active .win-commandring {
    background-color: transparent;
    border-color: rgb(0, 0, 0);
}
button[aria-checked=true]:-ms-keyboard-active .win-commandring {
    background-color: transparent;
    border-color: rgb(0, 0, 0);
}
button[aria-checked=true]:disabled .win-commandring,
button[aria-checked=true]:disabled:active .win-commandring {
    background-color: rgba(0, 0, 0, 0.4);
    border-color: rgba(0, 0, 0, 0.4);
}

虽然这与我们当前修改边框颜色的需求无关,但是请注意,当背景色被悬停时,它的不透明度值为 0.13。这使得只有当用户悬停在按钮上时,按钮才不完全透明。

边框颜色是我们想要影响的,在这种情况下,边框颜色在悬停状态下和正常状态下是完全一样的。我们不能修改ui-light.css文件,但是我们可以覆盖正在讨论的样式规则。如果我们将清单 8-7 中的样式规则添加到我们自己页面的 CSS 文件中,那么我们将得到我们想要的效果。图 8-12 显示了带有灰色边框的效果。

清单 8-7 。 对悬停状态的边框颜色进行轻微的覆盖样式改变

button:hover .win-commandring {
    background-color: rgba(0, 0, 0, 0.13);
    border-color: gray;

}

9781430249832_Fig08-12.jpg

图 8-12 。将鼠标悬停在命令按钮上,它周围的圆形边框会变成灰色

这里要注意的是,没有什么是对开发者隐藏的。用于创建 Windows 8 应用程序的所有样式属性都可以浏览、附加和覆盖。

设置窗格

像应用程序栏一样,Windows 8 应用程序中的设置面板对用户来说是可预测的。从屏幕右侧向外滑动符咒并选择设置符咒(参见图 8-13 )即可访问。从主设置面板中选取一个设置类别会在屏幕右侧弹出一个特定的设置面板。这个特定的设置面板是我们将要使用 WinJS 定制的。UI . SettingsFlyout 弹出控件。

9781430249832_Fig08-13.jpg

图 8-13 。设置符总是作为齿轮图标出现在符条的底部

与应用程序栏不同,设置面板是一个白板,开发者需要设计和实现它,所以你有很大的自由,但你不是没有帮助。有许多样式会影响设置面板,并使其具有标准的 Windows 8 设计。我将在这里强调这些。

考虑一下清单 8-8 中的 HTML 片段和图 8-14 中产生的设置窗格。

清单 8-8 。 完成一个设置弹出按钮的定义

<!-- lst0808.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Listing 8-8</title>

<!-- WinJS references -->
<link href="//Microsoft.WinJS.1.0/css/ui-light.css" rel="stylesheet" />
<script src="//Microsoft.WinJS.1.0/js/base.js"></script>
<script src="//Microsoft.WinJS.1.0/js/ui.js"></script>

<link href="lst0808.css" rel="stylesheet" />
<script src="lst0808.js"></script>
</head>
<body>
<div class="lst0808 fragment">
<section aria-label="Main content" role="main">

<p>Open the Settings charm and choose Listing 8-8 to see the resulting settings pane</p>

<!-- BEGINSETTINGSFLYOUT -->
<div id="sampleSettings" data-win-control="WinJS.UI.SettingsFlyout"
            aria-label="App Settings Flyout">
<div class="win-ui-dark win-header">
<button type="button" onclick="WinJS.UI.SettingsFlyout.show()"
                    class="win-backbutton"></button>
<div class="win-label">Preferences</div>
</div>
<div class="win-content">
<div class="win-settings-section">
<h3>Toggle switch</h3>
<p>Use toggle switches to let users set Boolean values.</p>
<div id="Toggle1" data-win-control="WinJS.UI.ToggleSwitch"
                        data-win-options="{title:'Download updates automatically',
                            checked:true}">
</div>
<div id="Toggle2" data-win-control="WinJS.UI.ToggleSwitch"
                        data-win-options="{title:'Install updates automatically'}">
</div>
</div>
</div>
</div>
<!-- ENDSETTINGSFLYOUT -->
</section>
</div>
</body>
</html>
/* lst0808.css */
// lst0808.js
(function () {
    "use strict";
    WinJS.UI.Pages.define("/pages/chapter8/lst0808/lst0808.html", {
        ready: function (element, options) {
            addSettingsContract()
        }
    });
    function addSettingsContract() {
        app.onsettings = function (e) {
            e.detail.applicationcommands = {
                "sampleSettings": {
                    title: "Listing 8-8",
                    href: "/pages/chapter8/lst0808/lst0808.html"
                }
            };
            WinJS.UI.SettingsFlyout.populateSettings(e);
        };
    }

})();

9781430249832_Fig08-14.jpg

图 8-14 。“设置”弹出按钮可从“设置”窗格中找到

这个设置面板的样式规则已经存在,我们只需通过编写结构,然后用正确的 CSS 类值装饰元素来应用它们。

我们将再次使用 DOM explorer 查看清单 8-9 中的结果 DOM。WinJS 添加的类和样式以粗体显示。同样以粗体显示的是添加的两个div元素(win-firstdivwin-finaldiv),尽管我们现在不太关心它们,因为它们与控件的样式没有太大关系

清单 8-9 。 这是设置弹出按钮渲染到动态 DOM 后的样子

<!-- HTML snippet from DOM Explorer -->
<divclass="win-overlay win-settingsflyout" id="Div1" role="dialog" aria-hidden="true"
style="left: auto; right: -346px; display: none; visibility: hidden; opacity: 1;"
    aria-label="App Settings Flyout" data-win-control="WinJS.UI.SettingsFlyout"
    unselectable="on">
<div tabindex="0" class="win-firstdiv" role="menuitem"
        aria-hidden="true" style="display: inline;"></div>
<div class="win-ui-dark win-header">
<button class="win-backbutton" aria-label="Back"
            onclick="WinJS.UI.SettingsFlyout.show()" type="button"></button>
<div class="win-label">Preferences</div>
</div>
<div class="win-content win-ui-light" style="opacity: 1;">
<div class="win-settings-section">
<h3>Toggle switch</h3>
<p>Use toggle switches to let users set Boolean values.</p>

            ...

</div>
</div>
<div tabindex="0" class="win-finaldiv" role="menuitem" aria-hidden="true"
        style="display: inline;"></div>
</div>

包含后退按钮和设置标题的div元素用win-ui-darkwin-header类值修饰,background-color使用我们必须在 CSS 片段中编写的唯一样式规则设置。back 按钮是一个带有 win-backbutton 类的标准 HTML 按钮,它与为页面声明 back 按钮的方式相同。然而,这两个后退按钮是不一样的,CSS 辨别的方式是使用带有选择器.win-settingsflyout .win-backbutton的样式规则(同样在 ui-light.css 文档中)。从.win-settingsflyout的出现可以看出,这种样式规则将仅限于设置弹出按钮中的后退按钮。

弹出按钮和菜单

弹出菜单和菜单是由 WinJS 库提供的,是更加全局的应用程序级控件,所以我现在将介绍它们的样式。

弹出菜单和菜单密切相关,因为它们具有相似的行为。弹出菜单和菜单都显示为覆盖在现有的设计图面上(很像大多数编程框架中的对话框)。它们也是轻消除的,这意味着用户可以通过触摸或点击外部的任何地方来消除它们。

弹出菜单让你可以自由控制内容的格式,而菜单更加结构化,更容易创建一致的菜单系统。你可以在http://msdn.microsoft.com/en-us/library/windows/apps/hh465325.aspx找到更多关于弹出菜单和菜单的信息。

弹出型按钮

弹出按钮只是一个 HTML 容器,所以你可以在里面随意创建任何你想要的东西。弹出型按钮是一种很好的提示用户输入的方式,它在用户启动动作的地方显示一个小的输入面板。例如,你可以通过提供一个添加用户按钮来提示用户添加一个新项目,然后当用户触摸它时,在按钮附近显示一个弹出按钮,提示他们输入名称。清单 8-10 和图 8-15 显示了一个添加用户按钮,该按钮显示了一个弹出按钮(位于按钮的右侧)。当用户触摸它时,会执行一个显示弹出按钮的事件。同样,我们将看到实现按钮和弹出按钮的完整示例,以及由 WinJS 创建的结果 DOM。

清单 8-10 。 一个弹出型实现的完整例子

<!-- lst0810.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Listing 8-10</title>

<!-- WinJS references -->
<link href="//Microsoft.WinJS.1.0/css/ui-light.css" rel="stylesheet" />
<script src="//Microsoft.WinJS.1.0/js/base.js"></script>
<script src="//Microsoft.WinJS.1.0/js/ui.js"></script>

<link href="lst0810.css" rel="stylesheet" />
<script src="lst0810.js"></script>
</head>
<body>
<div class="lst0810 fragment">
<section aria-label="Main content" role="main">
<button id="showFlyoutButton">Add user</button>

<div id="flyout1" data-win-control="WinJS.UI.Flyout"
                data-win-options="{placement: 'right'}">
<label for="input">Name</label>
<input type="text" />
</div>
</section>
</div>
</body>
</html>

/* lst0810.css */
(empty)

// lst0810.js
(function () {
    "use strict";

    WinJS.UI.Pages.define("/pages/chapter8/lst0810/lst0810.html", {
        ready: function (element, options) {
            var showFlyoutButton = element.querySelector("#showFlyoutButton");
            var flyout = element.querySelector("#flyout1");
            showFlyoutButton.onclick = function(e) {
                flyout.winControl.show(showFlyoutButton);
            }
        }
    });

})();

9781430249832_Fig08-15.jpg

图 8-15 。提示用户输入用户名的弹出控件

清单 8-11 是添加了 WinJS 添加的类和内联样式的结果 DOM,以粗体显示

清单 8-11 。 生成的 DOM 为弹出型控件

<divclass="win-overlay win-flyout win-ui-light" id="Div1" role="dialog"
    aria-hidden="true"style="left: 210px; top: 146.5px; right: auto;
        bottom: auto; display: none; visibility: hidden; opacity: 0;"
        aria-label="Flyout" data-win-options="{placement: 'right'}"
    data-win-control="WinJS.UI.Flyout" unselectable="on">
<div tabindex="0" class="win-firstdiv" role="menuitem" aria-hidden="true"
        style="display: inline;"></div>
<label for="input">Name</label>
<input class="win-hidefocus" type="text">
<div tabindex="0" class="win-finaldiv" role="menuitem" aria-hidden="true"
        style="display: inline;"></div>
</div>

像所有其他控件一样,弹出控件是使用标准 HTML 元素实例化的——通常是一个div元素。当然,您可以使用您给它的 ID 来引用您创建的任何弹出按钮,但是如果您想引用应用程序中的每个弹出按钮呢?如果您知道每个弹出控件在结果 DOM 中都有一个win-flyout类,那么您就知道可以通过使用类选择器(.win-flyout)来引用应用程序中的所有弹出控件(或者在您选择的任何给定范围内)。然后,清单 8-12 中的 CSS 将你的应用程序中的弹出控件设置为灰色背景和细的深蓝色边框。

清单 8-12 。 影响所有带有灰色背景和深蓝色边框的弹出按钮

/* CSS snippet */
.win-flyout {
    background-color: gray;
    border: 1px solid darkblue;
}

菜单

菜单由一个或多个 MenuCommand 对象组成。清单 8-13 和图 8-16 包括了一个带有标题菜单的 Windows 8 页面的完整例子。标题菜单是一种推荐的设计,用于将用户从应用程序的一部分快速导航到另一部分。页面标题旁边有一个小的人字形符号,它给用户提示标题是一个菜单,可以通过触摸来调用标题菜单。

清单 8-13 。 一个 WinJS。菜单在 HTML 中声明,并添加 JavaScript 以在用户单击 时显示它

<!-- lst0813.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Listing 8-13</title>

<!-- WinJS references -->
<link href="//Microsoft.WinJS.1.0/css/ui-light.css" rel="stylesheet" />
<script src="//Microsoft.WinJS.1.0/js/base.js"></script>
<script src="//Microsoft.WinJS.1.0/js/ui.js"></script>

<link href="lst0813.css" rel="stylesheet" />
<script src="lst0813.js"></script>
</head>
<body>
<div class="lst0813 fragment">
<section aria-label="Main content" role="main">
<h1 class="menu win-type-ellipsis">
<span class="title">Header Menu</span>
<span class="chevron win-type-x-large">&#xe099</span>
</h1>

<div id="headerMenu" data-win-control="WinJS.UI.Menu">
<button data-win-control="WinJS.UI.MenuCommand"
                    data-win-options="{id:'s1',label:'Section One'}"></button>
<button data-win-control="WinJS.UI.MenuCommand"
                    data-win-options="{id:'s2',label:'Section Two'}"></button>
<button data-win-control="WinJS.UI.MenuCommand"
                    data-win-options="{id:'s3',label:'Section Three'}"></button>
<hr data-win-control="WinJS.UI.MenuCommand"
                    data-win-options="{id:'separator',type:'separator'}" />
<button data-win-control="WinJS.UI.MenuCommand"
                    data-win-options="{id:'sHome',label:'Home'}"></button>
</div>

</section>
</div>
</body>
</html>

/* lst0813.css */
. lst0813 .chevron {
    vertical-align:8px;
}

// lst0813.js
(function () {
    "use strict";

    WinJS.UI.Pages.define("/pages/chapter8/lst0813/lst0813.html", {
        ready: function (element, options) {
            element.querySelector(".menu").onclick = function () { showHeaderMenu(element); };
        }
    });

    function showHeaderMenu(element) {
        var title = element.querySelector(".menu");
        var menu = element.querySelector("#headerMenu").winControl;
        menu.anchor = title;
        menu.placement = "bottom";
        menu.alignment = "left";

        menu.show();
    }

})();

9781430249832_Fig08-16.jpg

图 8-16 。标题已经变成了操作菜单

既然您已经看到了如何创建这个简单的菜单,那么让我们来看看实际为应用程序呈现的 HTML。这可以使用 DOM Explorer 找到。

从清单 8-14 中的粗体文本可以看出,每个按钮都被赋予了一个类别win-command。这对于我们这些 WinJS 的消费者来说非常方便。UI.Menu 控件,因为这意味着将一种样式应用于所有菜单项将变得足够容易。

清单 8-14 。 结果 DOM 为在清单 8-13 中创建的菜单

<!-- HTML snippet from DOM Explorer -->
<div class="win-overlay win-flyout win-ui-light win-menu " id="headerMenu" role="menu"
style="display: none; visibility: hidden; opacity: 0;" aria-label="Menu"
    data-win-control="WinJS.UI.Menu" unselectable="on">
<buttonclass="win-command" id="s1" role="menuitem" aria-label="Section One"
        type="button" data-win-options="{id:'s1',label:'Section One'}"
        data-win-control="WinJS.UI.MenuCommand">Section One</button>
<buttonclass="win-command" id="s2" role="menuitem" aria-label="Section Two"
        type="button" data-win-options="{id:'s2',label:'Section Two'}"
        data-win-control="WinJS.UI.MenuCommand">Section Two</button>
<buttonclass="win-command" id="s3" role="menuitem" aria-label="Section Three"
        type="button" data-win-options="{id:'s3',label:'Section Three'}"
        data-win-control="WinJS.UI.MenuCommand">Section Three</button>
<hrclass="win-command" id="separator" data-win-options="{id:'separator',type:'separator'}"
        data-win-control="WinJS.UI.MenuCommand">
<buttonclass="win-command" id="sHome" role="menuitem" aria-label="Home" type="button"
        data-win-options="{id:'sHome',label:'Home'}"
        data-win-control="WinJS.UI.MenuCommand">Home</button>
</div>

让我们用清单 8-15 中的粗体样式规则来修改 CSS。

清单 8-15 。 清单 8-13 中的 CSS 覆盖了菜单按钮,使它们变成斜体

/* menus.css */
.lst0813 .chevron {
    vertical-align:8px;
}

.lst0813 .win-command {
    font-style: italic;
}

如你所料,在图 8-17 中可以看到,每个菜单命令都是斜体的。

9781430249832_Fig08-17.jpg

图 8-17 。将样式应用到 win-command 类会影响每个菜单项

高对比度模式

Windows 8 通过提供高对比度模式来适应弱视用户,用户可以从 PC 设置|轻松访问中选择该模式,并期望该选择将应用于其系统上的所有应用程序。

在大多数情况下,如果你使用标准的样式和方法来创建你的应用程序中的视觉元素,你就不需要做任何事情来实现你的应用程序中的高对比度支持。但是,关于高对比度模式下的可见性,您可能有一些自定义的注意事项,并且您应该熟悉为实现这一点而需要覆盖的样式。

通过媒体查询的方式应用高对比度样式。特定于供应商的值-ms-high-contrast 可作为媒体查询的表达式,当用户在其设备上选择高对比度模式时,该值为真。

有高对比度的风格规则来控制你的应用程序的几乎每一个视觉方面。

我将只画一个在高对比度模式下影响应用程序的样式属性的例子,并向您展示如何覆盖它。 ui-light.css 中的第 2848 行开始高对比度模式的主媒体查询,第 3068–3074 行(清单 8-16 )定义了 win-appbar 类的样式。

清单 8-16 。 只是-ms-高对比度介质查询中定义的众多样式规则之一

@media (-ms-high-contrast)
{
    ...

    /*
    AppBar high contrast colors.
    */
    .win-appbar {
        background-color: ButtonFace;
        border-color: Highlight;

    ...
}

让我们假设我们正在开发的客户已经确定纯白色的应用程序栏背景同样可见,并且更好地匹配他们的品牌,并要求我们覆盖默认值。为此,我们只需要在页面的 CSS 文件中克隆这个样式规则(包括媒体查询),如清单 8-17 中的所示。

清单 8-17 。 在高对比度模式下覆盖应用程序栏的背景颜色

@media (-ms-high-contrast)
{
    .win-appbar {
        background-color: white;
}

如果您确实需要覆盖内置的高对比度样式,请注意始终保持良好的可视性。一个没有很好地提供高对比度模式的应用程序可能会在应用程序认证过程中被发现,更糟糕的是,会阻止弱视用户享受你的应用程序。

摘要

在这一章中,我们看到了 WinJS 影响我们应用程序风格的许多方式,并将其扩展到我们作为开发人员,这样我们不仅可以展现 Windows 8 生态系统的个性,还可以展示我们自己的个性。

我们已经了解了如何设计应用程序的版式、应用程序栏、设置窗格弹出菜单和菜单,我们还了解了如何实现高对比度模式以及如何在必要的地方扩展它。

在下一章中,我们将保持同样的思路,但是更多地关注 WinJS 提供给我们的许多控件的样式。

九、WinJS 控件样式

image 注意 HTML 给了我们标准元素,WinJS 用自定义控件扩展了它们。我们所需要的就是设计好它们。

我们已经学习了如何使用样式规则来选择和样式化元素。我们已经知道我们在样式表中创建这些规则。我们了解到,您的 Windows 8 应用中的 WinJS 库为我们提供了两个主题版本的样式表,为我们的应用赋予了 Windows 8 个性。样式表会影响背景颜色、字体和文本大小。

WinJS 库中的样式表也会影响所有的标准 HTML 控件。当你在 HTML 中声明一个button控件时,由于 WinJS 样式表,它看起来像一个 Windows 8 按钮。当您添加选择框、文本框、复选框或任何其他内置 HTML 控件时,它们将看起来像 Windows 8 控件,这也是因为 WinJS 样式表。

但 WinJS 也给了你更多的控件(和一个框架来编写你自己的控件),并定义了它们以这样一种方式呈现,你可以影响它们的样式,给它们你自己的应用程序的个性。

这些内置 HTML 控件和 WinJS 扩展控件的样式是我们将在本章中讨论的内容。我们将查看每个的默认样式,我们将查看添加的自定义类和定义的伪元素,它们将为我们提供对样式规则的良好控制。

记住 WinJS 给我们提供了两种不同的 CSS 样式表:ui-dark.cssui-light.css。他们的名字揭示了他们的目的。ui-dark.css样式表为我们提供了一个黑色背景和一个白色前景,而ui-light.css给了我们相反的效果。为了简单起见,我们只看一下ui-light.css文件,因为两者在结构上是相同的。

除了这两个主题样式表之外,我们还讨论了两个主题类,这两个主题类可以将深色或浅色主题应用于整个应用程序,而不是应用于它的某些子部分。不要混淆样式表(ui-dark.cssui-light.css)和类(win-ui-darkwin-ui-light))。

我们在前一章看到了一些以win-为前缀的类,我们将在这里看到更多。有了这个命名约定,可以方便地提醒我们什么是 WinJS 带来的,什么是 WinJS 带来的。我们将要介绍的大多数类都属于或应用于某个控件,但是win-scrollview类可以附加到几乎任何元素上。它只应用了四个样式属性,使目标元素能够让用户滑动(通过触摸)或滚动(通过鼠标)来查看屏幕外的更多内容。

HTML 控件

HTML 标准定义了许多控件。实际上,最近在 HTML 的最新版本中——在 HTML5 中——引入了相当多的这种方法。HTML 控件涵盖了许多常见的用户交互任务,鼓励 Windows 8 应用程序开发人员在 HTML 控件存在并适合工作时使用它们。

对于每个 HTML 控件,我将讨论 WinJS 样式表分配给它们的默认样式、它们可用的伪类和伪元素,以及可以添加到元素以影响其呈现方式的任何类。我将讨论以下 HTML 控件:

  • 纽扣
  • 文件上传
  • 文本输入
  • 单选按钮
  • 复选框
  • 范围
  • 选择框
  • 进度指示器

与 WinJS 控件不同,HTML 控件不会被“搞乱”。当您在标记中放入类似于<img src="mypicture.png"/>的东西时,它会原封不动地一直传到客户端。WinJS 控件的工作方式非常不同。您在标记中放置了一个 WinJS 控件,但是在呈现给用户之前,它会被一些 JavaScript 处理。我们将在后面的部分中讨论这一点。

对于我们的 HTML 控件(元素),我们将只看 WinJS 库中的样式表如何默认设置这些控件的样式。请记住,您将能够完全覆盖这些风格,我们将在第十章中对此进行详尽的研究。

纽扣

一个按钮发出命令。用户知道按钮,并且早在计算机出现之前就一直在按按钮。按钮也早已成为 HTML 的一部分。

在 HTML 中声明一个按钮有多种方法。您可以使用 input 元素声明一个按钮,并使用下面三个代码片段中的类型属性之一来确定它的行为:<input type="button"/><input type="reset"/><input type="submit"/>。这些按钮看起来是一样的,但是它们的功能会有所不同。

  • button的一个type声明了一个没有任何功能的按钮。这需要编写一些 JavaScript 来赋予它一些实际的功能。
  • reset中的type声明了一个按钮,当按下该按钮时,会将表单中的所有其他控件重置为初始值。
  • submittype创建一个按钮,当按下该按钮时,将发出一个包含所有表单值的 POST HTTP 请求。

HTML5 引入了一种更简单的新方法来定义按钮——使用button元素。元素相当于<input type="button"/>,它声明了一个按钮,但是没有分配任何功能。button元素比输入元素更具语义性。我的意思是,它对人类读者来说更容易理解。我建议只使用button元素。

清单 9-1。 一个超级简单的按钮

<!-- HTML snippet -->
<button>click me!</button>

9781430249832_Fig09-01.jpg

图 9-1。一个简单的按钮

对于第一个控件,我们来讨论发现元素上应用的样式属性。要发现button元素的应用属性,请遵循以下步骤:

1.在 Visual Studio 2012 中创建新的 Windows 8 项目(或现有项目中的新页面)

2.在 HTML 中添加一个简单的 HTML 按钮

  • <button>My Button</button>

3.通过按 F5 启动应用程序进行调试

4.切换回 Visual Studio 并转到 DOM Explorer 选项卡。如果没有 DOM Explorer 选项卡,请转到调试菜单,转到 Windows,然后选择 DOM Explorer。

5.按下 DOM Explorer 左上角的选择元素按钮。你应该会自动切换到正在运行的应用程序。

6.将鼠标悬停在按钮上,注意蓝色轮廓,然后点按按钮。您应该会自动切换回仍处于调试模式的 Visual Studio。

7.单击 DOM Explorer 右窗格中的“跟踪样式”选项卡。

“跟踪样式”面板列出了影响所选元素的所有样式属性,并告诉您这些属性是在哪里定义的,如果这些属性被其他地方的另一个规则覆盖,它甚至会用删除线呈现这些属性。在上面步骤中创建的按钮的跟踪样式面板中,您应该会看到标题为::-ms-browse background-color的规则。在该属性下有一个针对button:hover(以及其他链接的选择器)的规则,它将background-color定义为rgba(205,205,205,0.82)。此方法可用于确定影响文档中任何元素的所有应用的样式属性。

无论使用哪种方法声明按钮,WinJS 都确定按钮的最小宽度应为 90 像素,最小高度应为 32 像素。这个最小按钮尺寸的主要原因之一是保持的可触摸性。即使是大手指也应该能够始终如一地停留在按钮上,任何小于 90 x 32 的尺寸都可能太小。

按钮也被赋予了一点填充,2 像素的边框和 11 磅的字体。按钮的背景颜色是灰色的,略透明,实际颜色值为 rgba (182,182,182,0.7),正如我们在前面的练习中看到的,它的悬停颜色略浅。这种背景颜色的差异创造了一个微妙但重要的视觉线索,即按钮正被悬停。

还有许多其他属性对按钮有效,您可以在 DOM Explorer 的 Trace Styles 面板中找到所有这些属性。

所有这些属性一起构成了 Windows 8 中的 home 按钮外观。你可以在图 9-2 中看到微软设计风格的简单性。

9781430249832_Fig09-02.jpg

图 9-2 。IE10 中按钮的默认呈现与应用 WinJS 样式表后出现在 Windows 8 应用程序中的版本相比较

以下伪类与按钮元素相关:

  • :悬停。如前所述,被悬停按钮的背景颜色比正常颜色稍浅。
  • :禁用。默认情况下,禁用的按钮具有透明背景、白色文本和低透明度(40%)的边框。

如果你声明了一个按钮并包含了一个win-backbutton类,WinJS 样式表会把这个按钮变成后退按钮。后退按钮是圆形的,内部呈现一个箭头字形。这一典型的 Windows 8 功能可以通过以下非常简单的 HTML 标记添加到您的应用程序中:

<button class="win-backbutton"/>

文件上传控制

文件上传控件对 HTML5 来说并不陌生,但是它已经得到了一些重要的改进。它在浏览器中看起来几乎是一样的——只显示一个文本框和一个浏览按钮。该控件允许用户一次上载一个或多个文件。

清单 9-2。 一个带有“文件”类型的输入会呈现一个文件上传控件。

<input type="file"/>

9781430249832_Fig09-03.jpg

图 9-3。默认文件上传控件

由于文件上传控件呈现多个可视元素(即文本框和浏览按钮),过去很难确定样式。复合元素可以很容易地被定位,但是不可能单独定位文本框和浏览按钮。然而,在 Internet Explorer 中,::-ms-value::-ms-browse伪元素被识别并允许这种更精细的控制。

WinJS 将文件上载控件的样式设置为 340 像素宽,32 像素高,周围有自定义边距。::-ms-value(代表文本框)和::-ms-browse(代表浏览按钮)都有两个像素的边框。

文本输入控件

一些 HTML 控件相当于具有单一行为的单一控件,但是input元素不在其中。元素的行为取决于它的属性type的值。但是,许多值会导致控件在用户看来像一个文本框,并接受(正如所料)字母数字键盘输入。type属性中的任何下列值将产生一个字母数字文本框:textpasswordemailnumbertelurlsearch。此外,textarea元素也会产生一个接受字母数字输入的文本框。所有这些结果控件的样式几乎都是一样的,所以我们将在清单 9-3 中一起查看它们。

清单 9-3。 七种方式声明一个文本框

<!-- HTML snippet -->
<input type="text"/> text<br />
<input type="password" /> password<br />
<input type="email" /> email<br />
<input type="number" /> number<br />
<input type="tel" /> tel<br />
<input type="url" /> url<br />
<input type="search" /> search<br />
<textarea></textarea> textarea

9781430249832_Fig09-04.jpg

图 9-4。几种不同的文本框类型

从 HTML5 开始,Internet Explorer 和 Windows 8 应用程序会在大多数文本框类型的右侧显示一个小的 X ,这样用户可以更容易地清除文本框的值。在 Internet Explorer 10 和 Windows 8 应用程序中,您可以通过使用 ::-ms-clear 伪元素将这个小字形作为样式。这对于那些想要很好地控制自己造型的人来说很有帮助。

9781430249832_Fig09-05.jpg

图 9-5。一个小的“x”字形呈现在文本框中,使清除框变得简单

同样,对于密码文本框,Internet Explorer 和 Windows 8 应用程序会呈现一个小的眼球字形,只要用户按下该字形,就会显示在字段中输入的密码。当用户知道没有人躲在他们身后或通过屏幕共享观看时,能够看到输入的密码以确保没有错误是有帮助的。

9781430249832_Fig09-06.jpg

图 9-6。还会为密码字段呈现一个字形,以临时取消屏蔽并显示内容

文本框的高度至少为 28 像素,宽度至少为 64 像素,具有 2 像素的实心边框。

textarea元素的最小高度为 39 像素,宽度为 260 像素,具有相同的 2 像素实心边框。对于textarea元素,默认情况下垂直溢出被设置为滚动,因此如果用户输入太多文本,滚动条将被呈现,并且仍然使用户能够访问和编辑他们的所有文本。

您可以向任何元素添加一个win-textarea类,赋予它一个textarea的样式特征,而不管实际的元素是什么。

单选按钮

单选按钮用于选择——非此即彼的选择。当您想让用户只决定一个值时,您可以给他们单选按钮。大多数人会认出熟悉的小圆形单选按钮。清单 9-4 显示了它们在 Windows 8 中的样子。

清单 9-4。 三个广播类型输入用标签指定。所有三个元素都有“水果”这个名称,因此它们的功能是互斥的(选择一个元素将取消选择其他元素)

<!-- HTML snippet -->
<label><input type="radio" name="fruit" value="Apple"/> Apple</label><br />
<label><input type="radio" name="fruit" value="Orange"/> Orange</label><br />
<label><input type="radio" name="fruit" value="Banana"/> Banana</label>

9781430249832_Fig09-07.jpg

图 9-7。准备好交互的三个单选按钮

单选按钮(及其密切相关的兄弟,复选框)被选中时会填充一个字形,开发人员可以使用::-ms-check伪元素来定位该字形本身。如果你想在你的单选按钮上有一个更多彩的点,这个伪元素将是你唯一的希望。

像许多 Windows 8 控件一样,单选按钮有一个 2 像素的实心边框。它们的默认大小也是 23 像素见方。

复选框

复选框和单选按钮在幕后有很多共同点,但对用户来说,它们是完全不同的。复选框用于选项;它们代表二进制标志。然而,如果这是你的目标,你应该考虑 WinJS 提供的 ToggleSwitch 控件。这是一个更友好和更易触摸的控制,有很多 Windows 8 的个性。更重要的是,ToggleSwitch 向用户暗示更改将立即生效,而复选框暗示更改只有在表单提交后才会生效。清单 9-5 展示了一个垂直列表中复选框的基本示例。

清单 9-5。 现在的类型是“复选框”。

<!-- HTML snippet -->
<label><input type="checkbox" value="Apple"/> Apple</label><br />
<label><input type="checkbox" value="Orange"/> Orange</label><br />
<label><input type="checkbox" value="Banana"/> Banana</label>

9781430249832_Fig09-08.jpg

图 9-8。呈现复选框,允许选择多个项目

默认情况下,复选框为 21 像素见方,具有典型的 2 像素实心边框。

范围

范围(或滑块控件)是 HTML5 的新控件。范围控件为用户提供了一个用户界面(UI ),以便于从一个范围的值(如整数范围的值)中进行选择。

清单 9-6。 一个简单的输入类型“范围”被声明

<!-- HTML snippet -->
<input type="range" />

组成范围控件的许多部分可以作为伪元素的目标。

  • ::-ms-track。 -代表可能值范围的滑块轨迹
  • ::-女士-拇指。 -用户拖动的代表控件上当前值的部分
  • ::-毫秒-滴答-之前。 -适用于出现在轨道顶部(从左到右布局)或左侧(从上到下布局)的刻度线
  • ::-毫秒-滴答-之后。 -适用于出现在轨道底部(从左到右布局)或底部(从上到下布局)的刻度线
  • ::-ms-填充-降低。 -适用于从最小值到当前值的轨迹部分
  • ::-ms-fill-upper。 -适用于从当前值到最大值的轨迹部分

正如您在图 9-9 的示例中所看到的,范围指示器呈现为一个简单的矩形,带有一个小的拇指滑块,范围的下部部分用颜色填充。您也可以看到,默认情况下,范围控件水平显示。但是,您可以给它添加win-vertical类值,它将垂直显示。根据您自己独特的用户界面决定范围指示器是水平的还是垂直的。

9781430249832_Fig09-09.jpg

图 9-9 。呈现 Windows 8 样式的范围控件

选择框

选择框实际上是 HTML 世界中的古董。该控件在列表中有一个或多个选项。它可以被配置为显示整个选项列表,或者只显示一个选项,在这种情况下,当用户展开列表时,其余的选项将被列出。它也可以配置为允许单个选定值或多个值。

清单 9-7。 三个水果被声明为选择列表中的选项,但只有其中一个被选中

<!-- HTML snippet -->
<select>
    <option selected>Apple</option>
    <option>Orange</option>
    <option>Banana</option>
</select>

9781430249832_Fig09-10.jpg

图 9-10。将呈现一个选择列表,其中显示选定的值

默认情况下,选择列表采用与其他具有 2px 矩形边框的控件相同的简单朴素的外观。呈现在选择列表右侧的扩展字形显示为一个简单的人字形,但是它可以用::-ms-expand伪元素作为目标,因此您可以非常容易地更改该字形。您还可以使用::-ms-value伪元素来定位列表中的单个元素。

进度指示器

进度指标无处不在,坦率地说,非常有用。在 HTML5 推出其语义标签版本之前,你在网上看到过它们,但它们是定制的,有点困难。现在声明一个进度指标是极其简单的;就像造型一样。清单 9-8 实际上在相同数量的代码行中声明了三个进度指示器。

清单 9-8。 进度指标设置为渲染为 3 种基本类型

<!-- HTML snippet -->
<progress value="1"></progress>
<progress></progress>
<progress class="win-ring"></progress>

9781430249832_Fig09-11.jpg

图 9-11。进度指示器的三种基本类型

您可以通过使用::-ms-fill伪元素来定位填充进度指示器的条。

如果您没有覆盖并指定其他内容,并且没有向进度指示器添加任何类,那么它将是 180 像素宽,6 像素高,没有边框。像所有其他控件一样,progress 元素的默认样式相当于一个干净简单的 UI 元素。

如果你没有为你的进度控制的value属性提供一个值,那么它将被认为是一个不确定的进度指示器。这意味着它不会呈现为一个填满的条形,而是一个花哨的飞行点,指示任务正在运行,但不会承诺任务可能何时完成。您可以使用:indeterminate伪类来选择不确定的进度元素。

WinJS 样式表可以识别一些类,因此您可以将它们添加到 progress 元素中以影响其外观。它们是:

  • win-medium. 带有win-medium类的进度指示器将为 280 像素宽而不是默认的 180 像素。
  • win-large. 带有win-large类的进度指示器将在其父容器中对齐到 100%宽度。
  • win-paused. 当一个确定的进度指示器被分配一个类别win-paused时,它将从完全不透明变为 50%的动画。所以简单来说,当你暂停一个进度指示器时,它会淡出。
  • win-error. 当一个进度指示器出错时(当它有一个赢-错类时),指示器的::-ms-fill将完全隐藏。
  • win-ring. 在你的进度指示器上添加一类win-ring,将它完全转换成一个旋转的圆点圆圈。这种紧凑的格式可能更适合您的设计。

WinJS 控件

像 HTML 元素一样,WinJS 控件产生的 UI 允许用户与应用程序进行交互。然而,相似之处也就到此为止。HTML 元素在您的标记中被指示一次,然后呈现在用户代理上。另一方面,WinJS 控件以具有特殊data-win-control属性的div元素开始,然后由 WinJS 库中的 JavaScript 处理,最后呈现用户代理知道如何处理的 HTML 元素。

因此,在这一节中,我们不仅要看控件的默认样式,还要广泛地看 JavaScript 处理到底呈现了什么。实际上,我们将使用非常有价值的 DOM Explorer 来获取呈现的 HTML,并查看它包含的所有内容。

您将看到呈现的 HTML 元素是用类值绘制的,这些类值充当句柄,让您为每个可能的复杂控件的各个方面添加样式。让我给你举个例子来说明我所说的。

DatePicker 控件产生多个元素,表示数据的组成部分。如果您需要单独为 month 组件添加一些样式,那么您很幸运,因为 month 组件将呈现在 WinJS 用win-datepicker-month修饰的 HTML 元素中。

所有这些类都以win-datepicker-开头,所以它们应该很容易被发现和区分。

让我们继续详细介绍许多 WinJS 控件,它们默认呈现什么,以及它们如何允许你用类值控制它们的样式。我们将查看以下 WinJS 控件:

  • 绑定模板
  • 日期选择器
  • TimePicker(时间选择器)
  • 评级
  • 切换开关
  • 列表视图
  • 语义缩放
  • FlipView
  • view box-检视方块
  • HtmlControl

绑定模板

大多数 WinJS 控件都可以在 WinJS 中找到。UI 命名空间,但模板控件在 WinJS。绑定名称空间,因为它与该名称空间的功能关系密切。

模板用于创建样板文件,对象可以呈现到其中。呈现旨在允许开发人员编写代表对象的简洁 HTML,但允许显示最终可能呈现到其中的任何对象。

关于模板的样式不需要说太多,因为大部分样式都留给了开发人员。然而,一个单独的值——win-template——被添加到模板的类中,这允许您有效地定位它们。

日期选择器

我们将要讨论的 DatePicker 和其余的 WinJS 控件都可以在 WinJS 中找到。UI 命名空间。就像它的名字一样,DatePicker 允许用户选择一个日历日期。这是一个非常简单的控件,它实际上只是将三个 HTML 选择列表呈现给标记——月份、日期和年份。默认的日期选择器如图 9-12 中的所示。

清单 9-9。 一个来自 WinJS 的 DatePicker 控件,使用带有 data-win-control 属性的标准 div 声明

<!-- HTML snippet -->
<div data-win-control="WinJS.UI.DatePicker"></div>

9781430249832_Fig09-12.jpg

图 9-12 。日期选择器呈现为三个选择框

当你查看图 9-12 中的输出时,很明显这一行 HTML 标记已经被处理了。结果包含三个独立的选择框。让我们看看清单 9-10 中在处理过程中实际创建的 HTML。

清单 9-10。

<!-- DOM Explorer snippet -->
<div class="win-datepicker " role="group" lang="en-US" dir="ltr"
    data-win-control="WinJS.UI.DatePicker">
    <select tabindex="0" class="win-datepicker-month win-order0 "
        aria-label="Select Month">
        <option value="January">January</option>
        <option value="February">February</option>
        <option value="March">March</option>
        ...
        <option value="October">October</option>
        <option value="November">November</option>
        <option value="December">December</option>
    </select>
    <select tabindex="0" class="win-datepicker-date win-order1 "
        aria-label="Select Day"><option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
        ...
        <option value="28">28</option>
        <option value="29">29</option>
        <option value="30">30</option>
    </select>
    <select tabindex="0" class="win-datepicker-year win-order2 "
        aria-label="Select Year">
        <option value="1912">1912</option>
        <option value="1913">1913</option>
        <option value="1914">1914</option>
        ...
        <option value="2110">2110</option>
        <option value="2111">2111</option>
        <option value="2112">2112</option>
    </select>
</div>

time picker〔??〕

如果你理解了日期选择器的工作原理,你就不会有时间选择器的问题了。他们几乎一模一样。TimePicker 控件提供小时、分钟和时间段,而不是呈现月、日和年的选择列表。

清单 9-11。 所有的 WinJS 控件都使用相同的语法进行声明

<!-- HTML snippet -->
<div data-win-control="WinJS.UI.TimePicker"></div>

9781430249832_Fig09-13.jpg

图 9-13 。TimePicker 控件也使用三个选择框

同样,我们添加了一行 HTML 标记,但是我们的输出包含三个不同的选择框。清单 9-12 显示了 DOM Explorer 生成的 HTML。

清单 9-12。清单 9-11 和图 9-13 中的时间选择器产生三个选择列表,分别是小时、分钟和时间段(为简洁起见,将其缩短)

<!-- DOM Explorer snippet -->
<div class="win-timepicker " role="group" lang="en-US" dir="ltr"
    data-win-control="WinJS.UI.TimePicker">
    <select tabindex="0" class="win-timepicker-hour win-order0 "
        aria-label="Select Hour">
        <option value="12">12</option>
        <option value="1">1</option>
        <option value="2">2</option>
        ...
        <option value="9">9</option>
        <option value="10">10</option>
        <option value="11">11</option>
    </select>
    <select tabindex="0" class="win-timepicker-minute win-order1 "
        aria-label="Select Minute">
        <option value="00">00</option>
        <option value="01">01</option>
        <option value="02">02</option>
        ...
        <option value="57">57</option>
        <option value="58">58</option>
        <option value="59">59</option>
    </select>
    <select tabindex="0" class="win-timepicker-period win-order2 "
        aria-label="Select A.M P.M">
        <option value="AM">AM</option>
        <option value="PM">PM</option>
    </select>
</div>

评级

许多应用程序包括允许用户为其他用户的商品和服务对商品和服务进行评级的功能。5 星评级系统是一个受欢迎的系统,这正是评级控制默认为您提供的,尽管它不限于 5 星,甚至不限于星。图 9-14 向您展示了典型分级控制的效果。

清单 9-13。 一个 WinJS 等级控制

<!-- HTML snippet -->
<div data-win-control="WinJS.UI.Rating"></div>

9781430249832_Fig09-14.jpg

图 9-14 。使用流行的 5 星系统默认呈现分级控制

快速浏览一下 DOM explorer,看看评级控件是如何组成的,可以看到清单 9-14 中的代码。

清单 9-14。 生成的 DOM 标记为默认评级控件

<!-- DOM Explorer snippet -->
<div tabindex="0" class="win-rating " role="slider" aria-readonly="false" aria-valuenow="Unrated"
    aria-valuemin="0" aria-valuemax="5" aria-label="User Rating" aria-valuetext="Unrated"
    data-win-control="WinJS.UI.Rating">
    <div class="win-star win-empty win-user "></div>
    <div class="win-star win-empty win-user "></div>
    <div class="win-star win-empty win-user "></div>
    <div class="win-star win-empty win-user "></div>
    <div class="win-star win-empty win-user "></div>
    <div class="win-star win-average win-full win-user " style="-ms-flex: 0 0 auto;
        padding-right: 0px; padding-left: 0px; border-right-color: currentColor;
        border-left-color: currentColor; border-right-width: 0px; border-left-width: 0px;
        border-right-style: none; border-left-style: none; display: none;"></div>
</div>

向分级控制添加一个类别win-small将导致分级控制的大小是标准分级控制的一半。星星将是 14 像素,而不是正常的 28 像素。不要认为“正常”和“小”是分级控制的唯一选择。与所有这些控件一样,权力掌握在作为开发人员的您手中。例如,你可以通过使用类似于清单 9-15 中的样式规则,使用巨大的星星作为你的分级控制。我们将在第十章中看到更多这样的覆盖和扩展样式,但这里只是我所说的一个例子。

在清单 9-15 的中,我们看到样式规则(包含在 WinJS 中)使开始变小,在同一个清单中我们看到一个名为win-huge的自定义规则,它做了相反的事情,使星星变大。

清单 9-15。 自定义样式规则增加星星的大小

/* .win-small (from WinJS) */
.win-rating.win-small .win-star {
    width: 14px;
    height: 14px;
    font-size: 14px;
    padding: 0 3px;
}

/* custom .win-huge style */
.win-rating.win-huge .win-star {
    width: 56px;
    height: 56px;
    font-size: 56px;
    padding: 0 12px;
}

切换开关

正如我前面提到的,ToggleSwitch 控件是传统 checkbox 的一个很好的替代品。ToggleSwitch 是一个更大、触摸更友好的二元决策目标。例如,使用它可以让用户选择打开或关闭游戏中的音乐。

清单 9-16 显示了控件,图 9-15 显示了典型的拨动开关。

清单 9-16。 一个 WinJS ToggleSwitch 控件

<!-- HTML snippet -->
<div data-win-control="WinJS.UI.ToggleSwitch"></div>

9781430249832_Fig09-15.jpg

图 9-15 。ToggleSwitch 是回答二元问题(如是/否)的一种更加用户友好的方式

为 ToggleSwitch 控件生成的 HTML 可能会让您感到惊讶。HTML 没有看起来像切换控件的本地元素。根本就不存在。然而,WinJS 控件不呈现新的图形元素。Windows 团队在这种情况下发挥了创造性,使用了进度指示器,但改变了滚动条本身和滑块的尺寸,使其看起来像一个开关。您可以在清单 9-17 中的结果 HTML 中看到进度指示器。

清单 9-17。 由此产生的 DOM 输出为一个默认的 ToggleSwitch 控件

<!-- DOM Explorer snippet -->
<div class="win-toggleswitch win-off " data-win-control="WinJS.UI.ToggleSwitch">
    <div class="win-title " id="ms__id32" role="note"></div>
    <div style="display: -ms-grid;">
        <div class="win-label ">On</div>
        <div class="win-label win-hidden ">Off</div>
        <input class="win-switch " role="checkbox" aria-checked="false"
            aria-disabled="false" aria-labelledby="ms__id32" type="range"
            max="1" step="1"></div>
</div>

注意,ToggleSwitch 控件实际上是使用一个<input type="range"/>元素实现的。1的一个max值和1的一个step value值使范围控制像一个二进制开关。这是一个非常聪明的方法来重复使用已经存在的东西。

因为 ToggleSwitch 使用范围控件,所以应用于范围元素的所有相同伪元素也可以应用于 ToggleSwitch。toggle 中的 range 元素被赋予一个 win-switch 类,所以一个#myToggle .win-switch::-ms-thumb的 CSS 选择器将选择一个idmyToggle的 ToggleSwitch 的滑块。这是伪元素列表:::-ms-thumb::-ms-tooltip::-ms-ticks-before::-ms-ticks-after::-ms-track::-ms-fill-upper::-ms-fill-lower

列表视图

ListView 是 Windows 8 应用程序中的一个重要控件。我在第七章向你介绍了它的功能,但是现在是时候为它的类剖析控件,并且弄清楚我们如何能以我们想要的方式设计它。图 9-16 再次向你展示了第七章中的 ListView 的样子。

9781430249832_Fig09-16.jpg

图 9-16 。典型的 ListView 借用自图 7-19 中的第七章

声明 ListView 的语法与其他 WinJS 控件是一样的,正如你在清单 9-18 中看到的,但是声明本身会导致一个空的 ListView,当你执行你的应用程序时你甚至看不到它。然而,查询 DOM Explorer 向我们保证它就在那里,您可以在清单 9-19 中看到结果。

清单 9-18。 声明为一个简单(且为空)的 ListView 控件

<!-- HTML snippet -->
<div data-win-control="WinJS.UI.ListView"></div>

在清单 9-19 中,DOM Explorer 向我们展示了一个简单的 ListView 控件生成的 HTML,它没有绑定任何数据。

清单 9-19。 为空 ListView 的标记

<!-- DOM Explorer snippet -->
<div tabindex="-1" class="win-listview win-swipeable " role="listbox"
    style="position: relative;" data-win-control="WinJS.UI.ListView">
    <div tabindex="-1" class="win-viewport win-horizontal " role="group"
        aria-label="Scrolling Container">
        <div id="ms__id31" aria-flowto="ms__id32"></div>
        <div class="win-surface ">
            <div class="win-backdrop " aria-hidden="true" style="width: 0px; height: 0px;"></div>
            <div class="win-backdrop " aria-hidden="true" style="width: 0px; height: 0px;"></div>
            <div class="_win-proxy"></div>
            <div tabindex="0" aria-hidden="true"></div>
            <div style="left: 0px; top: 0px; width: 100%; height: 100%; position: absolute;">
            </div>
            <div tabindex="0" aria-hidden="true"></div>
        </div>
        <div id="ms__id32" x-ms-aria-flowfrom="ms__id31"></div>
    </div>
    <div tabindex="0" aria-hidden="true"></div>
    <div aria-hidden="true" style="left: 50%; top: 50%; position: absolute;">
        <div tabindex="0" aria-hidden="true"></div>
        <div aria-hidden="true" style="width: 0px; height: 0px;"></div>
        <div tabindex="0" aria-hidden="true"></div>
    </div>
    <div tabindex="0" aria-hidden="true"></div>
</div>

正如您所看到的,这一条有很多内容(请记住,这是一个空列表!)现在让我们来看看一个只有少量数据填充的 ListView。

清单 9-20。 HTML 和 JavaScript 不仅用于声明 ListView,还用于填充它

<!-- HTML snippet -->
<div data-win-control="WinJS.UI.ListView"></div>
<div data-win-control="WinJS.Binding.Template">
    <span data-win-bind="innerText:this"></span>
</div>

// JavaScript snippet
var list = new WinJS.Binding.List(["one", "two", "three"]);
var listViewElement = element.querySelector("[data-win-control='WinJS.UI.ListView']");
var templateElement = element.querySelector("[data-win-control='WinJS.Binding.Template']");
listViewElement.winControl.itemDataSource = list.dataSource;
listViewElement.itemTemplate = templateElement;

清单 9-21。ListView 的结果 DOM 标记,其中绑定了一些数据

<!-- DOM Explorer snippet -->
<div tabindex="-1" class="win-listview win-swipeable " role="listbox" style="position: relative;"
    data-win-control="WinJS.UI.ListView">
    <div tabindex="-1" class="win-viewport win-horizontal " role="group"
        style="opacity: 1; -ms-scroll-limit-x-min: 0px;" aria-label="Scrolling Container">
        <div id="ms__id42" aria-flowto="ms__id35"></div>
        <div class="win-surface " style="width: 35px; opacity: 1;">
            <div class="win-backdrop " aria-hidden="true"
                style='left: 35px; top: 0px; width: 0px; height: 390px;
                background-image: url("data:image/png;base64,…");'></div>
            <div class="win-backdrop " aria-hidden="true"
                style='left: 0px; top: 0px; width: 0px; height: 390px;
                background-image: url("data:image/png;base64,…");'></div>
            <div class="_win-proxy"></div>
            <div tabindex="0" aria-hidden="true"></div>
            <div style="left: 0px; top: 0px; width: 100%; height: 100%;
                position: absolute; clip: auto;">
                <div class="win-container " style="left: 0px; top: 0px;
                    width: 25px; height: 20px;">
                    <div tabindex="0" aria-hidden="true"></div>
                    <span class="win-item " id="ms__id35" role="option" aria-posinset="1"
                        aria-setsize="3" aria-flowto="ms__id37" x-ms-aria-flowfrom="ms__id42">
                        one
                    </span>
                    <div tabindex="0" aria-hidden="true"></div>
                </div>
                <div class="win-container " style="left: 0px; top: 30px;
                    width: 25px; height: 20px;">
                    <span class="win-item " id="ms__id37" role="option" aria-posinset="2"
                        aria-setsize="3" aria-flowto="ms__id39" x-ms-aria-flowfrom="ms__id35">
                        two
                    </span>
                </div>
                <div class="win-container " style="left: 0px; top: 60px;
                    width: 25px; height: 20px;">
                    <span class="win-item " id="ms__id39" role="option" aria-posinset="3"
                        aria-setsize="3" aria-flowto="ms__id43" x-ms-aria-flowfrom="ms__id37">
                        three
                    </span>
                </div>
            </div>
            <div tabindex="0" aria-hidden="true"></div>
        </div>
        <div id="ms__id43" x-ms-aria-flowfrom="ms__id39"></div>
    </div>
    <div tabindex="0" aria-hidden="true"></div>
    <div aria-hidden="true" style="left: 50%; top: 50%; position: absolute;">
        <div aria-hidden="true" style="width: 0px; height: 0px;"></div>
    </div>
    <div tabindex="0" aria-hidden="true"></div>
</div>

很明显,这个数据绑定 ListView 中还有更多的内容,包括我想引起您注意的一些事情:

  • 请注意,整个 ListView 位于一个包含类win-listview的元素中。这成为整个控件的一个方便的句柄。
  • 注意类win-swipeable的使用。如果您在默认样式表中查找win-swipeable的样式,您会发现它负责滚动行为。
  • 注意win-horizontal类的使用。ListView 控件也可以设置为垂直的,这是最终决定方向和行为的类。
  • 注意列表中每个单独项目周围的win-containerwin-item元素。当您需要样式化列表中的项目时,这两个类中的一个是缩小您的选择范围,只列出容器或列表项目的好方法。
  • 注意绝对位置。第一项设置有0pxtop值,第二项设置有30px,第三项设置有60px
  • 请注意 win-viewport 和 win-surface 类的使用。这两个组件之间的区别很重要,你马上就会看到。

Windows Dev Center 上的文章http://msdn.microsoft.com/en-us/library/windows/apps/hh850406.aspx非常有助于可视化组成 ListView 的可视(和功能)组件。看看图 9-17 ,出自那篇文章。理解win-listviewwin-surfacewin-viewport类是绝对必要的。

9781430249832_Fig09-17.jpg

图 9-17 。一个图,显示了组成列表视图的各种组件,这在试图添加或覆盖影响这个相对复杂的 WinJS 控件的样式时非常有用

可以把win-viewport想象成一个框架,当用户滚动时,win-surface可以在这个框架后面滑动。在win-listview上添加或操作样式属性,如果它会影响整个控件的话。win-viewport包含滚动行为(overflow-xoverflow-y)。Style win-surface如果你想影响实际出现在列表项后面的背景。

语义缩放

与 ListView 相关的是 SemanticZoom 控件。SemanticZoom 控件意味着包含两个 ListView 控件——标准 ListView(视图附近的)和另一个表示同一数据的逻辑缩小视图(视图和视图)。通常,远视图显示代表近视图中数据的组。组列表比完整的数据集更简洁,并且附加的特征是用户能够从远视图中选择一个组,并直接导航到近视图中的该组。

图 9-18 显示了语义缩放控件的简化模型。在第一个图像中,显示了多个组及其关联的项目。在用户在列表上做了缩放手势后,它们被动画化并转换成第二个图像,其中显示组,但不显示它们的项目。

9781430249832_Fig09-18.jpg

图 9-18 。语义缩放为用户定位,也方便了导航

请注意,第三组在放大列表中并不完全可见(图 9-18 中的第一幅图像),但它在缩小列表中。从语义上缩小数据列表不仅是定位和消化大型列表的一种方式,也是导航大型列表的一种更简单的方式。

语义缩放是 Windows 8 独有的功能,旨在帮助用户更轻松地定位和导航大型数据列表。

您可能需要将 SemanticZoom 控件作为目标来设置其样式或对其进行操作。SemanticZoom 控件的呈现版本有点复杂,但是要引用它,只需知道 WinJS 分配给它的类就足够了。您可以在清单 9-23 中看到,这个类名是win-semanticzoom。你也可以看到我提到的复杂性。列表中的近视图和远视图列表已被折叠并变暗,但仍然存在相当多的标记。这种标记样式和定位控件,并向其添加转换和过渡,以赋予其作为 Windows 8 SemanticZoom 控件的独特个性和行为。

清单 9-22。 用于创建 SemanticZoom 控件的 HTML、CSS 和 JavaScript

<!-- HTML snippet -->
<div data-win-control="WinJS.UI.SemanticZoom">
    <div id="list1" data-win-control="WinJS.UI.ListView"></div>
    <div id="list2" data-win-control="WinJS.UI.ListView"></div>
</div>
<div id="itemtemplate" data-win-control="WinJS.Binding.Template">
    <span data-win-bind="innerText:this"></span>
</div>
<div id="grouptemplate" data-win-control="WinJS.Binding.Template">
    <h2 data-win-bind="innerText:this"></h2>
</div>
<div id="semantictemplate" data-win-control="WinJS.Binding.Template">
    <div data-win-bind="innerText:this"></div>
</div>

/* CSS snippet */
.lst0922  #list1 .win-item { width:80px; }

// JavaScript snippet
var numbersList = new WinJS.Binding.List([1,2,3,4,5,6,7,8,9,10]).createGrouped(
    function(item) { return (item <= 5 ? "1-5" : "6-10"); },
    function (item) { return (item <= 5 ? "1-5" : "6-10"); }
);
var list1 = element.querySelector("#list1").winControl;
list1.itemDataSource = numbersList.dataSource;
list1.itemTemplate = element.querySelector("#itemtemplate");
list1.groupDataSource = numbersList.groups.dataSource;
list1.groupHeaderTemplate = element.querySelector("#grouptemplate");

var list2 = element.querySelector("#list2").winControl;
list2.itemDataSource = numbersList.groups.dataSource;
list2.itemTemplate = element.querySelector("#semantictemplate");

清单 9-23。semantic zoom 控件的结果 DOM 标记(为简洁起见,列表变暗并折叠)

<!-- DOM Explorer snippet -->
<div class="win-semanticzoom " role="ms-semanticzoomcontainer" aria-checked="false"
    style="overflow: hidden; position: relative;" aria-label=""
    data-win-control="WinJS.UI.SemanticZoom">
    <div style="left: 0px; top: 0px; width: 1264px; height: 400px; overflow: hidden;
        position: absolute;">
        <div style="left: 0px; top: 0px; width: 1264px; height: 400px; overflow: hidden;
            visibility: visible; position: absolute; transition-property: transform;
            transition-duration: 0s; transition-timing-function: linear;">
            <div style="transform-origin: 695.615px 200px; transition: transform 0.33s
                ease-in-out 34ms, opacity 0.33s ease-in-out 34ms; left: -680.61px; top: 0px;
                width: 2625.23px; height: 400px;overflow: hidden; position: absolute;
                opacity: 1; transform: scale(1);">

                <div class="win-listview win-swipeable win-groups" id="list1" ...>...</div>

            </div>
        </div>
        <div style="left: 0px; top: 0px; width: 1264px; height: 400px; overflow: hidden;
            visibility: hidden; position: absolute; transition-property: transform;
            transition-duration: 0s;transition-timing-function: linear;">
            <div style="transform-origin: 78.2px 200px; transition: transform 0.33s
                ease-in-out 34ms, opacity 0.33s ease-in-out 34ms; left: -63.2px; top: 0px;
                width: 1390.4px; height: 400px; overflow: hidden; position: absolute;
                opacity: 0; transform: scale(1.53846);">

                <div class="win-listview win-swipeable" id="list2" ...>...</div>

            </div>
        </div>
    </div>
    <button tabindex="-1" class="win-semanticzoom-button win-semanticzoom-button-location ltr "
        style="visibility: hidden; opacity: 0;"></button>
    <div tabindex="-1" aria-hidden="true"></div>
</div>

翻页视图

在许多情况下,FlipView 控件是一个很好的选择。它可以用来浏览图像、文章或应用程序的整个部分。一个简单的左右滑动手势就可以命令 FlipView 切换到下一个项目,包括动画、缓动和捕捉。

FlipView 是另一个需要用一点代码来演示的控件,原因是它是一个数据绑定控件。这意味着,就像 ListView 一样,我们将把我们的 FlipView 绑定到一个 WinJS。只要列表中的任何内容发生变化,我们的 FlipView 就会自动响应,而无需我们手动捕捉事件并更新 FlipView 中的项目。

此外,像 ListView 一样,FlipView 让我们有机会指定用于呈现每个项目的模板。

清单 9-24。 绑定了一些简单图像的 FlipView

<!-- HTML snippet -->
<div id="flipview" data-win-control="WinJS.UI.FlipView"></div>
<div id="template" data-win-control="WinJS.Binding.Template">
    <img data-win-bind="src:this"/>
</div>

/* CSS snippet */
.lst0924 .win-flipview {
    width: 480px;
    height: 320px;
}

//JavaScript
var flipview = element.querySelector("#flipview").winControl;
var template = element.querySelector("#template");

var fruitList = new WinJS.Binding.List([
    "/pages/chapter9/lst0924/peaches.png",
    "/pages/chapter9/lst0924/grapes.png",
    "/pages/chapter9/lst0924/orange.png
"]);
flipview.itemDataSource = fruitList.dataSource;
flipview.itemTemplate = template;

图 9-19 看起来是一个单独的图像,但是你可以看到箭头被绘制在每条边上。箭头是可见的,所以鼠标用户可以点击一些东西,但是触摸用户可以简单地滑动列表以改变到新的图像。

9781430249832_Fig09-19.jpg

图 9-19 。当您使用鼠标与 Windows 8 交互时,下一个和上一个箭头会呈现在 FlipView 的每一侧。你也可以随时使用滑动手势进行切换

整个 FlipView 本身有一个win-flipview类,这使得在运行的应用程序中引用 FlipView 控件变得很容易。但是,填充 FlipView 的项目会呈现在一个嵌入的 div 上,该 div 具有 win-surface 类。像 ListView 一样,您可以将这个元素想象成一个包含所有项目的长条,它可以滑动以一次显示一帧。

最后,FlipView 有两个导航按钮,只有在用户使用鼠标时才会出现。如果用户一直使用触摸进行交互,导航按钮就没有必要出现,因为只需一个简单的滑动手势就可以向前或向后导航。你可以在清单 9-25 中看到这些按钮的标记。它们是 HTML 按钮,被赋予了类值win-navbuttonwin-navleftwin-navright。您当然不必满足于默认情况下为 FlipView 呈现的左右箭头。您可以通过引用这些类值来覆盖它。我们将在第十章中了解更多。

清单 9-25。WinJS flip view 声明产生的标记

<div tabindex="-1" class="win-flipview " id="flipview" role="listbox" style="overflow: hidden;"
    aria-label="" data-win-control="WinJS.UI.FlipView">
    <div style="width: 100%; height: 100%; position: relative; z-index: 0;">
        <div tabindex="0" aria-hidden="true"></div>
        <div class="win-surface " role="group" style="-ms-scroll-snap-x: mandatory
            snapInterval(0px, 480px); width: 100%; height: 100%; position: relative;
            -ms-overflow-x: scroll; -ms-overflow-y: hidden; -ms-scroll-limit-x-min: 240000px;
            -ms-scroll-limit-x-max: 240960px; -ms-overflow-style: none;"
            aria-label="Scrolling Container">
            <div style="width: 100%; height: 100%; position: relative;">
                <div id="ms__id5" aria-flowto="ms__id36"></div>
                <div style="left: 240000px; width: 480px; height: 320px; overflow: hidden;
                    position: absolute;">
                    <div class="win-item " style="-ms-overflow-style: auto;">
                        <div tabindex="0" aria-hidden="true"></div>
                        <div tabindex="0" class="win-template " id="ms__id36" role="option"
                            aria-selected="true" aria-posinset="1" aria-setsize="3"
                            aria-flowto="ms__id37" x-ms-aria-flowfrom="ms__id5">
                            <img src="/pages/chapter9/lst0924/peaches.png"
                                data-win-bind="src:this">
                        </div>
                        <div tabindex="0" aria-hidden="true"></div>
                    </div>
                </div>
                <div style="left: 240480px; width: 480px; height: 320px; overflow: hidden;
                    position: absolute;">
                    <div class="win-item " style="-ms-overflow-style: auto;">
                        <div tabindex="0" class="win-template " id="ms__id37" role="option"
                            aria-selected="false" aria-flowto="ms__id38"
                            x-ms-aria-flowfrom="ms__id36">
                            <img src="/pages/chapter9/lst0924/grapes.png"
                                data-win-bind="src:this">
                        </div>
                    </div>
                </div>
                <div style="left: 240960px; width: 480px; height: 320px; overflow: hidden;
                    position: absolute;">
                    <div class="win-item " style="-ms-overflow-style: auto;">
                        <div tabindex="0" class="win-template " id="ms__id38" role="option"
                            aria-selected="false" aria-flowto="ms__id6"
                            x-ms-aria-flowfrom="ms__id37">
                            <img src="/pages/chapter9/lst0924/orange.png"
                                data-win-bind="src:this">
                        </div>
                    </div>
                </div>
                <div style="left: 239040px; width: 480px; height: 320px; overflow: hidden;
                    position: absolute;">
                    <div class="win-item " style="-ms-overflow-style: auto;"></div>
                </div>
                <div style="left: 239520px; width: 480px; height: 320px; overflow: hidden;
                    position: absolute;">
                    <div class="win-item " style="-ms-overflow-style: auto;"></div>
                </div>
                <div id="ms__id6" x-ms-aria-flowfrom="ms__id38"></div>
            </div>
        </div>
        <div tabindex="0" aria-hidden="true"></div>
        <button tabindex="-1" class="win-navbutton win-navleft " aria-hidden="true"
            style="visibility: hidden; z-index: 1000; opacity: 0;" aria-label="Previous"
            type="button">·</button>
        <button tabindex="-1" class="win-navbutton win-navright " aria-hidden="false"
            style="visibility: hidden; z-index: 1000; opacity: 0;" aria-label="Next"
            type="button">·</button>
    </div>
</div>

view box-检视方块

第七章的中介绍了视图框的功能,但是让我们快速回顾一下。视图框与其父容器的大小对齐,然后缩放单个子项(只能有一个),而不影响其纵横比以适应视图框的大小。它还会响应视图状态的变化,因此当用户旋转平板电脑时,ViewBox 会相应地处理其内容的缩放。

我们没有花时间在第七章来看一个视图框是如何实现的。现在,让我们看看渲染视图框的完整 DOM Explorer 输出。

在清单 9-26 中,我们有一个 div,它包含一个包含 div 的视图框。请注意,在 CSS 中,父级是垂直定向的,宽度为 100 像素,高度为 300 像素。还要注意,孩子的方向是水平的,宽度为 300 像素,高度为 100 像素。ViewBox 没有剪切子元素、拉伸父元素或呈现滚动条来显示隐藏的内容,而是保留了子元素并简单地缩放它以适合父 div。

清单 9-26。 包含 ViewBox 的父 div,view box 包含子 div

<!-- HTML snippet -->
<div class="parent">
    <div data-win-control="WinJS.UI.ViewBox">
        <div class="child"></div>
    </div>
</div>

/* CSS snippet */
.lst0926 .parent {
    width: 100px;
    height: 300px;
    border: solid 2px gray;
}

.lst0926 .child {
    width: 300px;
    height: 100px;
    border: solid 2px;
}

清单 9-27。 视图框缩放了子 div 以适合其父 div

<div class="parent">
    <div class="win-viewbox " data-win-control="WinJS.UI.ViewBox">
        <div class="horizontal" style="transform-origin: left top;
            transform: translate(0px, 133.333px) scale(0.333333); "></div>
    </div>
</div>

ViewBox 行为的一个巨大优势是子 div 可以包含任何形式的内容,但它的缩放和定位是相同的。举例来说,这是一个很好的方法,可以将整个游戏板缩放到适合横向模式或纵向模式。

html control〔??〕

HtmlControl 使得从另一个文档中引入 HTML 并将其呈现到另一个文档的任何区域变得非常简单。您可以使用 HtmlControls 从更小的页面组件中构建页面,这些组件可能更易于维护或协作。

清单 9-28。 使用 WinJS HtmlControl 将 HTML“导入”到单独文件中的 HTML 代码片段

<!-- HTML snippet -->
<div class="parent">
    <div data-win-control="WinJS.UI.HtmlControl"
        data-win-options="{uri:'/pages/chapter9/lst0928/page.html'}">
   </div>
</div>

/* CSS snippet */
.parent {
    border: solid 2px gray;
    width: 200px;
    padding: 5px;
}

<!-- page.html -->
<!DOCTYPE html>
<html>
    <head>
        <title></title>
    </head>
    <body>
        Hello, I came from page.html
    </body>
</html>

9781430249832_Fig09-20.jpg

图 9-20。将外部 HTML 导入 div 的结果

你可以看到“你好,我来自 page.html”在一个单独的 html 文件中,但是我们使用了一个 HtmlControl 来声明性地(在清单 9-28 中没有 JavaScript)将它导入代码片段中的 HTML。

清单 9-29。html control 的结果 DOM 标记包含一个 pagecontrol 类

<!-- DOM Explorer snippet -->
<div class="parent">
    <div class="pagecontrol " data-win-options="{uri:'/pages/chapter9/lst0929/page.html'}"
        data-win-control="WinJS.UI.HtmlControl">
        Hello, I came from page.html
    </div>
</div>

我们最初赋予属性data-win-controldata-win-options的 div 也被赋予了一个类名pagecontrol,这将再次使我们易于识别和定位。

在 WinJS 中的一个页面是一套完整的 HTML、CSS 和 JavaScript,代表一个单一的视图。在传统的 web 开发中,页面通常使用超链接进行导航。Windows 8 当然仍然支持这种风格的导航,但使用内置框架进行单页面导航通常是一个更好的主意。当实现这种导航模型时,逻辑页面仍然是一个实际的 HTML 页面(通常也是一个 CSS 和 JavaScript 文件),但是您通常不会超链接到它。相反,您将使用导航框架导航到它,这将导致页面的最终呈现被附加到起始页的 DOM(default . html)。

出于几个原因,理解这些页面的标识方式变得相当重要。幸运的是,这是一个非常优雅且易于理解的约定。

就在每个页面的 HTML 文件的body元素中,您会发现一个具有两个类名的div元素。第一个是页面名称,第二个是fragment。然后,helloworld页面看起来就像你在清单 9-30 中看到的一样。

清单 9-30。 一个使用 WinJS 导航模型的页面包含一个片段——一个简单的 div,有两个类名,让 WinJS 和我们更容易定位

<!-- helloworld.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Orders Page</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.1.0/css/ui-light.css" rel="stylesheet" />
    <script src="//Microsoft.WinJS.1.0/js/base.js"></script>
    <script src="//Microsoft.WinJS.1.0/js/ui.js"></script>

    <link href="helloworld.css" rel="stylesheet" />
    <script src="helloworld.js"></script>
</head>
<body>
    <div class="helloworld fragment ">
        <section aria-label="Main content" role="main">
            <p>Hello, World!</p>
        </section>
    </div>
</body>
</html>

注意加粗的类名。第一个匹配页面的名称,第二个是fragment。正是这整个片段将被注入到default.html页面中。

不过,您也可以将这些类用作自己的句柄。如果你想创建一个影响页面的样式,你可以给它加上前缀.fragment。如果你想应用一个只影响helloworld页面的样式,你可以在它前面加上.helloworld

对 WinJS 提供给我们的基本 HTML 控件和额外控件的样式的一瞥不足以彻底理解这个用于创建 Windows 8 应用程序的强大库,所以我建议你花一些时间在ui-light.css文档中,以理解它对各种控件做了什么,从而理解你可以扩展或覆盖什么。

下一章是关于扩展和重写的。如果 WinJS 已经确定一个按钮的边框是灰色的,而你希望它是深绿色的,那么这将是一个覆盖。如果 WinJS 根本没有提到按钮的背景颜色,但是你需要它,那么这就是按钮样式的扩展。

摘要

在这一章中,我们了解到 WinJS 在 Windows 8 应用程序中帮了我们很多。WinJS 提供了许多只由本地 HTML 元素组成的控件。它们提供了更丰富的用户交互和更一致的 Windows 8 体验。

WinJS 也给了我们一个 CSS 样式规则的大集合,这些规则同样适用于本地 HTML 元素和 WinJS 控件。这些样式规则使您的应用程序中的视觉元素的布局和样式看起来像 Windows 8 应用程序一样简单,并赋予它自己独特的风格。

WinJS 库使我们可以轻松地声明它的控件,同时它处理我们的简单声明,将其转换为本机 HTML 元素。当它创建最终被呈现的 HTML 时,它将有用的类名添加到各个部分,这样我们就有了“钩子”,我们可以使用它来设计整个控件的样式或针对单个组件。

这一切都是为了给用户提供更好的控制和更容易的开发之间的平衡,而且非常有效。

十、替代和定义样式

image 注意你已经了解了 CSS 以及它是如何在 Windows 8 应用中实现的。现在是时候开始创建你自己的风格,覆盖和扩展 WinJS 提供的风格,让你的应用程序有自己的个性,同时符合微软的设计原则。

这一章的目标是学习如何按照我们的需要来设计我们的应用程序。好的设计不完全是客观的,但是有很多原则适用,当然也有很多围绕好的 Windows 8 设计的原则可以极大地促进你的应用程序的成功。

为了控制我们的应用程序中的 UI,我们将首先找到 WinJS 样式表中定义的应用于我们的应用程序中的元素的样式规则,然后我们将覆盖这些规则中定义的样式属性,或者我们将添加自己的属性来扩展它们。此外,我们将创建自己的样式规则,应用于页面上的元素或者我们编写的自定义控件。

定位样式选择器

在我们可以覆盖和扩展 WinJS 中定义的样式规则之前,我们必须找到影响相关视觉元素的一个或多个规则。有这么多的规则,这不是一个微不足道的任务。

我们已经看到,WinJS 为我们提供了丰富的样式,这就是为什么在没有任何样式代码的情况下,我们从一个看起来像 Windows 8 应用程序的应用程序开始。WinJS 库中的样式不仅数量众多,而且有些还相当长。清单 10-1 显示了在ui-light.css样式表中可以找到的许多样式规则选择器中的两个。第一个简单地选择了一个按钮,但是第二个显示了它们可以变得多具体,并且为列表视图中没有“selection style filled”的选定的、悬停的容器选择了选择边框。

清单 10-1 。一个非常简单和一个非常复杂的风格选择器

button {
   ...
}

.win-listview:not(.win-selectionstylefilled)
    .win-container.win-selected:hover .win-selectionborder {
   ...
}

如果没有 DOM Explorer 中的 Trace Style 窗格的帮助,发现在哪里以及如何应用样式规则将是一项充满猜测的艰巨任务。它将原本痛苦的任务变成了无痛的任务。

让我们通过一个练习来发现相关的风格规则。首先,我们将寻找影响 ListView 控件的规则。

练习 10-1:在列表视图中定位选择边框

当您在 ListView 中选择一个项目时,您会得到一些视觉指示器,让您知道您已经选择了一个项目。您可以在 ListView 上设置属性,以确定用户是否可以选择零个、一个或多个项目。在本练习中,我们将深入研究动态 DOM,并找到决定 ListView 的选择边框和复选标记的样式规则。毕竟,如果我们要改变他们,我们必须找到他们。

清单 10-2 恢复了第七章中的一个列表视图示例。在图 10-1 中,注意列表项 5 上的边框、角填充和勾号。

清单 10-2 。这里有一些你以前在第七章中见过的代码。这是一个绑定到一系列数字的列表视图。

<!-- HTML snippet -->

<div id="list" data-win-control="WinJS.UI.ListView"></div>

<div id="template" data-win-control="WinJS.Binding.Template">

<div class="grid">

<div data-win-bind="innerText:this"></div>

</div>

</div>

/* CSS snippet */

.win-listview {

height: 100%;

}

.win-listview .win-item .grid {

display:-ms-grid;

-ms-grid-rows:160px;

-ms-grid-columns:160px;

background-color:gray;

color:white;

}

.win-listview .win-item .grid div {

font-size: 36px;

font-weight: bold;

-ms-grid-column-align: center;

-ms-grid-row-align: center;

}

//JavaScript snippet

var numbersList = new WinJS.Binding.List();

var list = document.querySelector("#list").winControl;

list.itemTemplate = document.querySelector("#template");

list.itemDataSource = numbersList.dataSource;

for (var i = 1; i <= 11; i++) {

numbersList.push(i);

}

9781430249832_Fig10-01.jpg

图 10-1 。右键单击第五个项目后,可以看到选择样式

该图块的这些额外功能之所以存在,是因为该项已被选中。我们知道所选项目的样式将会在 WinJS 库中,并且因为我们已经选择在这个页面上使用ui-light.css文件,所以我们知道它将会在那个文件中。让我们看看这个边框和复选标记是在样式表中什么地方定义的,这样我们就有希望修改它们。

在本练习中,您可以使用本书附带的示例项目,该项目可在www.apress.com/9781430249832http://codefoster.com/cssbookcode获得。

  1. Run the app in debug mode by pressing F5.

    当您按下F5或选择 Debug | Start Debugging 时,您的应用程序就会启动,Visual Studio 会将其调试工具与其相关联,并为您提供 DOM Explorer。你应该在 Visual Studio 中看到类似图 10-2 的东西。

    9781430249832_Fig10-02.jpg

    图 10-2 。当 Visual Studio 2012 在调试模式下运行时,非常有用的 DOM Explorer 始终可用(但仅适用于 HTML/JS 应用程序)

  2. 在运行的应用程序中,导航到第十章的部分,然后导航到清单 10-2 。

  3. 通过右键单击选择项目编号 5。

  4. Switch to Visual Studio and press the Select Element button (CTRL + B) at the top of the DOM Explorer.

    Visual Studio 应该会将您切换到正在运行的应用程序,您应该会注意到,将鼠标悬停在元素上方会以蓝色突出显示它们,以便更容易选择您想要的目标。

  5. Click on the selected ListView item.

    选择目标元素应该会将您带回 Visual Studio 中的 DOM Explorer,您选择的元素会在 DOM 中突出显示,并允许您查看定义元素的 HTML 标记。它是 live DOM,因此它不局限于您在开发中在.html文件中创作的 HTML,而是甚至包括可能已经在运行时以编程方式附加的 DOM 元素。这让人松了一口气,因为几乎整个 ListView 都是由 WinJS 在运行时创建的。记住,我们在标记中声明 ListView 所做的只是<div data-win-control="WinJS.UI.ListView"></div>

    顺便说一句,如果您不能直接单击您感兴趣的元素,您可以尝试只在它附近单击,然后通过在 DOM Explorer 中手动导航 DOM 来完成对确切元素的定位。

  6. Locate the div with a class value of win-selectionbordercontainer, expand it, and select one of its child elements.

    当您选择 ListView 项目时,您被带回 Visual Studio,并且很可能 DOM 中突出显示的元素是<div class="grid">,再往下一点,您应该会看到添加了更多的div元素(当您选择项目编号 5 时),其中一个元素有一个类win-selectionbordercontainer

    如果您展开这个div,您会发现它有四个单独的div元素组成了所选项目的边框。选择其中一个。图 10-3 应该清楚地表明选择哪个元素。

    9781430249832_Fig10-03.jpg

    图 10-3 。win-selectionbordercontainer 仅在选择项目时出现在 DOM 中

  7. Select the Trace Styles tab in the right pane of the DOM Explorer.

    您应该会看到一个影响所选元素的所有样式属性的列表,该列表应该类似于图 10-4 。

    9781430249832_Fig10-04.jpg

    图 10-4 。应用于所选对象的所有样式及其值和跟踪信息都显示在 DOM Explorer 的“跟踪样式”窗格中

    在查找颜色的来源时,Visual Studio 做的一件非常有用的事情是在“跟踪样式”窗格中的值旁边呈现一个小的正方形颜色样本。我意识到当你读到这里的时候,图 10-4 可能会以灰度呈现,但是请相信我,它显示的颜色和我们在图 10-1 中所选项目周围看到的颜色是一样的。因此,看起来我们已经找到了样式属性的来源。

  8. 展开其中一个边框颜色属性以查看原点。

例如,当你扩展border-left-color属性时(通过点击它旁边的小三角符号),你会看到类似于图 10-5 的东西。

9781430249832_Fig10-05.jpg

图 10-5 。“跟踪样式”窗格不仅显示样式规则的值,还显示它的定义位置

最右边的一列告诉我们这个样式规则已经在ui-light.css文件中定义了。我们可能已经猜到了,因为我们的大部分规则都在里面。单击文件名将在 Visual Studio 中启动该文件。如果您尚未安装 Visual Studio 2012 Update 1,您将被带到该文件,但不幸的是,您不会直接导航到样式规则。但是,安装了 Update 1 后,您将直接进入有问题的规则。您不能依赖的一件事是跟踪样式窗格和实际 CSS 文件之间的样式规则是一字不差的。有时类和伪类选择器的顺序会被重新安排。我发现图 10-5 中的样式规则来源于ui-light.css的 2250–2252 行:

win-listview:not(.win-selectionstylefilled) .win-container.win-selected .win-selectionborder {

border-color: rgb(70, 23, 180);

}

找到了所选 ListView 项目具有边框颜色rgb(70, 23, 180)(深紫色)的来源和原因,我们就有了覆盖该边框颜色或向其添加其他属性所需的工具。

然而,在我们开始覆盖和添加属性之前,让我们再看一个样式规则位置的练习。

练习 10-2:定位进度元素的填充颜色

在本练习中,我们将找到赋予progress元素填充颜色的样式规则。清单 10-3 和产生的图 10-6 显示了这些简单的进度元素之一。你可以看到进度值左边的区域默认填充了我们在 ListView 中发现的选择矩形的颜色:深紫色。

清单 10-3 。一个简单的进度标签填充到 60%

<!-- HTML snippet -->

<progress value="60" max="100"></progress>

9781430249832_Fig10-06.jpg

图 10-6 。填充区域用颜色表示,而未填充区域是浅灰色

在这个练习中,遵循你在练习 10-1 中使用的相同步骤,你应该得到一个看起来像图 10-7 的跟踪样式窗格。

9781430249832_Fig10-07.jpg

图 10-7 。“跟踪样式”窗格还指示哪些样式已被覆盖。如果您希望应用一种样式,但不知道哪一个规则覆盖了它,这将非常有帮助

如图 10-7 所示,扩展颜色属性揭示了与上一个练习有些不同的东西。有两条规则,第一条被删除了。这表明第一个样式规则在该元素上生效,但是后来(在样式表的后面)被另一个更具体的或后续的规则覆盖。回想一下第三章中的内容,这种有意的样式级联是 CSS 的一个正常功能,跟踪样式窗格非常有助于告诉我们哪些规则被应用了,哪些没有,以及为什么。

单击有问题的规则旁边的样式表链接,将我们带到ui-light.css文件中的第 2172–2175 行,它们是:

progress {

background-color: rgba(0, 0, 0, 0.2);

color: rgb(70, 23, 180);

}

我们再次找到了罪魁祸首。这个样式规则不仅确定填充颜色是相同的深紫色,而且确定背景颜色(未填充部分的颜色)是 20%不透明度的黑色。

覆盖和扩展样式

WinJS 自带的样式表是烤出来的。它们是只读的,不建议也没有必要考虑更改它们。相反,您应该覆盖并扩展内置样式。

覆盖样式

在前面的练习中,我们学习了如何找出影响 UI 中某个元素的样式规则,这很重要。重要的是要知道为什么有些元素看起来像它们的样子或者被放置在它们的位置。在ui-light.css样式表中定位样式规则似乎无关紧要,因为该文件是只读的,我们没有希望对其进行修改。

确实我们不能(或者至少不应该!)改变 WinJS 样式表中的样式,但是我们能够改变特定元素的外观,我们通过覆盖规则中的样式属性来实现。

请记住,WinJS 样式表是在所有其他样式表之前定义的。清单 10-4 展示了一个典型的 HTML 文件的<head>在 Windows 8 应用程序中的样子。

清单 10-4 。来自 Windows 8 HTML 页面的典型< head >部分,显示了样式表被引用的顺序


<head>
    <meta charset="utf-8" />
    <title>Lorem Ipsum</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.1.0/css/ui-light.css" rel="stylesheet" />
    <script src="//Microsoft.WinJS.1.0/js/base.js"></script>
    <script src="//Microsoft.WinJS.1.0/js/ui.js"></script>

    <link href="lorem.css" rel="stylesheet" />
    <script src="lorem.js"></script>
</head>

首先声明的是ui-light.css样式表,随后是仅适用于该页面的lorem.css样式表。这意味着假设特异性相同或更高,我们在lorem.css中定义的样式规则中的属性将覆盖来自ui-light.css的样式规则中的属性。

如果你还不清楚第三章中的特异性这个话题,你可能会想重温一下,因为这是它出现的地方。如果ui-light.css使用比lorem.css中的规则更具体的样式规则来设置属性,那么前者将获胜,并且没有覆盖。

这意味着我们可以简单地在本地 CSS 文件中编写一个样式规则(在前面的例子中为lorem.css),匹配已经存在的样式规则的选择器,并期望样式规则中的属性覆盖ui-light.css中的属性。你可以在清单 10-5 中看到这样的例子。

清单 10-5 。下面的进度规则与 WinJS 样式表中已存在的规则选择器相匹配。不过,定义的属性在 WinJS 样式表之后,所以我们可以预期它们会被覆盖。

<!-- HTML snippet -->
<progress value="60" max="100"></progress>

/* CSS snippet */
progress {
    background-color: gray;
    color: lightgray;
}

正如你在图 10-8 中看到的,我们的覆盖相当于反转了进度条 ?? 的颜色。填充区域现在更亮(lightgray),未填充区域现在更暗(gray)。

9781430249832_Fig10-08.jpg

图 10-8 。正如所料,填充颜色是浅灰色,背景(未填充)现在更深了

可以将术语important!(包括感叹号)添加到样式属性中,以强制发生覆盖,即使特异性值已经以其他方式确定。

清单 10-6 显示了一个 HTML 段落,它有两个似乎都适用的样式规则。但是,在特异性方面,ID 选择器胜出,所以即使.error样式规则在#footerText规则之后,它也不会有任何影响。

清单 10-6 。两个样式规则竞争设置元素的文本颜色

<!-- HTML snippet -->
<p id="footerText" class="error">
    There was an error loading the file.
</p>

/* CSS snippet */
#footerText {
    color: black;
}

.error {
    color: red;
}

然而,我们可以用清单 10-7 中的强制覆盖。

清单 10-7 。两个样式规则竞相设置同一个元素

<!-- HTML snippet -->
<p id="footerText" class="error">
    There was an error loading the file.
</p>

/* CSS snippet */
#footerText {
    color: black;
}

.error {
    color: red  important! ;
}

我建议不要使用important!关键字。今天可能会产生预期的结果,但是明天你就会遇到麻烦。这是一个明确的代码味道,通常会导致糟糕的 CSS 架构,难以阅读和维护。

扩展样式

在 WinJS(或任何其他)库中定位样式规则不仅有助于我们覆盖它们定义的属性,而且有助于我们添加它们没有定义的属性。使用相同的选择器确保了我们添加的属性将总是应用于完全相同的元素,并节省了我们从头开始编写选择器的时间。

让我们再看一下 progress 控件来说明如何扩展一个样式。在清单 10-5 中,我们覆盖了一个样式属性,方法是在本地样式表中用与 WinJS 样式表相同的选择器定义一个样式规则,然后重新定义一个已经存在的属性。为了扩展样式,我们做了同样的事情,除了我们添加了最初没有定义的样式属性。

我们在清单 10-5 中重新定义的进度样式规则已经包含了一个color属性。在清单 10-8 中,我们改变了覆盖属性,给进度控件一个灰色的填充颜色和一个白色的未填充颜色,并且我们添加了一个新的属性(border),这在 WinJS 中没有定义。

清单 10-8 。除了覆盖已经存在的样式属性,我们还可以指定没有的新属性

<!-- HTML snippet -->
<progress value="60" max="100"></progress>

/* CSS snippet */
progress {
    background-color: white;
    color: gray;
    border: 1px solid gray;
}

添加灰色的实线边框给了我们的 progress 元素一个包含框,如图 10-9 所示。

9781430249832_Fig10-09.jpg

图 10-9 。边框赋予此进度指示器全新的外观

一些例子有助于了解什么样的样式覆盖和扩展在 Windows 8 应用程序中是有用的。我们已经看到了进度控制的例子。接下来我们再看两个。我们将看看:

  • 格式化范围输入元素上的滑块
  • 更改分级控制中的默认图标

格式化范围输入元素上的滑块

让我们来改变默认的范围输入元素(<input type="range"/>),使用户触摸的滑块是圆形的,其轮廓颜色与元素较低的填充颜色相匹配。图 10-10 显示了一个范围输入元素的默认外观。

9781430249832_Fig10-10.jpg

图 10-10 。范围输入元素的默认外观

“跟踪样式”窗格为我们提供了一些关于样式的有用信息,这些样式使范围输入元素看起来如此,并帮助我们知道在我们自己的样式选择器中使用什么选择器来覆盖和扩展属性并影响我们所追求的更改。清单 10-9 显示了我们应该放在本地样式表中的 CSS 来完成这个任务。

清单 10-9 。用于在范围输入上获得圆形滑块的 HTML 和 CSS

<!-- HTML snippet -->
<input type="range" />

/* CSS snippet */
input[type=range]::-ms-track {
    height:16px;
}
input[type=range]::-ms-fill-lower {
    background-color:gray;
}

input[type=range]::-ms-thumb {
    background-color:white;
    border:1px solid gray;
    height:14px;
    width:14px;
    border-radius:14px;

}

9781430249832_Fig10-11.jpg

图 10-11。对于你的应用程序的个性来说,圆形滑块可能是一个更好的设计选择

更改评级控件中的默认图标

接下来,我们将看看评级控制。与 range input 元素不同,Rating 控件是由 WinJS 提供的自定义控件。一般来说,WinJS 控件会比基本 HTML 元素更复杂,因为它们通常由大量的基本 HTML 元素组成。当深入 WinJS 控件了解如何覆盖和扩展样式时,“跟踪样式”窗格是我们最好的朋友。

我将改变默认的 5 颗星星的数量,然后我将改变图标,以摆脱无聊,旧的恒星系统。通常,评级系统使用五颗星,并要求用户从一到五进行评级。这是一个常见的系统,但假设我们正在为一家鸡肉餐馆开发一个应用程序。我们可能想给顾客一个从 1 到 10 的系统,用鸡而不是星星来评价他们的饭菜。这才是有个性的应用!

图 10-12 显示了选择了三颗星的评级控件的默认外观。

9781430249832_Fig10-12.jpg

图 10-12 。默认分级控制使用星形图标和最高分级值 5

我在http://office.microsoft.com的时候在免费的微软 Office 图片库里发现了一个小鸡图标。然后我用我最喜欢的矢量编辑工具——CorelDRAW——为图像可能出现的所有状态做了四种变化。结果如图 10-13 中的所示。

9781430249832_Fig10-13.jpg

图 10-13 。小鸡图标的四种变体,涵盖了分级控制元素可能处于的每个状态

在清单 10-10 中,我声明了一个 Rating 控件,并给了它一个任意的值 6.4 来说明控件如何显示部分值。通常averageRating的值不会被硬编码,而是从数据表中计算出来。

通过简单的搜索,很容易在ui-light.css文件中找到评级控制样式。请注意样式规则如下所示:


.win-rating .win-star:before {
    content: "\E082";
}

如果你还记得《??》第三章,那么content属性是一个有趣的属性,它通过样式化来添加内容。这就是默认分级控制获得星级的方式。如果你能找到一个合适的,你可以改变这里引用的字符,但是你也可以放弃这个字符而使用一个图像来代替。这就是我们在清单 10-10 中所做的。再看覆盖.win-rating .win-star:before的样式规则。它清除了内容值,因此省略了星形符号。

一旦星星消失了,我们可以自由地添加我们自己的图像,你可以在清单 10-10 中的样式规则中看到我们是如何做的。

清单 10-10 。把五星变成十只鸡的密码

<!-- HTML snippet -->
<div class="chicken" data-win-control="WinJS.UI.Rating"
    data-win-options="{averageRating: 6.4, maxRating: 10}"></div>

/* CSS snippet */
.win-rating.chicken .win-star:before
{
    content: ""; /*hide the default star glyph*/
}

.win-rating.chicken .win-star
{
    background-repeat: no-repeat;
    background-origin: content-box; /*to allow putting padding between chickens*/
    background-position: left;
    height: 30px;
    background-size: 100% 100%;
}

.win-rating.chicken .win-star.win-empty, .win-rating.chicken .win-star.win-tentative.win-empty
{
    background-image: urlimg/chicken_empty.svg");
}

.win-rating.chicken .win-star.win-full, .win-rating.chicken .win-star.win-tentative.win-full
{
    background-image: urlimg/chicken_full.svg");
}

.win-rating.chicken .win-star.win-disabled.win-empty
{
    background-image: urlimg/chicken_disabled_empty.svg");
}

.win-rating.chicken .win-star.win-disabled.win-full
{
    background-image: urlimg/chicken_disabled_full.svg");
}

图 10-14 显示了生成的小鸡图标。我设计的chicken_empty.svg图像看起来像一个占位符,这样用户就能清楚地知道他能选择多少种可能的鸡肉评级。

9781430249832_Fig10-14.jpg

图 10-14 。我们看到平均 10 只鸡中有 6.4 只

也有可能(尽管清单 10-10 中的代码还没有到此为止)区分用户选择的图像和代表所有用户选择的平均图像。我们可以通过向一些规则添加一个类.win-user并向其他规则添加一个类.win-average来实现。

覆盖和扩展已经定义的样式规则是非常常见的,但 WinJS 样式表实际上只是一个起点,您会发现有很多理由在您的应用程序中创建自己的样式规则。在下一节中,我们将会看到一些技巧以及一些在应用程序中定义我们自己风格的策略。

定义样式

在很多情况下,你会覆盖现有的风格,更多的情况下,你会定义自己的风格。对于您自己的样式定义,您可能正在创建简单的类规则,或者您可能正在创建您自己的整个自定义控件,包括它自己的类、它自己的默认样式,以及任何开发人员使用它来覆盖它的样式的能力,就像我们对 WinJS 库所做的那样。

在下面的每个例子中,我都将包含完整的代码清单。目标是学习 CSS 样式,并且解决方案的整个上下文应该对此有所帮助。您将在 CSS 样式中看到前缀,如.lst1011,它将 CSS 的范围限定为特定的代码清单。

ListView 中的特色项目

举个例子,假设我们正在开发一个销售点应用程序的 hub 页面,上面有两个 ListView 控件。第一个是产品列表,第二个是可能适用的折扣列表。折扣列表中的项目都是相同的,但是产品列表中的项目可能是“特色”的,以引起注意。特色项目的概念应该只适用于第一个列表。特色项目应该有一个纯灰色的背景,而所有其他非特色项目应该有一个简单的白色背景。使用 ID 选择器将样式限定在适当的列表中,使用类选择器处理特色项目。

本例中的解决方案相当简单,但是它涉及到针对产品列表(而不是折扣列表),并且在产品列表中,它涉及到区分特色产品和其他产品。显然可能有不止一个特色项目,所以我们应该使用一个类选择器。类选择器就是为此而生的。

清单 10-11 中的表示了整个解决方案,下面是对代码的解释。

清单 10-11 。在列表视图中显示项目的完整解决方案代码

<!-- lst1011.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Listing 10-11</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.1.0/css/ui-light.css" rel="stylesheet" />
    <script src="//Microsoft.WinJS.1.0/js/base.js"></script>
    <script src="//Microsoft.WinJS.1.0/js/ui.js"></script>

    <link href="lst1011.css" rel="stylesheet" />
    <script src="lst1011.js"></script>
</head>
<body>
    <div class="lst1011 fragment">
        <section aria-label="Main content" role="main">

            <div id="productsList" data-win-control="WinJS.UI.ListView"></div>
            <div id="discountsList" data-win-control="WinJS.UI.ListView"></div>

            <div id="productItemTemplate" data-win-control="WinJS.Binding.Template">
                <div xsdata-win-bind="className:isFeatured
                    lst1011.Converters.isFeaturedToString;" >
                    <h3 data-win-bind="innerText:name"></h3>
                    <div data-win-bind="innerText:price" class="win-type-xx-small"></div>
                </div>
            </div>
            <div id="discountItemTemplate" data-win-control="WinJS.Binding.Template">
                ...
            </div>
        </section>
    </div>
</body>
</html>

/* lst1011.css */
.lst1011 #productsList .widget {
    width:200px;
    height:100px;
    border: 1px solid gray;
    padding:10px;
}
.lst1011 #productsList .widget.featured {
    background-color: lightgray;
}

// lst1011.js
(function () {
    "use strict";

    var products = [
        {name: "Widget 1", isFeatured: true, price: 72.99},
        {name: "Widget 2", isFeatured: false, price: 149.99},
        {name: "Widget 3", isFeatured: true, price: 14.99},
        {name: "Widget 4", isFeatured: true, price: 50.99},
        {name: "Widget 5", isFeatured: false, price: 71.99},
        {name: "Widget 6", isFeatured: false, price: 65.99},
        {name: "Widget 7", isFeatured: false, price: 66},
        {name: "Widget 8", isFeatured: false, price: 165.99},
        {name: "Widget 9", isFeatured: true, price: 60.99},
        {name: "Widget 10", isFeatured: false, price: 6.99}
    ];

    WinJS.Namespace.define("lst1011.Converters", {
        isFeaturedToString: WinJS.Binding.converter(function(value) {
            return (value ? "widget featured" : "widget");
        })
    });

    WinJS.UI.Pages.define("/pages/chapter10/lst1011/lst1011.html", {
        ready: function (element, options) {
            var productsList = element.querySelector("#productsList").winControl;
            productsList.itemDataSource = new WinJS.Binding.List(products).dataSource;
            productsList.itemTemplate = element.querySelector("#productItemTemplate");

        }
    });

})();

9781430249832_Fig10-15.jpg

图 10-15。特色项目以灰色背景突出显示

lst 1011 . html

lst1011.html文件声明了产品和折扣列表视图及其惟一的 ID 属性。这个 ID 属性很重要,因为它允许我们确定将要创建的样式的范围,以便只影响产品列表。

产品项模板中的第一个 div 包含一个名为data-win-bind的属性,在该属性中我们定义了(粗体)数据绑定项的一个属性(isFeatured)和元素的一个属性(className)。我们还实现了一个叫做转换器的东西,这当然不在我们的主题范围之内。然而,我想让您注意到这一点,这样您就不会想知道属性值isFeatured如何变成类名,比如widget featured

lst1011.js

lst1011.js 文件负责的列表绑定也超出了范围,但是您已经见过几次了,所以无论如何您可能会学到语法。然而,代码定义了一个小部件数组,每个小部件都有一个名为isFeatured的属性,其中的布尔值true应该表示一个特色产品。这个简单的数组变成了一个绑定。列表,以便它可以绑定到 ListView。

lst1011.css

最后,我们的 lst1011.css 文件使特色项目真正脱颖而出。它定义了两个样式规则:一个用于具有widget类的项目,另一个用于同时具有widgetfeatured类的项目。

所有的小部件都应用了第一个规则,但是只有有特色的小部件获得了额外的background-color属性,该属性将项目的背景设置为lightgray

自定义 WinJS 控件

在下面的例子中,假设我们发现自己重复了一些我们不希望的标记,因此我们决定创建一个自定义的 WinJS 控件来封装它。我们发现重复的标记是地址字段:地址、城市、州和邮政编码。在我们的应用程序中,我们需要收集帐单和送货地址。

WinJS 不仅提供了许多自定义的复合控件供我们使用,还允许我们创建自己的控件。我们完全用 JavaScript 创建控件,然后用与内置 WinJS 控件相同的方式在 HTML 中声明它们。在我看来,这是一个非常优雅和强大的模式。

从消费页面(我们实际上使用这个自定义控件),我们仍然需要访问地址控件的单个元素,这样我们就可以,例如,向地址字段添加一些 CSS 属性。这正是我们在这个例子中要做的。我们将在两个控件中的地址周围添加一个双边框,以显示我们具有该功能。清单 10-12 把所有这些都放在一起,后面是对每个文件的解释。

清单 10-12 。创建自定义 WinJS 控件并在标记中轻松声明其中两个控件的代码

<!-- lst1012.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Listing 10-12</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.1.0/css/ui-light.css" rel="stylesheet" />
    <script src="//Microsoft.WinJS.1.0/js/base.js"></script>
    <script src="//Microsoft.WinJS.1.0/js/ui.js"></script>

    <link href="lst1012.css" rel="stylesheet" />
    <script src="AddressControl.js"></script>
    <script src="lst1012.js"></script>
</head>
<body>
    <div class="lst1012 fragment">
        <section aria-label="Main content" role="main">
            <h3>Billing Address</h3>
            <div data-win-control="lst1012.Controls.Address"></div>

            <h3>Shipping Address</h3>
            <div data-win-control="lst1012.Controls.Address"></div>
        </section>
    </div>
</body>
</html>

/* lst1012.css */
.lst1012 .abc-address {
    background-color: lightgray;
}

// lst1012.js
(function () {
    "use strict";

    WinJS.UI.Pages.define("/pages/chapter10/lst1012/lst1012.html", {
        ready: function (element, options) {

        }
    });
})();

// AddressControl.js
WinJS.Namespace.define("lst1012.Controls", {
    Address: WinJS.Class.define(
        function (element) {
            element.innerHTML =
                "Address: <input class='abc-address ' style='width:300px;'/><br/>" +
                "City/St/Zip: <input class='abc-city '/><select class='abc-state '>
                <option></option><option>Alabama</option><option>Alaska</option>
                <option>...</option></select><input class='abc-zip ' style='width:80px;'/><br/>";
        }
    )

});

9781430249832_Fig10-16.jpg

图 10-16。两个地址字段都按照我们确定的方式进行了样式化,因为地址字段被赋予了一个类名,并且页面样式表中的规则已经将该类作为目标

地址控制

AddressControl.js文件完成了定义自定义地址控件的所有工作,我们完全控制它生成的标记,包括控件消费者可以使用的类属性(粗体)。

此时,这些类属性是您真正需要注意的唯一事情,所以如果您现在推迟了 JavaScript 学习,您可以浏览文件的其余部分。请注意,我已经大大简化了自定义控件的创建。一个实现良好的自定义控件当然会有更多的功能。

lst 1012 . html

lst1012.html文件中,您会注意到对AddressControl.js文件的引用和引用控件的标记(粗体)。我在这里手动声明了自定义控件的两个实例,但是它们也可以在 ListView 的模板中声明,或者动态添加到页面中。

lst 1012 . CSSt1]

最后,lst1012.css中一个简单的 CSS 规则证明了我们可以通过在地址字段周围添加双边框来访问单个元素。如果您是自定义控件的作者和消费者,那么您将不会有问题匹配类定义(在您的控件的 JavaScript 文件中)和样式定义(在您的 CSS 文件中)。如果您正在使用第三方控件,那么发现它们定义了什么元素和类是一件简单的事情,就像我已经展示的那样使用 DOM Explorer。

lst1012.js

lst1012.js文件本质上是空的,但是我把它留在了清单中,这样你就知道它没有在你背后做任何事情。

摘要

我们已经看到了如何深入 DOM 并查看其元素的结构,无论这些元素是由我们显式编写的还是通过包含自定义 WinJS 控件编写的。然后,我们学习了如何从 WinJS 库中找到已经应用于这些元素的样式规则。

找到这些元素和已经针对它们的样式规则是理解如何覆盖和扩展这些样式以赋予应用程序我们自己的品牌和个性的重要的第一步。

除了覆盖现有的内容,我们还能够显式地创建自己的元素和类,或者通过创建自定义控件,然后定义自己的样式来控制它们的外观。

本章中的例子是你可以在你的应用程序中设计元素的无数方法中的一个很小的例子。请记住,您也可以使用相同的技术在其他 CSS 库中定位和覆盖样式(我们将在附录 A 中讨论除 WinJS 之外的 CSS 库)。

十一、附录 A:CSS 库和资源

image 注意一些你可能想加入书签的链接。

我们已经大致介绍了 CSS,并重点介绍了它如何应用于创建 Windows 8 应用程序,但我可以向您保证,还有更多的内容需要介绍。要掌握 CSS 和 Windows 8 应用程序的创建,你需要花费数百个小时实际编写应用程序,并且你需要大量的信息。我不能用几百个小时来帮助你,但幸运的是,我可以提供大量的信息。

在这个附录中,我将尝试从大量的信息中提取 CSS 的精髓。我会介绍一些 CSS 库,助手,以及一些对我帮助很大的学习和参考网站。

图书代码

image www.apress.com/9781430249832http://codefoster.com/cssbookcode

本书中的清单包含了相当多的代码,您可能希望选择复制一些代码粘贴到您自己的项目中。你可以通过访问www.apress.com/9781430249832或我的网站http://codefoster.com/cssbookcode获得这本书的所有源代码,在那里你将被引导到 CodePlex 上的源代码。代码编译成一个 Windows 8 应用程序,该应用程序按章节索引这本书的所有代码列表,并让您看到它们的运行。

代码方式

image codeshow.codeplex.com

codeSHOW 是一个用 HTML、CSS 和 JavaScript 编写的 Windows 8 应用程序,唯一的宗旨是帮助你学习用 HTML、CSS 和 JavaScript 制作自己的应用程序。你可以通过访问codeshow.codeplex.com访问 codeSHOW 项目的所有源代码,或者如果你安装了 Windows 8,你可以浏览 aka.ms/codeshowapp 并下载该应用程序。

codeSHOW 应用程序展示了许多演示,并允许你查看代码来一窥创建它所用的 HTML、CSS 和 JavaScript。这是学习如何实现一个你知道你需要的特性的好方法。然而,这并不是应用程序架构中的重要一课。为此,欢迎您从 CodePlex 链接下载 codeSHOW 项目本身的完整源代码,并看看 codeSHOW 是如何创建的。

环球网财团

image w3.org/style/css

万维网联盟(又称 W3C)是处理互联网底层技术的国际标准机构(有很多)。W3C 于 90 年代中期在麻省理工学院(MIT)成立。它是由一个叫蒂姆·伯纳斯·李的人创建的,直到今天他还是代理主管。

正如你可能想象的那样,W3C 有自己的工作要做,促进对如此多的 web 标准提案的讨论,然后决定该组织将推荐哪些标准。每一个被提议的标准都经历了四个阶段:工作草案、候选推荐、提议推荐,最后是 W3C 推荐(REC)。

如果你在 web stack 工作,你应该经常访问w3.org网站。其他所有的讨论(包括这本书)甚至 web 标准的实现相对于w3.org来说都是猜想,而w3.org才是所有应该被衡量的标准。

你会期望一个来自网络标准组织的相当不错的网站,而w3.org不会让人失望。要深入研究所有关于 CSS 的 W3 标准,请访问w3.org/Style/CSS/,花些时间四处看看。

微软开发者网络(MSDN)

image http://msdn.microsoft.com/en-us/library/ie/ms531209(v=vs.85).aspx

MSDN 网络是一个巨大的图书馆,是各种微软开发(包括 CSS)的大量文档来源。你肯定会发现微软实现 CSS 标准的一些角落,我在本书中没有找到足够的篇幅来介绍。

当你已经在 CSS 的某些方面参考了 W3C 标准,并且想知道 Internet Explorer 或 Windows 8 应用程序中行为的细微差别时,我建议你看看 MSDN。

CSS 技巧

image css-tricks.com

Chris Coyier 的 CSS 技巧让我受益匪浅。这是一个美丽的网站,有丰富的关于 CSS 的信息。这些文章是一流的,信息量大得惊人。我通常要么搜索网站,要么滚动到底部,点击“更多博客文章”来浏览文章。

CSS Tricks 不仅有高质量的文章,社区论坛也不错。你可以在 CSS 问题上得到一些真正的帮助,希望你能从别人那里找到一些你可以回答的问题。当我寻找一点设计灵感时,我倾向于访问 CSS 技巧。画廊区在这方面特别有效。

Web 平台文档

image webplatform.org

Web Platform Docs 网站是一个完全由社区驱动的 Web 平台文档网站。它在所有重要的 web 技术上提供了很多帮助,比如 HTML、CSS、JavaScript、SVG 等等。

在我看来,这个网站还处于初级阶段,设计还有点困难,但它是一个很好的开放平台,可以用来学习网络平台,帮助教授他人,培养社区。

减去

image lesscss.org

这不仅仅是另一个链接到一个有好的 CSS 文档的站点。哦不。少即是多!LESS 是一个非常简单但是非常有帮助的 CSS 助手。LESS 允许我们以一种更加智能和程序化的方式来描述我们的风格。

我们编写更少的代码并在 HTML 页面上引用它,就像我们使用 CSS 一样,但是在页面被请求之前,处理器将更少的代码转换成 CSS,这就是我们的 HTML 文件所能看到的全部内容。LESS 创建了您本可以自己创建但不想创建的 CSS。

在得到处理之前,并非所有 LESS 代码都是有效的 CSS 代码。相反,所有 CSS 代码都是有效的少代码。这意味着你可以把你现有的 CSS 文件(无论其大小)转换成一个更小的文件,只要改变它们的文件扩展名。然后,您可以开始一次引入一个较少的功能。

LESS 还允许定义变量、混合、嵌套规则以及函数和操作。让我们花一点时间来看看这些,然后看看如何在 Windows 8 中设置得更少。

变量

为了引入变量,让我们用一个例子:你的设计者(即使你是你的设计者 ??)告诉你你的主题颜色将是 R71 G82 B49,你可以想象你将会在你的 CSS 文件中洒满 ??。这种特定的颜色可能会成为主页上项目的边框、标题中文本的颜色以及应用程序栏的背景。

在整个文件中散布这样的值会让你觉得很糟糕。这意味着如果你的设计师改变了主意,你将会在不止一个地方改变颜色,你将会犯一个错误。

但是,如果您使用较少,您有机会定义一个变量来表示这种原色。你甚至可以称这个变量为primaryColor。在列出 A-1 的中可以看到这种颜色的定义以及随后从几个地方引用它的例子。

列举 A-1。 一个少变量用来存储一种颜色

@primaryColor: rgb(71,82,49);

#myListView .win-item {
    border: 1px solid @primaryColor;
    color: @primaryColor;
}

.win-appbar { background-color: @primaryColor; }

列出 A-1 展示了实际颜色——rgb(71,82,49)——是如何只定义一次的。如果在将来的某个时候,我们的设计师通知我们,这个色调应该调整一点,它只需要在一个地方完成。

Mixins

Mixins 允许我们中混合一个类和另一个类。也就是说,我们可以让一个类继承另一个类的所有样式属性。在清单 A-2 中,您将看到一个设置边框和背景颜色的引用类(.reference)。两个后续的类(.styleA.styleB)应该都采用相同的边框和背景颜色属性,但也需要添加一些自己的属性。

清单 A-2。 少了一个混信的例子

.reference {
    border: 1px solid black;
    background-color: gray;
}

.styleA {
    .reference;
    font-size: large;
}

.styleB {
    .reference;
    font-weight: bold;
}

在某些情况下,Mixins 可以节省大量的输入。

除了简单地引用另一个类,您还有机会用 mixin 向被引用的样式传递一个参数。明白我在清单 A-3 中的意思。

清单 A-3。 带有传递参数的 mixin

.reference (@borderWidth: 1px) {
    border: @borderWidth solid black;
    background-color: gray;
}

.styleA {
    .reference;
    font-size: large;
}

.styleB {
    .reference(2px);
    font-weight: bold;
}

你能看到那里发生了什么吗?.reference样式现在接受一个名为borderWidth的参数,默认值为1px.styleA属性包含了.reference,但是没有为borderWidth参数指定一个值,因此它获得了.reference规则的所有属性,并为边框宽度指定了一个默认值。然而,.styleB属性包括。reference但是为borderWidth参数指定了一个值2px,因此它获得了具有 2 像素边框的.reference规则的属性。

Mixins 允许一些定义非常优雅的 CSS,这些 CSS 具有表达性、简洁和集中化,因此易于维护。

嵌套规则

你可能已经注意到一些 CSS 样式的选择器会变得很长。要访问主节中名为myDivdiv元素中的类error列表中的列表项,您最终会得到类似于section[role=main] #myDiv .error li { ... }的东西,它们甚至会比这长得多。

为了避免这种情况,可以使用 LESS 的嵌套规则。嵌套规则允许您在一个规则中定义另一个规则,推论是每个规则的父选择器应该应用于它。清单 A-4 中的两个 CSS 部分具有相同的效果。

列举 A-4。 对 CSS 类进行比较,以较少使用嵌套规则

/* using classic CSS */
section[role=main] {
    font-size: large;
}

section[role=main] #myDiv {
    font-weight: bold;
}

section[role=main] #myDiv .error li {
    color: red;
}

/* using LESS nested rules */
section[role=main] {
    font-size: large;
    #myDiv {
        font-weight: bold;
        .error {
            li {
                color: red;
            }
        }
    }
}

请注意在列出 A-4 的经典 CSS 部分中出现的冗余。这种重复导致维护困难和代码容易出错。在这种情况下,LESS 版本并没有短很多,但关键是它的定义更符合逻辑,可读性和可维护性更好。

请记住,即使你的应用程序中使用了更少的内容,客户端最终还是会使用生成的 CSS。这不是浏览器引擎必须要破译的东西。它仍然以预期的格式接收 CSS 样式规则。

功能和操作

我们应该关注的 LESS 的最后一个特性是它提供的函数和操作。

LESS 给了我们一些计算值的智能方法,很多时候计算出的值是有帮助的。如果一个基线长度是 10 个像素,但是一些元素应该比我们应该能够计算的差多或少 2 个像素,不是吗?

函数和运算经常与变量一起使用。我们来看几个例子。首先,我们将查看列表 A-5 中的一些长度计算。

清单 A-5。 少计算确定边框宽度

@borderWidth: 10px;

.styleA {
    border-width: (@borderWidth - 2);
    border-style: solid;
    border-color: black;
}

.styleB {
    border-width: (@borderWidth + 2);
    border-style: solid;
    border-color: black;
}

清单 A-5 中的.styleA规则会产生一个 8 像素的边框,而.styleB规则会产生一个 12 像素的边框。当您决定更改基本边框宽度时,比显式指定这些值更有优势。此时,所有计算的边界都会自动调整。

这些计算也可以在颜色上完成,并且有一些函数给了我们一些更好的功能。添加颜色可能与您的直觉相反,但它实际上只是添加颜色的单个红色、绿色和蓝色成分。一种十六进制颜色#123456加上另一种颜色#111111会产生一种颜色#234567。颜色也可以相乘、相除或相减。

除了基本的数学运算,还有一些颜色函数可用。如果我有一个基色#77C121,并且想要一个暗 10%的颜色,我可以避免设计时往返于我最喜欢的图形包,而是只使用 LESS 的darken()函数。我将在清单 A-6 中展示一个这样的例子。

清单 A-6。 使用变暗()函数来计算更暗的颜色

@baseColor: #77C121;

.normal {
    background-color: @baseColor;
}

.disabled {
    background-color: darken(@baseColor, 10%);
}

在清单 A-6 中,一个被禁用的项目会自动获得一个比基色暗 10%的阴影。这大概是我在 LESS 里最喜欢的一招了。能够创建基本调色板,然后从中表达衍生颜色,这是一种非常优雅的应用程序风格。对基色的简单改变相当于自动创建全新的衍生配色方案。

设置

设置您的 Windows 8 应用程序以减少使用并不困难。

首先,让我们回忆一下典型的 web 架构,以及 LESS 适合它的地方。CSS 通常存在于 web 项目中,从静态服务器文件提供给客户端。对于使用 LESS 的 web 项目,CSS 代码本质上是动态的,因为 LESS 处理器必须从 LESS 代码中生成它。这个过程既可以发生在服务器上,也可以发生在客户端。

在 Windows 8 应用中,我们的架构与典型的 web 应用略有不同。Windows 8 设备既是服务器又是客户端,因此有效地消除了服务器和客户端之间的界限。因此,对于应用程序,我建议按照以下步骤使用 LESS JavaScript 库进行处理:

  1. 更改 CSS 文件的扩展名。css 到。较少的
  2. 将您的样式表引用更改为:<link rel="stylesheet/less" type="text/css" href="myLessStylesheet.less">。注意rel(关系)属性值- stylesheet/less
  3. lessjss.org下载less.js库,放到你的 Windows 8 应用的js文件夹中。请记住,如果您使用 Windows 资源管理器将它直接放入应用程序的文件夹结构中,那么您需要在 Visual Studio 中显示隐藏的文件,然后右键单击less.js文件并选择“包含在项目中”。
  4. 添加对您下载的less.js库的引用。我建议你把这个放在default.html页面上。它应该看起来像:<script src="/js/less.js" type="text/javascript"></script>

我觉得你现在穿得少了,我想你会喜欢的。

其他在线图库

image cssflavor.com``csscollection.com``csselite.com``csstemplates.net

你可以在网上找到许多图库,它们可以作为学习工具、灵感来源和项目模板来源。CSS Flavor、CSS Collection、CSS Elite 和 CSS Templates 等网站包含使用 CSS 创建的示例网站的良好集合。

花些时间浏览这些网站提供的图库,当你找到一个你喜欢的模板或一个有你正在寻找的样式或布局功能的模板时,只需复制并研究它的 CSS 样式表。请务必阅读各种网站的使用说明,以确保您符合要求。

当你需要针对一条信息来实现一个新功能或解决一个具有挑战性的问题时,像我在本附录中概述的那些参考网站是非常好的,但是仅仅花时间浏览内容也是非常有益的。尝试一下,你会发现自己学到了你从来不知道自己需要的东西。浏览在线 CSS 图库是学习如何应用所学理论的另一个好方法。所以不要随便找好的代码就用。写好代码,分享出来!成为开发者社区的一员是加速你学习的另一种方式,也是享受学习的一种方式。

十二、附录 B:样式化 SVG

image 注意太多的现代开发者忽略了矢量图形在图像生成和显示解决方案方面的强大功能。

SVG 格式介绍

可缩放矢量图形(SVG) 是用于定义基于矢量图形的的 XML 格式。像 HTML、CSS 和 JavaScript 一样,SVG 是 W3C 的众多项目之一。这也不是一个新项目——它从 1999 年就开始了。

尽管 SVG 很难获得关注,但我认为这有几个不同的原因。一个原因是矢量图形不像位图那样为人们所熟悉。很多人都知道像 Adobe Photoshop 这样的图形软件包,但是很少有人深入研究 Adobe Illustrator 或其他任何处理矢量图形的优秀工具。如果你是矢量图像编辑的新手,那么让我澄清一下。

首先,考虑位图图像如何存储为颜色值的二维矩阵。当正方形颜色值(像素)足够小时,人眼会被骗去看图像。另一方面,矢量图像的存储方式完全不同。在矢量插图中,我们使用的不是像素的集合,而是对象的集合,这些对象可能是熟悉的形状(如矩形和椭圆形),也可能是由许多点和曲线值定义的复杂线条。反过来,这些对象由顶点(或其他基本形状描述符)组成。存储的是这些顶点或形状描述符,而不是像素矩阵。

当您比较位图和矢量图形时,SVG 有一些很大的优势。其中一些优势会影响图形的创建(当你从零开始或者编辑一个现有图形的时候),其他的会影响矢量图像在一个正在运行的应用中的使用。

对于图形创建,SVG 的一些显著优势是:

  • 以矢量格式创建和存储的插图可缩放。这意味着你存储多大的矢量插图并不重要,因为你总是可以从它生成你喜欢的位图。您可以使用相同的矢量图像来打印邮票或路边广告牌。
  • 矢量插图更容易修改。组成合成图像的所有组件对象都单独存在,并且可以单独修改。你可以打开一个摩托车的矢量图像,只选择前灯,然后修改它的形状。对于位图图像来说,这一壮举要困难得多。
  • 由于可以进行修改以匹配您自己项目的配色方案,因此使用基于矢量的库存图像通常要容易得多。

在跑步应用中使用矢量图像的其他一些显著优势包括:

  • 对于几乎所有的图像,矢量版本将会小得多。描述一个圆的形状并不需要像用像素明确描绘它那样多的信息。
  • 矢量插图,由描述的物体形状组成,更容易动画化。软件可以计算两点之间的中间步骤,颜色值的差异,甚至是将一个形状变形为另一个形状的数学模型。用同样的方式制作像素动画几乎是不可能的。
  • 矢量插图可以根据用户碰巧使用的屏幕大小调整大小。Windows 8 会自动缩放应用中的所有资产。为了让你的位图图像看起来不错,你必须提供它的多个版本。但是,如果资源是矢量图像,您不需要这样做,并且可以确保它在任何大小下看起来都很棒。

我发现太多的开发者(甚至设计师!)仍然试图在 Photoshop 等位图编辑器中完成所有的图形工作。位图编辑器对于照片、绘画和许多其他事情来说是非常优秀的,但是在许多图形创建用例中,学习和切换到矢量包将极大地提高您的生产力。

我选择的矢量编辑器是 CorelDRAW。我已经使用 Corel 的矢量包很多年了,发现它的界面非常精确和直观。还有一些其他专业的矢量设计应用程序可供你研究,但是如果你正在寻找一个仍然提供很多功能的免费应用程序,试试 inkscape.org 的 InkScape。这个开源的矢量图形编辑器做的非常好。InkScape 没有自己的矢量文件格式,而是完全依赖于 SVG 格式。

矢量图像的另一个主要优点是可以制作动画。因为矢量图像是被描述的对象(而不仅仅是像素),所以它们可以被动态地改变形状,并且因为矢量图像是由离散的元素组成的,所以那些元素可以被相当容易地移动。想象一下一只基于向量的狗的下颚移动来模拟说话。

HTML 中嵌入的 SVG

SVG 只是 XML,而 HTML 几乎是 XML,所以这两种语言很好地结合在一起,SVG 可以毫不费力地嵌入到 HTML 文档中。这两种语言都类似于 XML,这也是 CSS 能够自然地对它们进行样式和布局的原因。

过去,在 HTML 中嵌入 SVG 要困难得多,但是 HTML5 使它变得容易得多。HTML 不是严格意义上的 XML,但 SVG 是,所以嵌入式 SVG 需要清楚地定界并给定一个适当的名称空间。清单 B-1 显示了在 HTML 中放入一些 SVG 的样子。

清单 B-1。SVG 中带有填充属性的简单圆

<!-- HTML snippet -->
<!DOCTYPE html>
<head>
<title>SVG</title>
<meta charset="utf-8" />
</head>
<body>
<h2>SVG in HTML5</h2>
<svg id="mySvg" height="200" FontName2">http://www.w3.org/2000/svg ">
    <circle id="graycircle" r="75" cx="75" cy="75" fill="gray" />
</svg>
</body>
</html>

清单 B-1 中的代码描述了你在图 B-1 中看到的半径为 75 像素的圆。它在一个具有与http://www.w3.org/2000/svg相等的xmlns (XML 名称空间)属性的svg标签中这样做。如果名称空间对你来说是新的,只需知道这实际上并没有链接到 w3.org 网站。它只是使用这个 URL 作为名称空间来惟一地将这个标记的内容标识为 SVG。

9781430249832_AppB-01.jpg

图 B-1 。灰色圆圈

SVG 的 CSS 属性

在清单 B-1 中,我们以一个灰色圆圈结束,因为我们使用了值为grayfill属性。您可以在 SVG 元素上使用类似这样的各种属性来设计它们的样式,但是我不推荐这种方法。最好所有的样式都在 CSS 中完成,以便以后可以修改。清单 B-2 产生了与清单 B-1 相同的灰色圆圈,只是这次的灰色填充是在一个单独的样式表中使用 CSS 样式属性提供的,而不是使用 fill 属性。

清单 B-2。 同样的灰色圆圈除了使用了 CSS 属性

<!-- HTML snippet -->
<!DOCTYPE html>
<head>
<title>SVG</title>
<meta charset="utf-8" />
</head>
<body>
<h2>SVG in HTML5</h2>
<svg id="mySvg" height="200" FontName2">http://www.w3.org/2000/svg ">
    <circle id="graycircle" r="75" cx="75" cy="75" />
</svg>
</body>
</html>

/* CSS snippet */
#graycircle {
    fill: gray;
}

这是我将在本附录中介绍的所有内容的基础。我们将探索可以在 CSS 中指定的影响 SVG 图形的各种样式属性。您会发现这些属性中有许多是 HTML 共有的,但也有一些是 SVG 特有的。我将介绍一些更常见的属性。有关所有可用属性的更多信息,请访问http://www.w3.org/TR/SVG11/propidx.html,在那里您可以找到详尽的参考资料。

填充

在 HTML 中,使用color属性来指定像文本这样的东西的前景色,使用背景色来指定应该填充元素背景的颜色。在 SVG 中,使用fill属性来指定图形元素的颜色。你已经在清单 B-2 中看到了这一点,其中我们的圆圈用灰色填充

除了指定元素应该填充的颜色,您还可以指定不透明度。在清单 B-3 中,我使用 CSS 网格将一个 SVG 元素堆叠在一段文本的顶部。添加一个设置为0.5 (50%)的fill-opacity属性,然后通过 SVG 使段落可见,正如你在图 B-2 中清楚看到的。

清单 B-3。 一个部分不透明的灰色圆圈,使其在一段文本上透明

<!-- HTML snippet -->
<div id="parent">
    <p id="text">Lorem ipsum dolor sit amet . . . </p>
    <svg id="mySvg" height="200" FontName2">http://www.w3.org/2000/svg ">
        <circle id="graycircle" r="75" cx="75" cy="75" />
    </svg>
</div>

/* CSS snippet */
.lstb03 #parent {
    display:-ms-grid;
}

.lstb03 #text {
    width:300px;

}
.lstb03 #graycircle {
    fill: gray;
    fill-opacity: 0.5;
}

9781430249832_AppB-02.jpg

图 B-2 。一个部分透明的圆圈显示它下面的段落文本

fill-rule 属性确定当样式引擎应用填充以确定形状的哪些部分被填充时所使用的算法。这两个选项是nonzeroevenodd。你不需要知道这些算法是如何工作的细节。我总是两个都尝试,看看哪一个能给我想要的结果。图 B-3 中的图形是 W3C 如何说明这两种算法之间的一些差异的。

9781430249832_AppB-03.jpg

图 B-3 。填充闭合形状的两种不同方法

笔画

笔画是独立的线条(直线或曲线)或环绕形状的线条。您将使用描边属性来设定描边的颜色、宽度、不透明度等等。

您可能想知道的前两个与笔画相关的属性是strokestroke-width。属性stroke控制笔画的颜色,属性stroke-width控制粗细。清单 B-4 设置了这两个属性,在我们正在处理的灰色圆圈周围添加了一条黑线。

清单 B-4。 给我们的灰色圆圈添加一个粗黑的轮廓

<!-- HTML snippet -->
<svg id="mySvg" height="200" FontName2">http://www.w3.org/2000/svg ">
    <circle id="graycircle" r="75" cx="75" cy="75" />
</svg>
/* CSS snippet */
.lstb04 #graycircle {
    fill: gray;
    fill-opacity: 0.5;
    stroke:black;
    stroke-width: 4px;
}

在清单 B-4 的中描述的圆圈将有一个 4 像素的黑色边框,正如你在图 B-4 中看到的。

9781430249832_AppB-04.jpg

图 B-4 。stroke 属性在我们的元素周围添加一个轮廓

除了这两个简单的属性,还有很多与中风相关的属性。分别是:stroke-dasharraystroke-dashoffsetstroke-linecapstroke-linejoinstroke-miterlimitstroke-opacity。这些属性允许您控制 SVG 元素上线条的许多其他方面,例如线条的虚线/圆点,以及线条在拐角周围和线条末端的行为。

关于这些扩展属性和 SVG 元素笔画所有方面的更深入的信息可以在http://www.w3.org/TR/SVG11/painting.html#StrokeProperties的 W3.org 上找到。

标记

标记是出现在线条两端或中间的字形。标记最常用于在元素末端创建箭头,但实际上标记可用于显示任何自定义形状,并使其出现在任何顶点。

创建标记是一个两步过程。首先定义标记的形状,然后将该标记定义应用于任何 SVG 元素。你可以在http://www.w3.org/TR/SVG11/painting.html#Markers找到标记的详细解释。

过滤器

滤镜就像镜头一样,可以将许多不同的效果应用到 SVG 图形的某些区域。你可以在 SVG 圆形的内容中添加一个滤镜,比如对圆形及其内容应用高斯模糊。图 B-5 是一个过滤器运行的好例子(同样来自 W3C)。

9781430249832_AppB-05.jpg

图 B-5 。一个应用过滤器的 W3C 示例

当然,使用您最喜欢的图形应用程序也可以实现该过滤器提供的相同效果,但是在运行时应用该过滤器有巨大的优势。在运行时应用的过滤器可以被你的应用程序的逻辑作为目标,因此可以有条件地应用。例如,只有当用户将鼠标悬停在图像上时,滤镜才会生效。同样,这可以通过提供多个位图图像的传统方式来实现,但是这种技术肯定有一定的限制。图像您想要一个过滤器的效果是动画。实际上用传统的方法是不可能做到的。

滤镜效果分两个阶段添加到元素中。首先使用filter 元素创建一个过滤器,然后使用filter 属性将该过滤器应用于一个元素。

过滤器是相对复杂的,所以为了简洁起见,我就不深入讨论了。然而,我确实想提到它们的一般功能,并让您知道它们是可用的。如果您想深入了解滤波器的主题,请访问http://www.w3.org/TR/SVG11/filters.html#FilterProperty

面具

蒙版是一种将透明层(也称为 alpha 通道)应用到任何 SVG 元素的方法,方法是提供一个显式蒙版,然后应用它。面具也可能是一个很长的话题,但是你至少应该知道它们是什么。

关于应用程序中相对冗长的属性(如遮罩、标记和过滤器)的快速说明:试图手动定义这些属性是疯狂的,因为记住语法并花时间键入它们是不允许的。通常,当您使用 Adobe Illustrator、CorelDRAW 或 InkScape 等矢量编辑图形软件时,这些会自动应用。

你可以在http://www.w3.org/TR/SVG/masking.html找到更多关于面具的信息。

指针事件

正如你已经看到的,SVG 形状有一个内部(?? 填充 ??)和一个外部(?? 描边 ??)。填充或描边中的一个或两个可能具有值none,因此将呈现不可见。这意味着当用户点击你的元素时需要做出一个有趣的选择。如果您创建了一个没有填充的圆,并在另一个元素的顶部结束,您可能希望也可能不希望该圆捕获单击事件。对于笔画来说也是如此。

在这种情况下,pointer-events属性是您指定行为的机会。默认情况下,这个值是visiblePainted,这是一个非常直观的选择,所以您可能不需要经常更改这个属性。

pointer-events属性的可用值有:visiblePaintedvisibleFillvisibleStrokevisiblepaintedfillstrokeallnone。设置了visiblePainted(默认)后,只有当元素被填充且可见时,元素才会捕捉填充上的点击(和其他指针事件),只有当笔画被设置且可见时,元素才会捕捉笔画上的点击。设置了visibleFill之后,如果你的元素是可见的(即使你的元素没有被填充),那么这个元素将会在填充时捕获点击。).设置了visibleStroke后,如果元素可见,它将捕获笔画上的点击(即使您没有设置笔画),但它不会捕获填充上的点击。使用paintedfillstroke值具有相似的功能,除了它们忽略元素是否实际可见。all值将导致总是点击——即使您没有设置填充或描边,并且两者都设置为不可见!最后,none值不会传递给指针事件。

如果将编程逻辑添加到像 SVG 这样的动态的、基于向量的图形语言中的想法让你兴奋,那么花些时间研究一下http://www.w3.org/TR/SVG/interact.html#PointerEventsProperty的指针事件。

能见度

就像 HTML 元素一样,SVG 元素的可见性可以被操纵来显示或隐藏单个元素,并且就像 HTML 一样,visible属性的值是visiblehiddencollapse。然而,collapse值的行为就像hidden一样,它隐藏了元素。visible值显然允许元素显示。

这就是我在这篇关于在 HTML 应用中使用 CSS for SVG 的简介中所要介绍的全部内容,但我想看看这些概念在 Windows 8 中的一些实际应用。

运用你所学的知识

让我们在几个例子中实现一些我们已经学到的 SVG CSS 概念。我们首先来看看如何在应用程序的背景中使用 SVG 图像作为带水印的图像。接下来,我们将创建一些自定义的 SVG 图像,显示在 ListView 的主项目标题后面。最后,我们将使用 JavaScript 动态构建一个时间线图形来生成 SVG。

带有 SVG 水印的背景图像

这里的目标是找到一个好的帆船的 SVG 图像,然后将它放置在我们的应用程序的背景中,光线要足够亮,这样它就不会阻碍出现在它上面的视觉元素和文本。

当我寻找好的矢量图像时,我通常从 office.microsoft.com 开始。如果你进行图像搜索,你可以指定你正在寻找的插图并且你希望它们的大小是可调整的,这将指示矢量图像应该被返回。这些图像通常是另一种矢量图像格式—WMF (Windows 图元文件)。要将图像保存为 SVG,您只需从网站上复制图像,粘贴到您的软件包中,然后将其保存为 SVG 文件。

我找到了一个我想使用的帆船图像(图 B-6 ),将其保存为 SVG,并且清单 B-5 显示了生成的 SVG 代码。我特意将 SVG 文件的全部内容留在清单中,这样您就可以了解描述这个简单的帆船需要多少 SVG 代码。

9781430249832_AppB-06.jpg

图 B-6 。简单的帆船图像

作为比较,SVG 格式的帆船在未压缩时占用 7098 字节的磁盘空间,在压缩时占用 3505 字节的磁盘空间。如果我们愿意,这个 7kb 的文件可以用来创建一艘 75 英尺高的帆船。同一艘帆船以 768 像素的高度呈现为未压缩的全色(尽管我意识到这不是必需的)位图,占用了 1,983,798 字节。以无损格式(PNG)压缩位图会将其压缩到 35,410 字节,但您肯定会看到矢量格式比位图有更大的优势。

清单 B-5。 组成一艘简单帆船的整个 SVG 文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" " http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd ">
<!-- Creator: CorelDRAW X6 -->
<svg FontName2">http://www.w3.org/2000/svg " xml:space="preserve" width="509px" height="571px"
version="1.1" style="shape-rendering:geometricPrecision;
text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
viewBox="0 0 50900 57045"
 xmlns:xlink=" http://www.w3.org/1999/xlink ">
 <defs>
  <style type="text/css">
   <![CDATA[
    .fil0 {fill:black}
   ]]>
  </style>
 </defs>
 <g id="Layer_x0020_1">
  <metadata id="CorelCorpID_0Corel-Layer"/>
  <g id="_695569408">
   <polygon class="fil0" points="22632,2398 22872,2368 23112,2308 23292,2188 23472,2038 23621,1859
23741,1679 23801,1439 23831,1199 23801,959 23741,719 23621,540 23472,360 23292,210 23112, 90
22872,30 22632,0 22392,30 22153,90 21973,210 21793,360 21643,540 21523,719 21463,959 21433,1199
21463,1439 21523,1679 21643,1859 21793,2038 21973,2188 22153,2308 22392,2368 "/>
   <polygon class="fil0" points="23741,5845 23681,6865 23561,9562 23352,13399 23142,17896
22932,22482 22782,26649 22722,29857 22752,31595 22932,32015 23382,32375 24011,32704 24850,33004
25840,33244 26919,33484 28118,33634 29347,33783 30606,33843 31865,33903 33094,33873 34263,33813
35312,33694 36212,33514 36961,33304 37531,33004 37890,32554 38100,31835 38160,30906 38070,29827
37890,28568 37590,27189 37201,25720 36781,24191 36271,22662 35762,21163 35192,19665 34653,18286
34113,16997 33604,15858 33154,14898 32734,14149 32315,13399 31775,12500 31146,11481 30456,10342
29707,9203 28957,8034 28178,6895 27398,5875 26649,4976 25960,4227 25300,3717 24761,3447 24311,3507
23981,3867 23771,4646 "/>
   <polygon class="fil0" points="8843,28178 9652,28687 10522,29167 11451,29587 12440,29946
13429,30246 14419,30516 15378,30726 16307,30936 17206,31086 18046,31205 18795,31295 19425,31385
19964,31415 20384,31445 20624,31475 20714,31475 20504,8933 20324,9113 19844,9652 19065,10462
18106,11511 16967,12800 15738,14239 14449,15798 13160,17446 11901,19125 10732,20774 9742,22392
8933,23921 8393,25300 8184,26499 8303,27458 "/>
   <polygon class="fil0" points="29017,35372 27968,35312 26949,35282 25960,35192 24970,35132
24041,35042 23112,34953 22213,34833 21313,34743 20414,34623 19515,34503 18615,34383 17686,34233
16757,34113 15828,33963 14838,33843 13849,33694 12860,33544 11811,33304 10702,33034 9562,32704
8393,32345 7254,31985 6115,31595 5036,31205 3987,30816 3058,30456 2188,30126 1439,29827 839,29587
390,29377 90,29257 0,29227 570,30186 989,31265 1319,32375 1589,33544 1829,34743 2068,35912
2338,37051 2668,38130 3088,39149 3627,40079 4347,40858 5216,41517 6325,41997 7674,42267 9323,42357
11271,42177 12740,41997 14149,41847 15468,41727 16727,41667 17896,41637 19035,41637 20084,41667
21073,41727 22033,41817 22932,41937 23771,42057 24581,42177 25330,42327 26050,42477 26739,42627
27398,42776 28028,42926 28628,43076 29197,43226 29767,43346 30306,43466 30846,43556 31385,43646
31895,43676 32435,43706 32944,43706 33484,43676 34023,43586 34563,43466 35132,43316 35702,43106
36301,42866 36931,42567 37560,42237 38190,41877 38819,41517 39479,41098 40108,40708 40738,40288
41338,39869 41907,39449 42477,39029 43016,38610 43496,38220 43945,37860 44365,37531 44755,37231
45055,36961 45384,36691 45744,36421 46104,36182 46493,35942 46853,35732 47243,35522 47633,35342
48022,35162 48412,35012 48802,34863 49191,34713 49551,34593 49911,34443 50270,34323 50600,34203
50900,34083 50360,34173 49791,34263 49191,34353 48562,34443 47872,34533 47183,34623 46434,34683
45684,34743 44935,34833 44125,34893 43346,34953 42537,34983 41727,35042 40888,35102 40079,35132
39269,35162 38460,35222 37650,35252 36841,35282 36092,35312 35312,35312 34593,35342 33873,35372
33184,35372 32524,35372 31895,35402 31295,35402 30756,35402 30246,35402 29797,35402 29377,35372 "/>
   <polygon class="fil0" points="32464,48442 31745,48322 31056,48232 30336,48112 29617,47992
28867,47902 28088,47782 27219,47663 26319,47513 25330,47393 24281,47243 23112,47093 21823,46943
20444,46763 18915,46583 17266,46404 15438,46194 13729,45984 12350,45774 11301,45564 10552,45384
10042,45204 9802,45025 9742,44845 9862,44695 10132,44575 10522,44425 11031,44335 11571,44245
12170,44155 12770,44095 13340,44065 13879,44065 14359,44065 14838,44065 15318,44095 15768,44095
16277,44125 16817,44185 17386,44245 18016,44305 18735,44365 19545,44485 20444,44575 21433,44725
22572,44875 23831,45055 25240,45264 26829,45474 28418,45714 29887,45894 31205,46044 32345,46164
33304,46254 34053,46314 34623,46314 34983,46254 35072,46164 34953,46044 34563,45834 33903,45594
32944,45294 31685,44965 30126,44545 28238,44065 27219,43826 26199,43616 25180,43436 24161,43286
23172,43166 22213,43076 21223,42986 20294,42926 19365,42896 18436,42896 17566,42866 16697,42896
15858,42926 15048,42956 14239,43016 13489,43076 12770,43136 12081,43196 11421,43286 10821,43376
10252,43436 9712,43526 9203,43616 8753,43676 8363,43766 8004,43826 7704,43886 7434,43945 7254,44005
7104,44035 7014,44065 6985,44065 6355,44755 5995,45324 5905,45834 6055,46224 6415,46553 6955,46823
7674,47003 8543,47153 9503,47273 10582,47333 11751,47393 12920,47423 14149,47453 15348,47483
16547,47543 17686,47603 18705,47693 19605,47752 20384,47842 21073,47902 21703,47992 22302,48052
22932,48142 23561,48232 24251,48352 25030,48472 25900,48592 26949,48712 28148,48862 29527,49041
31146,49221 33004,49431 34803,49611 36242,49791 37321,49911 38100,50031 38610,50121 38879,50181
38939,50241 38819,50270 38550,50300 38190,50300 37770,50300 37321,50300 36841,50300 36421,50270
36062,50270 35822,50270 35582,50270 35282,50300 34923,50300 34473,50330 33963,50360 33424,50390
32824,50450 32195,50480 31505,50510 30816,50570 30126,50600 29407,50630 28687,50660 27968,50660
27249,50690 26559,50690 25840,50690 25090,50720 24311,50720 23502,50750 22632,50810 21793,50840
20924,50900 20054,50960 19185,51020 18316,51080 17476,51140 16637,51230 15858,51290 15108,51380
14389,51440 13729,51530 13130,51619 12590,51679 12081,51769 11631,51859 11211,51949 10851,52039
10522,52159 10192,52309 9892,52459 9622,52639 9383,52848 9113,53088 8873,53358 8633,53628 8393,53988
8124,54347 7944,54737 7914,55097 8004,55426 8214,55726 8513,55996 8873,56236 9263,56446 9682,56626
10102,56775 10492,56895 10821,56985 11061,57045 11241,57045 11301,57045 11181,56985 10941,56895
10282,56685 9712,56446 9293,56146 9083,55846 9083,55456 9353,55037 9982,54527 10941,53928
11541,53628 12140,53388 12800,53148 13489,52968 14209,52789 15018,52639 15858,52549 16787,52429
17806,52369 18885,52309 20084,52249 21403,52219 22812,52189 24341,52159 25990,52129 27788,52099
29557,52069 31086,52009 32405,51949 33574,51859 34563,51799 35432,51679 36152,51589 36811,51470
37381,51350 37920,51230 38400,51110 38909,50990 39389,50870 39929,50750 40528,50660 41188,50540
41757,50420 42057,50330 42117,50181 41967,50061 41607,49941 41098,49791 40438,49641 39659,49491
38819,49371 37890,49221 36931,49071 35942,48922 35012,48802 34083,48682 33214,48562 "/>
  </g>
 </g>
</svg>

现在是时候让 SVG 文件包含在一些 HTML 后面了。在我们的 HTML 中,实际上有几种显示 SVG 的方法。

  • 我们实际上可以将清单 B-5 中的 SVG 代码粘贴到我们的页面中,或者使用 JavaScript 将它附加到我们的 DOM 中。
  • img标签直接支持 SVG 文件,所以我们可以使用类似于<img src="sailboat.svg"/>.的东西
  • 我们还可以设置希望它显示在后面的元素的background-image属性。

在这种情况下,没有理由不使用img标签或设置background-image属性。这更容易,也更符合我们的情况。如果要对 SVG 进行操作、设计样式或制作动画,我们需要将 SVG 代码添加到 DOM 中(手动或使用 JavaScript)。在这种情况下,我们不是,所以我们将保持简单。清单 B-6 将帆船图形放在主div元素后面,将它们放入一个网格中。你可以在图 B-7 中看到结果。

清单 B-6。 在一段文字后面添加帆船水印

<!-- HTML snippet -->
<div class="grid">
    <img id="sailboat" src="sailboat.svg"/>
    <div id="main">
        Ut commodo consequat leo . . .
    </div>
</div>

/* CSS snippet */
.lstb06 #sailboat {
    opacity:0.05;
}

.lstb06 .grid {
    display:-ms-grid;
}

.lstb06 #main {
    border:solid 1px gray;
    font-size:large;
    width:500px;
    height:500px;
    padding:10px;
}

9781430249832_AppB-07.jpg

图 B-7 。一艘几乎透明的黑色帆船在段落文本后面显得非常暗淡

集线器上精美的 SVG 磁贴

许多 Windows 8 应用程序使用内置的 ListView 控件来显示项目网格。典型的 Windows 8 网格项目是矩形的,看起来像瓷砖。你不会拘泥于这种外观,尽管它实现的极简主义和效率是一个应该考虑的强大设计功能。

让我们假设您正在创建一个绘画应用程序,您的网格的一部分显示了所有正在绘制文档的用户,您希望这些项目以一种形状来表示,这种形状看起来大致像一个矩形,只是由几个笔画组成。在这种情况下,我们仍然希望有瓷砖——除了有点花哨。在这种情况下,您可以渲染出一个位图并将其放在每个项目的后面,但是您会失去我提到的 SVG 的优势。因此,让我们使用 SVG,让我们自己创建图形。

我跳到 CorelDRAW 上,很快找到了一个笔刷,让我可以创建图 B-8 中的图形。

9781430249832_AppB-08.jpg

图 B-8 。一个使用 CorelDRAW 生成的图形,我们希望用于我们的中心瓷砖

CorelDRAW 可以将图形保存为 SVG。我不会列出这段代码,因为它相当长——不过仍然只有大约 125kb 未压缩。

我们的下一步是在 ListView 的图块后面获得图形。清单 B-7 应该可以搞定。

清单 B-7。 一个带有 CSS 的 ListView,将笔画图形放在每个项目的后面

<!-- HTML snippet -->
<section aria-label="Main content" role="main">
    <div id="listview" data-win-control="WinJS.UI.ListView"></div>
</section>

<!--Templates-->
<div id="itemtemplate" data-win-control="WinJS.Binding.Template">
    <h2 data-win-bind="innerText:name"></h2>
</div>

/* CSS snippet */
.lstb07 #listview .win-item {
    display:-ms-grid;
    -ms-grid-columns:1fr;
    -ms-grid-rows:1fr;
    width:250px;
    height:174px;
    background: url(paint.svg) no-repeat;
    background-size:250px auto;
}

    .lstb07 #listview .win-item h2 {
        -ms-grid-column-align:center;
        -ms-grid-row-align:center;
        color:white;
        font-family:'Segoe WP';
        font-size:40px;
        font-weight:bold;
        position:relative;
        top:-10px;
    }

// JavaScript snippet
var list = new WinJS.Binding.List(
    [{ name: "Doc1" }, { name: "Doc2" }, { name: "Doc3" }]
);
var listview = element.querySelector("#listview").winControl;
listview.itemDataSource = list.dataSource;
listview.itemTemplate = element.querySelector("#itemtemplate");
listview.onloadingstatechanged = function (e) { debugger; };

所有这些代码的结果是一个 ListView,就像你在图 B-9 中看到的那样,带有定制的项目形状和覆盖的文本。

9781430249832_AppB-09.jpg

图 B-9 。生成的笔画项目赋予项目一些特征

使用 SVG 构建的时间线图形

前两个应用程序使用了包含 SVG 的简单方法——即使用img标签和background-image CSS 属性,并简单地引用一个 SVG 文件。现在让我们采取一个稍微困难一点的方法,让我们更好地控制我们的矢量图像。

假设我们想要创建一个简单的时间线图形,其中包含一个代表某个时间长度的灰色条,然后是每个时间点的白点。为了实现这一点,我们将创建 SVG 图像,然后查看它是如何组成的。

我在 CorelDRAW 重新开始。这次我创建了一个非常简单的灰色时间线,用白点标记时间点。同样,这个特殊的图形非常简单,我当然可以通过在记事本中手动编写 SVG 来编写它,但是在现实生活中,您可能会使用更复杂的图形。我创建的简单时间线图形可以在图 B-10 中看到。

9781430249832_AppB-10.jpg

图 B-10 。我们希望用 JavaScript 动态构建的时间线

然后我生成了 SVG,对它进行了一些调整,然后使用记事本查看它(清单 B-8 )。

清单 B-8。CorelDRAW 为图 B-10 中的时间轴图形生成的 SVG 代码。

<svg FontName2">http://www.w3.org/2000/svg " width="1000px" height="200px" >
 <defs>
  <style type="text/css">
   <![CDATA[
    .str0 {stroke:black;stroke-width:2}
    .fil0 {fill:gray}
    .fil1 {fill:white}
   ]]>
  </style>
 </defs>
 <g>
  <rect class="fil0" x="30" y="90" width="940" height="20"/>
  <circle class="fil1 str0" cx="30" cy="100" r="10"/>
  <circle class="fil1 str0" cx="70" cy="100" r="10"/>
  <circle class="fil1 str0" cx="400" cy="100" r="10"/>
  <circle class="fil1 str0" cx="970" cy="100" r="10"/>
 </g>
</svg>

元素本身在g元素内部。这些元素的样式在嵌入的样式表中。我们将使用本文档告诉我们的关于如何创建时间线图形的内容,并使用 JavaScript 自己创建它。这将给我们以编程方式放置点的灵活性。我们还将提取 CSS 样式,将它们放入文档的样式表中,并为它们赋予更有意义的类名。很高兴我们所有的风格都在一个地方。就像 HTML 一样,我们希望 SVG 是图形结构的定义,让样式表处理颜色等。清单 B-9 和图 B-11 展示了如何动态创建这个时间线。

清单 B-9。 构建时间轴的 HTML、CSS 和 JavaScript

<!-- HTML snippet -->
<div id="container"></div>

/* CSS snippet */
.lstb09 .circle {
    stroke:black;
    stroke-width:2;
    fill:white;
}

.lstb09 .line {
    fill:gray;
}

// JavaScript snippet
var points = [0,1,3,7,10];
var container = element.querySelector("#container");

//transform the points and generate SVG
var result;
points.forEach(function (p) {
    result += format("    <circle class='circle' cx='{0}' cy='100' r='10'/>", (p * 94) + 30);
});

//add resulting SVG to the DOM
container.innerHTML = format(
    "<svg xmlns=' http://www.w3.org/2000/svg ' width='1000px' height='200px' >" +
    "  <g>" +
    "    <rect class='line' x='30' y='90' width='940' height='20'/>" +
    "    {0}" +
    "  </g>" +
    "</svg>", result);

9781430249832_AppB-11.jpg

图 B-11 产生的时间线正是我们所寻找的,现在可以很容易地改变这些值

将它转换成 WinJS 自定义控件是一个好主意,这样您就可以使用<div data-win-control="MyApp.Controls.Timeline"></div>在代码中的任何地方创建它们。

顺便说一句,如果你想更深入地使用 JavaScript 来生成和操作 SVG 图形,在http://www.farinspace.com/top-svg-javascript-libraries-worth-looking-at/有一个很棒的帖子,它将向你介绍 11 个 JavaScript 库,这些库将对你有所帮助。

您已经了解了令人兴奋的 SVG 空间,并看到了它如何增强您的 Windows 8 应用程序。我们研究了许多影响 SVG 元素的 CSS 样式属性(尽管肯定不是全部),并在 Windows 8 应用中浏览了三个强大的 SVG 应用。

我希望你在阅读这篇文章时学到了一些东西,但更重要的是,我希望你受到启发,去尝试它,研究你不知道的领域,这样你就可以掌握将 SVG 编织到你的应用程序中,并使用 CSS 对它进行样式化。

posted @ 2024-08-13 14:06  绝不原创的飞龙  阅读(0)  评论(0编辑  收藏  举报