CSS Windows8 应用开发教程(三)
七、布局属性
注 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;
}
图 7-1。这个示例布局看起来很不错,但是使用表格来定义布局从来都不是一个好主意
显示和定位
如果表格是布局上的禁忌,那怎么办?下一个更好的解决方案是使用带有显示和位置属性的div元素,手动将它们发送到屏幕上应该出现的位置。一个div(division 的缩写)是一个抽象元素,通常用来将内容的各个部分分开。它以块模式呈现,这意味着前面和后面的元素呈现在它的上面和下面。这与行内元素形成对比,行内元素像文字处理器中的文本一样流动。
这些div元素可以用样式属性进行格式化和定位,这使它们成为布局中表格的良好替代品。今天,它们在 web 上无处不在,但这并不是说开发人员乐于处理它们!使用div元素作为布局的技术带来了自己的痛苦,这就是为什么各地的 web 开发人员对已经出现或即将出现的新格式功能感到兴奋。然而,我们将在这里详细讨论经典的div定位,因为它是基础性的,因为你肯定会在“野外”看到它
display属性决定了它的目标将如何显示,成为布局主题中非常重要的属性。display属性的有效值(包括特定于微软供应商的值)为inline、block、list-item、run-in、inline-block、table、inline-table、table-row-group、table-header-group、table-footer-group、table-row、table-column-group、table-column、table-cell、table-caption、-ms-flexbox、-ms-inline-flexbox、-ms-grid、-ms-inline-grid和none。
显示属性
有很多值,所以我认为我们至少需要粗略地介绍一些显示属性,以便对布局有一个全面的了解。
在一条直线上的
当一个元素为inline时,它的行为就像文字处理器中的文本——也就是说,它相对于其前一个兄弟元素水平呈现,从一行流到下一行,并影响其自身的行高。尝试使用width和height调整内联元素的大小不会影响它们。
街区
块元素占用尽可能多的宽度,这迫使它们在自己的行上。指定多个块元素会导致元素在渲染时垂直堆叠。可以调整块元素的大小。
内嵌块
从父元素的角度来看,设置为显示为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属性进行定位,值为static、absolute、relative和fixed。static的值为默认值。位置值的好处将在以下章节中介绍。
静电
默认的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;
}
图 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;
}
图 7-3 。该元素位于文档的左上角,并失去了它在 div 元素列表中的位置
我们已经讨论了边距、边框和填充属性,但是我想提醒您,它们在内容布局中确实起着很大的作用。Windows 8 设计的核心原则之一是少即是多。这意味着我们没有尽力在屏幕上显示尽可能多的信息。我们并不试图让用户一次点击就能完成所有事情,这似乎是网站设计的趋势。所以我们不怕给我们的内容布局增加一些有意的、有目的的呼吸空间。这通常采用边距或填充的形式,所以要准备好使用它们。
浮动
可以使用float属性告诉块级元素浮动。浮动元素允许后续的内联内容呈现在自身旁边(而不是下面)。您可以提供left或right的值来决定应该填充块级元素内容的哪一侧。
当按照预期使用时,浮动工作得很好,但是 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;
}
图 7-4 。默认情况下,flexbox 是水平的,可以很好地布局 div 元素
flexbox 的价值是轻量级的,易于定义,一旦你开始寻找,你会发现它到处都有很好的用途。
图 7-4 中的柔性盒没有指定尺寸,所以它只是符合其内容的尺寸。注意在清单 7-5 中,当我们向 flexbox 添加width和height属性(并添加一个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;
}
图 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;
}
图 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;
}
图 7-7 。所有项目都在伸缩,但偶数项目占据了两倍的水平空间
在我们继续研究网格之前,还有一个 flexbox 人才您应该看到。
如果 flexbox 的项目有明确的大小,并且它们溢出了 flexbox,flexbox 会像任何其他有溢出的div一样处理它们——默认情况下,它会显示溢出溢出了它的边界。它看起来有点像图 7-8 。
图 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;
}
图 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-box的box-sizing值来包括总宽度的填充值。
允许任何子项垂直溢出是不理想的。这样做会让用户在两个不同的滚动方向之间感到困惑。然而,overflow-y: auto;属性将确保如果内容变得太高,至少不会溢出底部,而是呈现一个滚动条。
该规则的最后两个属性提供了底部边距和右填充,以便在不覆盖内容的情况下为滚动条提供呈现空间。
最后一个样式规则很简单,它只声明最后一个子元素的宽度不应该是 80%,而是 100%。如果不将此属性应用于最后一个子元素,强制捕捉点将不允许您导航到最后一个元素。
你可以在图 7-10 中看到这段代码的结果,但更好的是你应该通过查看源代码来探究这个解决方案的行为。
图 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;
}
图 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; }
图 7-12。子项目会移至其指定的储存格
网格的行被定义为 1fr 1fr。这是以空格分隔的所有行的行高列表。在这种情况下,我们使用了代表fraction的fr单元标志符。如果所有行都被赋予小数值 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; }
图 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; }
图 7-14。标题横跨三列,占据了应用程序布局的整个顶部
在 Windows 8 应用程序中使用网格
就像我们对 flexbox 所做的一样,我们将看一个在真实的 Windows 8 应用程序中使用网格的例子。
让我们想象一下,我们正在显示一个用户的个人资料,其中包括他的面部照片和一些关于他的信息,这些信息形成了一个布局,就像你在图 7-15 中看到的。
图 7-15 。所需轮廓布局的粗略描述
这是 CSS 网格的完美候选。首先,请注意我们的布局已经分成三列。接下来,请注意,虽然我们的内容没有形成漂亮的行,但我们可以使用三行,然后进行一些跨越来达到相同的效果。
图 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 视图的自定义布局。
图 7-17 。最终的结果看起来很像我们想要的配置文件布局
视图框和列表视图
我们已经介绍了 CSS 为我们提供的传统和现代布局实现,但当您在 Windows 8 应用程序上工作时,您可以使用 Windows Library for JavaScript(WinJS ),并且 WinJS 为您提供了一些更现代的布局控制。尽管它们不是 CSS 属性,但在本书的上下文中还是值得一看的。
首先是WinJS.UI.ViewBox。ViewBox 是一个控件,当您希望内容根据应用程序视图状态的变化自动缩放时,可以向其添加内容。例如,如果一个应用程序在平板电脑上运行,它可能会旋转到纵向方向,在这种情况下,可能需要放大或缩小其中的内容。我说的不是基于新的维度重新呈现它的内容。我说的是内容的直接缩放,就像在 ViewBox 介入之前呈现的那样。
这种缩放之所以成为可能,要感谢我们在第六章中已经学过的缩放和平移变换函数。ViewBox 很乐意为您应用这种效果,它唯一要求的是您只给它一个子项目。这并不是一个大问题,因为即使你有多个子项,你所要做的就是把它们像一个简单的 div 一样包装在一个容器中。
如图 7-18 中的所示,查看框适合子项目而不改变其纵横比。在横向方向上,高度将约束孩子的尺寸,而在纵向方向上,宽度将是约束。
图 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 中,你可以看到一个非常基本的列表视图,但是列表视图有很多扩展功能,包括项目分组。在这种情况下,标题呈现在每组项目的上方,开发人员必须提供一个标题模板来定义标题的外观。
图 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元素宣布哪个是哪个。选择了几个项目来产生效果。
图 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 中,布局中可能会出现缺口。可能有必要添加一些逻辑来确保根据项目大小添加项目的顺序。
图 7-21 。ListView 项目是不对称的,第一个项目比其他项目大
滚动控制
当一个元素的内容溢出该元素的边界,并且该元素的overflow属性被设置为auto或scroll时,滚动条将被呈现,用户将能够使用鼠标或触摸来滚动。这是一个解决内容多于屏幕的老办法。
传统上,对容器如何滚动的大部分控制都留给了用户代理,但一些特定于微软供应商的属性给了应用程序作者一些发言权。我们将对滚动限制、轨道、捕捉点以及一个叫做链接的行为有发言权。我们现在来看一下每一项。
限制
容器滚动的距离由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。数值为none或railed,默认值为railed。
捕捉点
有时,内容完全是线性的,应该从头到尾平滑滚动,无论用户在哪里停止,都应该停止,但其他时候,内容是以数字单位或部分排列的,帮助用户着陆更有意义,以便下一部分的开始与容器的开始对齐。这些点称为捕捉点。
微软已经在其浏览器引擎中实现了捕捉点,尽管它们在 CSS 标准中并不存在。这就是相关属性具有特定于供应商的前缀的原因。
可以用两种方法之一定义捕捉点:强制或近似。
将容器定义为使用强制捕捉点意味着它将始终停在最近的捕捉点。它永远不会停在中间的某个地方。然而,将它定义为使用邻近捕捉点意味着,如果它足够接近捕捉点,那么它会找到到达捕捉点的路径,但是如果它不够接近,那么它会让步,在点之间停止。
捕捉点属性
让我们看看一些捕捉点属性。
-ms-scroll-snap-points-x 和-ms-scroll-snap-points-y
使用两个函数之一定义捕捉点:snapInterval()或snapList()。
snapInterval()功能采用起始长度和捕捉长度。传入0px和100px将从 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 库中的样式规则集合和我们自己的自制样式表。我们还会将这些规则应用到我们的应用程序中,并覆盖和扩展它们,使它们成为我们自己的规则。
所以和我一起换挡,让我们继续巡航!
八、全局样式
注意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 中看到这种斜坡的一个典型例子。请注意,识别应用程序的标题、区分各个部分以及阅读标题和正文内容是多么容易。还要注意,这是通过文字斜坡和一些空间的战略性使用实现的。我们的布局不需要任何线条或方框来分隔应用程序的各个部分。
图 8-1 。Windows 8 的文字渐变使得辨别内容的相对重要性变得容易
图 8-1 中的示例应用程序屏幕使用了标准的 Windows 8 light 主题(ui-light.css)。这个主题是白色背景和黑色文字的原因。与之形成鲜明对比的还有一个暗黑主题(ui-dark.css)。图 8-2 显示了这次使用黑暗主题的相同视图。应用程序开发者可以选择使用哪个主题,这是一个应用程序一个应用程序的决定。它不受操作系统级别设置的控制。黑暗主题中的斜坡字体显然在传达结构方面也同样有效。
图 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>
图 8-3 。根据 WinJS 样式表呈现的结果
只有在 HTML 中定义了标题标签(h1、h2、h3、h4、h5和h6)时,每种标题样式才适用,但是每种样式都有一个相应的类,可以添加到您想要的任何元素中。
h1的阶级对应是win-type-xx-large,h2的是win-type-x-large,h3的是win-type-large,h4的是win-type-medium,h5的是win-type-small,h6的是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;
}
图 8-4 。结果是一个样式像 h2 元素的按钮
还有两个赢型风格规则,分别是win-type-ellipsis和win-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 "
图 8-5 。由于 win-type-interactive 类的缘故,文本的跨度呈现得比其余部分大,并且将提供悬停反馈
抓拍视图
当用户在使用另一个应用程序时,如果他希望保持该应用程序可用(尽管是较小的格式),他会对该应用程序进行快照。对于用户来说,在使用 Windows 8 时获得次要任务的简洁垂直视图是一个很好的功能。对于开发人员来说,这是一个设计考虑,也是一个必要的考虑,因为没有办法配置您的应用程序来禁止捕捉。当你的应用被抓取时,会有很多变化;最值得注意的是,可用的水平像素从至少 1024 降低到只有 320。毫无疑问,每个应用程序都可以被抓拍,所以你必须考虑到这种情况,即使只是通知用户应用程序在被抓拍之前无法正常运行。
至少 WinJS 为您完成了一些为快照视图转换应用程序的工作。WinJS 中的 CSS 文件定义了一个媒体查询,以检测您的应用程序是否处于快照视图中,并设置各种样式属性来保持您的应用程序的可用性和设计。
图 8-6 和 8-7 显示了一个全屏状态和快照状态的样本 app。在这种情况下,没有规定在抓取时调整应用程序,所以它本质上只是显示全屏应用程序的左侧 320 像素。首先,这绝对是您应该避免交付到 Windows 应用商店的行为。
图 8-6 。全屏显示的示例应用程序
图 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 图标来表示,那么它应该是。仅当无法使用内置集合中的图标表示时,才创建自定义图标。
图 8-8 。除非用户显示下面的应用程序栏,否则 Bing 地图应用程序只会显示一张地图。这就是用户沉浸在内容中的含义
除了下面的应用程序栏用于命令之外,上面的应用程序栏可供开发者使用,应该主要用于某种形式的导航。在图 8-9 中,可以看到 Internet Explorer 10 应用(Windows Store 版本),可以看到上面的应用栏是用来显示浏览器选项卡缩略图的。
图 8-9 。Internet Explorer 实现了一个上层和一个下层的应用程序栏
图 8-10 展示了必应团队打造的预装在 Windows 8 中的运动 app 。在这个应用程序中,下方和上方的应用程序栏都在使用中。上部用于导航到各种可用的运动,而下部的应用程序栏可用于刷新运动数据。
图 8-10 。Bing Sports 应用程序的上部应用程序栏会将用户导航到不同的体育赛事
应用程序栏在不同的应用程序中具有统一的外观和功能是很重要的,所以你会发现你通常不需要在应用程序栏中添加样式规则。通常情况下,它们的默认样式就足够了。
在一本关于使用 CSS 样式化应用程序的书中,我们真正想做的是理解应用程序栏的一般结构,以及它是用什么类来修饰的,这样我们就有希望通过添加或覆盖样式来定制它。为了做到这一点,我们将查看实现应用程序栏的完整代码,然后我们将看到生成的 DOM 是什么样子,并突出显示重要的部分(参见图 8-11 )。
})();
图 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-overlay、win-appbar、win-commandlayout和win-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;
}
图 8-12 。将鼠标悬停在命令按钮上,它周围的圆形边框会变成灰色
这里要注意的是,没有什么是对开发者隐藏的。用于创建 Windows 8 应用程序的所有样式属性都可以浏览、附加和覆盖。
设置窗格
像应用程序栏一样,Windows 8 应用程序中的设置面板对用户来说是可预测的。从屏幕右侧向外滑动符咒并选择设置符咒(参见图 8-13 )即可访问。从主设置面板中选取一个设置类别会在屏幕右侧弹出一个特定的设置面板。这个特定的设置面板是我们将要使用 WinJS 定制的。UI . SettingsFlyout 弹出控件。
图 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);
};
}
})();
图 8-14 。“设置”弹出按钮可从“设置”窗格中找到
这个设置面板的样式规则已经存在,我们只需通过编写结构,然后用正确的 CSS 类值装饰元素来应用它们。
我们将再次使用 DOM explorer 查看清单 8-9 中的结果 DOM。WinJS 添加的类和样式以粗体显示。同样以粗体显示的是添加的两个div元素(win-firstdiv和win-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-dark和win-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);
}
}
});
})();
图 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"></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();
}
})();
图 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 中可以看到,每个菜单命令都是斜体的。
图 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 控件样式
注意 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.css和ui-light.css。他们的名字揭示了他们的目的。ui-dark.css样式表为我们提供了一个黑色背景和一个白色前景,而ui-light.css给了我们相反的效果。为了简单起见,我们只看一下ui-light.css文件,因为两者在结构上是相同的。
除了这两个主题样式表之外,我们还讨论了两个主题类,这两个主题类可以将深色或浅色主题应用于整个应用程序,而不是应用于它的某些子部分。不要混淆样式表(ui-dark.css和ui-light.css)和类(win-ui-dark和win-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声明了一个按钮,当按下该按钮时,会将表单中的所有其他控件重置为初始值。submit的type创建一个按钮,当按下该按钮时,将发出一个包含所有表单值的 POST HTTP 请求。
HTML5 引入了一种更简单的新方法来定义按钮——使用button元素。元素相当于<input type="button"/>,它声明了一个按钮,但是没有分配任何功能。button元素比输入元素更具语义性。我的意思是,它对人类读者来说更容易理解。我建议只使用button元素。
清单 9-1。 一个超级简单的按钮
<!-- HTML snippet -->
<button>click me!</button>
图 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 中看到微软设计风格的简单性。
图 9-2 。IE10 中按钮的默认呈现与应用 WinJS 样式表后出现在 Windows 8 应用程序中的版本相比较
以下伪类与按钮元素相关:
- **:悬停。**如前所述,被悬停按钮的背景颜色比正常颜色稍浅。
- **:禁用。**默认情况下,禁用的按钮具有透明背景、白色文本和低透明度(40%)的边框。
如果你声明了一个按钮并包含了一个win-backbutton类,WinJS 样式表会把这个按钮变成后退按钮。后退按钮是圆形的,内部呈现一个箭头字形。这一典型的 Windows 8 功能可以通过以下非常简单的 HTML 标记添加到您的应用程序中:
<button class="win-backbutton"/>
文件上传控制
文件上传控件对 HTML5 来说并不陌生,但是它已经得到了一些重要的改进。它在浏览器中看起来几乎是一样的——只显示一个文本框和一个浏览按钮。该控件允许用户一次上载一个或多个文件。
清单 9-2。 一个带有“文件”类型的输入会呈现一个文件上传控件。
<input type="file"/>
图 9-3。默认文件上传控件
由于文件上传控件呈现多个可视元素(即文本框和浏览按钮),过去很难确定样式。复合元素可以很容易地被定位,但是不可能单独定位文本框和浏览按钮。然而,在 Internet Explorer 中,::-ms-value和::-ms-browse伪元素被识别并允许这种更精细的控制。
WinJS 将文件上载控件的样式设置为 340 像素宽,32 像素高,周围有自定义边距。::-ms-value(代表文本框)和::-ms-browse(代表浏览按钮)都有两个像素的边框。
文本输入控件
一些 HTML 控件相当于具有单一行为的单一控件,但是input元素不在其中。元素的行为取决于它的属性type的值。但是,许多值会导致控件在用户看来像一个文本框,并接受(正如所料)字母数字键盘输入。type属性中的任何下列值将产生一个字母数字文本框:text、password、email、number、tel、url或search。此外,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
图 9-4。几种不同的文本框类型
从 HTML5 开始,Internet Explorer 和 Windows 8 应用程序会在大多数文本框类型的右侧显示一个小的 X ,这样用户可以更容易地清除文本框的值。在 Internet Explorer 10 和 Windows 8 应用程序中,您可以通过使用 ::-ms-clear 伪元素将这个小字形作为样式。这对于那些想要很好地控制自己造型的人来说很有帮助。
图 9-5。一个小的“x”字形呈现在文本框中,使清除框变得简单
同样,对于密码文本框,Internet Explorer 和 Windows 8 应用程序会呈现一个小的眼球字形,只要用户按下该字形,就会显示在字段中输入的密码。当用户知道没有人躲在他们身后或通过屏幕共享观看时,能够看到输入的密码以确保没有错误是有帮助的。
图 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>
图 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>
图 9-8。呈现复选框,允许选择多个项目
默认情况下,复选框为 21 像素见方,具有典型的 2 像素实心边框。
范围
范围(或滑块控件)是 HTML5 的新控件。范围控件为用户提供了一个用户界面(UI ),以便于从一个范围的值(如整数范围的值)中进行选择。
清单 9-6。 一个简单的输入类型“范围”被声明
<!-- HTML snippet -->
<input type="range" />
组成范围控件的许多部分可以作为伪元素的目标。
- ::-ms-track。 -代表可能值范围的滑块轨迹
- ::-女士-拇指。 -用户拖动的代表控件上当前值的部分
- ::-毫秒-滴答-之前。 -适用于出现在轨道顶部(从左到右布局)或左侧(从上到下布局)的刻度线
- ::-毫秒-滴答-之后。 -适用于出现在轨道底部(从左到右布局)或底部(从上到下布局)的刻度线
- ::-ms-填充-降低。 -适用于从最小值到当前值的轨迹部分
- ::-ms-fill-upper。 -适用于从当前值到最大值的轨迹部分
正如您在图 9-9 的示例中所看到的,范围指示器呈现为一个简单的矩形,带有一个小的拇指滑块,范围的下部部分用颜色填充。您也可以看到,默认情况下,范围控件水平显示。但是,您可以给它添加win-vertical类值,它将垂直显示。根据您自己独特的用户界面决定范围指示器是水平的还是垂直的。
图 9-9 。呈现 Windows 8 样式的范围控件
选择框
选择框实际上是 HTML 世界中的古董。该控件在列表中有一个或多个选项。它可以被配置为显示整个选项列表,或者只显示一个选项,在这种情况下,当用户展开列表时,其余的选项将被列出。它也可以配置为允许单个选定值或多个值。
清单 9-7。 三个水果被声明为选择列表中的选项,但只有其中一个被选中
<!-- HTML snippet -->
<select>
<option selected>Apple</option>
<option>Orange</option>
<option>Banana</option>
</select>
图 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>
图 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>
图 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>
图 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>
图 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>
图 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 选择器将选择一个id为myToggle的 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 的样子。
图 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-container和win-item元素。当您需要样式化列表中的项目时,这两个类中的一个是缩小您的选择范围,只列出容器或列表项目的好方法。 - 注意绝对位置。第一项设置有
0px的top值,第二项设置有30px,第三项设置有60px。 - 请注意 win-viewport 和 win-surface 类的使用。这两个组件之间的区别很重要,你马上就会看到。
Windows Dev Center 上的文章http://msdn.microsoft.com/en-us/library/windows/apps/hh850406.aspx非常有助于可视化组成 ListView 的可视(和功能)组件。看看图 9-17 ,出自那篇文章。理解win-listview、win-surface和win-viewport类是绝对必要的。
图 9-17 。一个图,显示了组成列表视图的各种组件,这在试图添加或覆盖影响这个相对复杂的 WinJS 控件的样式时非常有用
可以把win-viewport想象成一个框架,当用户滚动时,win-surface可以在这个框架后面滑动。在win-listview上添加或操作样式属性,如果它会影响整个控件的话。win-viewport包含滚动行为(overflow-x和overflow-y)。Style win-surface如果你想影响实际出现在列表项后面的背景。
语义缩放
与 ListView 相关的是 SemanticZoom 控件。SemanticZoom 控件意味着包含两个 ListView 控件——标准 ListView(视图附近的*)和另一个表示同一数据的逻辑缩小视图(视图和视图)。通常,远视图显示代表近视图中数据的组。组列表比完整的数据集更简洁,并且附加的特征是用户能够从远视图中选择一个组,并直接导航到近视图中的该组。*
图 9-18 显示了语义缩放控件的简化模型。在第一个图像中,显示了多个组及其关联的项目。在用户在列表上做了缩放手势后,它们被动画化并转换成第二个图像,其中显示组,但不显示它们的项目。
图 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 看起来是一个单独的图像,但是你可以看到箭头被绘制在每条边上。箭头是可见的,所以鼠标用户可以点击一些东西,但是触摸用户可以简单地滑动列表以改变到新的图像。
图 9-19 。当您使用鼠标与 Windows 8 交互时,下一个和上一个箭头会呈现在 FlipView 的每一侧。你也可以随时使用滑动手势进行切换
整个 FlipView 本身有一个win-flipview类,这使得在运行的应用程序中引用 FlipView 控件变得很容易。但是,填充 FlipView 的项目会呈现在一个嵌入的 div 上,该 div 具有 win-surface 类。像 ListView 一样,您可以将这个元素想象成一个包含所有项目的长条,它可以滑动以一次显示一帧。
最后,FlipView 有两个导航按钮,只有在用户使用鼠标时才会出现。如果用户一直使用触摸进行交互,导航按钮就没有必要出现,因为只需一个简单的滑动手势就可以向前或向后导航。你可以在清单 9-25 中看到这些按钮的标记。它们是 HTML 按钮,被赋予了类值win-navbutton和win-navleft或win-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>
图 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-control和data-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 时,它将有用的类名添加到各个部分,这样我们就有了“钩子”,我们可以使用它来设计整个控件的样式或针对单个组件。
这一切都是为了给用户提供更好的控制和更容易的开发之间的平衡,而且非常有效。