HTML5-和-CSS-响应式-Web-设计-全-

HTML5 和 CSS 响应式 Web 设计(全)

原文:zh.annas-archive.org/md5/BF3881984EFC9B87954F91E00BDCB9A3

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

响应式网页设计提供了一个单一的解决方案,可以在手机、桌面电脑和各种设备上都呈现出色。它会自动根据用户屏幕的大小做出响应,为今天和明天的设备提供最佳的体验。

这本书涵盖了响应式网页设计的每一个重要方面。此外,它通过应用 HTML5 和 CSS3 提供的最新和最有用的技术,扩展了响应式设计方法,使设计比以往更精简和更易维护。它还解释了编写和交付代码、图像和文件的常见最佳实践方法。

如果你能理解 HTML 和 CSS,你就能构建一个响应式网页设计。

这本书涵盖了什么

第一章,“响应式网页设计的要点”,是对编写响应式网页设计的关键要素进行快速介绍。

第二章,“媒体查询-支持不同的视口”,涵盖了关于 CSS 媒体查询的一切:它们的功能、语法以及你可以使用它们的各种方式。

第三章,“流动布局和响应式图像”,向你展示如何编写比例布局和响应式图像,并全面探讨了 Flexbox 布局。

第四章,“HTML5 用于响应式网页设计”,涵盖了 HTML5 的所有语义元素、文本级语义和可访问性考虑。我们还介绍了如何使用 HTML5 在页面中插入视频和音频。

第五章,“CSS3-选择器、排版、颜色模式和新功能”,深入探讨了 CSS 的无限可能性:选择器、HSLA 和 RGBA 颜色、网页排版、视口相对单位等等。

第六章,“用 CSS3 创建令人惊叹的美学”,涵盖了 CSS 滤镜、框阴影、线性和径向渐变、多重背景,以及如何将背景图像定位到高分辨率设备。

第七章,“使用 SVG 实现分辨率独立性”,解释了我们在文档中使用 SVG 以及作为背景图像的一切所需,以及如何使用 JavaScript 与它们交互。

第八章,“过渡、变换和动画”,我们的 CSS 开始动起来,探讨了如何使用 CSS 进行交互和动画。

第九章,“用 HTML5 和 CSS3 征服表单”,网页表单一直很难处理,但最新的 HTML5 和 CSS3 功能使它们比以往更容易处理。

第十章,“接近响应式网页设计”,探讨了在着手进行响应式网页设计之前的基本考虑因素,并提供了一些最后一刻的智慧金点子,以帮助你在响应式探索中取得成功。

你需要为这本书做些什么

  • 文本编辑器

  • 永远绿色的浏览器

  • 对平庸笑话的偏爱

这本书适合谁

你是在写两个网站吗:一个是为移动设备,一个是为更大的显示器?或者你已经实现了你的第一个“RWD”,但是在努力将它们整合在一起?如果是这样,那么《使用 HTML5 和 CSS3 进行响应式网页设计第二版》将为你提供一切你需要的,让你的网站更上一层楼。

你需要一些 HTML 和 CSS 知识来跟上进度,但是关于响应式设计和制作出色网站的一切你都能在这本书中找到!

惯例

在本书中,您将找到许多文本样式,用以区分不同类型的信息。以下是这些样式的一些示例及其含义的解释。

文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 用户名显示如下:“我们可以通过在<head>中添加这个片段来轻松解决之前的问题。”

代码块设置如下:

img {
    max-width: 100%;
}

新术语重要单词以粗体显示。屏幕上看到的单词,例如菜单或对话框中的单词,显示在文本中,如:“最简单的情况下,您选择一个 URL,然后点击开始测试。”

注意

警告或重要说明显示在这样的框中。

提示

技巧和窍门显示如下。

第一章:响应式网页设计的基本要素

仅仅几年前,网站可以以固定宽度构建,预期所有最终用户都会获得相当一致的体验。这个固定宽度(通常为 960 像素宽或周围)对于笔记本电脑屏幕来说并不太宽,而具有大分辨率显示器的用户只是在两侧有大量的边距。

但是在 2007 年,苹果的 iPhone 引领了第一个真正可用的手机浏览体验,人们访问和互动网络的方式永远改变了。

在这本书的第一版中,曾经指出过:

“在 2010 年 7 月至 2011 年 7 月的 12 个月内,全球移动浏览器使用率从 2.86%上升到 7.02%。”

在 2015 年中期,同一统计系统(gs.statcounter.com)报告称,这一数字已上升至 33.47%。作为对比,北美的移动设备占比为 25.86%。

无论从任何角度来看,移动设备的使用量都在不断增加,而与此同时,27 英寸和 30 英寸的显示器现在也很常见。现在,浏览网络的最小屏幕和最大屏幕之间的差异比以往任何时候都要大。

值得庆幸的是,对于不断扩大的浏览器和设备环境,有一个解决方案。使用 HTML5 和 CSS3 构建的响应式网页设计可以使网站在多个设备和屏幕上“只需工作”。它使网站的布局和功能能够响应其环境(屏幕大小,输入类型,设备/浏览器功能)。

此外,使用 HTML5 和 CSS3 构建的响应式网页设计可以在无需基于服务器的后端解决方案的情况下实现。

开始我们的探索

无论您是响应式网页设计,HTML5 还是 CSS3 的新手,还是已经很熟练,我希望这一章能够达到两个目的中的一个。

如果您已经在响应式网页设计中使用 HTML5 和 CSS3,这一章应该作为一个快速和基本的复习。或者,如果您是新手,可以将其视为一种基本的“训练营”,涵盖基本要素,以便我们都在同一页面上。

在本章结束时,我们将涵盖您需要编写完全响应式网页的所有内容。

您可能想知道其他九章为什么在这里。在本章结束时,这一点也应该显而易见。

以下是本章的内容:

  • 定义响应式网页设计

  • 如何设置浏览器支持级别

  • 关于工具和文本编辑器的简要讨论

  • 我们的第一个响应式示例:一个简单的 HTML5 页面

  • 视口meta标签的重要性

  • 如何使图像按比例缩放到其容器

  • 编写 CSS3 媒体查询以创建设计断点

  • 我们基本示例的不足之处

  • 为什么我们的旅程才刚刚开始

定义响应式网页设计

术语“响应式网页设计”是由 Ethan Marcotte 于 2010 年创造的。在他的开创性的A List Apart文章中(www.alistapart.com/articles/responsive-web-design/),他将三种现有技术(灵活的网格布局,灵活的图像/媒体和媒体查询)整合成一种统一的方法,并将其命名为响应式网页设计。

简而言之,响应式网页设计

响应式网页设计是以最相关的格式呈现网页内容,以适应视口和访问设备。

在其初期,典型的响应式设计是从“桌面”固定宽度设计开始构建的。然后,内容被重新排列或删除,以使设计在较小的屏幕上工作。然而,流程发展并且变得明显,从设计到内容和开发,一切都在相反的方向工作得更好;从较小的屏幕开始,逐渐扩展。

在我们开始之前,我想在继续之前讨论一些主题;浏览器支持和文本编辑器/工具。

设置浏览器支持级别

响应式网页设计的普及和普遍性使其比以往更容易向客户和利益相关者推销。大多数人对响应式网页设计有一些了解。一个单一的代码库可以在所有设备上完美运行的概念是一个令人信服的提议。

在开始响应式设计项目时,几乎总会出现一个问题,那就是浏览器支持的问题。由于浏览器和设备的变种如此之多,支持每一个浏览器的变种并不总是切实可行。也许时间是一个限制因素,也许是金钱。也许两者都是。

通常情况下,浏览器越老,为了与现代浏览器获得功能或美学上的平等,需要的工作和代码就越多。因此,通过分层体验并仅为更有能力的浏览器提供增强的视觉和功能,可能更有意义,也更快。

在本书的上一版中,花了一些时间来介绍如何为非常老的仅限桌面浏览器提供支持。在这一版中,我们将不再介绍。

当我在 2015 年中写这篇文章时,Internet Explorer 6、7 和 8 几乎已经消失。即使 IE 9 在全球浏览器市场上的份额只有 2.45%(IE 10 只有 1.94%,而 IE 11 正在稳步上升)。如果你别无选择,只能为 Internet Explorer 8 及以下版本开发,我对你表示同情,但我必须坦率地告诉你,这本书中你可以使用的内容将不会太多。

对于其他人来说,你应该向你的客户/资助者解释为什么为不景气的浏览器开发可能是一个错误,并且在各个方面,主要为现代浏览器和平台投入开发时间和资源是明智的财务决策。

然而,真正重要的统计数据只有你自己的。除了极端情况外,我们构建的网站应该至少在每个常见的浏览器中都是功能性的。除了基本功能外,对于任何网络项目来说,提前决定你想要完全增强体验的平台,以及你愿意让视觉/功能异常的平台。

你还会发现,从最简单的“基本水平”体验开始,并增强(一种被称为渐进增强的方法)比从相反的方向解决问题更容易——首先构建最终体验,然后尝试为能力较弱的平台提供后备(一种被称为优雅降级的方法)。

为了说明为什么提前知道这一点很重要,考虑一下,如果你不幸有 25%的网站访问者使用 Internet Explorer 9(例如),你需要考虑该浏览器支持哪些功能,并相应地调整你的解决方案。如果大量用户使用旧的移动电话平台,比如 Android 2,同样需要谨慎。你可以考虑一个“基本”体验,这将取决于项目。

如果没有合适的数据,我会应用一个简单而粗糙的逻辑来确定是否应该花时间开发特定的平台/浏览器版本:如果开发和支持浏览器 X 的成本超过了浏览器 X 上的用户创造的收入/收益;不要为浏览器 X 开发特定的解决方案。

这很少是一个你是否能够“修复”旧平台/版本的问题。问题是你是否应该。

在考虑哪些平台和浏览器版本支持哪些功能时,如果你还没有,要熟悉caniuse.com网站。它提供了一个简单的界面,用于确定我们将在整个过程中查看的功能的浏览器支持情况。

设置浏览器支持级别

关于工具和文本编辑器的简短说明。

使用什么文本编辑器或 IDE 系统来构建响应式网页设计并不重要。如果最简单的文本编辑器可以让你高效地编写 HTML、CSS 和 JavaScript,那就完全没问题。同样,并没有必需的工具是必不可少的,以便让响应式网页设计顺利进行。你实际上只需要一些能让你编写 HTML、CSS 和 JavaScript 的东西。无论你偏好 Sublime Text、Vim、Coda、Visual Studio 还是记事本 - 都无关紧要。只需使用最适合你的工具。

然而,现在有更多的工具(通常是免费的)可以消除建站过程中许多手动和耗时的任务。例如,CSS 处理器(Sass、LESS、Stylus、PostCSS)可以帮助组织代码、变量、颜色处理和算术。像 PostCSS 这样的工具还可以自动完成可怕且无趣的工作,比如 CSS 供应商前缀。此外,“Linting”和验证工具可以在你工作时检查你的 HTML、JavaScript 和 CSS 代码是否符合标准,消除许多浪费时间的拼写错误或语法错误。

新的工具不断涌现,并且它们不断改进。因此,虽然我们会在进行中提到一些相关和有益的工具,但要知道可能会有更好的东西即将出现。因此,在我们的示例中,我们不会依赖于除基于标准的 HTML 和 CSS 之外的任何东西。然而,你应该尽可能使用任何工具来快速可靠地生成你的前端代码。

我们的第一个响应式示例

在第一段中,我承诺到本章结束时,你将知道构建完全响应式网页所需的一切。到目前为止,我只是在围绕手头的问题进行讨论。是时候付诸行动了。

注意

代码示例

你可以通过访问rwd.education/download.zip或通过 GitHub github.com/benfrain/rwd来下载本书中的所有代码示例。值得知道的是,在整个章节中构建的个别示例中,代码下载只提供了示例的最终版本。例如,如果你下载了第二章, 媒体查询-支持不同的视口的代码示例,这些示例将是在第二章, 媒体查询-支持不同的视口结束时的状态。除了文本中提供的中间状态外,不提供其他中间状态。

我们的基本 HTML 文件

我们将从一个简单的 HTML5 结构开始。现在不用担心每一行都做了什么(特别是<head>的内容,我们将在第四章中详细介绍,响应式网页设计的 HTML5)。

暂时,只需专注于<body>标签内的元素。我相当确定那里没有什么看起来太不寻常的东西;一些 div,一个用于标志的图形,一张图片(看起来很美味的烤饼),一两段文字和一列项目的列表。

以下是代码的摘要版本。为了简洁起见,我已经在下面的代码中删除了段落文字,因为我们只需要关注结构。但是,你应该知道这是一个食谱,描述了如何制作司康饼;典型的英式蛋糕。

如果你想看完整的 HTML 文件,可以从rwd.education网站下载。

<!doctype html>
<html class="no-js" lang="en">
    <head>
        <meta charset="utf-8">
        <title>Our first responsive web page with HTML5 and CSS3</title>
        <meta name="description" content="A basic responsive web page – an example from Chapter 1">
        <link rel="stylesheet" href="css/styles.css">
    </head>
    <body>
        <div class="Header">
            <a href="/" class="LogoWrapper"><img src="img/SOC-Logo.png" alt="Scone O'Clock logo" /></a>
            <p class="Strap">Scones: the most resplendent of snacks</p>
        </div>
        <div class="IntroWrapper">
            <p class="IntroText">Occasionally maligned and misunderstood; the scone is a quintessentially British classic.</p>
            <div class="MoneyShot">
                <img class="MoneyShotImg" src="img/scones.jpg" alt="Incredible scones" />
                <p class="ImageCaption">Incredible scones, picture from Wikipedia</p>
            </div>
        </div>
        <p>Recipe and serving suggestions follow.</p>
        <div class="Ingredients">
            <h3 class="SubHeader">Ingredients</h3>
            <ul>

            </ul>
        </div>
        <div class="HowToMake">
            <h3 class="SubHeader">Method</h3>
            <ol class="MethodWrapper">

            </ol>
        </div>
    </body>
</html>

默认情况下,网页是灵活的。如果你打开示例页面,即使在这一点上(没有媒体查询的情况下),调整浏览器窗口大小,你会看到文本会根据需要重新排列。

在不同的设备上呢?没有任何 CSS 的情况下,在 iPhone 上的呈现如下:

我们的基本 HTML 文件

正如你所看到的,它在 iPhone 上呈现得像一个“正常”的网页。原因是 iOS 默认将网页呈现为 980px 宽,并将其缩小到视口中。

浏览器的可视区域在技术上被称为viewport。视口很少等同于设备的屏幕尺寸,特别是在用户可以调整浏览器窗口大小的情况下。

因此,从现在开始,当我们提到网页的可用空间时,我们通常会使用这个更准确的术语。

我们可以通过在<head>中添加以下片段来轻松解决之前的问题:

<meta name="viewport" content="width=device-width">

这个 viewport meta标签是一种非标准(但事实上的标准)的告诉浏览器如何呈现页面的方式。在这种情况下,我们的 viewport meta标签实际上是在说“使内容以设备的宽度呈现”。实际上,最好的办法可能是直接向您展示这一行对适用设备的影响:

我们的基本 HTML 文件

太好了!现在文本以更“原生”的大小呈现和流动了。让我们继续。

我们将在第二章中介绍meta标签及其各种设置和变体(以及相同功能的基于标准的版本),媒体查询-支持不同的视口

驯服图片

他们说一张图片胜过千言万语。在我们的示例页面中写了这么多关于烤饼干的内容,却没有展示这些美味的图片。我要在页面顶部添加一张烤饼干的图片;一种“英雄”图片,吸引用户阅读页面。

驯服图片

哦!那张漂亮的大图片(宽度为 2000px)强制我们的页面呈现得有点乱。我们需要解决这个问题。我们可以通过 CSS 为图片添加固定宽度,但问题在于我们希望图片能够根据不同的屏幕尺寸进行缩放。

例如,我们的 iPhone 示例宽度为 320px,所以我们可以将该图片的宽度设置为 320px,但是如果用户旋转屏幕会发生什么呢?320px 宽的视口现在变成了 480px 宽。幸运的是,通过一行 CSS 代码很容易实现图片的流动,使其可以根据容器的可用宽度进行缩放。

我现在要创建css/styles.css CSS 文件,并将其链接到 HTML 页面的头部。

这是我要添加的第一件事。通常我会设置一些其他默认值,我们将在后面的章节中讨论这些默认值,但是为了我们的目的,我很乐意只用这个来开始:

img {
    max-width: 100%;
}

现在当页面刷新后,我们看到的更接近我们预期的东西。

驯服图片

这个基于max-width的规则的作用只是规定所有的图片的宽度最大为 100%(即它们应该扩展到 100%的大小,不再更大)。如果包含元素(如body或者它所在的div)的宽度小于图片的固有宽度,图片将简单地缩放到最大可用空间。

提示

为什么不简单地使用 width: 100%?

要使图片流动,您也可以使用更常用的 width 属性。例如,width: 100%,但这会产生不同的效果。当使用width属性时,图片将以该宽度显示,而不考虑其固有大小。在我们的示例中,结果将是 logo(也是一张图片)拉伸以填满其容器的 100%。对于比图片(如我们的 logo)宽得多的容器,这会导致图片过大。

太好了。现在一切都按预期布局。无论视口大小如何,都没有内容横向溢出页面。

然而,如果我们在更大的视口中查看页面,基本样式开始变得字面上和比喻上都被拉伸了。看一下大约在 1400px 大小的示例页面:

驯服图片

哦,天哪!事实上,即使在大约 600px 宽的时候,它开始受到影响。在这一点上,如果我们能重新安排一些东西就会很方便。也许调整一下图片的大小并将其放在一边。也许改变一些字体大小和元素的背景颜色。

幸运的是,我们可以通过使用 CSS 媒体查询来轻松实现所有这些功能,以使事情按照我们的意愿进行弯曲。

进入媒体查询

正如我们已经确定的那样,在 600px 宽的某个点之后,我们当前的布局开始显得拉伸。让我们使用 CSS3 媒体查询根据屏幕宽度调整布局。媒体查询允许我们根据一些条件(例如屏幕宽度和高度)应用特定的 CSS 规则。

提示

不要将断点设置为流行的设备宽度

“断点”是用来定义响应式设计应该显著改变的点。

当人们开始使用媒体查询时,常见的做法是在设计中设置特定于当时流行设备的断点。当时通常是 iPhone(320px x 480px)和 iPad(768px x 1024px)定义了这些“断点”。

那种做法当时是一个糟糕的选择,现在甚至更糟。问题在于,通过这样做,我们是在为特定的屏幕尺寸定制设计。我们需要一个响应式设计——不管查看它的屏幕尺寸是多大,它都能够适应;而不是只在特定尺寸下才能看起来最好。

因此,让内容和设计本身决定断点的相关性。也许你的初始布局在 500px 宽及以上开始看起来不对,也许是 800px。你自己的项目设计应该决定何时需要断点。

我们将在第二章中涵盖整个 CSS 媒体查询范围,媒体查询-支持不同的视口,巧妙地命名为媒体查询

然而,为了将我们的基本示例整理成形,我们将集中讨论一种媒体查询类型;最小宽度媒体查询。在这种类型的媒体查询中,只有在视口达到最小定义宽度时,才会应用其中的 CSS 规则。可以使用一系列不同的长度单位来指定确切的最小宽度,包括百分比、em、rem 和 px。在 CSS 中,最小宽度媒体查询的写法如下:

@media screen and (min-width: 50em) {
    /* styles */
}

@media指令告诉浏览器我们正在开始一个媒体查询,screen部分(在这种情况下,声明“屏幕”在技术上并不需要,但我们将在下一章中详细处理这个问题)告诉浏览器这些规则应该适用于所有屏幕类型,and (min-width: 50em)告诉浏览器这些规则应该限制在所有大于 50em 大小的视口上。

提示

我相信是 Bryan Rieger (www.slideshare.net/bryanrieger/rethinking-the-mobile-web-by-yiibu)首先写道:

"对媒体查询的支持的缺失实际上是第一个媒体查询。"

他的意思是,我们写的第一条规则,除了媒体查询之外,应该是我们的“基本”规则,然后我们可以为更有能力的设备增强这些规则。

目前,只需意识到这种方法首先强调我们最小的屏幕,并允许我们根据设计的需要逐步添加细节。

修改示例以适应更大的屏幕

我们已经确定我们的设计在大约 600px/37.5rem 宽度时开始受到影响。

因此,让我们通过一个简单的示例来混合一下,展示在不同的视口尺寸下如何布局不同的内容。

提示

几乎所有的浏览器都有一个默认的文本大小为 16px,所以你可以通过将 px 值除以 16 来轻松地将宽度转换为 rems。我们将在第二章中讨论为什么你可能想要这样做,媒体查询-支持不同的视口

首先,我们将阻止主要的“英雄”图像变得过大,并将其保持在右侧。然后介绍文本可以位于左侧。

然后我们将有主要的文本部分,描述如何制作烤饼的“方法”,位于左侧,下面有一个小的方框部分,详细介绍右侧的配料。

所有这些变化都可以通过在媒体查询中封装这些特定样式来相对简单地实现。以下是添加相关样式后的情况:

修改示例以适应更大的屏幕

在较小的屏幕上,它看起来基本上与以前一样,但一旦视口达到 50rem 或更宽,它就会调整到新的布局。

以下是添加的布局样式:

@media screen and (min-width: 50rem) {
    .IntroWrapper {
        display: table;
        table-layout: fixed;
        width: 100%;
    }

    .MoneyShot,
    .IntroText {
        display: table-cell;
        width: 50%;
        vertical-align: middle;
        text-align: center;
    }

    .IntroText {
        padding: .5rem;
        font-size: 2.5rem;
        text-align: left;
    }

    .Ingredients {
        font-size: .9rem;
        float: right;
        padding: 1rem;
        margin: 0 0 .5rem 1rem;
        border-radius: 3px;
        background-color: #ffffdf;
        border: 2px solid #e8cfa9;
    }

    .Ingredients h3 {
        margin: 0;
    }
}

这并不太糟糕,是吗?只需很少的代码,我们就建立了一个可以根据视口大小做出响应并在需要时提供更合适布局的页面。通过添加更多的样式,页面看起来甚至更加舒适。有了这些,我们基本的响应式页面现在在 iPhone 上看起来是这样的:

修改示例以适应更大的屏幕

就像上面的 50rem 宽度一样:

修改示例以适应更大的屏幕

这些进一步的视觉装饰并没有增加对响应式发生的理解,因此我在这里省略了它们,但如果你想查看相关代码,请在rwd.educationgithub.com/benfrain/rwd下载本章代码。

这只是一个非常基本的示例,但它已经包含了构建响应式网页设计的基本方法论。

重申我们所涵盖的基本要点;从“基础”样式开始,这些样式可以在任何设备上使用。然后随着视口大小和/或功能的增加逐渐添加增强功能。

注意

你可以在这里找到 CSS 媒体查询(Level 3)的完整规范:www.w3.org/TR/css3-mediaqueries/

这里还有一个 CSS 媒体查询(Level 4)的工作草案:dev.w3.org/csswg/mediaqueries-4/

我们示例的不足之处

在本章中,我们已经涵盖了基本响应式 HTML5 和 CSS3 驱动的网页的所有基本组成部分。

但你我都知道,这个基本的响应式示例很少是我们要构建的限制。也不应该反映我们能够构建的限制。

如果我们希望我们的页面对不同的光照条件做出响应怎么办?当人们使用不同的指向设备(例如手指而不是鼠标)时,链接的大小会发生变化怎么办?如果我们想要简单地使用 CSS 来实现动画和移动视觉元素呢?

然后是标记。我们如何使用更多语义元素来标记页面;文章、部分、菜单等,或者制作具有内置验证的表单(无需 JavaScript)?如果我们想要在不同的视口上更改元素的视觉顺序呢?

别忘了图片。在这个示例中,我们有流体图片,但如果人们在手机上访问这个页面,他们将需要下载一个大的图形(宽度为 2000 像素),而这个图形只会以其中的一小部分显示在他们的手机上。这将使页面加载速度比必要的慢得多。肯定有更好的办法吧?

那么标志和图标呢?在这个示例中,我们使用了 PNG,但我们可以轻松地使用可伸缩矢量图形SVG)来享受具有分辨率独立性的图形。这样它们看起来会非常清晰,无论查看屏幕的分辨率如何。

希望你有时间留下来,因为这些正是我们将在接下来的章节中回答的问题。

总结

干得好,现在你知道并理解了创建完全响应式网页所需的基本要素。然而,正如我们刚刚发现的,有很多地方可以改进。

但这没关系。我们不只是想要能够制作称职的响应式网页设计,我们还想要能够创造“最佳体验”。所以让我们继续努力吧。

首先,我们将深入了解所有三级和四级 CSS 媒体查询所提供的内容。我们已经看到网页如何响应视口宽度,但现在我们可以做的远不止这些,而且很快会有更多有趣的东西出现在你的浏览器中。让我们去看一看。

第二章:媒体查询-支持不同的视口

在上一章中,我们简要介绍了响应式网页的基本组件:流体布局、流体图像和媒体查询。

本章将详细介绍媒体查询,希望能够提供充分理解它们的能力、语法和未来发展所需的一切。

在本章中,我们将:

  • 了解为什么响应式网页设计需要媒体查询

  • 了解媒体查询语法

  • 学习如何在link标签中使用媒体查询,以及在 CSS 文件中使用 CSS @import语句和媒体查询本身

  • 了解我们可以测试的设备功能

  • 使用媒体查询来促进视觉变化,取决于可用的屏幕空间

  • 考虑媒体查询是否应该被分组在一起或根据需要编写

  • 了解meta视口标签,以便在 iOS 和 Android 设备上使媒体查询按预期工作

  • 考虑未来媒体查询规范提出的功能

CSS3 规范由许多模块组成。媒体查询(Level 3)只是其中之一。媒体查询允许我们根据设备的功能来定位特定的 CSS 样式。例如,只需几行 CSS,我们就可以根据视口宽度、屏幕宽高比、方向(横向或纵向)等来改变内容的显示方式。

媒体查询被广泛实现。除了古老版本的 Internet Explorer(8 及以下)之外,几乎所有浏览器都支持它们。简而言之,没有任何理由不使用它们!

提示

W3C 的规范经过一系列的批准过程。如果你有一天空闲,可以去看看它们在www.w3.org/2005/10/Process-20051014/tr上的官方解释。简化版本是,规范从工作草案WD)到候选推荐CR),再到建议推荐PR),最后在许多年后到达 W3C 推荐(REC)。比其他模块更成熟的模块通常更安全使用。例如,CSS 变换模块 Level 3(www.w3.org/TR/css3-3d-transforms/)自 2009 年 3 月以来一直处于 WD 状态,而它的浏览器支持要比 CR 模块如媒体查询差得多。

为什么响应式网页设计需要媒体查询

CSS3 媒体查询使我们能够将特定的 CSS 样式定位到特定的设备功能或情况上。如果你前往 W3C 的 CSS3 媒体查询模块规范(www.w3.org/TR/css3-mediaqueries/),你会看到这是他们对媒体查询的官方介绍:

“媒体查询由媒体类型和零个或多个表达式组成,用于检查特定媒体特征的条件。可以在媒体查询中使用的媒体特征包括'宽度'、'高度'和'颜色'。通过使用媒体查询,演示可以根据特定范围的输出设备进行定制,而不改变内容本身。”

没有媒体查询,我们将无法仅使用 CSS 大幅改变网站的视觉效果。它们使我们能够编写防御性的 CSS 规则,以预防诸如纵向屏幕方向、小或大视口尺寸等情况。

尽管流体布局可以在很大程度上实现设计,但考虑到我们希望覆盖的屏幕尺寸范围,有时我们需要更全面地修改布局。媒体查询使这成为可能。把它们看作是 CSS 的基本条件逻辑。

CSS 中的基本条件逻辑

真正的编程语言都有一些设施,可以处理两种或更多可能的情况。这通常采用条件逻辑的形式,以if/else语句为典型。

如果编程术语让你的眼睛发痒,不要害怕;这是一个非常简单的概念。每当你去咖啡馆时让朋友帮你点餐时,你可能都在规定条件逻辑,“如果他们有三重巧克力松饼,我就要一个,如果没有,我就要一块胡萝卜蛋糕”。这是一个简单的条件语句,有两种可能的结果(在这种情况下同样好)。

在撰写本文时,CSS 不支持真正的条件逻辑或编程特性。循环、函数、迭代和复杂的数学仍然完全属于 CSS 处理器的领域(我是否提到了一本关于 Sass 预处理器的精彩书籍,名为Sass and Compass for Designers?)。然而,媒体查询是 CSS 中允许我们编写基本条件逻辑的一种机制。通过使用媒体查询,其中的样式根据是否满足某些条件而作用域。

注意

编程特性即将到来

CSS 预处理器的流行使得负责 CSS 规范的人们开始注意到这一点。现在有一个 CSS 变量的 WD 规范:www.w3.org/TR/css-variables/

然而,目前浏览器支持仅限于 Firefox,因此目前真的不值得考虑在实际中使用。

媒体查询语法

CSS 媒体查询是什么样的,更重要的是,它是如何工作的?

在任何 CSS 文件的底部输入以下代码,并预览相关的网页。或者,你可以打开example_02-01

body {
  background-color: grey;
}
@media screen and (min-width: 320px) {
  body {
    background-color: green;
  }
}
@media screen and (min-width: 550px) {
  body {
    background-color: yellow;
  }
}
@media screen and (min-width: 768px) {
  body {
    background-color: orange;
  }
}
@media screen and (min-width: 960px) {
  body {
    background-color: red;
  }
}

现在,在浏览器中预览文件并调整窗口大小。页面的背景颜色将根据当前的视口大小而变化。我们将很快介绍语法的工作原理。首先,重要的是要知道如何以及在哪里可以使用媒体查询。

链接标签中的媒体查询

自 CSS2 以来一直在使用 CSS 的人会知道,可以使用<link>标签的媒体属性来指定样式表适用的设备类型(例如,screenprint)。考虑以下示例(你会将其放在你的标记的<head>标签中):

<link rel="style sheet" type="text/css" media="screen" href="screen-styles.css">

媒体查询增加了根据设备的能力或特性来定位样式的能力,而不仅仅是设备类型。把它看作是对浏览器的一个问题。如果浏览器的答案是“true”,那么封闭的样式将被应用。如果答案是“false”,它们就不会。媒体查询不仅仅询问浏览器“你是屏幕吗?”——就像我们只能用 CSS2 来问的那样——媒体查询问得更多。相反,媒体查询可能会问,“你是屏幕,你是纵向的吗?”让我们以此为例:

<link rel="stylesheet" media="screen and (orientation: portrait)" href="portrait-screen.css" />

首先,媒体查询表达式询问类型(你是屏幕吗?),然后是特性(你的屏幕是纵向的吗?)。portrait-screen.css样式表将应用于任何具有纵向屏幕方向的屏幕设备,并对其他设备忽略。通过在媒体查询的开头添加 not,可以颠倒任何媒体查询表达式的逻辑。例如,以下代码将否定我们之前例子中的结果,将文件应用于任何不是纵向屏幕的屏幕:

<link rel="stylesheet" media="not screen and (orientation: portrait)" href="portrait-screen.css" />

组合媒体查询

也可以将多个表达式串联在一起。例如,让我们扩展我们之前的一个例子,并将文件限制为视口大于 800 像素的设备。

<link rel="stylesheet" media="screen and (orientation: portrait) and (min-width: 800px)" href="800wide-portrait-screen.css" />

此外,我们可以有一个媒体查询列表。如果列出的任何查询为 true,则将应用该文件。如果没有一个为 true,则不会应用。以下是一个例子:

<link rel="stylesheet" media="screen and (orientation: portrait) and (min-width: 800px), projection" href="800wide-portrait-screen.css" />

这里有两点需要注意。首先,逗号分隔每个媒体查询。其次,在投影后,你会注意到括号中没有尾随的特性/值组合。这是因为在没有这些值的情况下,媒体查询将应用于所有媒体类型。在我们的例子中,样式将应用于所有投影仪。

提示

你应该知道,可以使用任何 CSS 长度单位来指定媒体查询。像素px)是最常用的,但emsem)和remsrem)同样适用。关于每种单位的优点,我在这里写了更多内容:benfrain.com/just-use-pixels

因此,如果你想在 800px(但以 em 单位指定)处设置断点,只需将像素数除以 16。例如,800px 也可以指定为 50em(800 / 16 = 50)。

使用@import 的媒体查询

我们还可以使用 CSS 的@import功能将样式表有条件地加载到现有样式表中。例如,以下代码将导入名为phone.css的样式表,前提是设备基于屏幕,并且视口最大为 360 像素:

@import url("phone.css") screen and (max-width:360px);

请记住,使用 CSS 的@import功能会增加 HTTP 请求(影响加载速度),因此请谨慎使用此方法。

CSS 中的媒体查询

到目前为止,我们已经将它们作为链接到我们将放置在 HTML 的<head></head>部分中的 CSS 文件,并作为@import语句。但是,更有可能的是,我们将希望在 CSS 样式表中使用媒体查询。例如,如果我们将以下代码添加到样式表中,它将使所有h1元素变为绿色,前提是设备的屏幕宽度为 400 像素或更小:

@media screen and (max-device-width: 400px) {
  h1 { color: green }
}

首先,我们指定要使用@media规则的媒体查询,然后指定要匹配的类型。在前面的示例中,我们只想将封闭的规则应用于屏幕(例如不适用于print)。然后,在括号内输入查询的具体内容。然后像任何 CSS 规则一样,我们打开大括号并编写所需的样式。

在这一点上,我可能需要指出的是,在大多数情况下,实际上不需要指定screen。这是规范中的关键点:

“媒体查询提供了适用于所有媒体类型的简写语法;关键字'all'可以省略(以及末尾的'and')。也就是说,如果媒体类型没有明确给出,它就是'all'。”

因此,除非你想要针对特定媒体类型的样式,否则可以省略screen and部分。这是我们从现在开始在示例文件中编写媒体查询的方式。

媒体查询可以测试什么?

在构建响应式设计时,最常使用的媒体查询通常与设备的视口宽度(width)有关。根据我的经验,我发现除了分辨率和视口高度偶尔需要使用外,几乎没有必要使用其他功能。但是,以防万一需要,这里是媒体查询级别 3 可以测试的所有功能列表。希望其中一些能引起你的兴趣:

  • width:视口宽度。

  • height:视口高度。

  • device-width:渲染表面的宽度(对于我们的目的,这通常是设备的屏幕宽度)。

  • device-height:渲染表面的高度(对于我们的目的,这通常是设备的屏幕高度)。

  • orientation:此功能检查设备是纵向还是横向。

  • aspect-ratio:基于视口宽度和高度的宽高比。16:9 的宽屏显示可以写为aspect-ratio: 16/9

  • device-aspect-ratio:此功能类似于aspect-ratio,但是基于设备渲染表面的宽度和高度,而不是视口。

  • color:每个颜色分量的位数。例如,min-color: 16将检查设备是否具有 16 位颜色。

  • color-index:设备颜色查找表中的条目数。值必须是数字,不能为负数。

  • monochrome:此功能测试单色帧缓冲区中每像素的位数。值将是一个数字(整数),例如,monochrome: 2,不能为负数。

  • resolution:此功能可用于测试屏幕或打印分辨率;例如,min-resolution: 300dpi。它还可以接受每厘米的点数;例如,min-resolution: 118dpcm

  • scan:这可以是渐进式或隔行扫描功能,主要适用于电视。例如,720p 高清电视(720p 中的 p 表示“渐进式”)可以使用scan: progressive进行定位,而 1080i 高清电视(1080i 中的 i 表示“隔行扫描”)可以使用scan: interlace进行定位。

  • grid:此功能指示设备是基于网格还是位图。

所有前述功能,除了scangrid,都可以用minmax进行前缀处理以创建范围。例如,考虑以下代码片段:

@import url("tiny.css") screen and (min-width:200px) and (max-width:360px);

在这里,宽度应用了最小(min)和最大(max)来设置一个范围。tiny.css 文件只会被导入到视口宽度最小为 200 像素,最大为 360 像素的屏幕设备中。

注意

CSS 媒体查询级别 4 中弃用的功能

值得注意的是,媒体查询级别 4 的草案规范弃用了一些功能(dev.w3.org/csswg/mediaqueries-4/#mf-deprecated);其中最明显的是device-heightdevice-widthdevice-aspect-ratio。浏览器将继续支持这些查询,但建议您不要编写使用它们的新样式表。

使用媒体查询来改变设计

由于它们的本质,样式表中更下面的样式(对我们来说是 CSS 文件)会覆盖更上面的等效样式(除非更上面的样式更具体)。因此,我们可以在样式表的开头设置基本样式,适用于我们设计的所有版本(或者至少提供我们的“基本”体验),然后在文档中进一步使用媒体查询来覆盖相关部分。例如,我们可能选择在有限的视口中将导航链接设置为纯文本(或者只是较小的文本),然后使用媒体查询来在更大的视口中覆盖这些样式,以便在更大的空间可用时为我们提供文本和图标。

让我们看看这在实践中是什么样子(example_02-02)。首先是标记:

<a href="#" class="CardLink CardLink_Hearts">Hearts</a>
<a href="#" class="CardLink CardLink_Clubs">Clubs</a>
<a href="#" class="CardLink CardLink_Spades">Spades</a>
<a href="#" class="CardLink CardLink_Diamonds">Diamonds</a>

现在 CSS:

.CardLink {
    display: block;
    color: #666;
    text-shadow: 0 2px 0 #efefef;
    text-decoration: none;
    height: 2.75rem;
    line-height: 2.75rem;
    border-bottom: 1px solid #bbb;
    position: relative;
}

@media (min-width: 300px) {
    .CardLink {
        padding-left: 1.8rem;
        font-size: 1.6rem;
    }
}

.CardLink:before {
    display: none;
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    left: 0;
}

.CardLink_Hearts:before {
    content: "♥";
}

.CardLink_Clubs:before {
    content: "♣";
}

.CardLink_Spades:before {
    content: "♠";
}

.CardLink_Diamonds:before {
    content: "♦";
}

@media (min-width: 300px) {
    .CardLink:before {
        display: block;
    }
}

提示

下载示例代码

您可以从您在www.packtpub.com的帐户中下载您购买的所有 Packt 图书的示例代码文件。如果您在其他地方购买了这本书,您可以访问www.packtpub.com/support并注册,以便将文件直接通过电子邮件发送给您。

这是一个小视口中链接的屏幕截图:

使用媒体查询来改变设计

这是它在较大的视口中的截图:

使用媒体查询来改变设计

任何 CSS 都可以包含在媒体查询中

重要的是要记住,您通常在 CSS 中编写的任何内容也可以包含在媒体查询中。因此,可以使用媒体查询在不同情况下(通常是不同的视口大小)完全改变站点的布局和外观。

HiDPI 设备的媒体查询

媒体查询的另一个常见用例是在高分辨率设备上查看站点时更改样式。考虑这个:

@media (min-resolution: 2dppx) {
  /* styles */
}

在这里,我们的媒体查询指定,我们只希望封闭的样式应用于屏幕分辨率为 2 像素每像素单位(2dppx)的情况。这将适用于 iPhone 4 等设备(苹果的 HiDPI 设备被称为“Retina”)以及大量的 Android 设备。您可以通过减少 dppx 值来将该媒体查询应用于更广泛的设备范围。

提示

在编写最小分辨率媒体查询时,确保运行有一个添加前缀的工具,以提供相关的供应商前缀,以获得尽可能广泛的支持。如果现在对供应商前缀这个术语不太理解,也不用担心,因为我们将在下一章更详细地讨论这个主题。

组织和编写媒体查询的考虑

在这一点上,我们将进行一个简短的偏离,考虑作者在编写和组织他们的媒体查询时可以采取的一些不同方法。每种方法都有一些好处和一些权衡,因此至少了解这些因素是值得的,即使您认为它们对您的需求基本上无关紧要。

链接到不同的 CSS 文件,带有媒体查询

从浏览器的角度来看,CSS 被认为是“渲染阻塞”的资源。浏览器需要在渲染页面之前获取和解析链接的 CSS 文件。

然而,现代浏览器足够聪明,可以区分哪些样式表(在头部链接的带有媒体查询的样式表)需要立即分析,哪些可以推迟到初始页面渲染之后再进行。

对于这些浏览器,链接到不适用媒体查询的 CSS 文件(例如,如果屏幕太小,媒体查询不适用)可以在初始页面加载后“推迟”,从而提供一些性能优势。

关于这个主题,Google 的开发者页面上有更多内容:developers.google.com/web/fundamentals/performance/critical-rendering-path/render-blocking-css

然而,我想特别提醒您注意这一部分:

“...请注意,“渲染阻塞”只是指浏览器是否必须在该资源上保持页面的初始渲染。无论哪种情况,浏览器都会下载 CSS 资源,尽管对于非阻塞资源,它的优先级较低。”

再次强调,所有链接的文件仍然会被下载,只是如果它们不立即应用,浏览器不会延迟页面的渲染。

因此,现代浏览器加载一个响应式网页(查看example_02-03)时,会下载链接了不同媒体查询的四个不同样式表(以适应不同的视口范围),但可能只会在渲染页面之前解析适用的样式表。

分离媒体查询的实际性

尽管我们刚刚了解到分割媒体查询的过程可能会带来一些好处,但将不同的媒体查询样式分开到不同的文件中并不总是有很大的实际优势(除了个人偏好和/或代码的分隔)。

毕竟,使用单独的文件会增加呈现页面所需的 HTTP 请求的数量,这反过来可能会使页面在某些其他情况下变慢。在网络上没有什么是容易的!因此,这实际上是一个问题,评估您的网站的整体性能,并在不同设备上测试每种情况。

我对此的默认立场是,除非项目有足够的时间进行性能优化,否则这是我寻求性能提升的最后一个地方。只有当我确定:

  • 所有图像都已经压缩

  • 所有脚本都被连接并进行了最小化

  • 所有资产都以 gzip 方式提供

  • 所有静态内容都通过 CDN 进行缓存

  • 所有多余的 CSS 规则已被删除

也许那时我会开始考虑将媒体查询拆分成单独的文件以获得性能提升。

提示

gzip 是一种压缩和解压缩文件格式。任何好的服务器都应该允许对 CSS 等文件进行 gzip 压缩,这将大大减小文件在从服务器到设备的传输过程中的大小(在这一点上,它被解压缩为其原生格式)。您可以在维基百科上找到 gzip 的一个很好的摘要:en.wikipedia.org/wiki/Gzip

嵌套媒体查询“内联”

除了极端情况外,我建议在现有样式表中添加媒体查询,与“正常”的规则一起。

如果您愿意这样做,还有一个考虑因素:媒体查询应该在相关选择器下声明吗?还是分离出一个单独的代码块,包含所有相同的媒体查询?我很高兴你问了。

合并媒体查询还是根据需要编写媒体查询?

我喜欢在原始的“正常”定义下编写媒体查询。例如,假设我想要根据视口宽度在样式表的不同位置更改一些元素的宽度,我会这样做:

.thing {
    width: 50%;
}

@media screen and (min-width: 30rem) {
    .thing {
        width: 75%;
    }
}

/* A few more styles would go between them */

.thing2 {
    width: 65%;
}

@media screen and (min-width: 30rem) {
    .thing2 {
        width: 75%;
    }
}

这乍一看似乎是疯狂的。我们有两个媒体查询都与屏幕最小宽度为 30rem 有关。重复相同的@media声明肯定是冗长和浪费的吧?我应该主张将所有相同的媒体查询分组到一个单独的代码块中,就像这样:

.thing {
    width: 50%;
}

.thing2 {
    width: 65%;
}

@media screen and (min-width: 30rem) {
    .thing {
        width: 75%;
    }
    .thing2 {
        width: 75%;
    }
}

这当然是一种方法。然而,从维护的角度来看,我觉得这更加困难。没有“正确”的方法,但我更喜欢为单个选择器定义一条规则,并在其后立即定义该规则的任何变体(例如在媒体查询中的更改)。这样我就不必搜索单独的代码块,找到与特定选择器相关的声明。

注意

通过 CSS 预处理器和后处理器,这甚至可以更加方便,因为媒体查询的“变体”可以直接嵌套在规则集中。我的另一本书Sass and Compass for Designers中有一个完整的章节介绍这个。

从冗长的角度来看,对前一种技术提出异议似乎是公平的。单单文件大小就足以成为不以这种方式编写媒体查询的理由了吧?毕竟,没有人希望为用户提供一个臃肿的 CSS 文件。然而,简单的事实是,gzip 压缩(应该压缩服务器上的所有可能的资源)将这种差异减少到完全可以忽略的程度。我过去做过各种测试,所以如果您想了解更多信息,请访问:benfrain.com/inline-or-combined-media-queries-in-sass-fight/。最重要的是,如果您宁愿直接在标准样式之后编写媒体查询,我认为您不应该担心文件大小。

提示

如果您想直接在原始规则之后编写媒体查询,但希望所有相同的媒体查询定义合并为一个,那么有许多构建工具(在撰写本文时,Grunt 和 Gulp 都有相关插件)可以实现这一点。

viewport meta 标签

为了充分利用媒体查询,您希望较小的屏幕设备以其原生尺寸显示网页(而不是在 980px 窗口中渲染,然后您必须放大和缩小)。

2007 年苹果发布 iPhone 时,他们引入了一个名为 viewport meta的专有meta标签,Android 和越来越多的其他平台现在也支持这个标签。viewport meta标签的目的是为了让网页与移动浏览器通信,告诉它们希望如何渲染页面。

在可预见的未来,任何您希望响应式的网页,并在小屏设备上良好呈现的网页,都需要使用这个meta标签。

提示

在模拟器和仿真器上测试响应式设计

尽管在真实设备上测试开发工作是无法替代的,但 Android 有模拟器,iOS 有仿真器。

对于一丝不苟的人来说,模拟器只是模拟相关设备,而仿真器实际上试图解释原始设备代码。

Windows、Linux 和 Mac 的 Android 模拟器可通过下载和安装 Android软件开发工具包SDK)免费获取,网址为developer.android.com/sdk/

iOS 模拟器仅适用于 Mac OS X 用户,并作为 Xcode 软件包的一部分(可从 Mac App Store 免费获取)。

浏览器本身也在其开发工具中包含了不断改进的模拟移动设备的工具。Firefox 和 Chrome 目前都有特定的设置来模拟不同的移动设备/视口。

viewport <meta>标签添加在 HTML 的<head>标签中。它可以设置为特定宽度(例如,我们可以指定为像素)或作为比例,例如2.0(实际大小的两倍)。以下是 viewport meta标签的示例,设置为显示浏览器为实际大小的两倍(200%):

<meta name="viewport" content="initial-scale=2.0,width=device-width" />

让我们分解前面的<meta>标签,以便我们了解发生了什么。name="viewport"属性是显而易见的。然后,content="initial-scale=2.0部分表示“将内容缩放到原始大小的两倍”(其中 0.5 表示原始大小的一半,3.0 表示原始大小的三倍,依此类推),而width=device-width部分告诉浏览器页面的宽度应等于设备宽度。

<meta>标签还可以用于控制用户在页面上放大和缩小的程度。此示例允许用户放大到设备宽度的三倍,缩小到设备宽度的一半:

<meta name="viewport" content="width=device-width, maximum-scale=3, minimum-scale=0.5" />

您还可以完全禁用用户缩放,尽管缩放是一个重要的辅助工具,但在实践中很少会适用:

<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />

user-scalable=no 是相关部分。

好了,我们将缩放比例更改为1.0,这意味着移动浏览器将以其视口的 100%呈现页面。将其设置为设备的宽度意味着我们的页面应该在所有支持的移动浏览器的宽度的 100%呈现。对于大多数情况,这个<meta>标签是合适的:

<meta name="viewport" content="width=device-width,initial-scale=1.0" />

提示

注意到 viewport meta元素的使用越来越多,W3C 正在努力将相同的功能引入 CSS。前往dev.w3.org/csswg/css-device-adapt/,了解有关新的@viewport声明的所有信息。这个想法是,您可以在 CSS 中写@viewport { width: 320px; },而不是在标记的<head>部分中写<meta>标签。这将把浏览器宽度设置为 320 像素。然而,浏览器支持有限,尽管为了尽可能覆盖所有基础并尽可能具有未来的性能,您可以使用meta标签和@viewport声明的组合。

到目前为止,您应该已经对媒体查询及其工作原理有了扎实的掌握。然而,在我们完全转移到另一个话题之前,我认为考虑一下媒体查询的下一个版本可能会有什么可能性是很好的。让我们来偷偷看一眼!

媒体查询 4 级

在撰写本文时,虽然 CSS 媒体查询 4 级有一个草案规范(dev.w3.org/csswg/mediaqueries-4/),但草案中的功能并没有得到很多浏览器的实现。这意味着虽然我们将简要介绍此规范的亮点,但它非常不稳定。在使用这些功能之前,请确保检查浏览器支持并仔细检查语法更改。

目前,虽然 4 级规范中还有其他功能,但我们只关注脚本、指针和悬停以及亮度。

脚本媒体特性

在 HTML 标签上设置一个类来指示默认情况下没有 JavaScript,然后在 JavaScript 运行时用不同的类替换该类是一种常见做法。这提供了一个简单的能力来根据新的 HTML 类分叉代码(包括 CSS)。具体来说,使用这种做法,你可以编写特定于启用 JavaScript 的用户的规则。

这可能会让人困惑,所以让我们考虑一些示例代码。默认情况下,这将是在 HTML 中编写的标签:

<html class="no-js">

当 JavaScript 在页面上运行时,它的第一个任务之一将是替换no-js类:

<html class="js">

完成后,我们可以编写特定的 CSS 规则,这些规则只在 JavaScript 存在时才适用。例如,.js .header { display: block; }

然而,CSS Media Queries Level 4 的脚本媒体特性旨在提供一种更标准的方式直接在 CSS 中执行此操作:

@media (scripting: none) {
    /* styles for when JavaScript not working */
}

当 JavaScript 存在时:

@media (scripting: enabled) {
    /* styles for when JavaScript is working */
}

最后,它还旨在提供确定 JavaScript 是否存在但仅在最初时。W3C 规范中给出的一个例子是,可以最初布置打印页面,但之后没有 JavaScript 可用。在这种情况下,你应该能够这样做:

@media (scripting: initial-only) {
    /* styles for when JavaScript works initially */
}

这个功能的当前编辑草案可以在这里阅读:dev.w3.org/csswg/mediaqueries-4/#mf-scripting

交互媒体特性

以下是 W3C 对指针媒体特性的介绍:

“指针媒体特性用于查询指针设备(如鼠标)的存在和准确性。如果设备有多个输入机制,则指针媒体特性必须反映“主要”输入机制的特性,由用户代理确定。”

指针特性有三种可能的状态:nonecoarsefine

粗糙指针设备可能是触摸屏设备上的手指。然而,它也可以是游戏控制台上没有鼠标那样精细控制的光标。

@media (pointer: coarse) {
    /* styles for when coarse pointer is present */
}

fine指针设备可能是鼠标,但也可能是触控笔或任何未来的精细指针机制。

@media (pointer: fine) {
    /* styles for when fine pointer is present */
}

就我而言,浏览器越早实现这些指针特性越好。目前,要知道用户是否有鼠标、触摸输入或两者都有是非常困难的。以及他们在任何时候使用的是哪一个。

提示

最安全的做法总是假设用户使用基于触摸的输入,并相应地调整用户界面元素的大小。这样,即使他们使用鼠标,也不会难以轻松使用界面。然而,如果你假设鼠标输入,并且无法可靠地检测触摸以修改界面,可能会导致困难的体验。

对于同时开发触摸和指针的挑战的很好概述,我推荐 Patrick H. Lauke 的这组幻灯片Getting touchypatrickhlauke.github.io/getting-touchy-presentation/

在这里阅读这个功能的编辑草案:dev.w3.org/csswg/mediaqueries-4/#mf-interaction

悬停媒体特性

正如你所想象的,悬停媒体特性测试用户在屏幕上悬停元素的能力。如果用户有多个输入设备(例如触摸和鼠标),则使用主要输入的特性。以下是可能的值和示例代码:

对于没有悬停能力的用户,我们可以以none的值为他们定制样式。

@media (hover: none) {
    /* styles for when the user cannot hover */
}

对于可以悬停但必须执行重要操作来启动它的用户,可以使用on-demand

@media (hover: on-demand) {
    /* styles for when the user can hover but doing so requires significant effort */
}

对于可以悬停的用户,可以单独使用hover

@media (hover) {
    /* styles for when the user can hover */
}

请注意,还有any-pointerany-hover媒体特性。它们类似于前面的 hover 和 pointer,但测试任何可能的输入设备的功能。

环境媒体特性

如果我们能够根据环境特征(如环境光水平)来改变我们的设计,那不是挺好的吗?这样,如果用户在较暗的房间里,我们可以降低所使用颜色的亮度。或者相反,在更明亮的阳光下增加对比度。环境媒体特性旨在解决这些问题。请考虑以下示例:

@media (light-level: normal) {
    /* styles for standard light conditions */
}
@media (light-level: dim) {
    /* styles for dim light conditions */
}
@media (light-level: washed) {
    /* styles for bright light conditions */
}

请记住,目前很少有这些 Level 4 媒体查询的实现。在我们能够安全使用它们之前,规范很可能会发生变化。然而,了解未来几年我们将拥有哪些新功能是有用的。

阅读此功能的编辑草案:dev.w3.org/csswg/mediaqueries-4/#mf-environment

摘要

在本章中,我们学习了什么是 CSS3 媒体查询,如何在 CSS 文件中包含它们,以及它们如何帮助我们创建响应式网页设计。我们还学习了如何使用meta标签使现代移动浏览器呈现页面,就像我们想要的那样。

然而,我们也了解到,单独使用媒体查询只能提供一个适应性的网页设计,从一个布局切换到另一个布局。而不能实现一个真正响应式的设计,能够平稳地从一个布局过渡到另一个布局。为了实现我们的最终目标,我们还需要利用流动布局。它们将允许我们的设计在媒体查询处理的断点之间灵活变化。在下一章中,我们将介绍如何创建流动布局,以平滑过渡我们的媒体查询断点之间的变化。

第三章:流式布局和响应式图片

亿万年前,在时间的迷雾中(嗯,是在 20 世纪 90 年代晚期),网站通常以百分比定义宽度。这些基于百分比的宽度可以流畅地调整到屏幕上,并被称为流式布局。

在之后的几年里,即在 2000 年代中期到晚期,人们对固定宽度设计产生了干扰(我责怪那些固执的印刷设计师和他们对像素完美精度的痴迷)。如今,当我们构建响应式网页设计时,我们需要回顾流式布局,并记住它们提供的所有好处。

在第二章, 媒体查询-支持不同的视口中,我们最终承认,虽然媒体查询允许我们的设计适应不同的视口大小,但通过从一组样式切换到另一组样式,我们需要一些能力在媒体查询提供的“断点”之间灵活调整我们的设计。通过编写“流式”布局,我们可以完美地满足这种需求;它将轻松地拉伸以填补媒体查询断点之间的空白。

2015 年,我们有比以往任何时候都更好的方法来构建响应式网站。现在有一个名为Flexible Box(或者更常见的称为Flexbox)的新 CSS 布局模块,它现在有足够的浏览器支持,可以在日常使用中使用。

它不仅可以提供流式布局机制。想要轻松地居中内容,更改标记的源顺序,并以相关的轻松方式创建令人惊叹的布局?Flexbox 是适合您的布局机制。本章的大部分内容涉及 Flexbox,涵盖了它所提供的所有令人难以置信的功能。

现在,有了指定的方法和语法,可以向设备发送最相关版本的图像以适应其视口。我们将在本章的最后一节中了解响应式图片的工作原理以及如何使其为我们工作。

在本章中,我们将涵盖:

  • 如何将固定像素尺寸转换为比例尺寸

  • 考虑现有的 CSS 布局机制及其不足之处

  • 了解 Flexbox 布局模块及其提供的好处

  • 学习响应式图片的分辨率切换和艺术方向的正确语法

将固定像素设计转换为流式比例布局

在像 Photoshop、Illustrator、Fireworks(已故)或 Sketch 这样的程序中制作的图形合成都有固定的像素尺寸。在将设计重新创建为浏览器中的流式布局时,开发人员需要将设计转换为比例尺寸。

有一个非常简单的公式可以将固定尺寸布局转换为响应式/流式等价物,这是响应式网页设计之父 Ethan Marcotte 在他 2009 年的文章Fluid Gridsalistapart.com/article/FLUIDGRIDS)中提出的:

目标/上下文=结果

如果任何类似数学的东西让您感到不安,可以这样想:将您想要的东西的单位除以它所在的单位。理解这一点将使您能够将任何固定尺寸布局转换为响应式/流式等价物。

考虑一个专为桌面设计的非常基本的页面布局。在理想的情况下,我们总是会从较小的屏幕布局转移到桌面布局,但为了说明比例,我们将从后往前看这两种情况。

这是布局的图像:

将固定像素设计转换为流式比例布局

布局宽度为 960 像素。页眉和页脚都是布局的全宽。左侧区域宽度为 200 像素,右侧区域宽度为 100 像素。即使我的数学能力有限,我也可以告诉您中间部分将宽 660 像素。我们需要将中间和侧面区域转换为比例尺寸。

首先,左侧。它的宽度是 200 个单位(目标)。将该尺寸除以 960 个单位(上下文),我们得到一个结果:.208333333. 现在,每当我们用这个公式得到结果时,我们需要将小数点向右移动两位。这将给我们 20.8333333%。这是将 200px 描述为 960px 的百分比。

好了,中间部分呢?660(目标)除以 960(上下文)给我们.6875. 将小数点向右移动两位,我们得到 68.75%。最后,右侧部分。100(目标)除以 960(上下文)给我们.104166667. 移动小数点,我们得到 10.4166667%。就是这么困难。跟我说:目标,除以上下文,等于结果。

为了证明这一点,让我们在浏览器中快速构建基本布局块。您可以在example_03-01中查看布局。这是 HTML:

<div class="Wrap">
    <div class="Header"></div>
    <div class="WrapMiddle">
        <div class="Left"></div>
        <div class="Middle"></div>
        <div class="Right"></div>
    </div>
    <div class="Footer"></div>
</div>

这是 CSS:

html,
body {
    margin: 0;
    padding: 0;
}

.Wrap {
    max-width: 1400px;
    margin: 0 auto;
}

.Header {
    width: 100%;
    height: 130px;
    background-color: #038C5A;
}

.WrapMiddle {
    width: 100%;
    font-size: 0;
}

.Left {
    height: 625px;
    width: 20.8333333%;
    background-color: #03A66A;
    display: inline-block;
}

.Middle {
    height: 625px;
    width: 68.75%;
    background-color: #bbbf90;
    display: inline-block;
}

.Right {
    height: 625px;
    width: 10.4166667%;
    background-color: #03A66A;
    display: inline-block;
}

.Footer {
    height: 200px;
    width: 100%;
    background-color: #025059;
}

如果您在浏览器中打开示例代码并调整页面大小,您会发现中间部分的尺寸保持相互成比例。您还可以通过调整.Wrap值的最大宽度来使布局的边界尺寸变大或变小(在示例中设置为1400px)。

提示

如果您查看标记并想知道为什么我没有使用headerfooteraside等语义元素,那就不用担心。第四章,响应式 Web 设计的 HTML5,详细介绍了这些语义 HTML5 元素。

现在,让我们考虑一下如何在较小的屏幕上具有相同的内容,然后转换为我们已经看到的布局。您可以在example_03-02中查看此布局的最终代码。

想法是,对于较小的屏幕,我们将有一个单独的内容'管道'。左侧区域将只能作为'离屏'区域查看;通常是菜单区域或类似的区域,位于可视屏幕区域之外,当按下菜单按钮时滑入。主要内容位于页眉下方,然后右侧部分位于其下方,最后是页脚区域。在我们的示例中,我们可以通过单击页眉的任何位置来显示左侧菜单区域。通常,在真正制作这种设计模式时,会使用菜单按钮来激活侧边菜单。

提示

为了在文档的 body 上切换类,我使用了一点 JavaScript。不过这并不是'production ready',因为我们在 JavaScript 中使用了'click'作为事件处理程序,理想情况下,我们应该有一些触摸的准备(以消除 iOS 设备上仍然存在的 300 毫秒延迟)。

正如您所期望的那样,当将这与我们新掌握的媒体查询技能相结合时,我们可以调整视口和设计,布局就会自动地从一个布局移动到另一个布局,并在两者之间拉伸。

我不打算在这里列出所有的 CSS,它都在example_03-02中。不过,这里有一个例子——左侧部分:

.Left {
    height: 625px;
    background-color: #03A66A;
    display: inline-block;
    position: absolute;
    left: -200px;
    width: 200px;
    font-size: .9rem;
    transition: transform .3s;
}

@media (min-width: 40rem) {
    .Left {
        width: 20.8333333%;
        left: 0;
        position: relative;
    }
}

您可以看到,首先是在没有媒体查询的情况下,小屏幕布局。然后,在较大的屏幕尺寸上,宽度变得成比例,定位相对,左值设置为零。我们不需要重新编写诸如heightdisplaybackground-color之类的属性,因为我们不会改变它们。

这是进步。我们已经结合了我们所学的两种核心响应式 Web 设计技术;将固定尺寸转换为比例,并使用媒体查询来针对视口大小调整 CSS 规则。

提示

在我们之前的例子中有两件重要的事情需要注意。首先,您可能想知道是否严格需要包括小数点后的所有数字。虽然这些宽度最终将被浏览器转换为像素,但它们的值将被保留用于未来的计算(例如,更准确地计算嵌套元素的宽度)。因此,我总是建议保留小数点后的数字。

其次,在一个真实的项目中,如果 JavaScript 不可用并且我们需要查看菜单的内容,我们应该做一些准备。我们在第八章中详细处理这种情况,过渡,变换和动画

我们为什么需要 Flexbox?

我们现在将详细介绍使用 CSS 弹性盒布局,或者更常见的 Flexbox。

然而,在我们这样做之前,我认为首先考虑现有布局技术的不足是明智的,比如内联块,浮动和表格。

内联块和空格

使用内联块作为布局机制的最大问题是它在 HTML 元素之间渲染空格。这不是一个错误(尽管大多数开发人员都希望有一种理智的方法来删除空格),但这意味着一些方法来删除不需要的空格,对我来说,这大约是 95%的时间。有很多方法可以做到这一点,在前面的例子中,我们使用了“字体大小为零”的方法;这种方法并非没有问题和局限性。但是,与其列出使用内联块时去除空格的每种可能的解决方法,不如查看这篇由无法抑制的 Chris Coyier 撰写的文章:css-tricks.com/fighting-the-space-between-inline-block-elements/

还值得指出的是,没有简单的方法在内联块内垂直居中内容。使用内联块,也没有办法让两个兄弟元素中一个具有固定宽度,另一个自动填充剩余空间。

浮动

我讨厌浮动。我说了。它们的好处是它们在各处的工作相当一致。然而,有两个主要的烦恼。

首先,当以百分比指定浮动元素的宽度时,它们的计算宽度在各个浏览器中并不一致(有些浏览器向上舍入,有些向下舍入)。这意味着有时部分内容会意外地下降到其他部分下面,而其他时候它们可能会在一侧留下令人恼火的间隙。

其次,通常需要“清除”浮动,以防止父框/元素坍塌。这很容易做到,但它不断提醒我们,浮动从来不是用作强大的布局机制。

表格和表格单元格

不要混淆display: tabledisplay: table-cell与等效的 HTML 元素。这些 CSS 属性仅模仿其基于 HTML 的兄弟的布局。它们绝对不会影响 HTML 的结构。

我发现使用 CSS 表格布局非常有用。首先,它们可以在元素之间实现一致和强大的垂直居中。此外,设置为display: table的元素内部设置为display: table-cell的元素可以完美地空出空间;它们不像浮动元素那样遇到四舍五入的问题。您还可以获得对 Internet Explorer 7 的全面支持!

然而,也有局限性。通常需要在项目周围包装额外的元素(为了获得完美的垂直居中的乐趣,表格单元格必须存在于设置为表格的元素内)。还不可能将设置为display: table-cell的项目包装到多行中。

总之,所有现有的布局方法都有严重的局限性。幸运的是,有一种新的 CSS 布局方法可以解决这些问题,还有更多。吹号声响起,铺开红地毯。Flexbox 来了。

介绍 Flexbox

Flexbox 解决了上述每种显示机制的不足。以下是它的超级功能的简要概述:

  • 它可以轻松地垂直居中内容

  • 它可以改变元素的视觉顺序

  • 它可以自动在框内对齐和排列元素,自动分配它们之间的可用空间。

  • 它可以让你看起来年轻 10 岁(可能不是,但在少量经验测试中(我)已经证明可以减轻压力)

通往 Flexbox 的崎岖之路

在达到我们今天拥有的相对稳定版本之前,Flexbox 经历了几次重大迭代。例如,考虑从 2009 年版本(www.w3.org/TR/2009/WD-css3-flexbox-20090723/)到 2011 年版本(www.w3.org/TR/2011/WD-css3-flexbox-20111129/),再到我们基于的 2014 年版本(www.w3.org/TR/css-flexbox-1/)。语法差异很大。

这些不同的规范意味着有三个主要的实现版本。您需要关注多少取决于您需要的浏览器支持级别。

Flexbox 的浏览器支持

让我们先说清楚:Internet Explorer 9、8 或更低版本都不支持 Flexbox。

对于您可能想要支持的其他所有内容(几乎所有移动浏览器),都有一种方法可以享受 Flexbox 的大多数(如果不是全部)功能。您可以在caniuse.com/上查看支持信息。

在我们深入 Flexbox 之前,我们需要进行一个简短但必要的偏离。

把前缀留给别人

我希望一旦您看到了 Flexbox 的一些示例,您会欣赏到它的实用性,并感到有能力使用它。然而,手动编写支持每个不同 Flexbox 规范所需的所有必要代码是一项艰巨的任务。这里有一个例子。我将设置三个与 Flexbox 相关的属性和值。考虑一下:

.flex {
    display: flex;
    flex: 1;
    justify-content: space-between;
}

这就是最新语法中属性和值的样子。然而,如果我们想要支持 Android 浏览器(v4 及以下)和 IE 10,实际上需要的是:

.flex {
    display: -webkit-box;
    display: -webkit-flex;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-flex: 1;
    -webkit-flex: 1;
        -ms-flex: 1;
            flex: 1;
    -webkit-box-pack: justify;
    -webkit-justify-content: space-between;
        -ms-flex-pack: justify;
            justify-content: space-between;
}

有必要写出所有这些,因为在过去几年里,随着浏览器发布了新功能的实验版本,它们都带有“供应商前缀”。每个供应商都有自己的前缀。例如,微软的是-ms-,WebKit 的是-webkit-,Mozilla 的是-moz-,等等。对于每个新功能,这意味着需要编写同一属性的多个版本;首先是供应商前缀版本,然后是官方的 W3C 版本。

这个咒语在 Web 历史上的结果是 CSS 看起来像前面的例子。这是在尽可能多的设备上使功能正常工作的唯一方法。如今,供应商很少添加前缀,但在可预见的未来,我们必须接受许多现有浏览器仍然需要前缀来启用某些功能的现实。这让我们回到了 Flexbox,这是供应商前缀的一个极端例子,不仅有多个供应商版本,还有不同的功能规范。记住并理解您需要以当前格式和每个以前的格式编写的所有内容并不是一件有趣的事情。

我不知道你怎么想,但我宁愿把时间花在做一些更有意义的事情上,而不是每次都写出那么多东西!简而言之,如果您打算愤怒地使用 Flexbox,请花时间设置自动前缀解决方案。

选择您的自动前缀解决方案

为了保持理智,准确且轻松地向 CSS 添加供应商前缀,使用某种形式的自动前缀解决方案。目前,我更喜欢 Autoprefixer(github.com/postcss/autoprefixer)。它快速、易于设置且非常准确。

大多数设置都有 Autoprefixer 的版本;您不一定需要基于命令行的构建工具(例如 Gulp 或 Grunt)。例如,如果您使用 Sublime Text,有一个版本可以直接从命令面板中使用:github.com/sindresorhus/sublime-autoprefixer。Atom、Brackets 和 Visual Studio 也有 Autoprefixer 的版本。

从这一点开始,除非必须说明一个观点,否则在代码示例中将不再有供应商前缀。

灵活起来

Flexbox 有四个关键特性:方向对齐排序灵活性。我们将通过一些示例来介绍所有这些特性以及它们之间的关系。

这些示例故意简单化;只是移动一些框和它们的内容,以便我们可以理解 Flexbox 的工作原理。

完美垂直居中的文本

请注意,这个第一个 Flexbox 示例是example_03-03

完美垂直居中的文本

这是标记:

<div class="CenterMe">
    Hello, I'm centered with Flexbox!
</div>

这是样式整个 CSS 规则:

.CenterMe {
    background-color: indigo;
    color: #ebebeb;
    font-family: 'Oswald', sans-serif;
    font-size: 2rem;
    text-transform: uppercase;
    height: 200px;
    display: flex;
    align-items: center;
    justify-content: center;
}

该规则中的大多数属性/值对仅仅是设置颜色和字体大小。我们感兴趣的三个属性是:

.CenterMe {    
    /* other properties */
    display: flex;
    align-items: center;
    justify-content: center;
}

如果您没有使用 Flexbox 或相关的 Box Alignment 规范中的任何属性(www.w3.org/TR/css3-align/),这些属性可能看起来有点陌生。让我们考虑每个属性的作用:

  • display: flex:这是 Flexbox 的基础。这仅仅是将项目设置为 Flexbox(而不是块、内联块等)。

  • align-items:这在 Flexbox 中沿交叉轴对齐项目(在我们的示例中垂直居中文本)。

  • justify-content:这设置了内容的主轴居中。对于 Flexbox 行,您可以将其视为文字处理器中设置文本左对齐、右对齐或居中的按钮(尽管我们很快将看到更多justify-content的值)。

好的,在我们深入了解 Flexbox 的属性之前,我们将考虑一些更多的示例。

提示

在其中一些示例中,我使用了谷歌托管的字体'Oswald'(并回退到无衬线字体)。在第五章中,CSS3 – 选择器、排版、颜色模式和新功能,我们将看看如何使用@font-face规则链接到自定义字体文件。

偏移项目

想要一个简单的导航项目列表,但其中一个偏移了一边?

这是它的样子:

偏移项目

这是标记:

<div class="MenuWrap">
    <a href="#" class="ListItem">Home</a>
    <a href="#" class="ListItem">About Us</a>
    <a href="#" class="ListItem">Products</a>
    <a href="#" class="ListItem">Policy</a>
    <a href="#" class="LastItem">Contact Us</a>
</div>

这是 CSS:

.MenuWrap {
    background-color: indigo;
    font-family: 'Oswald', sans-serif;
    font-size: 1rem;
    min-height: 2.75rem;
    display: flex;
    align-items: center;
    padding: 0 1rem;
}

.ListItem,
.LastItem {
    color: #ebebeb;
    text-decoration: none;
}

.ListItem {
    margin-right: 1rem;
}

.LastItem {
    margin-left: auto;
}

怎么样,没有一个浮动、内联块或表格单元格!当您在包裹元素上设置display: flex;时,该元素的子元素就成为了 flex 项目,然后使用 flex 布局模型进行布局。这里的神奇属性是margin-left: auto,它使该项目在该侧使用所有可用的边距。

颠倒项目的顺序

想要颠倒项目的顺序吗?

颠倒项目的顺序

只需在包裹元素上添加flex-direction: row-reverse;并将偏移项上的margin-left: auto更改为margin-right: auto

.MenuWrap {
    background-color: indigo;
    font-family: 'Oswald', sans-serif;
    font-size: 1rem;
    min-height: 2.75rem;
    display: flex;
    flex-direction: row-reverse;
    align-items: center;
    padding: 0 1rem;
}

.ListItem,
.LastItem {
    color: #ebebeb;
    text-decoration: none;
}

.ListItem {
    margin-right: 1rem;
}

.LastItem {
    margin-right: auto;
}

如果我们想要它们垂直布局呢?

简单。在包裹元素上更改为flex-direction: column;并删除自动边距:

.MenuWrap {
    background-color: indigo;
    font-family: 'Oswald', sans-serif;
    font-size: 1rem;
    min-height: 2.75rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 0 1rem;
}

.ListItem,
.LastItem {
    color: #ebebeb;
    text-decoration: none;
}

列反转

想要它们以相反的方向堆叠吗?只需更改为flex-direction: column-reverse;就可以了。

注意

您应该知道有一个flex-flow属性,它是设置flex-directionflex-wrap的快捷方式。例如,flex-flow: row wrap;会将方向设置为行,并设置换行。然而,至少在最初,我发现更容易分别指定这两个设置。flex-wrap属性在最旧的 Flexbox 实现中也不存在,因此可能会在某些浏览器中使整个声明无效。

不同的媒体查询内的不同 Flexbox 布局

正如其名称所示,Flexbox 本质上是灵活的,所以在较小的视口上,我们选择列出项目,并在空间允许时选择行样式布局。这对 Flexbox 来说非常简单:

.MenuWrap {
    background-color: indigo;
    font-family: 'Oswald', sans-serif;
    font-size: 1rem;
    min-height: 2.75rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 0 1rem;
}

@media (min-width: 31.25em) {
    .MenuWrap {
        flex-direction: row;
    }    
}

.ListItem,
.LastItem {
    color: #ebebeb;
    text-decoration: none;
}

@media (min-width: 31.25em) {
    .ListItem {
        margin-right: 1rem;
    }
    .LastItem {
        margin-left: auto;
    }
}

您可以将其视为example_03-05。确保调整浏览器窗口大小以查看不同的布局。

内联弹性

Flexbox 有一个内联变体,以补充内联块和内联表格。你可能已经猜到了,它是display: inline-flex;。由于它美丽的居中能力,你可以用很少的努力做一些古怪的事情。

内联弹性

这是标记:

<p>Here is a sentence with a <a href="http://www.w3.org/TR/css-flexbox-1/#flex-containers" class="InlineFlex">inline-flex link</a>.</p>

这是那个的 CSS:

.InlineFlex {
    display: inline-flex;
    align-items: center;    
    height: 120px;
    padding: 0 4px;
    background-color: indigo;
    text-decoration: none;
    border-radius: 3px;
    color: #ddd;
}

当项目被匿名设置为inline-flex(例如,它们的父元素没有设置为display: flex;)时,它们保留元素之间的空白,就像 inline-block 或 inline-table 一样。然而,如果它们在一个 flex 容器中,那么空白将被移除,就像在表格中的 table-cell 项目一样。

当然,你并不总是需要在 Flexbox 中居中项目。有许多不同的选项。让我们现在来看看这些。

Flexbox 对齐属性

如果你想玩玩这个例子,你可以在example_03-07找到它。记住你下载的例子代码将会在我们完成这一部分时的位置,所以如果你想“跟着做”,你可能更喜欢删除示例文件中的 CSS,然后重新开始。

理解 Flexbox 对齐的重要事情是轴的概念。有两个轴要考虑,'主轴'和'交叉轴'。每个代表什么取决于 Flexbox 的方向。例如,如果你的 Flexbox 的方向设置为row,主轴将是水平轴,交叉轴将是垂直轴。

相反,如果你的 Flexbox 方向设置为column,主轴将是垂直轴,交叉轴将是水平轴。

规范(www.w3.org/TR/css-flexbox-1/#justify-content-property)提供了以下插图来帮助作者:

Flexbox 对齐属性

这是我们示例的基本标记:

<div class="FlexWrapper">
    <div class="FlexInner">I am content in the inner Flexbox.</div>
</div>

让我们设置基本的 Flexbox 相关样式:

.FlexWrapper {
    background-color: indigo;
    display: flex;
    height: 200px;
    width: 400px;
}

.FlexInner {
    background-color: #34005B;
    display: flex;
    height: 100px;
    width: 200px;
}

在浏览器中,这产生了这个效果:

Flexbox 对齐属性

好了,让我们来测试一下这些属性的效果。

align-items 属性

align-items属性将项目在交叉轴上定位。如果我们将这个属性应用到我们的包裹元素上,就像这样:

.FlexWrapper {
    background-color: indigo;
    display: flex;
    height: 200px;
    width: 400px;
    align-items: center;
}

正如你所想象的,盒子中的项目垂直居中:

align-items 属性

相同的效果将应用于任何数量的子元素。

align-self 属性

有时,你可能只想将一个项目拉到不同的对齐方式。单独的 flex 项目可以使用align-self属性来对齐自己。在这一点上,我将删除之前的对齐属性,将另外两个项目添加到标记中(它们已经被赋予了.FlexInner HTML 类),并在中间的项目上添加另一个 HTML 类(.AlignSelf),并使用它来添加align-self属性。此时查看 CSS 可能更具说明性:

.FlexWrapper {
    background-color: indigo;
    display: flex;
    height: 200px;
    width: 400px;
}
.FlexInner {
    background-color: #34005B;
    display: flex;
    height: 100px;
    width: 200px;
}

.AlignSelf {
    align-self: flex-end;
}

这是在浏览器中的效果:

align-self 属性

哇!Flexbox 真的让这些变化变得微不足道。在这个例子中,align-self的值被设置为flex-end。在看主轴上的对齐之前,让我们考虑一下我们可以在交叉轴上使用的可能值。

可能的对齐值

对于交叉轴对齐,Flexbox 有以下可能的值:

  • flex-start:将元素设置为flex-start会使其从其 flex 容器的“起始”边开始

  • flex-end:设置为flex-end会将元素对齐到 flex 容器的末尾

  • center:将其放在 flex 容器的中间

  • baseline:设置容器中所有 flex 项目,使它们的基线对齐

  • stretch:使项目拉伸到其 flex 容器的大小(在交叉轴上)

注意

使用这些属性有一些特殊之处,所以如果有什么不顺利的地方,总是参考规范中的任何边缘情况场景:www.w3.org/TR/css-flexbox-1/

justify-content 属性

主轴上的对齐由justify-content控制(对于非 Flexbox/block-level 项目,还提出了justify-self属性(www.w3.org/TR/css3-align/)。justify-content的可能值包括:

  • flex-start

  • flex-end

  • center

  • space-between

  • space-around

前三个正是你现在所期望的。然而,让我们看看space-betweenspace-around的作用。考虑这个标记:

<div class="FlexWrapper">
    <div class="FlexInner">I am content in the inner Flexbox 1.</div>
    <div class="FlexInner">I am content in the inner Flexbox 2.</div>
    <div class="FlexInner">I am content in the inner Flexbox 3.</div>
</div>

然后考虑这个 CSS。我们将三个 flex 项(FlexInner)的宽度分别设置为 25%,并由一个设置为 100%宽度的 flex 容器(FlexWrapper)包裹。

.FlexWrapper {
    background-color: indigo;
    display: flex;
    justify-content: space-between;
    height: 200px;
    width: 100%;
}
.FlexItems {
    background-color: #34005B;
    display: flex;
    height: 100px;
    width: 25%;
}

由于这三个项目只占用了可用空间的 75%,justify-content解释了我们希望浏览器如何处理剩余空间。space-between的值在项目之间放置相等的空间,而space-around则将其放置在周围。也许这里的屏幕截图会有所帮助:这是space-between

justify-content 属性

如果我们切换到space-around,会发生什么呢?

justify-content 属性

我认为这两个值非常方便。

提示

Flexbox 的各种对齐属性目前正在被规范为 CSS Box Alignment Module Level 3。这应该为其他显示属性(如display: block;display: table;)提供相同的基本对齐功能。规范仍在进行中,因此请查看www.w3.org/TR/css3-align/的状态。

flex 属性

我们已经在这些 flex 项上使用了width属性,但也可以使用flex属性定义宽度或'灵活性'。为了说明这一点,考虑另一个例子;相同的标记,但是为项目修改了 CSS:

.FlexItems {
    border: 1px solid #ebebeb;
    background-color: #34005B;
    display: flex;
    height: 100px;
    flex: 1;
}

flex属性实际上是指定三个单独属性的一种简写方式:flex-growflex-shrinkflex-basis。规范在www.w3.org/TR/css-flexbox-1/中更详细地涵盖了这些单独的属性。然而,规范建议作者使用flex简写属性,这就是我们在这里使用的,明白了吗?

flex 属性

对于 flex 项,如果存在flex属性(并且浏览器支持),则使用该属性来调整项目的大小,而不是宽度或高度值(如果存在)。即使在flex属性之后指定了宽度或高度值,它仍然没有效果。让我们看看每个值的作用。

  • flex-grow(传递给 flex 的第一个值)是与其他 flex 项相关的,当有空闲空间时,flex 项可以增长的量

  • flex-shrink是与其他 flex 项相关的,当没有足够的空间时,flex 项可以缩小的量

  • flex-basis(传递给 Flex 的最后一个值)是 flex 项的基础大小

虽然可能只写flex: 1,但我建议将所有值写入flex属性。我认为这样更清楚你的意图。例如:flex: 1 1 auto表示项目将占用可用空间的 1 部分,当空间不足时它也会缩小 1 部分,而弹性的基础大小是内容的固有宽度(如果没有弹性,内容的大小将是多少)。

让我们再试一下:flex: 0 0 50px表示此项目既不会增长也不会缩小,其基础大小为 50px(因此无论有多少空闲空间,它都将是 50px)。flex: 2 0 50%呢?这将占用两个'部分'的可用空间,它不会缩小,其基础大小为 50%。希望这些简短的例子能让 flex 属性变得更加清晰。

提示

如果将flex-shrink值设置为零,则 flex 基础实际上就像最小宽度一样。

您可以将flex属性视为设置比例的一种方式。每个 flex 项设置为 1,它们各自占据相等的空间:

flex 属性

好了,为了测试这个理论,让我们修改标记中的 HTML 类:

<div class="FlexWrapper">
    <div class="FlexItems FlexOne">I am content in the inner Flexbox 1.</div>
    <div class="FlexItems FlexTwo">I am content in the inner Flexbox 2.</div>
    <div class="FlexItems FlexThree">I am content in the inner Flexbox 3.</div>
</div>

然后这是修改后的 CSS:

.FlexItems {
    border: 1px solid #ebebeb;
    background-color: #34005B;
    display: flex;
    height: 100px;
}

.FlexOne {
    flex: 1.5 0 auto;
}

.FlexTwo,
.FlexThree {
    flex: 1 0 auto;
}

在这种情况下,FlexOne占据了FlexTwoFlexThree占据的 1.5 倍空间。

这种简写语法确实非常有用,可以快速建立项目之间的关系。例如,如果有请求说,“这需要比其他项目宽 1.8 倍”,您可以很容易地使用 flex 属性满足该请求。

希望非常强大的 flex 属性现在开始有点意义了?

我可以写上关于 Flexbox 的章节!我们可以看很多例子。然而,在我们继续本章的另一个主题(响应式图片)之前,我还有两件事想与您分享。

简单的粘性页脚

假设您希望在内容不足以将其推到底部时,页脚位于视口底部。以前实现这一点总是很麻烦,但使用 Flexbox 很简单。考虑以下标记(可以在example_03-08中查看):

<body>
    <div class="MainContent">
        Here is a bunch of text up at the top. But there isn't enough content to push the footer to the bottom of the page.
    </div>
    <div class="Footer">
        However, thanks to flexbox, I've been put in my place.
    </div>
</body>

这是 CSS:

html,
body {
    margin: 0;
    padding: 0;
}

html {
    height: 100%;
}

body {
    font-family: 'Oswald', sans-serif;
    color: #ebebeb;
    display: flex;
    flex-direction: column;
    min-height: 100%;
}

.MainContent {
    flex: 1;
    color: #333;
    padding: .5rem;
}

.Footer {
    background-color: violet;
    padding: .5rem;
}

在浏览器中查看并测试向.MainContentdiv添加更多内容。您会发现当内容不足时,页脚会固定在视口底部。当有足够内容时,它会显示在内容下方。

这是因为我们的flex属性设置为在有空间时增长。由于我们的 body 是一个 100%最小高度的 flex 容器,主内容可以扩展到所有可用空间。很美。

更改源顺序

自 CSS 诞生以来,在网页中切换 HTML 元素的视觉顺序只有一种方法。通过将元素包装在设置为display: table的东西中,然后在元素之间切换display属性,即在display: table-caption(将其放在顶部),display: table-footer-group(将其发送到底部)和display: table-header-group(将其发送到display: table-caption下方的项目)。然而,尽管这种技术很强大,但这是一个幸运的意外,而不是这些设置的真正意图。

然而,Flexbox 内置了视觉源重新排序。让我们看看它是如何工作的。

考虑这个标记:

<div class="FlexWrapper">
    <div class="FlexItems FlexHeader">I am content in the Header.</div>
    <div class="FlexItems FlexSideOne">I am content in the SideOne.</div>
    <div class="FlexItems FlexContent">I am content in the Content.</div>
    <div class="FlexItems FlexSideTwo">I am content in the SideTwo.</div>
    <div class="FlexItems FlexFooter">I am content in the Footer.</div>
</div>

您可以在这里看到包装器中的第三个项目具有FlexContent的 HTML 类-想象一下这个div将保存页面的主要内容。

好的,让我们保持简单。我们将为更容易区分各个部分添加一些简单的颜色,并使这些项目按照它们在标记中出现的顺序一个接一个地排列。

.FlexWrapper {
    background-color: indigo;
    display: flex;
    flex-direction: column;
}

.FlexItems {
    display: flex;
    align-items: center;
    min-height: 6.25rem;
    padding: 1rem;
}

.FlexHeader {
    background-color: #105B63;    
}

.FlexContent {
    background-color: #FFFAD5;
}

.FlexSideOne {
    background-color: #FFD34E;
}

.FlexSideTwo {
    background-color: #DB9E36;
}

.FlexFooter {
    background-color: #BD4932;
}

在浏览器中呈现如下:

更改源顺序

现在,假设我们想要交换.FlexContent的顺序成为第一项,而不改变标记。使用 Flexbox 很简单,只需添加一个属性/值对:

.FlexContent {
    background-color: #FFFAD5;
    order: -1;
}

order属性让我们简单而明智地修改 Flexbox 中项目的顺序。在这个例子中,值为-1表示我们希望它在所有其他项目之前。

提示

如果您想频繁切换项目的顺序,我建议更加声明性地为每个添加一个顺序号。当您将它们与媒体查询结合使用时,这样做会使事情变得更容易理解。

让我们将我们的新源顺序更改功能与一些媒体查询结合起来,以在不同尺寸下产生不同的布局和不同的顺序。

注意

注意:您可以在example_03-09中查看此完成的示例。

由于通常认为将主要内容放在文档开头是明智的,让我们将标记修改为这样:

<div class="FlexWrapper">
    <div class="FlexItems FlexContent">I am content in the Content.</div>
    <div class="FlexItems FlexSideOne">I am content in the SideOne.</div>
    <div class="FlexItems FlexSideTwo">I am content in the SideTwo.</div>
    <div class="FlexItems FlexHeader">I am content in the Header.</div>
    <div class="FlexItems FlexFooter">I am content in the Footer.</div>
</div>

首先是页面内容,然后是我们的两个侧边栏区域,然后是页眉,最后是页脚。由于我将使用 Flexbox,我们可以按照对文档有意义的顺序来构造 HTML,而不管需要如何在视觉上布局。

对于最小的屏幕(在任何媒体查询之外),我将按照这个顺序进行:

.FlexHeader {
    background-color: #105B63;
    order: 1;
}

.FlexContent {
    background-color: #FFFAD5;
    order: 2;
}

.FlexSideOne {
    background-color: #FFD34E;
    order: 3;
}

.FlexSideTwo {
    background-color: #DB9E36;
    order: 4;
}

.FlexFooter {
    background-color: #BD4932;
    order: 5;
}

这在浏览器中给我们的是这样的:

更改源顺序

然后,在断点处,我切换到这个:

@media (min-width: 30rem) {
    .FlexWrapper {
        flex-flow: row wrap;
    }
    .FlexHeader {
        width: 100%;
    }
    .FlexContent {
        flex: 1;
        order: 3;
    }
    .FlexSideOne {
        width: 150px;
        order: 2;
    }
    .FlexSideTwo {
        width: 150px;
        order: 4;
    }
    .FlexFooter {
        width: 100%;
    }
}

这在浏览器中给我们的是这样的:

更改源顺序

注意

在这个例子中,使用了快捷方式flex-flow: row wrap。这允许 flex 项目换行到多行。这是支持较差的属性之一,因此,取决于需要多远的支持,可能需要将内容和两个侧边栏包装在另一个元素中。

总结 Flexbox

使用 Flexbox 布局系统时有无限的可能性,由于其固有的“灵活性”,它非常适合响应式设计。如果你以前从未使用过 Flexbox 构建任何东西,所有新的属性和值可能看起来有点奇怪,有时很容易实现以前需要更多工作的布局。要根据最新版本的规范检查实施细节,请确保查看www.w3.org/TR/css-flexbox-1/

我认为你会喜欢使用 Flexbox 构建东西。

注意

紧随灵活盒子布局模块之后的是网格布局模块 1 级:www.w3.org/TR/css3-grid-layout/

与 Flexbox 相比,它相对不成熟(就像 Flexbox 的早期历史一样,网格布局已经经历了一些重大变化),因此我们在这里不会详细讨论它。然而,这绝对是一个值得关注的属性,因为它向我们承诺了更多的布局能力。

响应式图片

根据用户设备和环境的特点为用户提供适当的图像一直是一个棘手的问题。这个问题在响应式网页设计的出现时就被突显出来,其本质是为每个设备提供单一的代码库。

响应式图片的固有问题

作为作者,你无法知道或计划每个可能访问你网站的设备。只有浏览器知道在它提供和渲染内容时使用它的设备的特点(例如屏幕尺寸和设备功能)。

相反,只有作者(你和我)知道我们拥有哪些图像的版本。例如,我们可能有同一图像的三个版本。小,中,大:每个版本都有不同的尺寸,以涵盖各种屏幕尺寸和密度的情况。浏览器不知道这一点。我们必须告诉它。

总结这个难题,我们知道我们拥有的图像是一半的解决方案,浏览器知道访问网站的设备的特点以及最合适的图像尺寸和分辨率是另一半的解决方案。

我们如何告诉浏览器我们拥有哪些图像,以便它可以为用户选择最合适的图像?

在响应式网页设计的最初几年,没有指定的方法。幸运的是,现在我们有了嵌入内容规范:html.spec.whatwg.org/multipage/embedded-content.html

嵌入内容规范描述了处理图像的简单分辨率切换(以便在更高分辨率屏幕上接收图像的更高分辨率版本)和“艺术方向”情况的方法,即作者希望用户根据一些设备特性(比如媒体查询)看到完全不同的图像。

演示响应式图像示例是棘手的。在单个屏幕上无法欣赏到特定语法或技术加载的不同图像。因此,接下来的示例将主要是代码,你只能相信我,它将在支持的浏览器中产生你需要的结果。

让我们看看你可能需要响应式图片的两种最常见情况。这些是在需要不同分辨率时切换图像,以及根据可用的视口空间改变图像。

使用 srcset 进行简单的分辨率切换

假设你有图像的三个版本。它们看起来都一样,只是一个是为较小的视口而设计的较小尺寸或分辨率,另一个是为中等尺寸视口而设计的,最后一个更大的版本适用于其他任何视口。以下是我们如何让浏览器知道我们有这三个版本可用。

<img src="img/scones_small.jpg" srcset="scones_medium.jpg 1.5x, scones_large.jpg 2x" alt="Scones taste amazing">

这是响应式图片中最简单的情况,所以让我们确保语法完全合理。

首先,src属性,你可能已经熟悉了,这里有一个双重作用;它指定了图像的小尺寸 1x 版本,如果浏览器不支持srcset属性,它也充当备用图像。这就是为什么我们在小图像上使用它。这样,忽略srcset信息的旧浏览器将得到最小且性能最佳的图像。

对于理解srcset的浏览器,我们在该属性后提供了一个逗号分隔的图像列表,供浏览器选择。在图像名称(如scones_medium.jpg)之后,我们发出了一个简单的分辨率提示。在这个例子中,使用了 1.5x 和 2x,但任何整数都是有效的。例如,3x 或 4x 也可以(只要你能找到适当分辨率的屏幕)。

然而,这里存在一个问题;一个 1440 像素宽,1x 屏幕的设备将得到与 480 像素宽,3x 屏幕相同的图像。这可能是期望的效果,也可能不是。

使用 srcset 和 sizes 进行高级切换

让我们考虑另一种情况。在响应式网页设计中,图像在较小的视口上可能是整个视口宽度,但在较大的尺寸上可能只有视口宽度的一半。第一章中的主要示例,“响应式网页设计的基本要素”,就是一个典型的例子。以下是我们如何向浏览器传达这些意图的方式:

<img srcset="scones-small.jpg 450w, scones-medium.jpg 900w" sizes="(min-width: 17em) 100vw, (min-width: 40em) 50vw" src="img/scones-small.jpg" alt="Scones">

在图像标签内部,我们再次使用srcset。然而,这一次,在指定图像之后,我们添加了一个带有 w 后缀的值。这告诉浏览器图像有多宽。在我们的例子中,我们有一个宽度为 450 像素的图像(名为scones-small.jpg)和一个宽度为 900 像素的图像(名为scones-medium.jpg)。重要的是要注意,这个带有w后缀的值并不是一个“真实”的尺寸。它只是对浏览器的一种指示,大致相当于“CSS 像素”的宽度。

提示

CSS 中究竟是什么定义了像素?我自己也想知道。然后我在www.w3.org/TR/css3-values/找到了解释,但后悔了。

当我们考虑sizes属性时,这个带有w后缀的值更有意义。sizes属性允许我们向浏览器传达我们图像的意图。在我们之前的例子中,第一个值相当于“对于至少宽度为 17em 的设备,我打算显示大约 100vw 宽的图像”。

注意

如果一些使用的单位,比如 vh(其中 1vh 等于视口高度的 1%)和 vw(其中 1vw 等于视口宽度的 1%)不合理,请务必阅读第五章,“CSS3 – 选择器,排版,颜色模式和新特性”。

第二部分有效地是,“嗨,浏览器,对于至少 40em 宽的设备,我只打算以 50vw 的宽度显示图像”。这可能看起来有点多余,直到你考虑 DPI(或 DPR,设备像素比)。例如,在一个 320px 宽的设备上,分辨率为 2 倍(如果以全宽度显示需要 640px 宽的图像),浏览器可能会决定 900px 宽的图像实际上更合适,因为它是满足所需尺寸的第一个选项。

你说浏览器可能会选择一张图像而不是另一张?

重要的是要记住,sizes属性只是对浏览器的提示。这并不一定意味着浏览器总是会遵守。这是一件好事。相信我,真的是。这意味着将来,如果浏览器有一种可靠的方式来确定网络条件,它可能会选择提供一张图像而不是另一张,因为在那时它知道的事情我们在这个时候作为作者可能无法知道。也许用户在他们的设备上设置了“只下载 1x 图像”或“只下载 2x 图像”的选项;在这些情况下,浏览器可以做出最佳选择。

与浏览器决定相反的是使用picture元素。使用这个元素可以确保浏览器提供你要求的确切图像。让我们看看它是如何工作的。

使用 picture 元素进行艺术指导

你可能会发现自己处于的最后一种情况是,你有不同的图像适用于不同的视口尺寸。例如,再次考虑我们基于蛋糕的例子,来自第一章,“响应式网页设计的基本原理”。也许在最小的屏幕上,我们想要一个特写的司康饼,上面有大量果酱和奶油。对于更大的屏幕,也许我们有一个更宽的图像想要使用。也许是一张装满各种蛋糕的桌子的全景照。最后,对于更大的视口,也许我们想要看到一个村庄街道上的蛋糕店的外部,人们坐在外面吃蛋糕和喝茶(我知道,听起来像天堂,对吧?)。我们需要三种在不同视口范围内最合适的图像。以下是我们如何使用picture解决这个问题的方法:

<picture>
    <source media="(min-width: 30em)" srcset="cake-table.jpg">
    <source media="(min-width: 60em)" srcset="cake-shop.jpg">
    <img src="img/scones.jpg" alt="One way or another, you WILL get cake.">
</picture>

首先,要注意的是,当你使用picture元素时,它只是一个包装器,用于方便其他图像进入img标签。如果你想以任何方式样式化图像,应该关注的是img

其次,在这里,srcset属性的工作方式与前面的示例完全相同。

第三,img标签提供了你的备用图像,也是如果浏览器理解picture但没有匹配的媒体定义时将显示的图像。只是为了非常清楚;不要在picture元素内省略img标签,否则事情就会变得不好。

picture的关键区别在于我们有一个source标签。在这里,我们可以使用媒体查询样式表达式明确告诉浏览器在匹配情况下使用哪个资源。例如,前面示例中的第一个告诉浏览器,“嘿,如果屏幕宽度至少为 30em,就加载cake-table.jpg图像”。只要条件匹配,浏览器就会忠实地遵守。

方便新潮的图像格式

作为一个额外的好处,picture还可以帮助我们提供图像的其他格式。'WebP'(更多信息请参阅developers.google.com/speed/webp/)是一种新的格式,许多浏览器不支持(caniuse.com/)。对于那些支持的浏览器,我们可以提供该格式的文件,对于不支持的浏览器,我们可以提供更常见的格式:

<picture>
    <source type="image/webp" srcset="scones-baby-yeah.webp">
    <img src="img/scones-baby-yeah.jpg" alt="Again, you WILL eat cake.">
</picture>

希望现在这已经变得更加简单明了。我们不再使用media属性,而是使用type(我们将在第四章中更多地使用 type 属性,响应式 Web 设计的 HTML5),尽管它更常用于指定视频来源(可能的视频来源类型可以在html.spec.whatwg.org/multipage/embedded-content.html找到),但在这里允许我们定义 WebP 作为首选图像格式。如果浏览器可以显示它,它将显示,否则它将获取img标签中的默认图像。

提示

有很多旧版浏览器永远无法使用官方的 W3C 响应式图像。除非有特定原因不这样做,我的建议是允许内置的回退功能发挥作用。使用一个合理大小的回退图像为他们提供良好的体验,并允许更有能力的设备享受增强的体验。

总结

在本章中,我们涵盖了很多内容。我们花了相当多的时间来熟悉 Flexbox,这是最新、最强大、现在也得到了很好支持的布局技术。我们还介绍了如何根据我们需要解决的问题,为我们的用户提供任意数量的替代图像。通过使用srcsetsizespicture,我们的用户应该始终能够获得最适合他们需求的图像,无论是现在还是将来。

到目前为止,我们已经看了很多 CSS 及其一些新兴的可能性和能力,但只有在响应式图像中,我们才看到了更现代的标记。让我们下一步来解决这个问题。

下一章将全面介绍 HTML5。它提供了什么,与上一个版本相比有什么变化,以及在很大程度上,我们如何最好地利用其新的语义元素来创建更清晰、更有意义的 HTML 文档。

第四章:响应式 Web 设计的 HTML5

如果您正在寻求使用 HTML5 应用程序编程接口API)的指导,我要引用一部伟大的西部电影中的一句话,说:“我不是你的 Huckleberry”。

我想和您一起看看 HTML5 的“词汇”部分;它的语义。更简洁地说,我们可以如何使用 HTML5 的新元素来描述我们在标记中放置的内容。本章大部分内容与响应式 Web 设计无关。然而,HTML 是构建所有基于 Web 的设计和应用程序的基础。谁不想在最坚实的基础上构建?

您可能会想知道“HTML5 到底是什么?”如果是这样,我会告诉您,HTML5 只是给 HTML 的最新版本的描述,这是我们用来构建网页的标签语言。HTML 本身是一个不断发展的标准,之前的主要版本是 4.01。

关于 HTML 的版本和时间线的更多背景信息,您可以阅读维基百科条目en.wikipedia.org/wiki/HTML#HTML_versions_timeline

提示

HTML5 现在是 W3C 的推荐标准。您可以在www.w3.org/TR/html5/上阅读规范。

本章我们将涵盖的主题有:

  • HTML5 得到了多大的支持?

  • 正确开始 HTML5 页面

  • 轻松的 HTML5

  • 新的语义元素

  • 文本级语义

  • 过时的功能

  • 将新元素投入使用

  • Web 内容可访问性指南WCAG)可访问性符合和Web 可访问性倡议-可访问丰富的互联网应用程序WAI-ARIA)用于更具可访问性的 Web 应用程序

  • 嵌入媒体

  • 响应式视频和 iFrames

  • 关于“离线优先”的说明

注意

HTML5 还提供了处理表单和用户输入的特定工具。这一系列功能大大减轻了像 JavaScript 这样的资源密集型技术对表单验证等方面的负担。然而,我们将在第九章中单独讨论 HTML5 表单,用 HTML5 和 CSS3 征服表单

HTML5 标记-所有现代浏览器都能理解

如今,我看到的大多数网站(包括我自己制作的所有网站)都是使用 HTML5 编写的,而不是旧的 HTML 4.01 标准。

所有现代浏览器都理解 HTML5 的新语义元素(新的结构元素、视频和音频标签),甚至旧版本的 Internet Explorer(Internet Explorer 9 之前的版本)也可以使用一个小的“polyfill”来渲染这些新元素。

注意

什么是 polyfill?

术语polyfill是由 Remy Sharp 创造的,意指用 Polyfilla(在美国称为Spackling Paste)填补旧浏览器中的裂缝。因此,polyfill 是 JavaScript 的“垫片”,可以在旧浏览器中有效地复制新功能。然而,重要的是要意识到 polyfill 会给您的代码增加额外的负担。因此,即使您可以添加 15 个 polyfill 脚本使 Internet Explorer 6 渲染网站与其他浏览器完全相同,也并不意味着您一定应该这样做。

如果您需要启用 HTML5 结构元素,我建议查看 Remy Sharp 的原始脚本(remysharp.com/2009/01/07/html5-enabling-script/)或创建 Modernizr 的自定义版本(modernizr.com)。如果您还没有接触或使用 Modernizr,下一章中有一个完整的部分介绍它。

考虑到这一点,让我们来考虑一下 HTML5 页面的开始。让我们了解所有的开放标签以及它们的作用。

正确开始 HTML5 页面

让我们从 HTML5 文档的开头开始。如果搞砸了这部分,你可能会花很长时间想知道为什么你的页面表现得不像应该的那样。前几行应该是这样的:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset=utf-8>

让我们逐个讨论这些标签。通常情况下,每次创建网页时它们都是一样的,但相信我,了解它们的作用是值得的。

文档类型

doctype是一种向浏览器传达我们拥有的文档类型的方式。否则,它可能不知道如何使用其中的内容。

我们用 HTML5 的doctype声明打开了我们的文档:

<!DOCTYPE html>

如果你喜欢小写字母,那么<!doctype html>也是一样好的。没有任何区别。

这是从 HTML 4.01 页面中的一个受欢迎的变化。它们过去通常是这样开始的:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

这真是一个巨大的痛苦!难怪我过去常常复制粘贴它!

另一方面,HTML5 的doctype非常简短,只是<!DOCTYPE html>。有趣的事实(至少对我来说):实际上它最终变成这样是因为确定这是告诉浏览器以“标准模式”渲染页面的最短方法。

提示

想要了解“怪癖”和“标准”模式是什么吗?维基百科可以帮到你:en.wikipedia.org/wiki/Quirks_mode

HTML 标签和 lang 属性

doctype声明之后,我们打开了html标签;我们文档的根标签。我们还使用lang属性来指定文档的语言,然后我们打开了<head>部分。

<html lang="en">
<head>

指定备用语言

根据 W3C 规范(www.w3.org/TR/html5/dom.html#the-lang-and-xml:lang-attributes),lang属性指定了元素内容的主要语言,以及包含文本的元素属性的语言。如果你不是用英语编写页面,最好指定正确的语言代码。例如,对于日语,HTML 标签将是<html lang="ja">。要查看完整的语言列表,请查看www.iana.org/assignments/language-subtag-registry

字符编码

最后,我们指定字符编码。由于它是一个空元素(不能包含任何内容),它不需要闭合标签:

<meta charset="utf-8">

除非你有充分的理由指定其他值,否则字符集的值几乎总是utf-8。对于好奇的人,关于这个主题的更多信息可以在www.w3.org/International/questions/qa-html-encoding-declarations#html5charset找到。

轻松的 HTML5

我记得,在学校的时候,我们非常严厉(但实际上非常好)的数学老师偶尔会缺席。班上会松一口气,因为替代老师通常是一个随和的人。他安静地坐着,不会大声喊叫或不断刺激我们。他在我们工作时不坚持要求安静,也不太在意我们是否遵守他解决问题的方式,重要的是答案以及我们如何得出答案。如果 HTML5 是一位数学老师,它就是那位随和的替代老师。现在我将解释这个奇怪的类比。

如果你注意写代码的方式,通常会大部分使用小写字母,用引号括起属性值,并为脚本和样式表声明“类型”。例如,也许你链接到一个样式表,就像这样:

<link href="CSS/main.css" rel="stylesheet" type="text/css" />

HTML5 不需要如此精确,它也可以接受这样的写法:

<link href=CSS/main.css rel=stylesheet >

你注意到了吗?没有结束标签/斜杠,属性值周围没有引号,也没有类型声明。然而,轻松的 HTML5 并不在乎。第二个例子和第一个一样有效。

这种更宽松的语法适用于整个文档,不仅仅是链接的资产。例如,如果你喜欢,可以这样指定一个 div:

<div id=wrapper>

这是完全有效的 HTML5。插入图像也是一样:

<img SRC=frontCarousel.png aLt=frontCarousel>

这也是有效的 HTML5。没有结束标签/斜杠,没有引号,大小写字母混合。你甚至可以省略诸如开头的<head>标签,页面仍然验证。XHTML 1.0 会对此有何看法?

提示

想要一个快捷的 HTML5 代码?考虑使用 HTML5 Boilerplate(html5boilerplate.com/)。这是一个预先制作的“最佳实践”HTML5 文件,包括必要的样式,polyfills 和可选工具,如 Modernizr。你可以通过查看代码获得很多很好的提示,还可以根据自己的特定需求定制模板。强烈推荐!

HTML5 标记的合理方法

就个人而言,我喜欢以“XHTML”风格编写我的标记。这意味着关闭标签,引用属性值,并遵循一致的大小写。有人可能会争辩说放弃其中一些做法会节省一些数据字节,但这就是工具的用途(如果需要,可以剥离任何不必要的字符/数据)。我希望我的标记尽可能易读,也鼓励其他人这样做。我认为代码的清晰度应该胜过简洁性。

因此,在编写 HTML5 文档时,我认为你可以编写干净易读的代码,同时仍然可以利用 HTML5 所提供的经济效益。举例来说,对于 CSS 链接,我会选择以下方式:

<link href="CSS/main.css" rel="stylesheet"/>

我保留了闭合标签和引号,但省略了type属性。这里要说明的是,你可以找到一个让自己满意的水平。HTML5 不会对你大喊大叫,在班上标记你的标记,并让你站在角落里戴着愚人帽子,因为你的标记没有验证(难道只有我的学校这样做吗?)。但是,你想怎么写你的标记都可以。

我在开玩笑吗?我现在就想让你知道,如果你在编写代码时不引用属性值和不关闭标签,我会默默地评判你。

提示

尽管 HTML5 的语法更宽松,但检查你的标记是否有效总是值得的。有效的标记更具可访问性。W3C 验证器就是为了这个目的而创建的:validator.w3.org/

我已经够抨击“嬉皮士”风格标记的作者了。让我们看看 HTML5 的更多好处。

万岁强大的标签

HTML5 的一个巨大的优势是现在我们可以将多个元素包装在一个<a>标签中(喔耶!是时候了,对吧?)。以前,如果你想让你的标记验证,就必须将每个元素包装在自己的<a>标签中。例如,看看以下 HTML 4.01 代码:

<h2><a href="index.html">The home page</a></h2>
<p><a href="index.html">This paragraph also links to the home page</a></p>
<a href="index.html"><img src="img/home-image.png" alt="home-slice" /></a>

有了 HTML5,我们可以放弃所有单独的<a>标签,而是用一个标签包装整个组:

<a href="index.html">
  <h2>The home page</h2>
  <p>This paragraph also links to the home page</p>
  <img src="img/home-image.png" alt="home-slice" />
</a>

需要记住的唯一限制是,可以理解的是,你不能在另一个<a>标签中包装一个<a>标签(因为,嗯,显而易见),或者另一个交互元素,如button(因为,嗯,显而易见!),也不能在<a>标签中包装一个表单(因为,嗯,你懂的)。

HTML5 中的新语义元素

如果我查看 OS X 字典中“语义”一词的定义,它被定义为:

“与语言学和逻辑有关的分支,关注意义”。

对于我们的目的,语义是赋予我们的标记意义的过程。这为什么重要?很高兴你问。

大多数网站遵循相当标准的结构约定;典型的区域包括标题、页脚、侧边栏、导航栏等。作为网页作者,我们通常会为我们使用的 div 命名,以更清晰地指定这些区域(例如,class="Header")。但是,就代码本身而言,任何用户代理(Web 浏览器、屏幕阅读器、搜索引擎爬虫等)查看它时无法确定每个div元素的目的。辅助技术的用户也会发现很难区分一个div和另一个div。HTML5 旨在通过新的语义元素解决这个问题。

注意

要获取 HTML5 元素的完整列表,请舒服地将浏览器指向www.w3.org/TR/html5/semantics.html#semantics

我们不会在这里涵盖所有新元素,只是我认为在日常响应式 Web 设计中最有益或有趣的元素。让我们深入了解。

<main>元素

很长一段时间,HTML5 没有元素来划分页面的主要内容。在网页的正文中,这将是包含主要内容块的元素。

起初,有人认为不在其他新的语义 HTML5 元素之一内的内容,通过否定,将成为主要内容。幸运的是,规范发生了变化,现在我们有了更具有声明性的方式来分组主要内容;这个名为<main>的标签。

无论您是包装页面的主要内容还是基于 Web 的应用程序的主要部分,main元素都是您应该将所有内容分组的元素。以下是规范中特别有用的一行:

“文档的主要内容区域包括该文档独有的内容,不包括在一组文档中重复的内容,例如站点导航链接、版权信息、站点标识和横幅以及搜索表单(除非文档或应用程序的主要功能是搜索表单)。”

值得注意的是,每个页面上不应该有多个主要内容(毕竟,您不能有两个主要内容),它不应该被用作其他语义 HTML5 元素的后代,例如articleasideheaderfooternavheader。但它们可以存在于主要元素中。

注意

阅读关于主要元素的官方说明:www.w3.org/TR/html5/grouping-content.html#the-main-element

<section>元素

<section>元素用于定义文档或应用程序的通用部分。例如,您可以选择围绕您的内容创建部分;一个部分用于联系信息,另一个部分用于新闻提要等。重要的是要理解它并不是用于样式目的。如果您需要包装一个元素仅用于样式化,您应该继续像以前一样使用div

在开发基于 Web 的应用程序时,我倾向于使用section作为可视组件的包装元素。它提供了一种简单的方法来查看标记中组件的开始和结束。

您还可以自行判断是否应该使用基于内容的部分(例如h1)来确定是否应该使用部分。如果没有,您最好选择div

注意

要查看 W3C HTML5 规范中关于<section>的内容,请访问以下网址:

www.w3.org/TR/html5/sections.html#the-section-element

<nav>元素用于包装到其他页面或同一页面内部的主要导航链接。它并不严格用于页脚(尽管可以),以及其他常见的其他页面链接组。

如果您通常使用无序列表(<ul>)和一堆列表标签(li)标记您的导航元素,您可能更适合使用nav和一些嵌套的a标签。

注意

要了解 W3C HTML5 规范对<nav>的说明,请访问以下网址:

www.w3.org/TR/html5/sections.html#the-nav-element

<article>元素

<article>元素和<section>一样容易引起混淆。我肯定在它们的规范之前读了很多遍,才明白它们的含义。这是我对规范的重新阐述。<article>元素用于包装一个独立的内容块。在构建页面时,问一下,您打算在<article>标签中使用的内容是否可以作为一个整体被复制到另一个网站上,并且仍然完全有意义?另一种思考方式是,您打算用<article>包装的内容是否实际上构成了 RSS 订阅中的一个单独的文章?显而易见的应该用<article>元素包装的内容的例子是博客文章或新闻报道。请注意,如果嵌套<article>元素,假定嵌套的<article>元素主要与外部文章相关。

注意

有关 W3C HTML5 规范对<article>的说明,请访问www.w3.org/TR/html5/sections.html#the-article-element

<aside>元素

<aside>元素用于与周围内容有关的内容。在实际操作中,我经常用它来制作侧边栏(当它包含合适的内容时)。它也被认为适合用于拉引语、广告和导航元素组。基本上,任何与主要内容不直接相关的内容都可以放在<aside>中。如果这是一个电子商务网站,我会考虑像“购买此商品的顾客还购买了”这样的区域作为<aside>的首选内容。

注意

有关 W3C HTML5 规范对<aside>的说明,请访问www.w3.org/TR/html5/sections.html#the-aside-element

<figure><figcaption>元素

规范涉及figure元素:

这样可以用来注释插图、图表、照片、代码清单等。

以下是我们如何使用它来修改第一章的一部分标记:

<figure class="MoneyShot">
  <img class="MoneyShotImg" src="img/scones.jpg" alt="Incredible scones" />
  <figcaption class="ImageCaption">Incredible scones, picture from Wikipedia</figcaption>
</figure>

您可以看到<figure>元素用于包装这个小的独立块。在内部,<figcaption>用于为父<figure>元素提供标题。

当图片或代码需要在旁边加上一点标题时(这在内容的主要文本中不合适),这是完美的。

注意

figure元素的规范可以在www.w3.org/TR/html5/grouping-content.html#the-figure-element找到。

figcaption的规范在www.w3.org/TR/html5/grouping-content.html#the-figcaption-element

<details><summary>元素

您有多少次想要在页面上创建一个简单的打开和关闭“小部件”?点击时打开一个带有附加信息的面板的摘要文本。HTML5 通过detailssummary元素实现了这种模式。考虑一下这个标记(您可以打开本章代码中的example3.html来自己尝试):

<details>
    <summary>I ate 15 scones in one day</summary>
    <p>Of course I didn't. It would probably kill me if I did. What a way to go. Mmmmmm, scones!</p>
</details>

在 Chrome 中打开时,默认只显示摘要文本:

和元素

单击摘要文本的任何位置都会打开面板。再次单击它会切换关闭。如果您希望面板默认打开,可以将open属性添加到details元素中:

<details open>
    <summary>I ate 15 scones in one day</summary>
    <p>Of course I didn't. It would probably kill me if I did. What a way to go. Mmmmmm, scones!</p>
</details>

和元素

支持的浏览器通常会添加一些默认样式来指示面板可以打开。在 Chrome(以及 Safari)中,这是一个深色的披露三角形。要禁用这个,你需要使用一个 WebKit 特定的私有伪选择器:

summary::-webkit-details-marker {
  display: none;
}

当然,你可以使用相同的选择器来以不同的样式显示标记。

目前,没有办法对打开和关闭进行动画处理。也没有(非 JavaScript)的办法在打开不同的详细信息面板时关闭其他详细信息面板(在同一级别)。我不确定这些愿望中的任何一个会(或应该)得到解决。你应该把它看作是一种通过 JavaScript 的display: none;切换来促进你所做的事情。

遗憾的是,截至我写这篇文章时(2015 年中),Firefox 或 Internet Explorer 不支持此元素(它们只将这两个元素呈现为内联元素)。存在 Polyfills(mathiasbynens.be/notes/html5-details-jquery),希望很快就会完全实现。

<header>元素

实际上,<header>元素可以用于站点页眉的“标志”区域。它也可以用作其他内容的介绍,比如<article>元素中的一个部分。你可以在同一页上使用它多次(例如,你可以在页面上的每个<section>中都有一个<header>)。

注意

这是 W3C HTML5 规范对<header>的说明:

www.w3.org/TR/html5/sections.html#the-header-element

<footer>元素

<footer>元素应该用于包含所在部分的信息。例如,它可能包含指向其他文档的链接或版权信息。与<header>一样,如果需要,它可以在页面中多次使用。例如,它可以用于博客的页脚,也可以用于博客文章中的footer部分。但是,规范解释说,博客文章作者的联系信息应该用<address>元素包装。

注意

查看 W3C HTML5 规范对<footer>的说明:

www.w3.org/TR/html5/sections.html#the-footer-element

<address>元素

<address>元素专门用于标记其最近的<article><body>祖先的联系信息。要混淆事情,要记住它不应该用于邮政地址等(除非它们确实是所讨论内容的联系地址)。相反,邮政地址和其他任意联系信息应该用老式的<p>标签包装。

我不喜欢<address>元素,因为根据我的经验,将物理地址标记为自己的元素会更有用,但这是我的个人抱怨。希望这对你来说更有意义。

注意

有关 W3C HTML5 规范对<address>的更多信息,请查看:

www.w3.org/TR/html5/sections.html#the-address-element

关于 h1-h6 元素的说明

直到最近我才意识到,使用h1-h6标签标记标题和副标题是不鼓励的。我说的是这种情况:

<h1>Scones:</h1>
<h2>The most resplendent of snacks</h2>

以下是 HTML5 规范的一句引用:

h1-h6 元素不得用于标记副标题、副标题、替代标题和标语,除非打算用作新部分或子部分的标题。

这绝对是规范中最不明确的句子之一!哎呀!

那么,我们应该如何编写这样的情况呢?规范实际上有一个专门的部分(www.w3.org/TR/html5/common-idioms.html#common-idioms)来专门讨论这个问题。就我个人而言,我更喜欢旧的<hgroup>元素,但可惜的是,那艘船已经启航了(更多信息请参见过时的 HTML 功能部分)。因此,为了遵循规范的建议,我们之前的例子可以重写为:

<h1>Scones:</h1>
<p>The most resplendent of snacks</p>

HTML5 文本级语义

除了我们已经看过的结构和分组元素之外,HTML5 还修改了一些以前被称为内联元素的标签。HTML5 规范现在将这些标签称为文本级语义(www.w3.org/TR/html5/text-level-semantics.html#text-level-semantics)。让我们看一些常见的例子。

<b>元素

从历史上看,<b>元素意味着“使其加粗”(www.w3.org/TR/html4/present/graphics.html#edef-B)。这是从以前样式选择是标记的一部分的时代。然而,现在你可以正式地将其仅用作 CSS 中的样式钩,因为 HTML5 规范现在声明<b>是:

“b 元素表示吸引注意力的文本范围,用于实用目的,而不传达任何额外的重要性,并且没有暗示另一种声音或情绪,例如文档摘要中的关键词,评论中的产品名称,交互式文本驱动软件中的可操作单词,或文章导言。”

虽然现在没有特定的含义与之相关联,但由于它是文本级的,它不打算用于包围大量的标记,对此请使用div。您还应该知道,因为它在历史上用于加粗文本,如果您希望<b>标签内的内容不显示为加粗,通常需要在 CSS 中重置字体重量。

<em>元素

好的,举起手来,我经常只是把<em>用作样式钩。我需要改变我的方式,因为在 HTML5 中:

em元素表示其内容的重点强调。

因此,除非您真的希望强调所包含的内容,否则请考虑使用<b>标签或者在相关情况下使用<i>标签。

<i>元素

HTML5 规范将<i>描述为:

“...以另一种声音或情绪,或以其他方式偏离正常散文的方式,表明文本的不同质量。”

可以说,它不仅仅用于使某物变斜体。例如,我们可以使用它来标记文本行中的奇怪名称:

<p>However, discussion on the hgroup element is now frustraneous as it's now gone the way of the <i>Raphus cucullatus</i>.</p>

注意

HTML5 中有许多其他文本级语义标签。要了解完整的信息,请查看规范的相关部分,网址如下:

www.w3.org/TR/html5/text-level-semantics.html#text-level-semantics

过时的 HTML 功能

除了脚本链接中的语言属性之类的东西,HTML 中还有一些您可能习惯使用的其他部分,现在在 HTML5 中被认为是“过时的”。重要的是要意识到在 HTML5 中有两种过时功能的阵营——符合和不符合。符合功能仍然可以工作,但会在验证器中生成警告。实际上,如果可以的话最好避免使用它们,但如果您使用它们,也不会导致天塌下来。不符合功能在某些浏览器中可能仍然可以渲染,但如果您使用它们,您会被认为是非常非常淘气的,并且您可能在周末得不到奖励!

在过时和不符合规范的功能方面,有相当多的功能。我承认很多我从未使用过(有些我甚至从未见过!)。你可能会有类似的反应。然而,如果你感兴趣,你可以在www.w3.org/TR/html5/obsolete.html找到完整的过时和不符合规范的功能列表。值得注意的过时和不符合规范的功能包括strikecenterfontacronymframeframeset

HTML5 中还有一些早期草案中存在的功能,现在已经被删除了。hgroup就是一个例子。该标签最初被提议用于包装标题组;一个标题h1和一个副标题h2可能被包装在hgroup元素中。然而,关于hgroup元素的讨论现在已经变得无用,因为它已经像 Raphus cucullatus 一样消失了(去吧,谷歌一下,你知道你想要的)。

使用 HTML5 元素

现在是练习刚刚学习的一些元素的时候了。让我们重新访问第一章中的示例,响应式网页设计的基本知识。如果我们将下面的标记与第一章中的原始标记进行比较(记住,你可以从rwd.education网站或 GitHub 存储库下载所有示例),你可以看到我们刚刚学习的新元素在下面的示例中是如何使用的。

<article>
  <header class="Header">
    <a href="/" class="LogoWrapper"><img src="img/SOC-Logo.png" alt="Scone O'Clock logo" /></a>
    <h1 class="Strap">Scones: the most resplendent of snacks</h1>

	</header>
  <section class="IntroWrapper">
    <p class="IntroText">Occasionally maligned and misunderstood; the scone is a quintessentially British classic.</p>
    <figure class="MoneyShot">
      <img class="MoneyShotImg" src="img/scones.jpg" alt="Incredible scones" />
      <figcaption class="ImageCaption">Incredible scones, picture from Wikipedia</figcaption>
    </figure>
  </section>
  <p>Recipe and serving suggestions follow.</p>
  <section class="Ingredients">
    <h3 class="SubHeader">Ingredients</h3>
  </section>
  <section class="HowToMake">
    <h3 class="SubHeader">Method</h3>
  </section>
  <footer>
    Made for the book, <a href="http://rwd.education">'Resonsive web design with HTML5 and CSS3'</a> by <address><a href="http://benfrain">Ben Frain</a></address>
  </footer>
</article>

运用常识进行元素选择

我已经删除了大部分内部内容,这样我们就可以集中精力关注结构。希望你会同意,很容易区分出标记的不同部分。然而,在这一点上,我也想提供一些建议;如果你并不总是为每个特定情况选择正确的元素,这并不是世界末日。例如,在前面的例子中,我使用<section>还是<div>并不是很重要。如果我们在应该使用<i>时使用了<em>,我并不认为这是对人类的罪行;W3C 的人员不会因为你做出了错误的选择而追捕你。只需运用一点常识。也就是说,如果你能在相关情况下使用<header><footer>等元素,那么这样做就具有固有的可访问性好处。

WCAG 和 WAI-ARIA 用于更易访问的网页应用程序

即使自 2011 年至 2012 年编写本书的第一版以来,W3C 在使作者更容易编写更易访问的网页方面已经取得了进展。

WCAG

WCAG 的存在是为了提供:

“一个共享的标准,用于满足国际上个人、组织和政府的网页内容可访问性需求。”

对于更普通的网页(而不是单页面网页应用程序等),集中精力关注 WCAG 指南是有意义的。它们提供了许多(大多数是常识)关于如何确保您的网页内容可访问的指南。每个建议都被评为符合级别:A、AA 或 AAA。有关这些符合级别的更多信息,请参阅www.w3.org/TR/UNDERSTANDING-WCAG20/conformance.html#uc-levels-head

你可能会发现,你已经遵守了许多指南,比如为图像提供替代文本。然而,你可以在www.w3.org/WAI/WCAG20/glance/Overview.html上快速了解这些指南,然后在www.w3.org/WAI/WCAG20/quickref/上建立自己的自定义快速参考清单。

我鼓励每个人花一两个小时看一看这个清单。许多指南都很容易实施,并为用户带来实际的好处。

WAI-ARIA

WAI-ARIA 的目标主要是解决使网页上的动态内容可访问的问题。它提供了一种描述自定义小部件(Web 应用程序中的动态部分)的角色、状态和属性的方法,以便辅助技术用户能够识别和使用它们。

例如,如果屏幕上的小部件显示不断更新的股票价格,那么如何盲人用户访问页面时知道呢?WAI-ARIA 试图解决这些问题。

不要为语义元素使用角色

以前建议为标题和页脚添加“地标”角色,如下所示:

<header role="banner">A header with ARIA landmark banner role</header>

然而,现在认为这是多余的。如果您查看前面列出的任何元素的规范,都有一个专门的允许的 ARIA 角色属性部分。以下是来自 section 元素的相关解释:

“允许的 ARIA 角色属性值:”

region role (default - do not set), alert, alertdialog, application, contentinfo, dialog, document, log, main, marquee, presentation, search or status."

其中关键部分是“role(默认-不设置)”。这意味着在元素本身已经隐含了 ARIA 角色时,明确添加 ARIA 角色到元素是没有意义的。规范中的一条说明现在已经明确表明了这一点。

“在大多数情况下,设置与默认隐式 ARIA 语义匹配的 ARIA 角色和/或 aria-*属性是不必要的,也不建议这样做,因为这些属性已经由浏览器设置。”

如果您只记住一件事

您可以尽可能使用正确的元素来帮助辅助技术。header元素比div class="Header"更有用。同样,如果页面上有一个按钮,请使用<button>元素(而不是span或其他样式看起来像button的元素)。我知道button元素并不总是允许精确的样式(例如,它不喜欢被设置为display: table-celldisplay: flex),在这种情况下至少选择次佳选择;通常是<a>标签。

进一步了解 ARIA

ARIA 不仅限于地标角色。要进一步了解,可以在www.w3.org/TR/wai-aria/roles上找到角色的完整列表和简洁的用法描述。

对于这个话题的轻松看法,我还推荐 Heydon Pickering 的书《Apps For All: Coding Accessible Web Applications》(可在shop.smashingmagazine.com/products/apps-for-all-coding-accessible-web-applications上获取)。

提示

免费使用非视觉桌面访问(NVDA)测试您的设计

如果您在 Windows 平台上开发,并且想要免费使用 NVDA 测试您的 ARIA 增强设计,您可以在以下网址获取:

www.nvda-project.org/

谷歌现在还为 Chrome 浏览器提供免费的“辅助开发者工具”(可跨平台使用);非常值得一试。

还有越来越多的工具可以帮助您快速测试自己的设计,例如michelf.ca/projects/sim-daltonism/是一个 Mac 应用程序,可以让您切换色盲类型,并在浮动调色板中预览。

最后,OS X 还包括 VoiceOver 实用程序,用于测试您的网页。

希望对 WAI-ARIA 和 WCAG 的简要介绍为您提供了足够的信息,让您更多地考虑如何支持辅助技术。也许在您的下一个 HTML5 项目中添加辅助技术支持会比您想象的更容易。

作为所有辅助功能的最终资源,A11Y 项目主页上有很多有用的链接和建议,网址为a11yproject.com/

在 HTML5 中嵌入媒体

对许多人来说,当苹果拒绝在其 iOS 设备中添加对 Flash 的支持时,HTML5 首次进入他们的词汇表。Flash 已经在市场上占据了主导地位(有人会认为是市场垄断),成为通过网络浏览器提供视频的首选插件。然而,苹果决定不使用 Adobe 的专有技术,而是依靠 HTML5 来处理丰富的媒体渲染。虽然 HTML5 在这个领域本来就取得了良好的进展,但苹果对 HTML5 的公开支持使其获得了重大的优势,并帮助其媒体工具在更广泛的社区中获得了更大的影响力。

正如你所想象的,Internet Explorer 8 及更低版本不支持 HTML5 视频和音频。大多数其他现代浏览器(Firefox 3.5+,Chrome 4+,Safari 4,Opera 10.5+,Internet Explorer 9+,iOS 3.2+,Opera Mobile 11+,Android 2.3+)都可以很好地处理它。

以 HTML5 方式添加视频和音频

在 HTML5 中,视频和音频非常简单。以前使用 HTML5 媒体的唯一真正困难之处在于列出媒体的备用源格式(因为不同的浏览器支持不同的文件格式)。如今,MP4 在桌面和移动平台上都是无处不在的,使得通过 HTML5 在网页中包含媒体变得轻而易举。以下是如何在页面中链接到视频文件的“简单至极”的示例:

<video src="img/myVideo.mp4"></video>

HTML5 允许一个<video></video>标签(或者用于音频的<audio></audio>)来完成所有繁重的工作。还可以在开放和闭合标签之间插入文本,以通知用户存在问题。通常还有其他属性需要添加,比如heightwidth。让我们添加这些:

<video src="img/myVideo.mp4" width="640" height="480">What, do you mean you don't understand HTML5?</video>

现在,如果我们将前面的代码片段添加到我们的页面中,并在 Safari 中查看它,它将出现,但没有播放控件。要获得默认的播放控件,我们需要添加controls属性。我们还可以添加autoplay属性(不建议-众所周知,每个人都讨厌自动播放的视频)。这在以下代码片段中进行了演示:

<video src="img/myVideo.mp4" width="640" height="480" controls autoplay> What, do you mean you don't understand HTML5?</video>

前面的代码片段的结果如下截图所示:

以 HTML5 方式添加视频和音频

其他属性包括preload来控制媒体的预加载(早期的 HTML5 采用者应该注意,preload 取代了 autobuffer),loop来重复播放视频,以及poster来定义视频的海报帧。如果视频播放可能会有延迟(或者缓冲可能需要一些时间),这将非常有用。要使用属性,只需将其添加到标签中。以下是包括所有这些属性的示例:

<video src="img/myVideo.mp4" width="640" height="480" controls autoplay preload="auto" loop poster="myVideoPoster.png">What, do you mean you don't understand HTML5?</video>

旧版浏览器的回退功能

<source>标签使我们能够根据需要提供备用方案。例如,除了提供视频的 MP4 版本外,如果我们想要确保 Internet Explorer 8 及更低版本有合适的备用方案,我们可以添加 Flash 回退。更进一步,如果用户在浏览器中没有任何合适的播放技术,我们可以提供文件本身的下载链接。以下是一个示例:

<video width="640" height="480" controls preload="auto" loop poster="myVideoPoster.png">
    <source src="img/myVideo.mp4" type="video/mp4">  
    <object width="640" height="480" type="application/x-shockwave-flash" data="myFlashVideo.SWF">
      <param name="movie" value="myFlashVideo.swf" />
      <param name="flashvars" value="controlbar=over&amp;image=myVideoPoster.jpg&amp;file=myVideo.mp4" />
      <img src="img/myVideoPoster.png" width="640" height="480" alt="__TITLE__"
           title="No video playback capabilities, please download the video below" />
    </object>
    <p><b>Download Video:</b>
  MP4 Format:  <a href="myVideo.mp4">"MP4"</a>
    </p>
</video>

该代码示例和示例视频文件(我在英国肥皂剧《加冕街》中出现,当时我还有头发,希望能与德尼罗一起出演)以 MP4 格式在本章代码的example2.html中。

音频和视频标签的工作方式几乎相同

<audio>标签遵循相同的原则,具有相同的属性(不包括widthheightposter)。两者之间的主要区别在于<audio>没有用于可见内容的播放区域。

响应式 HTML5 视频和 iFrames

我们已经看到,支持旧版浏览器会导致代码膨胀。以<video>标签开始的一两行最终变成了 10 行或更多行(还有一个额外的 Flash 文件),只是为了让旧版的 Internet Explorer 满意!就我个人而言,我通常愿意放弃 Flash 回退,以追求更小的代码占用空间,但每个用例都不同。

现在,我们可爱的 HTML5 视频实现的唯一问题是它不是响应式的。没错,在一个使用 HTML5 和 CSS3 的响应式网页设计示例中,它并没有“响应”。

值得庆幸的是,对于 HTML5 嵌入视频,修复很容易。只需在标记中删除任何高度和宽度属性(例如,删除width="640" height="480"),并在 CSS 中添加以下内容:

video { max-width: 100%; height: auto; }

然而,虽然这对于我们可能在本地托管的文件效果很好,但它并没有解决嵌入在 iFrame 中的视频的问题(比如 YouTube、Vimeo 等)。以下代码将在页面中添加来自 YouTube 的《午夜逃亡》电影预告:

<iframe width="960" height="720" src="img/watch?v=B1_N28DA3gY" frameborder="0" allowfullscreen></iframe>

然而,如果按原样添加到页面上,即使添加了之前的 CSS 规则,如果视口小于 960px 宽,事情就会开始被裁剪。

解决这个问题最简单的方法是使用加利西 CSS 大师 Thierry Koblentz 开创的一个小 CSS 技巧;基本上是为视频创建一个正确宽高比的框。我不想泄露这位魔术师的解释,去阅读一下alistapart.com/article/creating-intrinsic-ratios-for-video

如果你感到懒惰,甚至不需要计算宽高比并自己插入,有一个在线服务可以为你做到。只需转到embedresponsively.com/,粘贴你的 iFrame URL 进去。它会为你生成一小段简单的代码,你可以粘贴到你的页面中。例如,我们的《午夜逃亡》预告片的结果如下:

<style>.embed-container { position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden; max-width: 100%; height: auto; } .embed-container iframe, .embed-container object, .embed-container embed { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }</style><div class='embed-container'><iframe src='http://www.youtube.com/embed/B1_N28DA3gY' frameborder='0' allowfullscreen></iframe></div>

就是这样,简单地添加到你的页面,就完成了:我们现在有了一个完全响应式的 YouTube 视频(注意:孩子们,不要理会 DeNiro 先生;吸烟有害健康)!

关于“离线优先”的说明

我相信构建响应式网页和基于 Web 的应用程序的理想方式是“离线优先”。这种方法意味着网站和应用程序将继续工作和加载,即使没有互联网连接。

HTML5 离线 Web 应用程序(www.w3.org/TR/2011/WD-html5-20110525/offline.html)是为了实现这个目标而指定的。

尽管离线 Web 应用程序的支持很好(caniuse.com/#feat=offline-apps),遗憾的是,这并不是一个完美的解决方案。虽然设置起来相对简单,但存在许多限制和陷阱。在这本书的范围之外记录它们都是不可能的。相反,我建议阅读 Jake Archibald 在这个主题上的幽默而全面的文章:alistapart.com/article/application-cache-is-a-douchebag

因此,我认为虽然使用离线 Web 应用程序(如diveintohtml5.info/offline.html中的教程)和 LocalStorage(或两者的某种组合)可以实现离线优先体验,但更好的解决方案将很快出现。我寄希望于“Service Workers”(www.w3.org/TR/service-workers/)。

在撰写本文时,Service Workers 仍然是一个相对较新的规范,但我建议你观看这个 15 分钟的介绍:www.youtube.com/watch?v=4uQMl7mFB6g。阅读这篇介绍www.html5rocks.com/en/tutorials/service-worker/introduction/,并在jakearchibald.github.io/isserviceworkerready/检查支持情况。

我希望如果我写第三版这本书的时候,我们能够考虑全面概述和实施这种技术。保持乐观。

总结

在本章中,我们涵盖了很多内容。从创建一个符合 HTML5 验证的页面的基础知识,到将丰富媒体(视频)嵌入我们的标记,并确保它具有响应性。

虽然不是专门针对响应式设计,但我们也讨论了如何编写语义丰富和有意义的代码,并考虑了如何确保页面对于依赖辅助技术的用户来说是有意义和可用的。

出于必要性,这是一个非常标记密集的章节,所以现在让我们改变方向。在接下来的几章中,我们将拥抱 CSS 的强大和灵活性。首先,让我们看看 CSS 3 和 4 选择器的强大功能,新的视口相关 CSS 单位,以及诸如 calc 和 HSL 颜色等功能。它们都将使我们能够创建更快、更有能力和更易维护的响应式设计。

第五章:CSS3-选择器、排版、颜色模式和新功能

在过去的几年里,CSS 已经拥有了许多新功能。一些功能使我们能够对元素进行动画和变换,其他功能允许我们创建背景图像、渐变、蒙版和滤镜效果,还有一些功能允许我们使 SVG 元素栩栩如生。

我们将在接下来的几章中了解所有这些功能。首先,我认为看一下过去几年中 CSS 中发生的一些基本变化会很有用:我们如何在页面上选择元素,我们可以使用哪些单位来样式和调整我们的元素,以及现有(和未来)伪类和伪元素如何使 CSS 变得更加强大。我们还将看看如何在我们的 CSS 代码中创建分支,以便在不同浏览器中支持不同的功能。

在本章中,我们将学习以下内容:

  • CSS 规则的解剖(定义规则、声明和属性、值对)

  • 响应式设计的快速和方便的 CSS 技巧(多列、换行、截断/文本省略、滚动区域)

  • 在 CSS 中实现功能分支(如何使一些规则适用于一些浏览器,而另一些规则适用于其他浏览器)

  • 如何使用子字符串属性选择器选择 HTML 元素

  • 基于 nth 的选择器是什么以及我们如何使用它们

  • 伪类和伪元素是什么(:empty, ::before, ::after, :target, :scope

  • CSS Level 4 选择器模块中的新选择器(:has

  • CSS 变量和自定义属性是什么,以及如何编写它们

  • CSS calc函数是什么以及如何使用它

  • 利用与视口相关的单位(vhvwvminvmax

  • 如何使用@font-face进行网络排版

  • RGB 和 HSL 颜色模式与 Alpha 透明度

没有人知道所有的东西

没有人能知道所有的东西。我已经使用 CSS 工作了十多年,每周我仍然会在 CSS 中发现一些新东西(或重新发现我已经忘记的东西)。因此,我认为试图了解每种可能的 CSS 属性和值的排列组合实际上并不值得追求。相反,我认为更明智的做法是掌握可能性。

因此,在本章中,我们将集中讨论一些在构建响应式网页设计时我发现最有用的技术、单位和选择器。我希望你能够掌握解决开发响应式网页设计时遇到的大多数问题所需的知识。

CSS 规则的解剖

在探索 CSS3 所提供的一些功能之前,为了避免混淆,让我们先确定一下我们用来描述 CSS 规则的术语。考虑以下示例:

.round { /* selector */
  border-radius: 10px; /* declaration */
}

这个规则由选择器(.round)和声明(border-radius: 10px;)组成。声明进一步由属性(border-radius:)和值(10px;)定义。我们对此有了共识吗?太好了,让我们继续前进。

提示

记得检查用户的支持情况

随着我们越来越深入了解 CSS3,请不要忘记访问caniuse.com/,如果您想了解特定 CSS3 或 HTML5 功能的当前浏览器支持水平。除了显示浏览器版本支持(可按功能搜索),它还提供了来自gs.statcounter.com/的最新全局使用统计数据。

快速实用的 CSS 技巧

在我的日常工作中,我发现我经常使用一些 CSS3 功能,而其他一些几乎从不使用。我认为分享我经常使用的那些可能会很有用。这些是 CSS3 的好东西,可以让生活变得更轻松,特别是在响应式设计中。它们可以相对轻松地解决以前可能是小头疼的问题。

响应式设计的 CSS 多列布局

是否曾经需要使单个文本出现在多个列中?您可以通过将内容拆分为不同的标记元素,然后进行相应的样式设置来解决问题。但是,仅出于样式目的而更改标记永远不是理想的。CSS 多列布局规范描述了我们如何轻松地跨越一个或多个内容片段跨越多个列。考虑以下标记:

<main>
    <p>lloremipsimLoremipsum dolor sit amet, consectetur
<!-- LOTS MORE TEXT -->
</p>
    <p>lloremipsimLoremipsum dolor sit amet, consectetur
<!-- LOTS MORE TEXT -->
</p>
</main>

使用 CSS 多列,您可以以多种方式使所有内容跨越多列流动。您可以使列具有特定的列宽(例如 12em),或者您可以指定内容需要跨越一定数量的列(例如 3 列)。

让我们看看实现这些情景所需的代码。对于固定宽度的列,请使用以下语法:

main {
  column-width: 12em;
}

这意味着无论视口大小如何,内容都将跨越宽度为 12em 的列。更改视口将动态调整显示的列数。您可以通过查看example_05-01(或 GitHub 存储库:github.com/benfrain/rwd)在浏览器中查看此内容。

考虑一下页面在 iPad 纵向方向(768px 宽视口)上的呈现方式:

CSS 多列布局用于响应式设计

然后在 Chrome 桌面上(大约 1100px 宽的视口):

CSS 多列布局用于响应式设计

简单的响应式文本列,工作量最小;我喜欢它!

固定列,可变宽度

如果您更喜欢保持固定数量的列并改变宽度,可以编写以下规则:

main {
  column-count: 4;
}

添加间隙和列分隔符

我们甚至可以进一步添加指定的列间隙和分隔符:

main {
  column-gap: 2em;
  column-rule: thin dotted #999;
  column-width: 12em;
}

这给我们带来了以下结果:

添加间隙和列分隔符

要阅读 CSS3 多列布局模块的规范,请访问www.w3.org/TR/css3-multicol/

目前,尽管在 W3C 的 CR 状态,但您可能仍需要供应商前缀以获得最大的兼容性。

我对使用 CSS 多列的唯一警告是,对于较长的文本跨度,它可能导致用户体验不佳。在这些情况下,用户将不得不在页面上上下滚动以阅读文本的列,这可能有点繁琐。

自动换行

您有多少次不得不将一个大 URL 添加到一个小空间中,然后感到绝望?看一下rwd.education/code/example_05-04。问题也可以在以下屏幕截图中看到;请注意,URL 正在超出其分配的空间。

Word wrapping

通过简单的 CSS3 声明很容易解决这个问题,碰巧的是,这也适用于 Internet Explorer 5.5 以前的旧版本!只需添加:

word-wrap: break-word;

到包含元素,效果如下截图所示。

Word wrapping

哇,长长的 URL 现在完美地换行了!

文本省略

文本截断过去是服务器端技术的专属领域。现在我们可以仅使用 CSS 进行文本省略/截断。让我们来考虑一下。

考虑这个标记(您可以在rwd.education/code/ch5/example_05-03/上在线查看此示例):

<p class="truncate">OK, listen up, I've figured out the key eternal happiness. All you need to do is eat lots of scones.</p>

但实际上我们希望将文本截断为 520px 宽。所以看起来像这样:

Text ellipsis

以下是使其发生的 CSS:

.truncate {
  width: 520px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: no-wrap;
}

提示

您可以在dev.w3.org/csswg/css-ui-3/上阅读有关text-overflow属性的规范。

每当内容的宽度超过定义的宽度时(如果它在一个灵活的容器内,宽度也可以设置为百分比,比如 100%),它将被截断。white-space: no-wrap属性/值对用于确保内容不会在周围的元素内换行。

创建水平滚动面板

希望您知道我是什么意思?水平滚动面板在 iTunes 商店和 Apple TV 上很常见,用于显示相关内容的面板(电影、专辑等)。当水平空间足够时,所有项目都是可见的。当空间有限时(考虑移动设备),面板可以从一侧滚动到另一侧。

滚动面板在现代 Android 和 iOS 设备上特别有效。如果您手头有一部现代 iOS 或 Android 设备,请在那上面看看下一个示例,同时在 Safari 或 Chrome 等桌面浏览器上查看:rwd.education/code/ch5/example_05-02/

我创建了一个 2014 年票房最高的电影的滚动面板。在 iPhone 上看起来是这样的:

创建水平滚动面板

我其实有点作弊。这种技术的关键是white-space属性,它实际上自 CSS 2.1 以来就存在了(www.w3.org/TR/CSS2/text.html)。然而,我打算将它与新的 Flexbox 布局机制一起使用,所以希望你不介意?

为了使这种技术起作用,我们只需要一个比其内容总和更窄的包装器,并在x轴上将其宽度设置为自动。这样,如果有足够的空间,它就不会滚动,但如果没有,它就会滚动。

.Scroll_Wrapper {
  width: 100%;
  white-space: nowrap;
  overflow-x: auto;
  overflow-y: hidden;
}

.Item {
  display: inline-flex;
}

通过使用white-space: nowrap,我们在说'当找到空白字符时,不要换行这些元素'。然后为了保持所有内容在一行中,我们将该容器的所有第一个子元素设置为内联显示。我们在这里使用inline-flex,但它也可以很容易地是内联、inline-blockinline-table

提示

::before 和::after 伪元素

如果查看示例代码,您会注意到::before伪元素用于显示项目的编号。如果使用伪元素,请记住::before::after要显示,它们必须有内容值,即使只是空格。当这些伪元素被显示时,它们就会分别像该元素的第一个和最后一个子元素一样行为。

为了使事情看起来更美观,我将尽可能隐藏滚动条。不幸的是,这些是特定于浏览器的,所以您需要手动添加它们(自动添加器工具不会添加它们,因为它们是专有属性)。我还将为 WebKit 浏览器(通常是 iOS 设备)添加触摸样式惯性滚动。现在更新的.Scroll_Wrapper规则看起来像这样:

.Scroll_Wrapper {
  width: 100%;
  white-space: nowrap;
  overflow-x: auto;
  overflow-y: hidden;
  /*Give us inertia style scrolling on WebKit based touch devices*/
  -webkit-overflow-scrolling: touch;
  /*Remove the scrollbars in supporting versions of IE*/
  -ms-overflow-style: none;
}

/*Stops the scrollbar appearing in WebKit browsers*/
.Scroll_Wrapper::-webkit-scrollbar {
  display: none;
}

当空间有限时,我们得到一个漂亮的可滚动水平面板。否则,内容就合适。

然而,这种模式有一些注意事项。首先,在撰写本文时,Firefox 没有允许隐藏滚动条的属性。其次,较旧的 Android 设备无法执行水平滚动(是的,真的)。因此,我倾向于通过特性检测来限定这种模式。我们将看看下面的工作原理。

在 CSS 中实现特性分支

当您构建响应式网页设计时,试图提供一个在每个设备上都能正常工作的单一设计,一个简单的事实是,您经常会遇到某些设备不支持的功能或技术。在这些情况下,您可能希望在您的 CSS 中创建一个分支;如果浏览器支持某个功能,则提供一段代码,如果不支持,则提供不同的代码。这是 JavaScript 中if/elseswitch语句处理的情况。

我们目前有两种可能的方法。一种完全基于 CSS,但浏览器实现较少,另一种只能在 JavaScript 库的帮助下实现,但支持范围更广。让我们依次考虑每种方法。

特性查询

在 CSS 中分叉代码的本机解决方案是使用'Feature Queries',这是 CSS 条件规则模块 3 级的一部分(www.w3.org/TR/css3-conditional/)。然而,目前,CSS 条件规则在 Internet Explorer(截至版本 11)和 Safari(包括 iOS 设备直到 iOS 8.1)中缺乏支持,因此支持几乎不普遍。

特性查询遵循与媒体查询类似的语法。考虑这个:

@supports (flashing-sausages: lincolnshire) {
  body {
    sausage-sound: sizzling;
    sausage-color: slighty-burnt;
    background-color: brown;
  }
}

这里的样式只有在浏览器支持flashing-sausages属性时才会应用。我非常确信没有浏览器会支持flashing-sausages功能(如果他们支持,我希望得到充分的认可),因此@supports块内的样式将不会应用。

让我们考虑一个更实际的例子。如果浏览器支持 Flexbox,则使用 Flexbox,否则使用其他布局技术。考虑这个例子:

@supports (display: flex) {
  .Item {
    display: inline-flex;
  }
}

@supports not (display: flex) {
  .Item {
    display: inline-block;
  }
}

在这里,我们为浏览器支持某个功能定义了一个代码块,为不支持该功能的情况定义了另一个代码块。如果浏览器支持@supports(是的,我意识到这很令人困惑),这种模式是可以的,但如果不支持,将不会应用任何这些样式。

如果您想覆盖不支持@supports的设备,最好先编写默认声明,然后在支持@support的声明之后编写特定的声明,这样如果支持@support存在,先前的规则将被覆盖,如果浏览器不支持@support,则将忽略@support块。因此,我们之前的示例可以重新设计为:

.Item {
  display: inline-block;
}

@supports (display: flex) {
  .Item {
    display: inline-flex;
  }
}

组合条件

您还可以结合条件。假设我们只想在支持 Flexbox 和pointer: coarse的情况下应用一些规则(如果您错过了,我们在第二章中介绍了'pointer'交互媒体特性,媒体查询-支持不同的视口)。可能会是这样:

@supports ((display: flex) and (pointer: coarse)) {
  .Item {
    display: inline-flex;
  }
}

这里我们使用了and关键字,但我们也可以使用or,或者用它来代替。例如,如果我们愿意在支持前两个属性/值组合或支持 3D 变换时应用样式:

@supports ((display: flex) and (pointer: coarse)) or (transform: translate3d(0, 0, 0)) {
  .Item {
    display: inline-flex;
  }
}

请注意,在上一个示例中,额外的括号将灵活和指针条件与变换条件分开。

不幸的是,正如我之前提到的,对@support的支持远非普遍。哎呀!一个响应式网页设计师该怎么办?不要担心,有一个很棒的 JavaScript 工具完全能够应对这一挑战。

Modernizr

@supports在浏览器中得到更广泛实现之前,我们可以使用一个名为 Modernizr 的 JavaScript 工具。目前,这是促进代码分叉的最健壮的方式。

当在 CSS 中需要分叉时,我尝试采用渐进增强方法。渐进增强意味着从简单的可访问代码开始;至少为功能较弱的设备提供功能设计的代码。然后逐渐增强更强大的设备的代码。

提示

我们将在第十章中更详细地讨论渐进增强,接近响应式网页设计

让我们看看如何使用 Modernizr 来促进渐进增强和分叉我们的 CSS 代码。

使用 Modernizr 进行特性检测

如果您是 Web 开发人员,很可能已经听说过 Modernizr,即使您可能尚未使用它。这是一个 JavaScript 库,您可以在页面中包含它来对浏览器进行特性测试。要开始使用 Modernizr,只需在页面的head部分包含指向下载文件的链接即可:

<script src="img/modernizr-2.8.3-custom.min.js"></script>

有了这个,当浏览器加载页面时,任何包含的测试都会运行。如果浏览器通过了测试,Modernizr 会方便地(对我们的目的)向根 HTML 标签添加相关的类。

例如,Mondernizr 完成其任务后,页面的 HTML 标签上的类可能如下所示:

<html class="js no-touch cssanimations csstransforms csstransforms3d csstransitions svg inlinesvg" lang="en">

在这种情况下,只测试了一些功能:动画,变换,SVG,内联 SVG 和对触摸的支持。有了这些类,代码可以被分叉,就像这样:

.widget {
  height: 1rem;
}

.touch .widget {
  height: 2rem;
}

在上面的例子中,小部件项目通常只有 1rem 高,但如果 HTML 上存在触摸类(感谢 Modernizr),那么小部件将有 2rem 高。

我们也可以改变逻辑:

.widget {
  height: 2rem;
}

.no-touch .widget {
  height: 1rem;
}

这样,我们将默认为项目高度为 2rem,并在出现no-touch类时调整高度。

无论您想如何构造结构,Modernizr 都提供了一种广泛支持的方式来分叉功能。当您想要使用transform3d等功能但仍为无法使用它的浏览器提供一个可用的替代品时,您会发现它特别有用。

提示

Modernizr 可以为您可能需要在其上分叉代码的大多数事物提供准确的测试,但并非所有事物都是如此。例如,溢出滚动通常很难进行准确测试。在设备类别不愉快的情况下,可能更有意义的是在不同的功能上分叉您的代码。例如,由于旧版 Android 版本难以进行水平滚动,您可能会使用no-svg进行分叉(因为 Android 2-2.3 也不支持 SVG)。

最后,您可能希望结合测试来创建自己的自定义测试。这有点超出了这里的范围,但如果这是您感兴趣的事情,请查看benfrain.com/combining-modernizr-tests-create-custom-convenience-forks/

新的 CSS3 选择器及其使用方法

CSS3 为在页面内选择元素提供了令人难以置信的能力。您可能认为这听起来并不那么花哨,但相信我,它会让您的生活变得更轻松,您会喜欢 CSS3 的!我最好对这个大胆的说法进行限定。

CSS3 属性选择器

您可能已经使用 CSS 属性选择器创建规则。例如,考虑以下规则:

img[alt] {
  border: 3px dashed #e15f5f;
}

这将针对标记中具有alt属性的任何图像标记。或者,假设我们想选择所有具有data-sausage属性的元素:

[data-sausage] {
  /* styles */
}

您只需要在方括号中指定属性。

提示

data-*类型的属性是在 HTML5 中引入的,用于提供一个无法通过任何其他现有机制合理存储的自定义数据的位置。这些的规范描述可以在www.w3.org/TR/2010/WD-html5-20101019/elements.html找到。

您还可以通过指定属性值来缩小范围。例如,考虑以下规则:

img[alt="sausages"] {
  /* Styles */
}

这将仅针对具有alt属性为sausages的图像。例如:

<img class="oscarMain" src="img/sausages.png" alt="sausages" />

到目前为止,这听起来像是“我们在 CSS2 中也可以做到这一点”。CSS3 给派对带来了什么?

CSS3 子字符串匹配属性选择器

CSS3 让我们根据其属性选择器的子字符串选择元素。听起来很复杂。其实不是!这三个选项是属性是否:

  • 以前缀开始

  • 包含一个实例

  • 以后缀结束

让我们看看它们是什么样子的。

“以...开始”的子字符串匹配属性选择器

考虑以下标记:

<img src="img/ace-film.jpg" alt="film-ace">
<img src="img/rubbish-film.jpg" alt="film-rubbish">

我们可以使用“以...开始”的子字符串匹配属性选择器来选择这两个图像,就像这样:

img[alt^="film"] {
    /* Styles */
}

所有这些中的关键字符都是^符号(该符号称为caret,尽管它也经常被称为“帽子”符号),意思是“以...开始”。因为两个alt标签都以film开头,我们的选择器选择了它们。

“包含一个实例”的子字符串匹配属性选择器

'包含一个实例'子字符串匹配属性选择器的语法如下:

[attribute*="value"] {
  /* Styles */
}

与所有属性选择器一样,如果需要,您可以将它们与类型选择器(引用实际使用的 HTML 元素)结合使用,尽管个人认为只有在必要时才这样做(以防您想要更改所使用的元素类型)。

让我们来试一个例子。考虑这个标记:

<p data-ingredients="scones cream jam">Will I get selected?</p>
We can select that element like this:
[data-ingredients*="cream"] {
  color: red;
}

所有这些中的关键字符是*符号,在这种情况下表示“包含”。

'以...开头'选择器在这个标记中不起作用,因为属性中的字符串并没有以'cream'开头。但它确实包含 'cream',因此'包含一个实例'子字符串属性选择器找到了它。

'以...结尾'子字符串匹配属性选择器

“以...结尾”子字符串匹配属性选择器的语法如下:

[attribute$="value"] {
  /* Styles */
}

一个例子应该有所帮助。考虑这个标记:

<p data-ingredients="scones cream jam">Will I get selected?</p>
<p data-ingredients="toast jam butter">Will I get selected?</p>
<p data-ingredients="jam toast butter">Will I get selected?</p>

假设我们只想选择data-ingredients属性中包含 scones、cream 和 jam 的元素(第一个元素)。我们不能使用'包含一个实例'(它会选择所有三个)或'以...开头'(它只会选择最后一个)子字符串属性选择器。但是,我们可以使用'以...结尾'子字符串属性选择器。

[data-ingredients$="jam"] {
color: red;
}

所有这些中的关键字符是$(美元)符号,表示“以...结尾”。

属性选择的陷阱

属性选择中有一个需要理解的“陷阱”:属性被视为单个字符串。考虑这个 CSS 规则:

[data-film^="film"] {
  color: red;
}

也许让你惊讶的是,它不会选择这个,即使属性中的一个单词以film开头:

<span data-film="awful moulin-rouge film">Moulin Rouge is dreadful</span>

这是因为这里的data-film属性不是以film开头的,在这种情况下它是以 awful 开头的(如果你看过《红磨坊》,你会知道它一开始就很糟糕,而且永远不会好转)。

除了我们刚才看到的子字符串匹配选择器,还有几种解决方法。您可以使用空格分隔选择器(注意波浪线符号),它一直支持到 Internet Explorer 7:

[data-film~="film"] {
  color: red;
}

您可以选择整个属性:

[data-film="awful moulin-rouge film"] {
  color: red;
}

或者,如果您只想根据属性中的一些字符串的存在来进行选择,您可以连接一对(或者需要的数量)'包含一个实例'子字符串属性选择器:

[data-film*="awful"][data-film*="moulin-rouge"] {
  color: red;
}

在这方面没有“正确”的做法,这实际上取决于您尝试选择的字符串的复杂性。

属性选择器允许您选择以数字开头的 ID 和类

在 HTML5 之前,以数字开头的 ID 或类名是无效的标记。HTML5 取消了这一限制。在涉及 ID 时,仍然有一些事情需要记住。ID 名称中不应该有空格,并且在页面上必须是唯一的。有关更多信息,请访问www.w3.org/html/wg/drafts/html/master/dom.html

尽管在 HTML5 中可以以数字开头命名 ID 和类值,但 CSS 仍限制您不能使用以数字开头的 ID 和类选择器(www.w3.org/TR/CSS21/syndata.html)。

幸运的是,我们可以通过使用属性选择器轻松解决这个问题。例如,[id="10"]

CSS3 结构伪类

CSS3 使我们能够更有力地选择基于 DOM 结构的元素。

让我们考虑一个常见的设计处理;我们正在为较大的视口设计导航栏,并且希望除了最后一个链接之外的所有链接都在左侧。

从历史上看,我们需要通过向最后一个链接添加类名来解决这个问题,以便我们可以选择它,就像这样:

<nav class="nav-Wrapper">
  <a href="/home" class="nav-Link">Home</a>
  <a href="/About" class="nav-Link">About</a>
  <a href="/Films" class="nav-Link">Films</a>
  <a href="/Forum" class="nav-Link">Forum</a>
  <a href="/Contact-Us" class="nav-Link nav-LinkLast">Contact Us</a>
</nav>

这本身可能会有问题。例如,有时,让内容管理系统向最后一个列表项添加类可能会非常困难。幸运的是,在这种情况下,这不再是一个问题。我们可以使用 CSS3 结构伪类来解决这个问题以及许多其他问题。

:last-child 选择器

CSS 2.1 已经有了一个适用于列表中第一项的选择器:

div:first-child {
  /* Styles */
}

然而,CSS3 添加了一个选择器,也可以匹配最后一个:

div:last-child {
  /* Styles */
}

让我们看看那个选择器如何解决我们之前的问题:

@media (min-width: 60rem) {
  .nav-Wrapper {
    display: flex;
  }
  .nav-Link:last-child {
    margin-left: auto;
  }
}

还有一些有用的选择器,用于当某物是唯一的项目时::only-child和唯一的类型::only-of-type

nth-child 选择器

nth-child选择器让我们解决更困难的问题。使用与之前相同的标记,让我们考虑一下 nth-child 选择器如何允许我们选择列表中的任何链接。

首先,选择每隔一个列表项怎么样?我们可以这样选择奇数项:

.nav-Link:nth-child(odd) {
  /* Styles */
}

或者,如果您想选择偶数:

.nav-Link:nth-child(even) {
  /* Styles */
}

理解 nth 规则的作用

对于初学者来说,基于 nth 的选择器可能看起来很吓人。然而,一旦你掌握了逻辑和语法,你会惊讶于你可以用它们做什么。让我们来看看。

CSS3 用几个基于 nth 的规则给了我们令人难以置信的灵活性:

  • nth-child(n)

  • nth-last-child(n)

  • nth-of-type(n)

  • nth-last-of-type(n)

我们已经看到在基于 nth 的表达式中可以使用(奇数)或(偶数)值,但(n)参数还可以以另外两种方式使用:

作为整数;例如,:nth-child(2)将选择第二个项目

作为数字表达式;例如,:nth-child(3n+1)将从 1 开始,然后选择每三个元素

整数属性很容易理解,只需输入您想要选择的元素编号。

选择器的数字表达式版本对于普通人来说可能有点令人困惑。如果数学对你来说很容易,我为这一部分道歉。对于其他人,让我们来分解一下。

分解数学

让我们考虑页面上的 10 个 span(您可以通过查看example_05-05来玩弄这些):

<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>

默认情况下,它们的样式将是这样的:

span {
  height: 2rem;
  width: 2rem;
  background-color: blue;
  display: inline-block;
}

正如你所想象的,这给了我们一行中的 10 个方块:

分解数学

好吧,让我们看看如何使用基于 nth 的选择来选择不同的项目。

为了实用性,当考虑括号内的表达式时,我从右边开始。所以,例如,如果我想弄清楚(2n+3)会选择什么,我从最右边的数字开始(这里的三表示从左边数第三个项目),并且知道它将从那一点开始选择每第二个元素。因此添加这个规则:

span:nth-child(2n+3) {
  color: #f90;
  border-radius: 50%;
}

在浏览器中的结果:

分解数学

如您所见,我们的 nth 选择器目标是第三个列表项,然后是之后的每第二个列表项(如果有 100 个列表项,它将继续选择每第二个列表项)。

如何从第二个项目开始选择所有内容?虽然你可以写成:nth-child(1n+2),但实际上你不需要第一个数字 1,因为除非另有说明,n 等于 1。因此,我们可以只写:nth-child(n+2)。同样,如果我们想选择每三个元素,而不是写成:nth-child(3n+3),我们可以只写:nth-child(3n),因为每三个项目都会从第三个项目开始,而不需要明确说明。表达式也可以使用负数,例如,:nth-child(3n-2)从-2 开始,然后选择每三个项目。

您还可以更改方向。默认情况下,一旦找到选择的第一部分,随后的选择将沿着 DOM 中的元素向下(因此在我们的示例中从左到右)。但是,您可以用减号来颠倒这一点。例如:

span:nth-child(-2n+3) {
  background-color: #f90;
  border-radius: 50%;
}

这个例子再次找到第三个项目,然后沿着相反的方向选择每两个元素(在 DOM 树中向上,因此在我们的示例中从右到左):

分解数学

希望基于 nth 的表达式现在完全合乎逻辑了?

nth-childnth-last-child的区别在于nth-last-child变体是从文档树的相反端起作用的。例如,:nth-last-child(-n+3)从末尾开始的 3 开始,然后选择其后的所有项目。这是浏览器中该规则给我们的内容:

分解数学

最后,让我们考虑:nth-of-type:nth-last-of-type。虽然前面的例子计算任何类型的子元素(始终记住nth-child选择器会选择同一 DOM 级别的所有子元素,而不管类),:nth-of-type:nth-last-of-type让您可以具体指定要选择的项目类型。考虑以下标记(example_05-06):

<span class="span-class"></span>
<span class="span-class"></span>
<span class="span-class"></span>
<span class="span-class"></span>
<span class="span-class"></span>
<div class="span-class"></div>
<div class="span-class"></div>
<div class="span-class"></div>
<div class="span-class"></div>
<div class="span-class"></div>

如果我们使用了选择器:

.span-class:nth-of-type(-2n+3) {
  background-color: #f90;
  border-radius: 50%;
}

尽管所有元素都具有相同的span-class,但实际上我们只会针对span元素(因为它们是首选类型)。这是被选中的内容:

分解数学

我们将看到 CSS4 选择器如何解决这个问题。

提示

CSS3 不像 JavaScript 和 jQuery 那样计数!

如果您习惯使用 JavaScript 和 jQuery,您会知道它从 0 开始计数(基于零索引)。例如,如果在 JavaScript 或 jQuery 中选择元素,整数值 1 实际上是第二个元素。然而,CSS3 从 1 开始,因此值为 1 的是它匹配的第一个项目。

响应式网页设计中的基于 nth 的选择

最后,让我们考虑一个真实的响应式网页设计问题,以及我们如何使用基于 nth 的选择来解决它。

还记得example_05-02中的水平滚动面板吗?让我们考虑一下在水平滚动不可能的情况下它可能会是什么样子。因此,使用相同的标记,让我们将 2014 年票房前十的电影变成网格。对于某些视口,网格将只有两个项目宽,随着视口的增加,我们显示三个项目,并且在更大的尺寸上,我们仍然显示四个。然而,这里有一个问题。无论视口大小如何,我们都希望防止底部的任何项目具有底部边框。您可以在example_05-09中查看此代码。

这是四个宽项目的外观:

响应式网页设计中的基于 nth 的选择

看到下面两个项目底部的烦人边框了吗?这就是我们需要移除的。但是,我希望有一个强大的解决方案,这样如果底部行还有另一个项目,边框也会被移除。现在,因为在不同的视口上每行的项目数量不同,我们还需要在不同的视口上更改基于 nth 的选择。为了简洁起见,我将向您展示匹配每行四个项目的选择(较大的视口)。您可以查看代码示例,以查看在不同视口上修改后的选择。

@media (min-width: 55rem) {
  .Item {
    width: 25%; 
  }
  /*  Get me every fourth item and of those, only ones that are in the last four items */
  .Item:nth-child(4n+1):nth-last-child(-n+4),
  /* Now get me every one after that same collection too. */
  .Item:nth-child(4n+1):nth-last-child(-n+4) ~ .Item {
    border-bottom: 0;
  }
}

注意

您会注意到我们在链接基于 nth 的伪类选择器。重要的是要理解,第一个不会过滤下一个的选择,而是元素必须匹配每个选择。对于我们之前的例子,第一个元素必须是四个中的第一个项目,并且也必须是最后四个中的一个。

很好!由于基于 nth 的选择,我们有一套防御性规则,可以删除底部边框,而不管视口大小或我们显示的项目数量如何。

响应式网页设计中的基于 nth 的选择

否定(:not)选择器

另一个方便的选择器是否定伪类选择器。这用于选择除其他东西之外的所有内容。考虑这个:

<div class="a-div"></div>
<div class="a-div"></div>
<div class="a-div"></div>
<div class="a-div not-me"></div>
<div class="a-div"></div>

然后这些样式:

div {
  display: inline-block;
  height: 2rem;
  width: 2rem;
  background-color: blue;
}

.a-div:not(.not-me) {
  background-color: orange;
  border-radius: 50%;
}

我们的最终规则将使具有.a-div类的每个元素变为橙色和圆形,但div也具有.not-me类除外。您可以在代码示例的example_05-07文件夹中找到该代码(请记住,您可以在rwd.education/上找到所有代码示例)。

提示

到目前为止,我们主要看了所谓的结构伪类(关于这方面的完整信息可在www.w3.org/TR/selectors/找到)。然而,CSS3 还有许多其他选择器。如果你正在开发一个 Web 应用程序,值得查看完整的 UI 元素状态伪类列表(www.w3.org/TR/selectors/),因为它们可以帮助你根据某些东西是否被选中来定位规则。

空的(:empty)选择器

我遇到过这样的情况,我有一个元素,在里面包含一些填充,并且动态插入内容。有时它会有内容,有时没有。问题是,当它不包含内容时,我仍然看到填充。考虑一下example_05-08中的 HTML 和 CSS:

<div class="thing"></div>
.thing {
  padding: 1rem;
  background-color: violet;
}

在那个div中没有任何内容,我仍然看到background-color。幸运的是,我们可以很容易地隐藏它,就像这样:

.thing:empty {
  display: none;
}

然而,要小心:empty选择器。例如,你可能认为这是空的:

<div class="thing"> </div>

不是!看看里面的空白。空白不是没有空间!

然而,要让事情更加混乱,要知道评论不会影响元素是否有空白或不。例如,这仍然被认为是空的:

<div class="thing"><!--I'm empty, honest I am--></div>

提示

伪元素的修正

伪元素自 CSS2 以来就存在,但 CSS3 规范对它们的使用语法进行了非常轻微的修订。为了提醒你,直到现在,p:first-line会定位<p>标签中的第一行。或者p:first-letter会定位第一个字母。然而,CSS3 要求我们用双冒号来区分这些伪元素和伪类(比如nth-child())。因此,我们应该写成p::first-letter。然而,需要注意的是,Internet Explorer 8 及更低版本不理解双冒号语法,它们只理解单冒号语法。

无论视口如何,都对:first-line做一些事情

关于:first-line伪元素,你可能会发现一个特别方便的地方是它是特定于视口的。例如,如果我们写下以下规则:

p::first-line {
  color: #ff0cff;
}

正如你所期望的,第一行呈现为一种可怕的粉色。然而,在不同的视口上,它呈现为不同的文本选择。

因此,不需要改变标记,使用响应式设计,有一种方便的方法可以使文本的第一行(如浏览器呈现的,而不是在标记中显示的)与其他行不同。

CSS 自定义属性和变量

由于 CSS 预处理器的流行,CSS 开始获得一些更多的“编程”特性。其中之一是自定义属性。它们更常被称为变量,尽管这不一定是它们唯一的用例。你可以在dev.w3.org/csswg/css-variables/找到完整的规范。需要警告的是,截至 2015 年初,浏览器实现还很少(只有 Firefox)。

CSS 自定义属性允许我们在样式表中存储信息,然后可以在该样式表中利用或者通过 JavaScript 进行操作。一个明显的用例是存储字体系列名称,然后引用它。以下是我们创建自定义属性的方法:

:root {
  --MainFont: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

在这里,我们使用:root伪类将自定义属性存储在文档根中(尽管你可以将它们存储在任何你喜欢的规则中)。

提示

:root伪类总是引用文档结构中最顶层的父元素。在 HTML 文档中,这总是 HTML 标签,但对于 SVG 文档(我们在第七章中看到 SVG,使用 SVG 实现分辨率独立性),它将引用不同的元素。

自定义属性总是以两个破折号开始,然后是自定义名称,然后是它的结束,就像 CSS 中的其他属性一样;用一个冒号表示。

我们可以用var()符号引用该值。就像这样:

.Title {
  font-family: var(--MainFont);
}

你显然可以以这种方式存储尽可能多的自定义属性。这种方法的主要好处是,你可以更改变量内的值,而每个使用该变量的规则都会得到新的值,而无需直接修改它们。

预计将来这些属性可能会被 JavaScript 解析和利用。关于这种疯狂的东西,你可能会对新的 CSS 扩展模块感兴趣:

dev.w3.org/csswg/css-extensions/

CSS calc

你有多少次试图编写布局并想到类似“它需要是父元素宽度的一半减去 10 像素”这样的东西?这在响应式网页设计中特别有用,因为我们永远不知道将查看我们网页的屏幕大小。幸运的是,CSS 现在有了一种方法来做到这一点。它被称为calc()函数。以下是 CSS 中的示例:

.thing {
  width: calc(50% - 10px);
}

加法、减法、除法和乘法都受支持,因此可以解决以前无法使用 JavaScript 解决的一系列问题。

浏览器支持相当不错,但一个值得注意的例外是 Android 4.3 及以下版本。请阅读规范:www.w3.org/TR/css3-values/

CSS 4 级选择器

CSS 选择器 4 级(最新版本是 2014 年 12 月 14 日的编辑草案,dev.w3.org/csswg/selectors-4/)中规定了许多新的选择器类型。然而,就在我写这篇文章的时候,浏览器中还没有它们的实现。因此,我们只看一个例子,因为它们可能会发生变化。

关系伪类选择器来自“逻辑组合”(dev.w3.org/csswg/selectors-4/)部分的最新草案。

:has 伪类

这个选择器采用这种格式:

a:has(figcaption) {
  padding: 1rem;
}

如果a标签包含figcaption,这将为任何a标签添加填充。你也可以与否定伪类结合来反转选择:

a:not(:has(figcaption)) {
  padding: 1rem;
}

如果a标签不包含figcaption元素,这将添加填充。

我要诚实地说,现在在这份草案中,并没有太多新的选择器让我感到兴奋。但谁知道他们在开始在浏览器中使用之前会想出什么呢?

响应式视口百分比长度(vmax、vmin、vh、vw)

现在我们改变一下方向。我们已经看过如何在响应式世界中选择项目。但是如何调整它们的大小呢?CSS 值和单位模块 3 级(www.w3.org/TR/css3-values/)引入了视口相关单位。这对于响应式网页设计非常有用,因为每个单位都是视口的百分比长度:

  • vw 单位(视口宽度)

  • vh 单位(视口高度)

  • vmin 单位(视口最小值;等于 vw 或 vh 中较小的一个)

  • vmax(视口最大值;等于 vw 或 vh 中较大的一个)

浏览器支持也不错(caniuse.com/)。

想要一个模态窗口,它的高度是浏览器高度的 90%?这很容易:

.modal {
  height: 90vh;
}

提示

视口相关单位虽然很有用,但一些浏览器的实现方式很奇怪。例如,iOS 8 中的 Safari 在你从页面顶部滚动时会改变可视屏幕区域(它会缩小地址栏),但不会对报告的视口高度进行任何更改。

然而,当与字体结合时,也许可以找到更多这些单位的实用性。例如,现在可以轻松创建根据视口大小而调整大小的文本。

现在,我可以立即向你展示。但是,我想使用一个不同的字体,这样无论你是在 Windows、Mac 还是 Linux 上查看示例,我们都能看到相同的东西。

好吧,我要诚实地说,这是一个廉价的手段,让我记录一下我们如何在 CSS3 中使用 Web 字体。

网络排版

多年来,网络一直不得不使用一些无聊的“网络安全”字体。当设计中必不可少的一些花哨的排版时,有必要用图形元素替代它,并使用文本缩进规则将实际文本从视口中移开。哦,快乐!

在这一过程中,还有一些创新的方法可以在页面上添加花哨的排版。sIFR(www.mikeindustries.com/blog/sifr/)和 Cufón(cufon.shoqolate.com/generate/)分别使用 Flash 和 JavaScript 重新制作文本元素,使其显示为它们原本打算的字体。幸运的是,CSS3 提供了一种现在已经准备好大放异彩的自定义网络排版的方法。

@font-face CSS 规则

@font-face CSS 规则自 CSS2 以来就存在(但随后在 CSS 2.1 中消失了)。甚至 Internet Explorer 4 部分支持它(不是吗)!那么,当我们应该谈论 CSS3 时,它在这里做什么呢?

事实证明,@font-face被重新引入到了 CSS3 字体模块中(www.w3.org/TR/css3-fonts)。由于在网络上使用字体的历史法律泥潭,直到最近几年,它才开始作为网络排版的事实解决方案而受到严重关注。

与网页上涉及资产的任何内容一样,没有单一的文件格式。就像图像可以是 JPG、PNG、GIF 和其他格式一样,字体也有自己的一套可供选择的格式。嵌入式开放类型(扩展名为.eot的文件)字体是 Internet Explorer(而不是其他任何人)的首选。其他人更喜欢更常见的 TrueType(.ttf文件扩展名),同时还有 SVG 和 Web 开放字体格式(.woff / .woff2扩展名)。

现在,需要为不同的浏览器实现提供相同字体的多个文件版本。

然而,好消息是为每个浏览器添加每种自定义字体格式很容易。让我们看看如何!

使用@font-face 实现网络字体

CSS 提供了一个@font-face“at-rule”来引用在线字体,然后可以用于显示文本。

现在有许多查看和获取网络字体的好资源,包括免费和付费的。我个人最喜欢免费字体的是 Font Squirrel(www.fontsquirrel.com/),尽管谷歌也提供免费网络字体,最终使用@font-face规则提供(www.google.com/webfonts)。还有来自 Typekit(www.typekit.com/)和 Font Deck(www.fontdeck.com/)的优秀付费服务。

在这个练习中,我将下载 Roboto。它是后来 Android 手机使用的字体,所以如果你有其中之一,它会很熟悉。否则,你只需要知道它是一种可爱的界面字体,设计用于在小屏幕上非常易读。你可以在www.fontsquirrel.com/fonts/roboto上自己获取它。

注意

如果可以下载特定于您打算使用的语言的字体的“子集”,请这样做。这意味着由于不包含您不打算使用的语言的字形,结果文件大小将小得多。

下载了@font-face套件后,打开 ZIP 文件,里面有不同 Roboto 字体的文件夹。我选择了 Roboto Regular 版本,在该文件夹中,字体以各种文件格式(WOFF、TTF、EOT 和 SVG)存在,还有一个包含字体堆栈的stylesheet.css文件。例如,Roboto Regular 的规则如下:

@font-face {
    font-family: 'robotoregular';
    src: url('Roboto-Regular-webfont.eot');
    src: url('Roboto-Regular-webfont.eot?#iefix') format('embedded-opentype'),
         url('Roboto-Regular-webfont.woff') format('woff'),
         url('Roboto-Regular-webfont.ttf') format('truetype'),
         url('Roboto-Regular-webfont.svg#robotoregular') format('svg');
    font-weight: normal;
    font-style: normal;
}

就像供应商前缀的工作方式一样,浏览器将应用来自该属性列表的样式(如果适用,较低的属性优先),并忽略它不理解的样式。这样,无论使用什么浏览器,都应该有一个可以使用的字体。

现在,尽管这段代码对于复制和粘贴的粉丝来说非常棒,但重要的是要注意字体存储的路径。例如,我倾向于从 ZIP 文件中复制字体并将其存储在一个名为fonts的文件夹中,该文件夹与我的css文件夹处于同一级别。因此,由于我通常将这个字体堆栈规则复制到我的主样式表中,我需要修改路径。因此,我的规则变成了:

@font-face {
    font-family: 'robotoregular';
    src: url('../fonts/Roboto-Regular-webfont.eot');
    src: url('../fonts/Roboto-Regular-webfont.eot?#iefix') format('embedded-opentype'),
         url('../fonts/Roboto-Regular-webfont.woff') format('woff'),
         url('../fonts/Roboto-Regular-webfont.ttf') format('truetype'),
         url('../fonts/Roboto-Regular-webfont.svg#robotoregular') format('svg');
    font-weight: normal;
    font-style: normal;
}

然后只需设置正确的字体和重量(如果需要)即可为相关的样式规则设置正确的字体。看看example_05-10,它与example_05-09的标记相同,我们只是将这个font-family声明为默认值:

body {
  font-family: robotoregular;
}

网络字体的一个额外好处是,如果合成文件使用与代码中使用的相同字体,您可以直接从合成文件中插入大小。例如,如果 Photoshop 中的字体大小为 24px,我们可以直接插入该值,或者将其转换为更灵活的单位,如 REM(假设根字体大小为 16px,24/16=1.5rem)。

然而,正如我之前提到的,现在我们可以使用视口相对大小。我们可以在这里使用它们,以便根据视口空间的大小来调整文本的大小。

body {
  font-family: robotoregular;
  font-size: 2.1vw;
}

@media (min-width: 45rem) {
  html,
  body {
    max-width: 50.75rem;
    font-size: 1.8vw;
  }
}

@media (min-width: 55rem) {
  html,
  body {
    max-width: 78.75rem;
    font-size: 1.7vw;
  }
}

如果您在浏览器中打开该示例并调整视口大小,您会看到只需几行 CSS,我们就可以使文本按照可用空间进行缩放。美妙!

关于自定义@font-face 排版和响应式设计的说明

网络排版的@font-face方法总体上非常好。在使用响应式设计技术时需要注意的唯一注意事项是字体文件的大小。例如,如果设备需要渲染我们示例的 Roboto Regular 的 SVG 字体格式,它需要额外获取 34 KB,而使用标准的网络安全字体(如 Arial)则不需要。我们在示例中使用了英文子集来减小文件大小,但这并不总是一个选项。如果您希望获得最佳的网站性能,请务必检查自定义字体的大小,并谨慎使用。

新的 CSS3 颜色格式和 alpha 透明度

到目前为止,在本章中,我们已经看到 CSS3 如何赋予我们新的选择能力,并使我们能够向设计中添加自定义排版。现在,我们将看看 CSS3 允许我们以前根本不可能的方式处理颜色的方法。

首先,CSS3 提供了两种声明颜色的新方法:RGB 和 HSL。此外,这两种格式使我们能够在它们旁边使用 alpha 通道(分别为 RGBA 和 HSLA)。

RGB 颜色

红色、绿色和蓝色RGB)是一个存在几十年的着色系统。它通过为颜色的红色、绿色和蓝色分量定义不同的值来工作。例如,红色可以在 CSS 中定义为十六进制值#fe0208

.redness {
  color: #fe0208;
}

提示

关于如何更直观地理解十六进制值的出色文章,我可以推荐 Smashing Magazine 上的这篇博文:www.smashingmagazine.com/2012/10/04/the-code-side-of-color/

然而,使用 CSS3,该颜色同样可以用 RGB 值来描述:

.redness {
  color: rgb(254, 2, 8);
}

大多数图像编辑应用程序在其颜色选择器中以 HEX 和 RGB 值显示颜色。例如,Photoshop 的颜色选择器显示了 R、G 和 B 框,显示了每个通道的值。例如,R 值可能是 254,G 值为 2,B 值为 8。这很容易转换为 CSS 的color属性值。在 CSS 中,定义颜色模式(例如 RGB)后,红色、绿色和蓝色的值以逗号分隔的顺序放在括号内(就像我们在之前的代码中所做的那样)。

HSL 颜色

除了 RGB,CSS3 还允许我们将颜色值声明为色调、饱和度和亮度HSL)。

提示

HSL 不同于 HSB!

不要犯错误认为图像编辑应用程序(如 Photoshop)中的颜色选择器中显示的色调、饱和度和亮度HSB)值与 HSL 相同-它并不相同!

HSL 如此令人愉快的原因在于,根据给定的值,很容易理解将表示的颜色。例如,除非你是某种颜色选择忍者,否则我敢打赌你无法立即告诉我 rgb(255, 51, 204)是什么颜色?没有人?我也不知道。然而,告诉我 hsl(315, 100%, 60%)的值,我可以猜测它在品红色和红色之间(实际上是一种节日粉色)。我怎么知道的?很简单。

HSL 基于 360°的色轮。它看起来像这样:

HSL 颜色

HSL 颜色定义中的第一个数字代表色调。从我们的色轮上可以看到,黄色在 60°,绿色在 120°,青色在 180°,蓝色在 240°,品红色在 300°,最后红色在 360°。因此,如上述 HSL 颜色的色调为 315,很容易知道它将在品红色(在 300°)和红色(在 360°)之间。

HSL 定义中的后两个值是饱和度和亮度,以百分比表示。这些只是改变基本色调。要获得更饱和或“色彩丰富”的外观,请在第二个值中使用更高的百分比。控制亮度的最终值可以在 0%至 100%之间变化,0%表示黑色,100%表示白色。

因此,一旦您将颜色定义为 HSL 值,仅通过改变饱和度和亮度百分比就可以轻松创建变化。例如,我们的红色可以用以下 HSL 值来定义:

.redness {
  color: hsl(359, 99%, 50%);
}

如果我们想要制作稍暗一些的颜色,我们可以使用相同的 HSL 值,仅改变亮度(最终值)百分比值:

.darker-red {
  color: hsl(359, 99%, 40%);
}

总之,如果你能记住“年轻人可以是混乱的顽童”(或者你想记住的任何其他助记符)的话,你就能大致写出 HSL 颜色值,而不需要使用取色器,并且还可以创建变化。在办公室派对上向 Ruby、Node 和.NET 的聪明人展示这个技巧,赢得一些快速的赞赏吧!

Alpha 通道

到目前为止,你可能会想为什么我们要使用 HSL 或 RGB 而不是多年来一直在使用的可靠的 HEX 值。HSL 和 RGB 与 HEX 的不同之处在于它们允许使用 alpha 透明通道,因此元素下面的东西可以透过来。

HSLA 颜色声明在语法上类似于标准的 HSL 规则。但是,除此之外,您必须将值声明为hsla(而不仅仅是hsl),并添加一个额外的不透明度值,以十进制值表示,介于 0(完全透明)和 1(完全不透明)之间。例如:

.redness-alpha {
  color: hsla(359, 99%, 50%, .5);
}

RGBA 语法遵循与 HSLA 等效项相同的约定:

.redness-alpha-rgba {
  color: rgba(255, 255, 255, 0.8);
}

提示

为什么不直接使用不透明度?

CSS3 还允许使用 opacity 声明设置元素的不透明度。值在 0 和 1 之间以十进制增量设置(例如,设置为 0.1 的不透明度为 10%)。然而,这与 RGBA 和 HSLA 不同之处在于,在元素上设置不透明度值会影响整个元素。而使用 HSLA 或 RGBA 设置值与此同时允许元素的特定部分具有 alpha 层。例如,一个元素可以具有背景的 HSLA 值,但其中的文本为纯色。

使用 CSS 颜色模块 4 进行颜色操作

尽管在非常早期的规范阶段,但在不久的将来,通过使用color()函数在 CSS 中进行颜色操作应该是可能的。

在有广泛的浏览器支持之前,最好由 CSS 预处理器/后处理器来处理这种情况(给自己一个忠告,立即买一本关于这个主题的书;我推荐那位了不起的人 Ben Frain 的《面向设计师的 Sass 和 Compass》)。

您可以在dev.w3.org/csswg/css-color-4/上关注 CSS 颜色模块 4 的进展。

总结

在本章中,我们已经学会了如何使用 CSS3 的新选择器轻松地选择几乎我们在页面上需要的任何东西。我们还看到了如何可以快速制作响应式的列和滚动面板,以解决常见且令人讨厌的问题,比如长 URL 换行。我们现在也了解了 CSS3 的新颜色模块,以及如何使用 RGB 和 HSL 应用颜色,包括透明的 alpha 层,以产生出色的美学效果。

在本章中,我们还学会了如何使用@font-face规则将网络排版添加到设计中,最终摆脱了单调的网络安全字体的束缚。尽管有了所有这些新特性和技术,我们只是触及了 CSS3 的潜力表面。让我们继续前进,看看 CSS3 如何通过文本阴影、盒子阴影、渐变和多重背景等方式,使响应式设计尽可能快速、高效和易于维护。

第六章:使用 CSS3 创建令人惊叹的美学效果

CSS3 的美学特性在响应式设计中非常有用,因为使用 CSS3 可以在许多情况下替换图像。这可以节省时间,使您的代码更易维护和灵活,并且减少了最终用户的页面“重量”。即使在典型的固定宽度桌面设计中,这些好处也是有用的,但在响应式设计中更加重要,因为在这些情况下使用 CSS 可以轻松地在不同的视口上创建不同的美学效果。

在本章中,我们将涵盖:

  • 如何使用 CSS3 创建文本阴影

  • 如何使用 CSS3 创建框阴影

  • 如何使用 CSS3 创建渐变背景

  • 如何使用 CSS3 创建多个背景

  • 使用 CSS3 背景渐变创建图案

  • 如何使用媒体查询实现高分辨率背景图像

  • 如何使用 CSS 滤镜(以及它们的性能影响)

让我们深入了解。

提示

供应商前缀

在实现实验性的 CSS 时,只需记住通过工具添加相关的供应商前缀,而不是手动添加。这可以确保最广泛的跨浏览器兼容性,并且也可以避免添加不再需要的前缀。在大多数章节中,我都提到了 Autoprefixer(github.com/postcss/autoprefixer),因为在撰写本文时,我认为它是最好的工具。

使用 CSS3 创建文本阴影

CSS3 中最广泛实现的功能之一是text-shadow。与@font-face一样,它曾经存在过,但在 CSS 2.1 中被删除了。幸运的是,它现在又回来了,并得到了广泛支持(适用于所有现代浏览器和 Internet Explorer 9 及以上版本)。让我们来看一下基本的语法:

.element {
    text-shadow: 1px 1px 1px #ccc;
}

请记住,简写规则中的值总是向右和向下排列(或者如果您喜欢,可以认为是顺时针)。因此,第一个值是阴影向右的量,第二个是向下的量,第三个值是模糊的量(阴影在消失之前移动的距离),最后一个值是颜色。

可以使用负值来实现左侧和上方的阴影。例如:

.text {
    text-shadow: -4px -4px 0px #dad7d7;
}

颜色值不需要定义为十六进制值。它同样可以是 HSL(A)或 RGB(A):

text-shadow: 4px 4px 0px hsla(140, 3%, 26%, 0.4);

但请记住,浏览器必须同时支持 HSL/RGB 颜色模式和text-shadow才能呈现效果。

您还可以使用任何其他有效的 CSS 长度单位来设置阴影值,例如 em、rem、ch、rem 等。就个人而言,我很少使用 em 或 rem 单位来设置text-shadow值。因为这些值总是非常低,使用 1px 或 2px 通常在所有视口上看起来都不错。

由于媒体查询,我们还可以轻松地在不同的视口尺寸下移除文本阴影。关键在于 none 值:

.text {
    text-shadow: .0625rem .0625rem 0 #bfbfbf;
}
@media (min-width: 30rem) {
    .text {
        text-shadow: none;
    }
}

提示

另外,值得知道的是,在 CSS 中,如果一个值以零开头,比如 0.14s,就不需要写前导零:.14s 和 0.14s 是完全相同的。

在不需要时省略模糊值

如果text-shadow不需要添加模糊,可以从声明中省略该值,例如:

.text {
    text-shadow: -4px -4px #dad7d7;
}

这是完全有效的。如果没有声明第三个值,浏览器会假定前两个值是偏移量。

多个文本阴影

可以通过逗号分隔两个或多个阴影来添加多个文本阴影。例如:

.multiple {
    text-shadow: 0px 1px #fff,4px 4px 0px #dad7d7;
}

此外,由于 CSS 对空白字符宽容,如果有助于可读性,您可以像这样布置值:

.text { 
    font-size: calc(100vmax / 40); /* 100 of vh or vw, whichever is larger divided by 40 */
    text-shadow: 
    3px 3px #bbb, /* right and down */
    -3px -3px #999; /* left and up */
}

提示

您可以在www.w3.org/TR/css3-text/上阅读text-shadow属性的 W3C 规范。

框阴影

框阴影允许您在应用到元素的外部或内部创建一个框形阴影。一旦了解了文本阴影,框阴影就很简单了;基本上,它们遵循相同的语法:水平偏移、垂直偏移、模糊、扩展(我们稍后会讨论扩展),以及颜色。

只需要四个可能的长度值中的两个(在没有最后两个的情况下,颜色的值定义阴影颜色,模糊半径使用零值)。让我们看一个简单的例子:

.shadow {
    box-shadow: 0px 3px 5px #444;
}

默认的 box-shadow 设置在元素的外部。另一个可选的关键字,inset,允许在元素内部应用 box-shadow。

内部阴影

box-shadow 属性也可以用来创建内部阴影。语法与普通的盒子阴影相同,只是值以关键字 inset 开头:

.inset {
    box-shadow: inset 0 0 40px #000;
}

一切都像以前一样运作,但声明的inset部分指示浏览器在内部设置效果。如果你看 example_06-01,你会看到每种类型的例子:

内部阴影

多个阴影

像 text-shadow 一样,您可以应用多个 box-shadow。用逗号分隔 box-shadow,它们按照从底部到顶部(最后到第一个)的顺序应用,如它们在列表中列出的那样。通过想到规则(在代码中)中最接近顶部的声明在浏览器中显示时出现在顺序的“顶部”,来提醒自己顺序。与 text-shadow 一样,您可能会发现使用空格在视觉上堆叠不同的 box-shadow 很有用:

box-shadow: inset 0 0 30px hsl(0, 0%, 0%), 
            inset 0 0 70px hsla(0, 97%, 53%, 1);

提示

在代码中堆叠更长的、多个值,一个在另一个下面,当使用版本控制系统时有一个额外的好处;它使得在“diff”两个文件版本时更容易发现差异。这就是我将选择器组堆叠在一起的主要原因。

理解扩展

说实话,多年来我并没有真正理解 box-shadow 的扩展值到底是做什么的。我认为“扩展”这个名字并不有用。把它想象成一个偏移更有帮助。让我解释一下。

看看 example_06-02 中左边的盒子。这是应用了标准的 box-shadow。右边的盒子应用了负的扩展值。它是用第四个值设置的。这是相关的代码:

.no-spread {
  box-shadow: 0 10px 10px;
}

.spread {
  box-shadow: 0 10px 10px -10px;
}

这是每个效果(右边带有扩展值的元素):

理解扩展

扩展值允许您按指定的数量在所有方向上扩展或收缩阴影。在这个例子中,负值将阴影向后拉。结果是我们只在底部看到阴影,而不是在所有方向看到模糊“泄漏”出来(因为负的扩展值正在抵消模糊)。

注意

您可以在www.w3.org/TR/css3-background/阅读 box-shadow 属性的 W3C 规范。

背景渐变

过去的日子里,要在元素上实现背景渐变,需要平铺一个薄的渐变图形切片。作为图形资源,这是一个相当经济的权衡。一张只有一两个像素宽的图像不会耗尽带宽,在单个站点上可以用于多个元素。

然而,如果我们需要调整渐变,仍然需要往返到图形编辑器。而且,偶尔,内容可能会“突破”渐变背景,超出图像的固定大小限制。这个问题在响应式设计中更加严重,因为页面的部分可能在不同的视口上增加。

然而,使用 CSS 背景图像渐变,事情要灵活得多。作为 CSS 图像值和替换内容模块 3 级的一部分,CSS 使我们能够创建线性和径向背景渐变。让我们看看如何定义它们。

提示

CSS 图像值和替换内容模块 3 级的规范可以在www.w3.org/TR/css3-images/找到。

线性渐变符号

线性渐变符号,在其最简单的形式中,看起来像这样:

.linear-gradient {
    background: linear-gradient(red, blue); 
}

这将创建一个线性渐变,从红色开始(默认从顶部开始)到蓝色。

指定渐变方向

现在,如果您想为梯度指定一个方向,有几种方法。梯度将始终从您发送它的相反方向开始。但是,当没有设置方向时,梯度将始终默认为从上到下的方向。例如:

.linear-gradient {
    background: linear-gradient(to top right, red, blue); 
}

在这种情况下,梯度朝右上方。它从左下角开始是红色,逐渐变为右上角的蓝色。

如果你更喜欢数学,你可能会认为写梯度会像这样:

.linear-gradient {
    background: linear-gradient(45deg, red, blue); 
}

但是,请记住,在矩形框上,一个梯度向'右上方'(始终是应用于的元素的右上方)的梯度将以与45deg(始终是从其起始点开始的 45 度)略有不同的位置结束。

值得知道的是,您还可以在盒子内部可见之前开始梯度。例如:

.linear-gradient {
    background: linear-gradient(red -50%, blue); 
}

这将呈现一个梯度,就好像它在盒子内部甚至在可见之前就开始了。

实际上,在上一个例子中,我们使用了一个颜色停止来定义颜色应该开始和结束的位置,所以让我们更全面地看一下。

颜色停止

背景梯度最方便的地方可能是颜色停止。它们提供了在梯度中设置哪种颜色在哪一点使用的方法。使用颜色停止,您可以指定您可能需要的复杂性。考虑这个例子:

.linear-gradient {
  margin: 1rem;  
  width: 400px;
  height: 200px;
  background: linear-gradient(#f90 0, #f90 2%, #555 2%, #eee 50%, #555 98%, #f90 98%, #f90 100%);
}

这是linear-gradient的呈现方式:

颜色停止

在这个例子(example_06-03)中,没有指定方向,因此默认的从上到下的方向适用。

梯度内的颜色停止以逗号分隔,并通过给出首先颜色,然后停止的位置来定义。通常建议不要在一个符号中混合使用单位,但您可以。您可以拥有尽可能多的颜色停止,并且颜色可以写为关键字、HEX、RGBA 或 HSLA 值。

提示

请注意,多年来已经有许多不同的背景梯度语法,因此这是一个特别难以手工编写回退的领域。冒着听起来像是一张破碎的唱片的风险(孩子们,如果你不知道'唱片'是什么,请问爸爸妈妈),使用诸如 Autoprefixer 之类的工具可以让您的生活更轻松。这样,您可以编写当前的 W3C 标准语法(如前面详细介绍的)并自动为您创建之前的版本。

阅读 W3C 规范,了解线性背景梯度www.w3.org/TR/css3-images/

为旧版浏览器添加回退

作为旧版浏览器的简单回退,只需首先定义一个纯色背景。这样,旧版浏览器将至少在不理解后面定义的梯度时呈现一个纯色背景。例如:

.thing {
  background: red;
  background: linear-gradient(45deg, red, blue); 
}

径向背景梯度

在 CSS 中创建径向梯度同样简单。这些通常从一个中心点开始,并以椭圆或圆形平滑地扩展开来。

这是径向背景梯度的语法(您可以在example_06-04中进行操作):

.radial-gradient {  
    margin: 1rem;
    width: 400px;
    height: 200px;
    background: radial-gradient(12rem circle at bottom,  yellow, orange, red);
}

径向梯度语法的分解

在指定属性(background:)之后,我们开始radial-gradient符号。首先,在第一个逗号之前,我们定义梯度的形状或大小和位置。我们上面使用了 12rem 圆形来定义形状和大小,但考虑一些其他例子:

  • 5em将是一个尺寸为 5em 的圆。如果只给出尺寸,可以省略'circle'部分。

  • circle将是容器的完整尺寸的圆形(如果省略,则径向梯度的大小默认为'最远的角' - 关于尺寸关键字的更多信息)

  • 40px 30px将是一个椭圆,就像在一个 40px 宽,30px 高的框内绘制一样

  • ellipse将创建一个椭圆形状,适合元素内

接下来,在尺寸和/或形状之后,我们定义位置。默认位置是中心,但让我们看看其他可能性以及它们如何定义:

  • 在右上方 从右上方开始径向渐变

  • 在右侧 100px 顶部 20px 从右侧边缘 100px 和顶部边缘 20px 开始渐变

  • 在左侧中心 从元素的左侧中间开始

我们以逗号结束我们的大小、形状和位置 '参数',然后定义任何颜色停止;它们的工作方式与 linear-gradient 完全相同。

为了简化表示:在第一个逗号之前是大小、形状和位置,然后在其后是尽可能多的颜色停止(每个停止之间用逗号分隔)。

响应式尺寸的方便 'extent' 关键字

对于响应式工作,您可能会发现按比例调整渐变的大小比使用固定像素尺寸更有优势。这样,当元素的大小发生变化时,您就知道自己已经覆盖到了(从字面上和比喻上)。有一些方便的尺寸关键字可以应用于渐变。您可以像这样写它们,而不是使用任何尺寸值:

background: radial-gradient(closest-side circle at center, #333, blue);

以下是它们各自的作用:

  • closest-side: 形状与盒子最靠近中心的边相遇(对于圆形),或者与最靠近中心的水平和垂直边相遇(对于椭圆)。

  • closest-corner: 形状与盒子的最近角完全相遇

  • farthest-side: 与 closest-side 相反,不是形状与最近的边相遇,而是大小与离其中心最远的边相遇(或在椭圆的情况下,与最远的垂直和水平边相遇)。

  • farthest-corner: 形状扩展到盒子的中心到最远角

  • cover: 与 farthest-corner 相同

  • contain: 与 closest-side 相同

阅读 W3C 规范,了解径向背景渐变 www.w3.org/TR/css3-images/

提示

完美的 CSS3 线性和径向渐变的快捷方法

如果手动定义渐变看起来很费力,那么有一些很棒的在线渐变生成器。我个人最喜欢的是 www.colorzilla.com/gradient-editor/。它使用图形编辑器风格的 GUI,允许您选择颜色、停止、渐变样式(支持线性和径向渐变),甚至是您想要最终渐变的颜色空间(HEX、RGB(A)、HSL(A))。还有很多预设的渐变可用作起点。如果这还不够,它甚至为您提供了可选的代码,用于修复 Internet Explorer 9 以显示渐变和为旧版浏览器提供备用的纯色。还不确定?那么您是否能够根据现有图像中的渐变值生成 CSS 渐变?我想这可能会说服您。

重复渐变

CSS3 还赋予了我们创建重复背景渐变的能力。让我们看看它是如何完成的:

.repeating-radial-gradient {
    background: repeating-radial-gradient(black 0px, orange 5px, red 10px);
}

这就是它的样子(不要看太久,可能会引起恶心):

重复渐变

首先,用重复前缀 linear-gradientradial-gradient,然后它遵循与正常渐变相同的语法。在这里,我使用了黑色、橙色和红色之间的像素距离(分别为 0px、5px 和 10px),但您也可以选择使用百分比。为了获得最佳效果,建议在渐变中使用相同的测量单位(如像素或百分比)。

注意

阅读 W3C 关于重复渐变的信息 www.w3.org/TR/css3-images/

还有一种使用背景渐变的方法我想和你分享。

背景渐变图案

尽管我经常在设计中使用微妙的线性渐变,但对于径向渐变和重复渐变的实际用途较少。然而,聪明的人们已经利用渐变的力量来创建背景渐变图案。让我们看一个来自 CSS 忍者 Lea Verou 的 CSS3 背景图案集合的例子,可在lea.verou.me/css3patterns/上找到。

.carbon-fibre {
    margin: 1rem;  
    width: 400px;
    height: 200px;
    background:
    radial-gradient(black 15%, transparent 16%) 0 0,
    radial-gradient(black 15%, transparent 16%) 8px 8px,
    radial-gradient(rgba(255,255,255,.1) 15%, transparent 20%) 0 1px,
    radial-gradient(rgba(255,255,255,.1) 15%, transparent 20%) 8px 9px;
    background-color:#282828;
    background-size:16px 16px;
}

这是在浏览器中得到的效果,一个carbon-fibre背景效果:

背景渐变图案

怎么样?只需几行 CSS3 代码,我们就有了一个易于编辑、响应式和可伸缩的背景图案。

提示

您可能会发现在规则的末尾添加background-repeat: no-repeat会更好地理解它的工作原理。

与往常一样,借助媒体查询,可以针对不同的响应式场景使用不同的声明。例如,尽管渐变图案在较小的视口上可能效果很好,但在较大的视口上最好使用纯色背景:

@media (min-width: 45rem) {
    .carbon-fibre {
        background: #333;
    }
}

您可以在example_06-05中查看此示例。

多重背景图像

尽管现在有点过时,但过去构建页面时通常需要在页面顶部和底部使用不同的背景图像,或者在页面内的内容部分使用不同的背景图像。在 CSS2.1 时代,通常需要额外的标记(一个用于页眉背景,另一个用于页脚背景)来实现这种效果。

使用 CSS3,您可以在元素上堆叠尽可能多的背景图像。

以下是语法:

.bg {
    background: 
        url('../img/1.png'),
        url('../img/2.png'),
        url('../img/3.png');
}

与多个阴影的堆叠顺序一样,首先列出的图像在浏览器中最靠近顶部。如果愿意,您还可以在同一声明中添加背景的一般颜色,如下所示:

.bg {
    background: 
    url('../img/1.png'),
    url('../img/2.png'),
    url('../img/3.png') left bottom, black;
}

最后指定颜色,这将显示在上面指定的每个图像下面。

提示

在指定多个背景元素时,您不必将不同的图像堆叠在不同的行上;我只是发现这种写法更容易阅读代码。

不理解多重背景规则的浏览器(如 Internet Explorer 8 及更低版本)将完全忽略该规则,因此您可能希望在 CSS3 多重背景规则之前立即声明一个“正常”的背景属性,作为非常老的浏览器的后备。

使用多个背景图像时,只要使用带有透明度的 PNG 文件,任何部分透明的背景图像都会显示在另一个背景图像下面。但是,背景图像不必彼此叠放,也不必都是相同的尺寸。

背景大小

要为每个图像设置不同的尺寸,请使用background-size属性。当使用多个图像时,语法如下:

.bg {
    background-size: 100% 50%, 300px 400px, auto;
}

每个图像的尺寸值(首先是宽度,然后是高度)都是按照在背景属性中列出的顺序,用逗号分隔声明的。与上面的示例一样,您可以在每个图像旁边使用百分比或像素值,以及以下内容:

  • auto:将元素设置为其本机大小

  • cover:将图像扩展,保持其纵横比,以覆盖元素的区域

  • contain:将图像扩展到元素内适应其最长的一侧,同时保持纵横比

背景位置

如果您有不同尺寸的不同背景图像,接下来您会希望能够以不同的方式定位它们。幸运的是,background-position属性也可以实现这一点。

让我们将所有这些背景图像功能与我们在之前章节中看到的一些响应式单位放在一起。

让我们创建一个简单的太空场景,由一个单一元素和三个背景图像组成,设置为三种不同的尺寸,并以三种不同的方式定位:

.bg-multi {
    height: 100vh;
    width: 100vw;
    background:
        url('rosetta.png'), 
        url('moon.png'),
        url('stars.jpg');
    background-size: 75vmax, 50vw, cover;
    background-position: top 50px right 80px, 40px 40px, top center;
    background-repeat: no-repeat;
}

您将在浏览器中看到类似于这样的东西:

背景位置

我们在底部有星星图片,然后是顶部的月亮,最后是顶部的罗塞塔空间探测器的图片。您可以在example_06-06中自行查看。请注意,如果调整浏览器窗口,响应式长度单位(vmaxvhvw)可以很好地工作,并保持比例,而基于像素的单位则不行。

注意

如果没有声明background-position,则会应用默认位置为左上角。

背景简写

有一种简写方法可以将不同的背景属性组合在一起。您可以在www.w3.org/TR/css3-background/的规范中阅读它。然而,到目前为止,我的经验是它会产生不稳定的结果。因此,我建议使用长格式方法,先声明多个图像,然后是大小,然后是位置。

注意

阅读 W3C 关于多个背景元素的文档,网址为www.w3.org/TR/css3-background/

高分辨率背景图片

由于媒体查询,我们可以加载不同的背景图片,不仅在不同的视口大小,还在不同的视口分辨率下。

例如,这是为'普通'和高 DPI 屏幕指定背景图片的官方方式。您可以在example_06-07中找到这个:

.bg {
    background-image: url('bg.jpg');
}
@media (min-resolution: 1.5dppx) {
    .bg {
        background-image: url('bg@1_5x.jpg');
    }
}

媒体查询的编写方式与宽度、高度或其他能力测试一样。在这个例子中,我们定义了bg@1_5x.jpg应该使用的最小分辨率为 1.5dppx(每个 CSS 像素的设备像素)。如果需要的话,我们也可以使用dpi(每英寸点数)或dpcm(每厘米点数)单位。然而,尽管支持较差,我发现 dppx 是最容易理解的单位;因为 2dppx 是两倍的分辨率,3dppx 将是三倍的分辨率。在 dpi 中考虑这一点就比较棘手。'标准'分辨率将是 96dpi,两倍分辨率将是 192dpi,依此类推。

目前对于'dppx'单位的支持并不是很好(在caniuse.com/上检查您的目标浏览器),因此为了使其在各处都能平稳运行,您需要编写几个版本的媒体查询分辨率,或者像往常一样,依赖工具来为您添加前缀。

提示

关于性能的简短说明

只需记住,大图像可能会减慢您网站的速度,并导致用户体验不佳。虽然背景图片不会阻止页面的渲染(在等待背景图片时,您仍然会看到页面的其余部分被绘制到页面上),但它会增加页面的总重量,这对于用户支付数据来说很重要。

CSS 滤镜

box-shadow存在一个明显的问题。正如其名称所暗示的那样,它仅限于应用于元素的矩形 CSS 框形状。这是一个使用 CSS 制作的三角形形状的屏幕截图(您可以在example_06-08中查看代码),应用了一个框阴影:

CSS filters

并不完全是我所希望的。幸运的是,我们可以通过 CSS 滤镜来解决这个问题,这是 Filter Effects Module Level 1 的一部分(www.w3.org/TR/filter-effects/)。它们的支持并不像box-shadow那样广泛,但在渐进增强的方法中效果很好。如果浏览器不理解如何处理滤镜,它就会简单地忽略它。对于支持滤镜的浏览器,这些花哨的效果会被渲染出来。

这是相同的元素,应用了 CSS drop-shadow滤镜,而不是box-shadow

CSS filters

以下是 CSS 滤镜的格式:

.filter-drop-shadow {
    filter: drop-shadow(8px 8px 6px #333);
}

filter属性之后,我们指定要使用的滤镜,在这个例子中是drop-shadow,然后传入滤镜的参数。drop-shadow遵循与box-shadow类似的语法,因此这很容易;x 和 y 偏移量,模糊度,然后是扩散半径(都是可选的),最后是颜色(同样是可选的,尽管我建议为了一致性而指定颜色)。

提示

CSS 滤镜实际上是基于具有更广泛支持的 SVG 滤镜的。我们将在第七章中看到基于 SVG 的等效物,使用 SVG 实现分辨率独立性

可用的 CSS 滤镜

有几个滤镜可供选择。我们将逐一查看。虽然大多数滤镜的图像随后会出现,但阅读本书的读者(使用单色图像的硬拷贝)可能会难以注意到差异。如果你处于这种情况,请记住你仍然可以通过打开example_06-08在浏览器中查看各种滤镜。我现在将列出每一个适当的值。正如你所想象的那样,更多的值意味着更多的滤镜应用。在使用图像的地方,相关代码之后会显示图像。

  • filter: url ('./img/filters.svg#filterRed'): 让你指定要使用的 SVG 滤镜。

  • filter: blur(3px): 使用单个长度值(但不是百分比)。可用的 CSS 滤镜

  • filter: brightness(2): 使用从 0 到 1 或 0%到 100%的值。0/0%是黑色,1/100%是'正常',而任何超过这个值的都会使元素更亮。可用的 CSS 滤镜

  • filter: contrast(2): 使用从 0 到 1 或 0%到 100%的值。0/0%是黑色,1/100%是'正常',而任何超过这个值的都会提高颜色对比度。可用的 CSS 滤镜

  • filter: drop-shadow(4px 4px 6px #333): 我们之前详细讨论过drop-shadow

  • filter: grayscale(.8): 使用从 0 到 1 的值,或者 0%到 100%的值来对元素应用不同程度的灰度。0 的值将没有灰度,而 1 的值将完全是灰度。可用的 CSS 滤镜

  • filter: hue-rotate(25deg): 使用从 0 到 360 度的值来调整颜色在色轮周围的颜色。可用的 CSS 滤镜

  • filter: invert(75%): 使用从 0 到 1 的值,或者 0%到 100%的值来定义元素颜色反转的程度。可用的 CSS 滤镜

  • filter: opacity(50%): 使用从 0 到 1 的值,或者 0%到 100%的值来改变元素的不透明度。这类似于你已经熟悉的不透明度属性。然而,正如我们将看到的那样,滤镜可以组合,这允许不透明度与其他滤镜一起组合。可用的 CSS 滤镜

  • filter: saturate(15%): 使用从 0 到 1 的值,或者 0%到 100%的值来去饱和图像,超过 1/100%则增加额外的饱和度。可用的 CSS 滤镜

  • filter: sepia(.75): 使用从 0 到 1 的值,或者 0%到 100%的值使元素呈现出更多的深褐色。0/0%使元素保持原样,而任何超过这个值的都会应用更多的深褐色,最多为 1/100%。可用的 CSS 滤镜

组合 CSS 滤镜

你也可以轻松地组合滤镜;只需用空格分隔它们。例如,这是如何一次应用不透明度模糊深褐色滤镜的:

.MultipleFilters {
    filter: opacity(10%) blur(2px) sepia(35%);
}

注意

注意:除了hue-rotate之外,使用滤镜时,不允许使用负值。

我想你会同意,CSS 滤镜提供了一些非常强大的效果。这些也是我们可以从一种情况过渡到另一种情况的效果。我们将在第八章中看到如何做到这一点,过渡,转换和动画

然而,在你对这些新玩具感到兴奋之前,我们需要就性能进行一次成熟的对话。

关于 CSS 性能的警告

在谈到 CSS 性能时,我希望你记住一件事:

"建筑在括号外,性能在括号内。"
--Ben Frain

让我扩展一下我的小格言:

就我所能证明的,担心 CSS 选择器(大括号外的部分)是快还是慢是毫无意义的。我在benfrain.com/css-performance-revisited-selectors-bloat-expensive-styles/中试图证明这一点。

然而,从 CSS 的角度来看,真正会使页面停滞不前的一件事是“昂贵”的属性(大括号内的部分)。当我们在某些样式方面使用术语“昂贵”时,它简单地意味着它给浏览器带来了很多开销。这是浏览器发现过于繁重的事情。

我们可以根据常识猜测什么可能会导致浏览器额外工作。基本上,任何在绘制屏幕之前必须计算的东西。例如,比较一个具有纯色背景的标准 div 和一个半透明图像,放在由多个渐变组成的背景之上,带有圆角和drop-shadow。后者更昂贵;它将导致浏览器进行更多的计算工作,随后会导致更多的开销。

因此,当您应用滤镜等效果时,请谨慎行事,并在可能的情况下测试页面速度是否在您希望支持的最低功率设备上受到影响。至少,在 Chrome 中打开开发工具功能,如连续页面重绘,并切换任何可能会导致问题的效果。这将为您提供数据(以毫秒为单位,显示当前视口绘制所需的时间),以便更明智地决定应用哪些效果。数字越低,页面的性能越快(尽管要注意浏览器/平台会有所不同,因此尽可能在真实设备上进行测试)。

有关此主题的更多信息,我建议参考以下资源:

developers.google.com/web/fundamentals/performance/rendering/

CSS 性能警告

关于 CSS 蒙版和裁剪的说明

在不久的将来,CSS 将能够在 CSS 蒙版模块 1 级中提供蒙版和裁剪。这些功能将使我们能够使用形状或任意路径(通过 SVG 或多边形点的方式指定)裁剪图像。遗憾的是,尽管规范处于更高级的 CR 阶段,但在我写这篇文章时,浏览器的实现仍然太多 bug,无法推荐。但是,这是一个不断变化的情况,因此在您阅读本文时,实现可能已经非常稳定。对于好奇的人,我将向您推荐规范www.w3.org/TR/css-masking/

我认为克里斯·科耶在这篇文章中很好地解释了支持方面的情况:

css-tricks.com/clipping-masking-css/

最后,萨拉·苏艾丹在这篇文章中提供了一个关于未来可能实现的概述和解释:

alistapart.com/article/css-shapes-101

摘要

在本章中,我们已经研究了一些最有用的 CSS 功能,用于在响应式网页设计中创建轻量级的美学效果。CSS3 的背景渐变减少了我们对背景效果图像的依赖。我们甚至考虑了它们如何用于创建无限重复的背景图案。我们还学习了如何使用文本阴影来创建简单的文本增强和使用盒子阴影来为元素的外部和内部添加阴影。我们还研究了 CSS 滤镜。它们使我们能够仅使用 CSS 实现更令人印象深刻的视觉效果,并且可以组合以获得真正令人印象深刻的结果。

在下一章中,我们将把注意力转向创建和使用 SVG(可伸缩矢量图形),它们通常被简称为 SVG。虽然这是一种非常成熟的技术,但只有在当前响应式和高性能网站的环境下,它才真正成熟起来。

第七章:使用 SVG 实现分辨率独立性

整本书都在写关于 SVG 的内容。SVG 是响应式网页设计的重要技术,因为它为所有屏幕分辨率提供了清晰和未来可靠的图形资产。

在 Web 上,使用 JPEG、GIF 或 PNG 等格式的图像,其视觉数据保存为固定像素。如果您以固定宽度和高度保存图形,并将图像放大到原始大小的两倍或更多,它们的限制很容易暴露出来。

这是我在浏览器中放大的 PNG 图像截图:

使用 SVG 实现分辨率独立性

你能看到图像明显呈现像素化吗?这是完全相同的图像,保存为矢量图像,以 SVG 格式,并放大到类似的级别:

使用 SVG 实现分辨率独立性

希望差异是显而易见的。

除了最小的图形资产外,尽可能使用 SVG 而不是 JPEG、GIF 或 PNG,将产生分辨率独立的图形,与位图图像相比,文件大小要小得多。

虽然我们将在本章涉及 SVG 的许多方面,但重点将放在如何将它们整合到您的工作流程中,同时还提供 SVG 的可能性概述。

在本章中,我们将涵盖:

  • SVG,简要历史,以及基本 SVG 文档的解剖

  • 使用流行的图像编辑软件和服务创建 SVG

  • 使用imgobject标签将 SVG 插入页面

  • 将 SVG 插入为背景图像

  • 直接(内联)将 SVG 插入 HTML

  • 重用 SVG 符号

  • 引用外部 SVG 符号

  • 每种插入方法可能具有的功能

  • 使用 SMIL 对 SVG 进行动画处理

  • 使用外部样式表对 SVG 进行样式设置

  • 使用内部样式对 SVG 进行样式设置

  • 使用 CSS 修改和动画 SVG

  • 媒体查询和 SVG

  • 优化 SVG

  • 使用 SVG 定义 CSS 滤镜

  • 使用 JavaScript 和 JavaScript 库操纵 SVG

  • 实施提示

  • 更多资源

SVG 是一个复杂的主题。本章的哪些部分与您的需求最相关将取决于您实际需要的 SVG。希望我能够提供一些快捷方式。

如果您只是想用 SVG 版本替换网站上的静态图形资产,以获得更清晰的图像和/或更小的文件大小,那么请查看使用 SVG 作为背景图像和在img标签中的较短部分。

如果您想了解哪些应用程序和服务可以帮助您生成和管理 SVG 资产,请跳转到使用流行的图像编辑软件和服务创建 SVG部分,获取一些有用的链接和指引。

如果您想更全面地了解 SVG,或者想要对 SVG 进行动画和操作,最好找个舒服的地方,准备一份您最喜欢的饮料,因为这会是一个相当长的过程。

为了开始我们的理解之旅,请和我一起回到 2001 年。

SVG 的简要历史

SVG 的首次发布是在 2001 年。这不是笔误。SVG 自 2001 年以来一直存在。虽然它在发展过程中获得了一些关注,但直到高分辨率设备的出现,它们才受到了广泛的关注和采用。以下是来自 1.1 规范的 SVG 介绍(www.w3.org/TR/SVG11/intro.html):

SVG 是一种用 XML 描述二维图形的语言[XML10]。SVG 允许三种类型的图形对象:矢量图形形状(例如,由直线和曲线组成的路径)、图像和文本。

正如其名称所示,SVG 允许将二维图像描述为矢量点的代码。这使它们成为图标、线条图和图表的理想选择。

由于矢量描述了相对点,它们可以按比例缩放到任何大小,而不会失去保真度。此外,就数据而言,由于 SVG 被描述为矢量点,与大小相当的 JPEG、GIF 或 PNG 文件相比,它们往往很小。

现在,浏览器对 SVG 的支持也非常好。Android 2.3 及以上版本,以及 Internet Explorer 9 及以上版本都支持 SVG(caniuse.com/#search=svg)。

作为文档的图形

通常情况下,如果您尝试在文本编辑器中查看图形文件的代码,生成的文本将完全无法理解。

SVG 图形的不同之处在于它们实际上是用一种标记样式语言描述的。SVG 是用可扩展标记语言XML)编写的,这是 HTML 的近亲。尽管您可能没有意识到,但 XML 实际上无处不在于互联网上。您使用 RSS 阅读器吗?那就是 XML。XML 是将 RSS 订阅的内容打包起来,使其可以轻松地被各种工具和服务使用的语言。

因此,不仅机器可以读取和理解 SVG 图形,我们也可以。

让我举个例子。看看这个星形图形:

作为文档的图形

这是一个名为Star.svg的 SVG 图形,位于example_07-01内。您可以在浏览器中打开此示例,它将显示为星形,或者您可以在文本编辑器中打开它,您可以看到生成它的代码。考虑一下:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="198px" height="188px" viewBox="0 0 198 188" version="1.1"   >
    <!-- Generator: Sketch 3.2.2 (9983) - http://www.bohemiancoding.com/sketch -->
    <title>Star 1</title>
    <desc>Created with Sketch.</desc>
    <defs></defs>
    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
        <polygon id="Star-1" stroke="#979797" stroke-width="3" fill="#F8E81C" sketch:type="MSShapeGroup" points="99 154 40.2214748 184.901699 51.4471742 119.45085 3.89434837 73.0983006 69.6107374 63.5491503 99 4 128.389263 63.5491503 194.105652 73.0983006 146.552826 119.45085 157.778525 184.901699 "></polygon>
    </g>
</svg>

这就是生成那个星形 SVG 图形所需的全部代码。

现在,通常情况下,如果您以前从未查看过 SVG 图形的代码,您可能会想知道为什么要这样做。如果您只想在网页上显示矢量图形,那么您确实不需要。只需找到一个可以将矢量艺术作品保存为 SVG 的图形应用程序,就可以了。我们将在接下来的页面中列出其中一些软件包。

然而,虽然只在图形编辑应用程序中使用 SVG 图形是常见且可能的,但如果您需要开始操作和动画化 SVG,了解 SVG 如何拼合以及如何调整它以满足您的需求可能会非常有用。

因此,让我们仔细看看 SVG 标记,并了解其中到底发生了什么。我想让您注意一些关键事项。

根 SVG 元素

这里的根 SVG 元素具有widthheightviewbox属性。

 <svg width="198px" height="188px" viewBox="0 0 198 188"

这些都在如何显示 SVG 图形中扮演着重要的角色。

希望在这一点上,您理解了“视口”这个术语。它在本书的大多数章节中都被用来描述设备上用于查看内容的区域。例如,移动设备可能有一个 320 像素乘以 480 像素的视口。台式电脑可能有一个 1920 像素乘以 1080 像素的视口。

SVG 的widthheight属性有效地创建了一个视口。通过这个定义的视口,我们可以窥视 SVG 内部定义的形状。就像网页一样,SVG 的内容可能比视口大,但这并不意味着其余部分不存在,它只是隐藏在我们当前的视图之外。

另一方面,视图框定义了 SVG 中所有形状所遵循的坐标系。

您可以将视图框的值 0 0 198 188 看作描述矩形的左上角和右下角区域。前两个值,在技术上称为min-xmin-y,描述了左上角,而后两个值,在技术上称为宽度和高度,描述了右下角。

具有viewbox属性使您可以执行缩放图像等操作。例如,如果像这样在viewbox属性中减半宽度和高度:

<svg width="198px" height="188px" viewBox="0 0 99 94"

形状将“缩放”以填充 SVG 的宽度和高度。

提示

要真正理解视图框和 SVG 坐标系统以及它所提供的机会,我建议阅读 Sara Soueidan 的这篇文章:sarasoueidan.com/blog/svg-coordinate-systems/ 以及 Jakob Jenkov 的这篇文章:tutorials.jenkov.com/svg/svg-viewport-view-box.html

命名空间

这个 SVG 文件为生成它的 Sketch 图形程序定义了一个额外的命名空间(xmlns是 XML 命名空间的缩写)。

这些命名空间引用通常只被生成 SVG 的程序使用,因此当 SVG 被用于网络时,它们通常是不需要的。用于减小 SVG 文件大小的优化过程通常会将它们剥离。

标题和描述标签

titledesc标签,使得 SVG 文档非常易于访问:

<title>Star 1</title>
    <desc>Created with Sketch.</desc>

这些标签可以用来描述图形的内容,当它们看不见时。然而,当 SVG 图形用于背景图形时,这些标签可以被删除以进一步减小文件大小。

定义标签

在我们的示例代码中有一个空的defs标签:

<defs></defs>

尽管在我们的示例中为空,但这是一个重要的元素。它用于存储各种可重用内容的定义,如渐变、符号、路径等。

g 元素

g元素用于将其他元素分组在一起。例如,如果你要绘制一辆汽车的 SVG,你可能会将组成整个车轮的形状放在g标签内。

<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">

在我们的g标签中,我们可以看到之前的 sketch 命名空间在这里被重用。这将帮助图形应用程序再次打开这个图形,但如果这个图像被绑定到其他地方,它就没有进一步的作用了。

SVG 形状

在这个示例中,最内部的节点是一个多边形。

<polygon id="Star-1" stroke="#979797" stroke-width="3" fill="#F8E81C" sketch:type="MSShapeGroup" points="99 154 40.2214748 184.901699 51.4471742 119.45085 3.89434837 73.0983006 69.6107374 63.5491503 99 4 128.389263 63.5491503 194.105652 73.0983006 146.552826 119.45085 157.778525 184.901699 "></polygon>

SVG 具有许多现成的形状可用(pathrectcircleellipselinepolylinepolygon)。

SVG 路径

SVG 路径与 SVG 的其他形状不同,因为它们由任意数量的连接点组成(使你可以自由地创建任何形状)。

所以这就是 SVG 文件的要点,希望现在你对正在发生的事情有了一个高层次的理解。虽然有些人会喜欢手写或编辑 SVG 文件的代码,但更多的人宁愿用图形软件生成 SVG。让我们考虑一些更受欢迎的选择。

使用流行的图像编辑软件和服务创建 SVG

虽然 SVG 可以在文本编辑器中打开、编辑和编写,但有许多应用程序提供图形用户界面(GUI),使得如果你来自图形编辑背景,编写复杂的 SVG 图形会更容易。也许最明显的选择是 Adobe 的 Illustrator(PC/Mac)。然而,它对于偶尔使用者来说是昂贵的,所以我个人偏好 Bohemian Coding 的 Sketch(仅限 Mac:bohemiancoding.com/sketch/)。这本身也不便宜(目前为 99 美元),但如果你使用 Mac,这仍然是我推荐的选择。

如果你使用 Windows/Linux 或者正在寻找更便宜的选择,可以考虑免费开源的 Inkscape(inkscape.org/en/)。它并不是最好看的工具,但它非常有能力(如果你需要任何证明,可以查看 Inkscape 画廊:inkscape.org/en/community/gallery/)。

最后,有一些在线编辑器。Google 有 SVG-edit (svg-edit.googlecode.com/svn/branches/stable/editor/svg-editor.html)。还有 Draw SVG (www.drawsvg.org),以及 Method Draw,这是 SVG-edit 的一个外观更好的分支(editor.method.ac/)。

使用 SVG 图标服务节省时间

前面提到的应用程序都可以让您从头开始创建 SVG 图形。但是,如果您想要的是图标,您可能可以通过从在线图标服务下载 SVG 版本来节省大量时间(对我来说,获得更好的结果)。我个人最喜欢的是icomoon.io/也很棒。

为了快速说明在线图标服务的好处,加载 icomoon.io 应用程序会为您提供一个可搜索的图标库(一些免费,一些付费):

使用 SVG 图标服务节省时间

您可以选择您想要的图标,然后单击下载。生成的文件包含 SVG、PNG 和 SVG 符号的图标,用于放置在defs元素中(请记住,defs元素是用于引用元素的容器元素)。

要自己看一下,请打开example_07-02,您可以看到我从icomoon.io/选择了五个图标后的下载文件。

将 SVG 插入到您的网页中

有许多与 SVG 图像(与常规图像格式 JPEG、GIF、PNG 不同)相关的事情(取决于浏览器),您可以做的事情。可能的范围在很大程度上取决于 SVG 插入到页面的方式。因此,在我们实际可以对 SVG 做些什么之前,我们将考虑我们实际上可以将它们放在页面上的各种方式。

使用 img 标签

使用 SVG 图形的最直接方法就是将其插入到 HTML 文档中的方式。我们只需使用一个好老旧的img标签:

<img src="img/mySconeVector.svg" alt="Amazing line art of a scone" />

这使得 SVG 的行为几乎与任何其他图像相同。关于这点没有更多要说的。

使用对象标签

object标签是 W3C 推荐的在网页中保存非 HTML 内容的容器(object 的规范位于www.w3.org/TR/html5/embedded-content-0.html)。我们可以利用它来像这样将 SVG 插入到我们的页面中:

<object data="img/svgfile.svg" type="image/svg+xml">
    <span class="fallback-info">Your browser doesn't support SVG</span>
</object>

datatype属性是必需的,尽管我总是建议两者都添加。data属性是您链接到 SVG 资产的位置,方式与链接到任何其他资产的方式相同。type属性描述了与内容相关的 MIME 类型。在这种情况下,image/svg+xml是用于指示数据为 SVG 的 MIME(互联网媒体类型)类型。如果您希望使用此容器限制 SVG 的大小,还可以添加widthheight属性。

通过object标签插入到页面中的 SVG 也可以通过 JavaScript 访问,这就是以这种方式插入它们的一个原因。但是,使用object标签的额外好处是,它为浏览器不理解数据类型时提供了一个简单的机制。例如,如果在不支持 SVG 的 Internet Explorer 8 中查看先前的object元素,它将简单地看到消息“您的浏览器不支持 SVG”。您可以使用此空间在img标签中提供备用图像。但是,请注意,根据我的粗略测试,浏览器将始终下载备用图像,无论它是否真正需要它。因此,如果您希望您的网站以尽可能短的时间加载(您会的,相信我),这实际上可能不是最佳选择。

注意

如果您想使用 jQuery 操作通过object标签插入的 SVG,您需要使用本机.contentDocument JavaScript 属性。然后,您可以使用 jQuery.attr来更改fill等内容。

提供备用的另一种方法是通过 CSS 添加background-image。例如,在我们上面的示例中,我们的备用 span 具有.fallback-info类。我们可以在 CSS 中使用它来链接到合适的background-image。这样,只有在需要时才会下载background-image

将 SVG 作为背景图像插入

SVG 可以像任何其他图像格式(PNG、JPG、GIF)一样在 CSS 中用作背景图像。在引用它们的方式上没有什么特别之处:

.item {
    background-image: url('image.svg');
}

对于不支持 SVG 的旧版浏览器,您可能希望在更广泛支持的格式(通常是 PNG)中包含一个“回退”图像。以下是一种在 IE8 和 Android 2 中实现的方法,因为 IE8 不支持 SVG 或background-size,而 Android 2.3 不支持 SVG 并且需要对background-size使用供应商前缀:

.item {
    background: url('image.png') no-repeat;
    background: url('image.svg') left top / auto auto no-repeat;
}

在 CSS 中,如果应用了两个等效的属性,样式表中较低的属性将始终覆盖上面的属性。在 CSS 中,浏览器总是会忽略它无法理解的规则中的属性/值对。因此,在这种情况下,旧版浏览器会得到 PNG,因为它们无法使用 SVG 或理解未加前缀的background-size属性,而实际上可以使用任何一种的新版浏览器会采用下面的规则,因为它覆盖了第一个规则。

您还可以借助 Modernizr 提供回退;这是用于测试浏览器功能的 JavaScript 工具(Modernizr 在第五章中有更详细的讨论,“CSS3-选择器、排版、颜色模式和新功能”)。Modernizr 对一些不同的 SVG 插入方法进行了单独测试,而下一个版本的 Modernizr(撰写时尚未发布)可能会对 CSS 中的 SVG 有更具体的内容。然而,目前您可以这样做:

.item {
    background-image: url('image.png');
}
.svg .item {
    background-image: url('image.svg');
}

或者如果更喜欢,可以颠倒逻辑:

.item {
    background-image: url('image.svg');
}
.no-svg .item {
    background-image: url('image.png');
}

当功能查询得到更全面的支持时,您也可以这样做:

.item {
    background-image: url('image.png');
}

@supports (fill: black) {
    .item {
        background-image: url('image.svg');
    }
}

@supports规则在这里起作用,因为fill是 SVG 属性,所以如果浏览器理解它,它将采用下面的规则而不是第一个规则。

如果您对 SVG 的需求主要是静态背景图像,比如图标之类的,我强烈建议将 SVG 作为背景图像实现。这是因为有许多工具可以自动创建图像精灵或样式表资产(这意味着将 SVG 作为数据 URI 包含),回退 PNG 资产以及从您创建的任何单个 SVG 生成所需的样式表。以这种方式使用 SVG 得到了很好的支持,图像本身缓存效果很好(因此在性能方面效果很好),而且实现起来很简单。

关于数据 URI 的简要说明

如果您阅读前面的部分,并想知道与 CSS 相关的数据统一资源标识符URI)是什么意思,它是一种在 CSS 文件本身中包含通常是外部资产(如图像)的方法。因此,我们可能会这样链接外部图像文件:

.external {
  background-image: url('Star.svg');
}

我们可以简单地在样式表中包含图像,使用数据 URI,如下所示:

.data-uri {
  background-image: url(data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%20standalone%3D%22no%22%3F%3E%0A%3Csvg%20width%3D%22198px%22%20height%3D%22188px%22%20viewBox%3D%220%200%20198%20188%22%20version%3D%221.1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20xmlns%3Asketch%3D%22http%3A%2F%2Fwww.bohemiancoding.com%2Fsketch%2Fns%22%3E%0A%20%20%20%20%3C%21--%20Generator%3A%20Sketch%203.2.2%20%289983%29%20-%20http%3A%2F%2Fwww.bohemiancoding.com%2Fsketch%20--%3E%0A%20%20%20%20%3Ctitle%3EStar%201%3C%2Ftitle%3E%0A%20%20%20%20%3Cdesc%3ECreated%20with%20Sketch.%3C%2Fdesc%3E%0A%20%20%20%20%3Cdefs%3E%3C%2Fdefs%3E%0A%20%20%20%20%3Cg%20id%3D%22Page-1%22%20stroke%3D%22none%22%20stroke-width%3D%221%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%20sketch%3Atype%3D%22MSPage%22%3E%0A%20%20%20%20%20%20%20%20%3Cpolygon%20id%3D%22Star-1%22%20stroke%3D%22%23979797%22%20stroke-width%3D%223%22%20fill%3D%22%23F8E81C%22%20sketch%3Atype%3D%22MSShapeGroup%22%20points%3D%2299%20154%2040.2214748%20184.901699%2051.4471742%20119.45085%203.89434837%2073.0983006%2069.6107374%2063.5491503%2099%204%20128.389263%2063.5491503%20194.105652%2073.0983006%20146.552826%20119.45085%20157.778525%20184.901699%20%22%3E%3C%2Fpolygon%3E%0A%20%20%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E);
}

这并不美观,但它提供了一种消除网络上的单独请求的方法。数据 URI 有不同的编码方法,还有很多工具可以从您的资产创建数据 URI。

如果以这种方式对 SVG 进行编码,我建议避免使用 base64 方法,因为它对 SVG 内容的压缩效果不如文本好。

生成图像精灵

我个人推荐的工具,用于生成图像精灵或数据 URI 资产,是 Iconizr(iconizr.com/)。它可以完全控制您希望生成的最终 SVG 和回退 PNG 资产。您可以将 SVG 和回退 PNG 文件输出为数据 URI 或图像精灵,甚至包括加载正确资产的必要 JavaScript 片段,如果您选择数据 URI,则强烈推荐使用。

此外,如果您在思考是选择数据 URI 还是图像精灵用于您的项目,我对数据 URI 或图像精灵的利弊进行了进一步研究,您可能会对此感兴趣,如果您面临同样的选择:benfrain.com/image-sprites-data-uris-icon-fonts-v-svgs/

虽然我非常喜欢 SVG 作为背景图像,但如果您想要动态地对其进行动画,或者通过 JavaScript 将值注入其中,最好选择将 SVG 数据“内联”插入 HTML。

内联插入 SVG

由于 SVG 仅仅是一个 XML 文档,您可以直接将其插入 HTML 中。例如:

<div>
    <h3>Inserted 'inline':</h3>
    <span class="inlineSVG">
        <svg id="svgInline" width="198" height="188" viewBox="0 0 198 188"  >
        <title>Star 1</title>
            <g class="star_Wrapper" fill="none" fill-rule="evenodd">
                <path id="star_Path" stroke="#979797" stroke-width="3" fill="#F8E81C" d="M99 154l-58.78 30.902 11.227-65.45L3.894 73.097l65.717-9.55L99 4l29.39 59.55 65.716 9.548-47.553 46.353 11.226 65.452z" />
            </g>
        </svg>
    </span>
</div>

不需要特殊的包装元素,您只需在 HTML 标记中插入 SVG 标记。还值得知道的是,如果在svg元素上删除任何widthheight属性,SVG 将会流动地缩放以适应包含元素。

在文档中插入 SVG 可能是最多功能的 SVG 特性。

从符号中重复使用图形对象

在本章的前面,我提到我从 IcoMoon(icomoon.io)中挑选并下载了一些图标。它们是描绘触摸手势的图标:滑动、捏、拖动等等。假设在您正在构建的网站中,您需要多次使用它们。请记住,我提到这些图标有 SVG 符号定义的版本?这就是我们现在要使用的。

example_07-09中,我们将在页面的 SVG 的defs元素中插入各种符号定义。您会注意到在 SVG 元素上使用了内联样式:display:noneheightwidth属性都被设置为零(如果您愿意,这些样式可以在 CSS 中设置)。这样做是为了使这个 SVG 不占用空间。我们只是使用这个 SVG 来容纳我们想要在其他地方使用的图形对象的符号。

因此,我们的标记从这里开始:

<body>
    <svg display="none" width="0" height="0" version="1.1"  >
    <defs>
    <symbol id="icon-drag-left-right" viewBox="0 0 1344 1024">
        <title>drag-left-right</title>
        <path class="path1" d="M256 192v-160l-224 224 224 224v-160h256v-128z"></path>

注意defs元素内的symbol元素?这是我们想要定义形状以供以后重用时使用的元素。

在 SVG 定义了我们工作所需的所有必要符号之后,我们有了所有我们的“正常”HTML 标记。然后,当我们想要使用其中一个符号时,我们可以这样做:

<svg class="icon-drag-left-right">
  <use xlink:href="#icon-drag-left-right"></use>
</svg>

这将显示拖动左右图标:

从符号中重复使用图形对象

这里的魔法是use元素。正如您可能已经从名称中猜到的那样,它用于利用已经在其他地方定义的现有图形对象。选择要引用的机制是xlink属性,在这种情况下,它引用了我们在标记开头内联的“拖动左右”图标(#icon-drag-left-right)的符号 ID。

当您重复使用一个符号时,除非您明确设置了大小(可以通过元素本身的属性或 CSS 设置),否则use将被设置为宽度和高度为 100%。因此,要调整我们的图标大小,我们可以这样做:

.icon-drag-left-right {
    width: 2.5rem;
    height: 2.5rem;
}

use元素可用于重用各种 SVG 内容:渐变、形状、符号等等。

内联 SVG 允许在不同的上下文中使用不同的颜色

使用内联 SVG,您还可以做一些有用的事情,比如根据上下文更改颜色,当您需要不同颜色的相同图标的多个版本时,这将非常有用:

.icon-drag-left-right {
    fill: #f90;
}

.different-context .icon-drag-left-right {
    fill: #ddd;
}

使双色图标继承其父元素的颜色

使用内联 SVG,您还可以玩得很开心,从单色图标创建双色效果(只要 SVG 由多个路径组成),并使用currentColor,这是最古老的 CSS 变量。要做到这一点,在 SVG 符号内部,将要成为一种颜色的路径的fill设置为currentColor。然后在 CSS 中使用颜色值对元素进行着色。对于 SVG 符号中没有填充的路径,设置为currentColor,它们将接收填充值。举例说明:

.icon-drag-left-right {
    width: 2.5rem;
    height: 2.5rem;
    fill: #f90;
    color: #ccc; /* this gets applied to the path that has it's fill attribute set to currentColor in the symbol */
}

这是同一个符号被重复使用了三次,每次都有不同的颜色和大小:

使双色图标继承其父元素的颜色

请记住,您可以查看example_07-09中的代码。还值得知道的是,颜色不一定要设置在元素本身上,它可以在任何父元素上;currentColor将从 DOM 树上的最近的父元素继承一个值。

以这种方式使用 SVG 有很多积极的方面。唯一的缺点是需要在每个要使用图标的页面上包含相同的 SVG 数据。不幸的是,这对性能来说是不好的,因为资产(SVG 数据)不容易被缓存。然而,还有另一种选择(如果您愿意添加一个脚本来支持 Internet Explorer)。

从外部来源重用图形对象

与其在每个页面中粘贴一组巨大的 SVG 符号,同时仍然使用use元素,不如链接到外部 SVG 文件并获取您想要使用的文档部分。看一下example-07-10,和我们在example_07-09中的三个图标以这种方式放在页面上:

<svg class="icon-drag-left-right">
    <use xlink:href="defs.svg#icon-drag-left-right"></use>
</svg>

重要的是要理解href。我们正在链接到外部 SVG 文件(defs.svg部分),然后指定我们想要使用的文件中的符号的 ID(#icon-drag-left-right部分)。

这种方法的好处是,浏览器会缓存资产(就像任何其他外部图像一样),并且它可以节省我们的标记,不用用充满符号定义的 SVG。缺点是,与内联放置defs时不同,对defs.svg进行的任何动态更改(例如,如果路径被 JavaScript 操纵)不会在use标签中更新。

不幸的是,Internet Explorer 不允许从外部资产引用符号。但是,有一个用于 IE9-11 的 polyfill 脚本,名为SVG For Everybody,它允许我们无论如何使用这种技术。请访问github.com/jonathantneal/svg4everybody了解更多信息。

使用那段 JavaScript 时,您可以愉快地引用外部资产,polyfill 将直接将 SVG 数据插入到文档的主体中,以支持 Internet Explorer。

您可以使用每种 SVG 插入方法(内联、对象、背景图像和 img)做什么

如前所述,SVG 与其他图形资产不同。它们的行为可能会有所不同,取决于它们被插入到页面的方式。正如我们所见,有四种主要的方式可以将 SVG 放置到页面上:

  • img标签内部

  • object标签内部

  • 作为背景图像

  • 内联

并且根据插入方法,某些功能将或将不可用。

要了解每种插入方法应该可能做什么,可能更简单的方法是考虑这个表格。

您可以使用每种 SVG 插入方法(内联、对象、背景图像和 img)做什么

现在有一些需要考虑的注意事项,用数字标记:

  • *1:当在对象内部使用 SVG 时,您可以使用外部样式表来为 SVG 设置样式,但您必须从 SVG 内部链接到该样式表

  • *2:您可以在外部资产中使用 SVG(可缓存),但在 Internet Explorer 中默认情况下无法工作

  • *3:'内联'的 SVG 中样式部分的媒体查询作用于其所在文档的大小(而不是 SVG 本身的大小)

浏览器分歧

请注意,SVG 的浏览器实现也有所不同。因此,仅仅因为上面所示的东西应该是可能的,并不意味着它们实际上在每个浏览器中都会出现,或者它们会表现一致!

例如,上表中的结果是基于example_07-03中的测试页面。

测试页面的行为在最新版本的 Firefox、Chrome 和 Safari 中是可比较的。然而,Internet Explorer 有时会做一些不同的事情。

例如,在所有支持 SVG 的 Internet Explorer 版本(目前为止,即 9、10 和 11),正如我们已经看到的,不可能引用外部 SVG 源。此外,Internet Explorer 会将外部样式表中的样式应用到 SVG 上,而不管它们是如何插入的(其他浏览器只有在 SVG 通过object或内联方式插入时才应用外部样式表中的样式)。Internet Explorer 也不允许通过 CSS 对 SVG 进行任何动画;在 Internet Explorer 中,SVG 的动画必须通过 JavaScript 完成。我再说一遍,给后排的人听:除了 JavaScript,你无法以任何其他方式在 Internet Explorer 中对 SVG 进行动画。

额外的 SVG 功能和奇特之处

让我们暂时抛开浏览器的缺陷,考虑一下表中的一些功能实际上允许什么,以及为什么你可能会或不会想要使用它们。

SVG 将始终以查看设备允许的最清晰方式呈现,而不管插入的方式如何。对于大多数实际情况,分辨率独立通常足以使用 SVG。然后只是选择适合你的工作流程和任务的插入方法的问题。

然而,还有其他一些值得知道的功能和奇特之处,比如 SMIL 动画、不同的链接外部样式表的方式、用字符数据分隔符标记内部样式、用 JavaScript 修改 SVG,以及在 SVG 中使用媒体查询。让我们接下来讨论这些。

SMIL 动画

SMIL 动画(www.w3.org/TR/smil-animation/)是一种在 SVG 文档内部定义动画的方法。

SMIL(如果你想知道,发音为“smile”)代表同步多媒体集成语言,是作为在 XML 文档内定义动画的一种方法而开发的(记住,SVG 是基于 XML 的)。

以下是一个基于 SMIL 的动画的示例:

<g class="star_Wrapper" fill="none" fill-rule="evenodd">
    <animate xlink:href="#star_Path" attributeName="fill" attributeType="XML" begin="0s" dur="2s" fill="freeze" from="#F8E81C" to="#14805e" />

    <path id="star_Path" stroke="#979797" stroke-width="3" fill="#F8E81C" d="M99 154l-58.78 30.902 11.227-65.45L3.894 73.097l65.717-9.55L99 4l29.39 59.55 65.716 9.548-47.553 46.353 11.226 65.452z" />
</g>

我抓取了我们之前看过的 SVG 的一部分。g是 SVG 中的分组元素,这个元素包括一个星形(id="star_Path"path元素)和animate元素内的 SMIL 动画。这个简单的动画将星星的填充颜色从黄色变为绿色,持续两秒。而且,无论 SVG 是以imgobjectbackground-image还是内联方式放在页面上(不,真的,除了 Internet Explorer 之外的任何最新浏览器中打开example_07-03都可以看到)。

注意

Tweening

如果你还不知道(我不知道),“tweening”作为一个术语只是“inbetweening”的缩写,因为它仅仅表示从一个动画点到另一个动画点的所有中间阶段。

哇!很棒,对吧?嗯,本来可以的。尽管已经成为标准一段时间,看起来 SMIL 的日子已经不多了。

SMIL 的结束

Internet Explorer 不支持 SMIL。没有。没有。没有。我可以用其他词语来表达,但我相信你明白在这一点上 Internet Explorer 对 SMIL 的支持并不多。

更糟糕的是(我知道,我在这里给你两个枪口),微软也没有引入它的计划。看看平台状态:status.modern.ie/svgsmilanimation?term=SMIL

此外,Chrome 现在已经表示了在 Chrome 浏览器中弃用 SMIL 的意图:groups.google.com/a/chromium.org/forum/#!topic/blink-dev/5o0yiO440LM

麦克风。放下。

注意

如果你仍然需要使用 SMIL,Sara Soueidan 在css-tricks.com/guide-svg-animations-smil/写了一篇关于 SMIL 动画的优秀而深入的文章。

幸运的是,我们有很多其他方法可以使 SVG 动画,我们很快就会介绍。所以如果你必须支持 Internet Explorer,请坚持下去。

用外部样式表样式化 SVG

可以用 CSS 样式化 SVG。这可以是 SVG 本身中的 CSS,也可以是 CSS 样式表中写所有你的“正常”CSS。

现在,如果你回到本章前面的特性表,你会发现当 SVG 通过img标签或作为背景图像(除了 Internet Explorer)包含时,使用外部 CSS 样式化 SVG 是不可能的。只有当 SVG 通过object标签或inline插入时才可能。

从 SVG 链接到外部样式表有两种语法。最直接的方式是这样的(你通常会在defs部分中添加这个):

<link href="styles.css" type="text/css" rel="stylesheet"/>

这类似于 HTML5 之前我们用来链接样式表的方式(例如,注意在 HTML5 中type属性不再是必需的)。然而,尽管这在许多浏览器中有效,但这并不是规范定义外部样式表应该如何在 SVG 中链接的方式(www.w3.org/TR/SVG/styling.html)。这是正确/官方的方式,实际上在 1999 年就为 XML 定义了(www.w3.org/1999/06/REC-xml-stylesheet-19990629/):

<?xml-stylesheet href="styles.css" type="text/css"?>

需要在文件中的开头 SVG 元素上方添加。例如:

<?xml-stylesheet href="styles.css" type="text/css"?>
<svg width="198" height="188" viewBox="0 0 198 188"  >

有趣的是,后一种语法是唯一在 Internet Explorer 中有效的。所以,当你需要从 SVG 链接到样式表时,我建议使用这种第二种语法以获得更广泛的支持。

你不必使用外部样式表;如果你愿意,你可以直接在 SVG 本身中使用内联样式。

使用内部样式样式化 SVG

你可以在 SVG 中放置 SVG 的样式。它们应该放在defs元素内。由于 SVG 是基于 XML 的,最安全的做法是包含Character DataCDATA)标记。CDATA 标记简单地告诉浏览器,字符数据定界部分内的信息可能被解释为 XML 标记,但不应该。语法是这样的:

<defs>
    <style type="text/css">
        <![CDATA[
            #star_Path {
                stroke: red;
            }
        ]]>
    </style>
</defs>

CSS 中的 SVG 属性和值

注意前面代码块中的stroke属性。那不是 CSS 属性,而是 SVG 属性。无论是内联声明还是外部样式表,你都可以使用许多特定的 SVG 属性。例如,对于 SVG,你不指定background-color,而是指定fill。你不指定border,而是指定stroke-width。关于 SVG 特定属性的完整列表,请查看这里的规范:www.w3.org/TR/SVG/styling.html

使用内联或外部 CSS,可以做所有你期望的“正常”CSS 事情;改变元素的外观,动画,转换元素等等。

用 CSS 动画 SVG

让我们考虑一个快速的示例,向 SVG 中添加 CSS 动画(记住,这些样式也可以很容易地放在外部样式表中)。

让我们以本章中一直在看的星星示例为例,让它旋转。你可以在example_07-07中看到完成的示例:

<div class="wrapper">
    <svg width="198" height="188" viewBox="0 0 220 200"  >
        <title>Star 1</title>
        <defs>
            <style type="text/css">
                <![CDATA[
                @keyframes spin {
                    0% {
                        transform: rotate(0deg);
                    }
                    100% {
                        transform: rotate(360deg);
                    }
                }
                .star_Wrapper {
                    animation: spin 2s 1s;
                    transform-origin: 50% 50%;
                }
                .wrapper {
                    padding: 2rem;
                    margin: 2rem;
                }
                ]]>
            </style>
            <g id="shape">
                <path fill="#14805e" d="M50 50h50v50H50z"/>
                <circle fill="#ebebeb" cx="50" cy="50" r="50"/>
            </g>
        </defs>
        <g class="star_Wrapper" fill="none" fill-rule="evenodd">
            <path id="star_Path" stroke="#333" stroke-width="3" fill="#F8E81C" d="M99 154l-58.78 30.902 11.227-65.45L3.894 73.097l65.717-9.55L99 4l29.39 59.55 65.716 9.548-47.553 46.353 11.226 65.453z"/>
        </g>
    </svg>
</div>

如果你在浏览器中加载这个示例,在 1 秒延迟后,星星将在 2 秒内旋转一整圈。

提示

注意 SVG 上设置了50% 50%的变换原点?这是因为,与 CSS 不同,SVG 的默认transform-origin不是50% 50%(两个轴的中心),实际上是0 0(左上角)。如果不设置这个属性,星星将围绕左上角旋转。

仅使用 CSS 动画就可以对 SVG 进行相当深入的动画处理(嗯,假设您不需要担心 Internet Explorer)。然而,当您想要添加交互性、支持 Internet Explorer 或同步多个事件时,通常最好依赖 JavaScript。好消息是,有很多优秀的库可以使对 SVG 进行动画处理变得非常容易。现在让我们看一个例子。

使用 JavaScript 对 SVG 进行动画处理

通过object标签或内联插入到页面中的 SVG,可以直接或间接地使用 JavaScript 来操作 SVG。

间接地,我指的是可以使用 JavaScript 在 SVG 上方或上方更改一个类,从而导致 CSS 动画开始。例如:

svg {
    /* no animation */
}

.added-with-js svg {
    /* animation */
}

然而,也可以直接通过 JavaScript 来对 SVG 进行动画处理。

如果只需要独立地对一两个元素进行动画处理,可能通过手动编写 JavaScript 代码来减少代码量。然而,如果需要对许多元素进行动画处理或同步元素的动画处理,就可以使用 JavaScript 库。最终,您需要判断是否可以为您试图实现的目标来合理地包含库的重量。

我推荐使用 GreenSock 动画平台(greensock.com)、Velocity.js(julian.com/research/velocity/)或 Snap.svg(snapsvg.io/)来通过 JavaScript 对 SVG 进行动画处理。在下一个示例中,我们将介绍使用 GreenSock 的一个非常简单的示例。

使用 GreenSock 对 SVG 进行动画处理的一个简单示例

假设我们想制作一个界面刻度盘,当我们点击按钮时,它会从零开始动画到我们输入的任意值。我们不仅希望刻度盘的描边在长度和颜色上进行动画处理,还希望数字从零到我们输入的值进行动画处理。您可以在example_07-08中查看已完成的实现。

因此,如果我们输入了 75,并点击了动画,它会填充到如下所示:

使用 GreenSock 对 SVG 进行动画处理的简单示例

为了简洁起见,我们不列出整个 JavaScript 文件(该文件有很多注释,因此在单独阅读时应该能够理解一些),我们只考虑关键点。

基本思路是我们已经将一个圆作为 SVG 的<path>(而不是<circle>元素)制作出来。由于它是一个路径,这意味着我们可以使用stroke-dashoffset技术对路径进行动画处理。关于这种技术的更多信息,请参见下面的方框中的部分,简而言之,我们使用 JavaScript 来测量路径的长度,然后使用stroke-dasharray属性来指定线条的渲染部分的长度和间隙的长度。然后我们使用stroke-dashoffset来改变dasharray的起始位置。这意味着您可以有效地从路径的“外部”开始描边并进行动画处理。这会产生路径正在被绘制的错觉。

如果要将dasharray的动画值设置为静态的已知值,可以通过 CSS 动画和一些试错来相对简单地实现这种效果(关于 CSS 动画的更多内容将在下一章中介绍)。

然而,除了动态值之外,与我们“绘制”线条的同时,我们还希望将描边颜色从一个值淡入到另一个值,并在文本节点中直观地计数到输入值。这相当于同时摸头、搓肚子,并从 10,000 开始倒数。GreenSock 使这些事情变得非常容易(动画部分;它不会搓你的肚子或摸你的头,尽管如果需要,它可以从 10,000 开始倒数)。以下是使 GreenSock 执行所有这些操作所需的 JavaScript 代码行:

// Animate the drawing of the line and color change
TweenLite.to(circlePath, 1.5, {'stroke-dashoffset': "-"+amount, stroke: strokeEndColour});
// Set a counter to zero and animate to the input value
var counter = { var: 0 };
TweenLite.to(counter, 1.5, {
    var: inputValue, 
    onUpdate: function () {
        text.textContent = Math.ceil(counter.var) + "%";
    },
    ease:Circ.easeOut
});

实质上,通过TweenLite.to()函数,您可以传入要进行动画处理的对象、动画处理应该发生的时间以及要更改的值(以及您希望将其更改为的值)。

GreenSock 网站有出色的文档和支持论坛,因此如果你发现自己需要同时同步多个动画,请确保从你的日程表中抽出一天的时间,熟悉一下 GreenSock。

提示

如果你以前没有接触过 SVG 的“线条绘制”技术,那么它是由 Polygon 杂志推广的,当 Vox Media 动画化了 Xbox One 和 Playstation 4 游戏机的几个线条绘制时。你可以在product.voxmedia.com/2013/11/25/5426880/polygon-feature-design-svg-animations-for-fun-and-profit上阅读原始帖子。

Jake Archibald 在jakearchibald.com/2013/animated-line-drawing-svg/上也有一个关于这种技术的更详细的解释。

优化 SVG

作为尽职的开发人员,我们希望确保资产尽可能小。使用 SVG 的最简单方法是利用可以优化 SVG 文档的自动化工具。除了明显的节约,比如删除元素(例如,去除标题和描述元素),还可以执行一系列微小的优化,这些优化加起来可以使 SVG 资产更加精简。

目前,对于这个任务,我建议使用 SVGO (github.com/svg/svgo)。如果你以前从未使用过 SVGO,我建议从 SVGOMG (jakearchibald.github.io/svgomg/)开始。这是 SVGO 的基于浏览器的版本,它使你可以切换各种优化插件,并即时获得文件节省的反馈。

还记得我们在本章开头的例子星形 SVG 标记吗?默认情况下,这个简单的 SVG 大小为 489 字节。通过 SVGO 处理,可以将大小减小到 218 字节,这还保留了viewBox。这是节省了 55.42%。如果你使用了大量的 SVG 图像,这些节省可能会真正累积起来。优化后的 SVG 标记如下所示:

<svg width="198" height="188" viewBox="0 0 198 188" ><path stroke="#979797" stroke-width="3" fill="#F8E81C" d="M99 154l-58.78 30.902 11.227-65.45L3.894 73.097l65.717-9.55L99 4l29.39 59.55 65.716 9.548-47.553 46.353 11.226 65.454z"/></svg>

在使用 SVGO 之前,要注意 SVGO 的受欢迎程度,许多其他 SVG 工具也使用它。例如,前面提到的 Iconizr (iconizr.com/)工具默认情况下会将你的 SVG 文件通过 SVGO 运行,然后再创建你的资产,因此请确保你不会不必要地进行双重优化。

使用 SVG 作为滤镜

在第六章中,我们看到了 CSS 滤镜效果。然而,它们目前不受 Internet Explorer 10 或 11 的支持。如果你想在这些浏览器中享受滤镜效果,这可能会让人沮丧。幸运的是,借助 SVG 的帮助,我们也可以创建适用于 Internet Explorer 10 和 11 的滤镜,但正如以往一样,这可能并不像你想象的那样简单。例如,在example_07-05中,我们有一个页面,其中包含以下标记:

<img class="HRH" src="img/queen@2x-1024x747.png"/>

这是英国女王的一张图片。通常,它看起来是这样的:

使用 SVG 作为滤镜

现在,在示例文件夹中还有一个在defs元素中定义了滤镜的 SVG。SVG 标记如下:

<svg  version="1.1">
     <defs>
          <filter id="myfilter" x="0" y="0">  
                <feColorMatrix in="SourceGraphic" type="hueRotate" values="90" result="A"/>
                <feGaussianBlur in="A" stdDeviation="6"/>
          </filter>
     </defs>
</svg>

在滤镜中,我们首先定义了一个 90 度的色相旋转(使用feColorMatrix),然后通过result属性将该效果传递给下一个滤镜(feGaussianBlur),模糊值为 6。请注意,我在这里故意做得很重。这不会产生一个好的美学效果,但这应该让你毫无疑问地知道效果已经起作用了!

现在,我们可以不将 SVG 标记添加到 HTML 中,而是将其留在原地,并使用与上一章中看到的相同的 CSS 滤镜语法来引用它。

.HRH {
    filter: url('filter.svg#myfilter');
}

在大多数现代浏览器(Chrome,Safari,Firefox)中,这是效果:

使用 SVG 作为滤镜

遗憾的是,这种方法在 IE 10 或 11 中不起作用。然而,还有另一种实现我们目标的方法,那就是使用 SVG 自己的图像标签将图像包含在 SVG 中。在example_07-06中,我们有以下标记:

<svg height="747px" width="1024px" viewbox="0 0 1024 747"  version="1.1">
     <defs>
          <filter id="myfilter" x="0" y="0">  
                <feColorMatrix in="SourceGraphic" type="hueRotate" values="90" result="A"/>
                <feGaussianBlur in="A" stdDeviation="6"/>
          </filter>
     </defs>
     <image x="0" y="0" height="747px" width="1024px"  xlink:href="queen@2x-1024x747.png" filter="url(#myfilter)"></image>
</svg>

这里的 SVG 标记与我们在上一个示例中使用的外部filter.svg过滤器非常相似,但添加了heightwidthviewbox属性。此外,我们要对其应用过滤器的图像是 SVG 中defs元素之外的唯一内容。为了链接到过滤器,我们使用filter属性并传递我们想要使用的过滤器的 ID(在这种情况下是在上面的defs元素中)。

虽然这种方法有点复杂,但它意味着你可以获得 SVG 提供的许多不同的滤镜效果,即使在 Internet Explorer 的 10 和 11 版本中也是如此。

关于 SVG 中的媒体查询的说明

所有理解 SVG 的浏览器都应该尊重 SVG 内定义的 CSS 媒体查询。然而,当涉及到 SVG 内的媒体查询时,有一些事情需要记住。

例如,假设你在 SVG 中插入了一个媒体查询,就像这样:

<style type="text/css"><![CDATA[
    #star_Path {
        stroke: red;
    }
    @media (min-width: 800px) {
        #star_Path {
            stroke: violet;
        }
    }
]]></style>

而 SVG 在页面上以 200px 的宽度显示,而视口宽度为 1200px。

我们可能期望星星的描边在屏幕宽度为 800px 及以上时是紫色的。毕竟,这就是我们设置媒体查询的方式。然而,当 SVG 通过img标签、作为背景图像或嵌入object标签放置在页面中时,它对外部 HTML 文档一无所知。因此,在这种情况下,min-width意味着 SVG 本身的最小宽度。因此,除非 SVG 本身在页面上以 800px 或更多的宽度显示,否则描边不会是紫色的。

相反,当你内联插入 SVG 时,它会(在某种意义上)与外部 HTML 文档合并。这里的min-width媒体查询是根据视口(就像 HTML 一样)来决定何时匹配媒体查询。

为了解决这个特定的问题并使相同的媒体查询行为一致,我们可以修改我们的媒体查询为:

@media (min-device-width: 800px) {
    #star_Path {
        stroke: violet;
    }
}

这样,无论 SVG 的大小或嵌入方式如何,它都会根据设备宽度(实际上是视口)进行调整。

实施提示

现在我们几乎到了本章的结尾,还有很多关于 SVG 的内容可以讨论。因此,这时我只列出一些无关的注意事项。它们不一定值得详细解释,但我会在这里列出它们的笔记形式,以防它们能让你省去一小时的谷歌搜索:

  • 如果你不需要为 SVG 添加动画,可以选择使用图像精灵或数据 URI 样式表。这样更容易提供回退资产,并且从性能的角度来看,它们几乎总是表现更好。

  • 尽可能自动化资产创建过程中的许多步骤;这样可以减少人为错误,并更快地产生可预测的结果。

  • 要在项目中插入静态 SVG,尽量选择一种交付机制并坚持使用(图像精灵、数据 URI 或内联)。如果以一种方式生成一些资产,以另一种方式生成其他资产,并维护各种实现,这可能会成为负担。

  • SVG 动画没有一个简单的“一刀切”的选择。对于偶尔和简单的动画,使用 CSS。对于复杂的交互式或时间轴样式的动画,还可以在 Internet Explorer 中工作,依赖于像 Greensock、Velocity.js 或 Snap.svg 这样的成熟库。

进一步的资源

正如我在本章开头提到的,我既没有空间,也没有知识来传授关于 SVG 的所有知识。因此,我想让你了解以下优秀的资源,它们提供了关于这个主题的额外深度和范围:

总结

在本章中,我们已经涵盖了许多必要的信息,以便开始理解和实施响应式项目中的 SVG。我们考虑了不同的图形应用程序和在线解决方案,以创建 SVG 资产,然后考虑了可能的各种插入方法以及每种方法允许的功能,以及需要注意的各种浏览器特性。

我们还考虑了如何链接到外部样式表,并在同一页面内重复使用 SVG 符号以及在外部引用时。我们甚至研究了如何使用 SVG 制作可以在 CSS 中引用和使用的滤镜,以获得比 CSS 滤镜更广泛的支持。

最后,我们考虑了如何利用 JavaScript 库来帮助动画化 SVG,以及如何借助 SVGO 工具优化 SVG。

在下一章中,我们将研究 CSS 过渡、变换和动画。与 SVG 相关的语法和技术中有许多可以在 SVG 文档中使用和应用的内容,因此也值得阅读该章节。所以,来杯热饮(你值得拥有),我马上就会再见到你。

第八章:过渡,变换和动画

在历史上,每当需要移动或在屏幕上动画元素时,这完全是 JavaScript 的专属领域。如今,CSS 可以通过三个主要代理来处理大部分运动工作:CSS 过渡,CSS 变换和 CSS 动画。实际上,只有过渡和动画与运动直接相关,变换只是允许我们改变元素,但正如我们将看到的那样,它们经常是成功运动效果的不可或缺的部分。

为了清楚地理解每个事物的责任,我将提供这个可能过于简化的总结:

  • 当您已经有要应用运动的事物的起始状态和结束状态,并且需要一种简单的方法从一个状态过渡到另一个状态时,请使用 CSS 过渡。

  • 如果您需要在不影响页面布局的情况下在视觉上转换项目,请使用 CSS 变换。

  • 如果您想要在不同的关键点上对元素执行一系列更改,请使用 CSS 动画。

好了,我们最好继续努力,了解如何运用所有这些能力。在本章中,我们将涵盖:

  • CSS3 过渡是什么以及我们如何使用它们

  • 如何编写 CSS3 过渡及其简写语法

  • CSS3 过渡时间函数(easecubic-bezier等)

  • 响应式网站的有趣过渡效果

  • CSS3 变换是什么以及我们如何使用它们

  • 理解不同的 2D 变换(缩放旋转倾斜平移等)

  • 理解 3D 变换

  • 如何使用keyframes和 CSS3 进行动画

CSS3 过渡是什么以及我们如何使用它们

过渡是使用 CSS 创建一些视觉“效果”的最简单方法,用于在一个状态和另一个状态之间进行过渡。让我们考虑一个简单的例子,当悬停时,一个元素从一个状态过渡到另一个状态。

在 CSS 中为超链接设置样式时,常见做法是创建悬停状态;这是一种明显的方式,可以让用户意识到他们悬停在的项目是一个链接。悬停状态对于越来越多的触摸屏设备来说并不重要,但对于鼠标用户来说,它们是网站和用户之间的一个很好且简单的交互。它们也很方便用于说明过渡效果,这就是我们将要开始的地方。

传统上,仅使用 CSS,悬停状态是一个开/关的事情。元素上有一组默认的属性和值,当指针悬停在该元素上时,属性和值会立即更改。然而,正如其名称所示,CSS3 过渡允许我们在一个或多个属性和值之间过渡到其他属性和值。

提示

首先要知道的是,您不能从display: none;进行过渡。当某物设置为display: none;时,它实际上没有在屏幕上“绘制”,因此没有现有状态可以进行过渡。为了创建某物淡入的效果,您必须过渡不透明度或位置值。其次,并非所有属性都可以进行过渡。为了确保您不会尝试不可能的事情,这是可过渡的属性列表:www.w3.org/TR/css3-transitions/

如果您打开example_08-01,您会看到nav中有一些链接。以下是相关的标记:

<nav>
    <a href="#">link1</a>
    <a href="#">link2</a>
    <a href="#">link3</a>
    <a href="#">link4</a>
    <a href="#">link5</a>
</nav>

这是相关的 CSS:

a {
    font-family: sans-serif;
    color: #fff;
    text-indent: 1rem;
    background-color: #ccc;
    display: inline-flex;
    flex: 1 1 20%;
    align-self: stretch;
    align-items: center;
    text-decoration: none;
    transition: box-shadow 1s;
}

a + a {
    border-left: 1px solid #aaa;
}

a:hover {
    box-shadow: inset 0 -3px 0 #CC3232;
}

这是两种状态,首先是默认状态:

CSS3 过渡是什么以及我们如何使用它们

然后这是悬停状态:

CSS3 过渡是什么以及我们如何使用它们

在这个例子中,当链接悬停时,我们在底部添加了一个红色的阴影(我选择了一个阴影,因为它不会像边框一样影响链接的布局)。通常,悬停在链接上会从第一个状态(没有红线)转换到第二个状态(红线);这是一个开/关的事情。然而,这一行:

transition: box-shadow 1s;

box-shadow从现有状态过渡到悬停状态,持续 1 秒。

提示

你会注意到在前面示例的 CSS 中,我们使用了相邻兄弟选择器+。这意味着如果一个选择器(在我们的示例中是锚点标签)直接跟在另一个选择器(另一个锚点标签)后面,那么应用封闭的样式。这在这里很有用,因为我们不希望第一个元素有左边框。

请注意,过渡属性应用于元素的原始状态,而不是元素最终的状态。简而言之,在“from”状态上应用过渡声明,而不是“to”状态。这样不同的状态,比如:active,也可以有不同的样式设置,并享受相同的过渡。

过渡的属性

可以使用最多四个属性声明过渡:

  • transition-property:要过渡的 CSS 属性的名称(例如background-colortext-shadowall以过渡每个可能的属性)。

  • transition-duration:过渡应该发生的时间长度(以秒为单位,例如.3s2s1.5s)。

  • transition-timing-function:过渡在持续时间内如何改变速度(例如easelinearease-inease-outease-in-outcubic-bezier)。

  • transition-delay:确定过渡开始之前的延迟的可选值。或者,可以使用负值立即开始过渡,但在过渡的“旅程”中间。它以秒为单位,例如.3s1s2.5s

单独使用,各种过渡属性可以创建这样的过渡:

.style {
    /*...(more styles)...*/
    transition-property: all;
    transition-duration: 1s;
    transition-timing-function: ease;
    transition-delay: 0s;
}

过渡的简写属性

我们可以将这些单独的声明合并成一个简写版本:

transition: all 1s ease 0s;

在写简写版本时要注意的一个重要点是,给出的第一个与时间相关的值总是被视为transition-duration。第二个与时间相关的值被视为transition-delay。我通常更喜欢简写版本,因为我通常只需要定义过渡的持续时间和应该过渡的属性。

这只是一个小问题,但是只定义你实际需要过渡的属性。只设置all非常方便,但如果你只需要过渡不透明度,那么只定义不透明度作为过渡属性。否则,你会让浏览器比必要的工作更加艰难。在大多数情况下,这并不是什么大问题,但是如果你希望在老设备上尽可能地提高性能,那么每一点都有帮助。

提示

过渡非常受支持,但是要确保你有像 Autoprefixer 这样的工具设置好,以添加任何与你需要支持的浏览器相关的供应商前缀。你也可以在caniuse.com上检查哪些浏览器支持各种功能。

简写版本:

过渡和 2D 变换在 IE9 及以下版本之外都可以工作,3D 变换在 IE9 及以下版本、Android 2.3 及以下版本以及 Safari 3.2 及以下版本之外都可以工作。

在不同的时间段内过渡不同的属性

当一个规则有多个声明的属性时,你不必以相同的方式过渡所有这些属性。考虑这条规则:

.style {
    /* ...(more styles)... */
    transition-property: border, color, text-shadow;
    transition-duration: 2s, 3s, 8s; 
}

在这里,我们已经指定了我们希望过渡bordercolortext-shadowtransition-property。然后在transition-duration声明中,我们规定了边框应该在 2 秒内过渡,颜色在 3 秒内过渡,文本阴影在 8 秒内过渡。逗号分隔的持续时间与逗号分隔的过渡属性的顺序相匹配。

理解时间函数

当你声明一个过渡时,属性、持续时间和延迟相对简单理解。然而,理解每个时间函数的作用可能会有点棘手。easelinearease-inease-outease-in-outcubic-bezier到底是什么?它们实际上都是预定义的三次贝塞尔曲线,本质上与缓动函数相同。或者更简单地说,这是过渡应该如何呈现的数学描述。通常更容易可视化这些曲线,所以我建议你去cubic-bezier.com/easings.net/看一看。

这两个网站都可以让你比较时间函数,并看到每个时间函数的区别。这是easings.net的截图——你可以悬停在每条线上演示缓动函数。

理解时间函数

然而,即使你能闭着眼睛写出自己的三次贝塞尔曲线,对于大多数实际情况来说,这可能并没有太大的区别。原因是,像任何增强功能一样,必须谨慎地使用过渡效果。对于“真实世界”的实现,过长的过渡时间会让网站感觉缓慢。例如,需要 5 秒才能完成过渡的导航链接会让用户感到沮丧,而不是惊叹。速度的感知对我们的用户非常重要,你和我必须集中精力让网站和应用程序尽可能地快。

因此,除非有充分的理由这样做,通常最好在短时间内使用默认的过渡(ease);我个人偏好最长 1 秒。

响应式网站的有趣过渡效果

你是否在成长过程中有过这样的情况,一个父母出门了,另一个父母说了类似这样的话:“好吧,你妈妈/爸爸出门了,我们要在你的早餐麦片上撒满糖,但你要答应他们回来后不告诉他们”?我肯定对我的孩子们做过这样的事。所以这样吧,趁没人注意,让我们玩一点。我不建议在生产中这样做,但是尝试将这个添加到你的响应式项目中。

* {
    transition: all 1s; 
}

在这里,我们使用 CSS 通用选择器*选择所有内容,然后为所有属性设置 1 秒的过渡时间(1s)。由于我们没有指定时间函数,因此默认情况下将使用 ease,并且如果没有添加替代值,则默认为 0 延迟。效果如何?嗯,尝试调整浏览器窗口大小,大多数东西(链接、悬停状态等)的行为都如你所期望的那样。然而,因为一切都在过渡,这也包括媒体查询中的任何规则,因此随着浏览器窗口的调整,元素会从一种状态流动到另一种状态。这是必要的吗?绝对不是!但是看起来很有趣,可以玩一下!现在,在你妈妈看到之前,删除这条规则!

CSS3 2D 变换

尽管听起来相似,但 CSS 变换与 CSS 过渡完全不同。可以这样理解:过渡使元素从一种状态平滑地转换到另一种状态,而变换则定义了元素实际上会变成什么样。我自己(虽然有点幼稚)记住这个区别的方式是这样的:想象一个变形金刚机器人,比如大黄蜂。当他变成卡车时,他已经变形了。然而,从机器人到卡车的过程是一个过渡(他正在从一种状态过渡到另一种状态)。

显然,如果你根本不知道奥普蒂默斯·普莱姆是谁或是什么,可以随意忽略最后几句。希望一切很快就会变得清晰起来。

有两组可用的 CSS3 变换:2D 和 3D。 2D 变体在浏览器方面得到了更广泛的实现,并且肯定更容易编写,所以让我们首先看看这些。 CSS3 2D 变换模块允许我们使用以下变换:

  • 缩放:用于缩放元素(放大或缩小)

  • 平移:在屏幕上移动元素(上,下,左和右)

  • rotate:按指定的角度旋转元素

  • 倾斜:用于倾斜具有其 x 和 y 坐标的元素

  • 矩阵:允许您以像素精度移动和形状变换

提示

重要的是要记住,变换发生在文档流之外。 任何被转换的元素都不会影响附近未被转换的元素的位置。

让我们尝试各种 2D 转换。 您可以通过在浏览器中打开example_08-02来测试这些转换中的每一个。 对所有变换应用了过渡,因此您可以更好地了解发生了什么。

缩放

这是scale的语法:

.scale:hover {
    transform: scale(1.4);
}

在我们的示例中悬停在“缩放”链接上会产生这种效果:

缩放

我们告诉浏览器,当悬停在此元素上时,我们希望元素的比例放大到原始值的 1.4 倍。

除了我们已经用来放大元素的值之外,通过使用小于 1 的值,我们可以缩小元素; 以下将使元素缩小到其一半大小:

transform: scale(0.5);

翻译

这是translate的语法:

.translate:hover {
    transform: translate(-20px, -20px);
}

这是我们的示例中该规则的效果:

翻译

translate属性告诉浏览器按指定的像素或百分比移动元素。 第一个值是x轴,第二个值是y轴。 括号中给出的正值将使元素向右或向下移动; 负值将使其向左或向上移动。

如果只传递一个值,则应用于x轴。 如果要指定一个轴来平移元素,还可以使用translateXtranslateY

使用 translate 将绝对定位的元素居中

translate提供了一种非常有用的方法,可以在相对定位的容器内居中绝对定位的元素。 您可以在example_08-03中查看此示例。

考虑以下标记:

<div class="outer">
    <div class="inner"></div>
</div>

然后是这个 CSS:

.outer {
    position: relative;
    height: 400px;
    background-color: #f90;
}

.inner {
    position: absolute;
    height: 200px;
    width: 200px;
    margin-top: -100px;
    margin-left: -100px;
    top: 50%;
    left: 50%;
}

您可能自己做过类似的事情。 当绝对定位元素的尺寸已知(在这种情况下为 200px x 200px)时,我们可以使用负边距将项目“拉回”到中心。 但是,当您想要包含内容并且无法知道其高度时会发生什么? 变换来拯救。

让我们在内部框中添加一些随机内容:

使用 translate 将绝对定位的元素居中

是的,就是那个问题! 好吧,让我们使用transform来解决这个问题。

.inner {
    position: absolute;
    width: 200px;
    background-color: #999;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

以下是结果:

使用 translate 将绝对定位的元素居中

在这里,topleft定位内部框在其容器内,使内部框的左上角从外部向下 50%处和向右 50%处开始。 然后transform在内部元素上起作用,并通过其自身宽度和高度的一半(-50%)在这些轴上定位。 很好!

旋转

rotate变换允许您旋转元素。 这是语法:

.rotate:hover {
    transform: rotate(30deg);
}

在浏览器中,发生了什么:

旋转

括号中的值应始终为度数(例如,90 度)。 正值始终顺时针应用,使用负值将使元素逆时针旋转。 您还可以通过指定以下值来使元素旋转:

transform: rotate(3600deg);

这将使元素在一个完整的圆圈中旋转 10 次。 对于这个特定值的实际用途很少,但是您知道,如果您发现自己为风车公司设计网站,它可能会派上用场。

倾斜

如果您在 Photoshop 中工作过一段时间,您可能对skew会做什么有一个很好的想法。它允许元素在其一个或两个轴上倾斜。这是我们示例的代码:

.skew:hover {
    transform: skew(40deg, 12deg);
}

将其设置为悬停链接会产生以下悬停效果:

倾斜

第一个值是应用于x轴的skew(在我们的示例中为 40 度),而第二个值(12 度)是应用于y轴的。省略第二个值意味着任何值仅仅应用于x轴(水平)。例如:

transform: skew(10deg);

矩阵

有人提到了一部被高估的电影吗?没有?什么?你想了解 CSS3 矩阵,而不是电影?好的。

我不会撒谎。我觉得矩阵变换语法看起来很可怕。这是我们的示例代码:

.matrix:hover {
    transform: matrix(1.678, -0.256, 1.522, 2.333, -51.533, -1.989);
}

它基本上允许您将许多其他变换(scalerotateskew等)组合成一个声明。前面的声明会在浏览器中产生以下效果:

矩阵

现在,我喜欢挑战,就像其他人一样(除非,你知道,是坐在暮光之城电影里),但我相信我们可以一致同意这个语法有点考验。对我来说,当我看规范并意识到它涉及超出我基本水平的数学知识时,情况变得更糟:www.w3.org/TR/css3-2d-transforms/

提示

如果您发现自己在 JavaScript 中进行动画工作而没有动画库的帮助,您可能需要更加熟悉矩阵。它是所有其他变换计算出的语法,因此如果您使用 JavaScript 获取动画的当前状态,您需要检查和理解的将是矩阵值。

作弊者和蠢蛋的矩阵变换

我绝对不是数学家,所以当需要创建基于矩阵的变换时,我会作弊。如果您的数学技能也不足,我建议您前往www.useragentman.com/matrix/

Matrix Construction Set 网站允许您将元素拖放到您想要的位置,然后在 CSS 文件中包括好的复制和粘贴代码(包括供应商前缀)。

transform-origin 属性

请注意,使用 CSS 时,默认的变换原点(浏览器用作变换中心的点)位于中间:元素的x轴和y轴上分别为 50%。这与 SVG 不同,后者默认为左上角(或 0 0)。

使用transform-origin属性,我们可以修改变换的起始点。

考虑我们之前的矩阵变换。默认的transform-origin是'50% 50%'(元素的中心)。Firefox 开发者工具显示了transform是如何应用的:

transform-origin 属性

现在,如果我们像这样调整transform-origin

.matrix:hover {
   transform: matrix(1.678, -0.256, 1.522, 2.333, -51.533, -1.989);
   transform-origin: 270px 20px;
}

然后你可以看到这样的效果:

transform-origin 属性

第一个值是水平偏移,第二个值是垂直偏移。您可以使用关键字。例如,left 等于 0%水平,right 等于 100%水平,top 等于 0%垂直,bottom 等于 100%垂直。或者,您可以使用长度,使用任何 CSS 长度单位。

如果您在transform-origin值中使用百分比,则水平/垂直偏移是相对于元素边界框的高度/宽度的。

如果您使用长度,则值是从元素边界框的左上角开始测量的。

有关transform-origin属性的完整信息可以在www.w3.org/TR/css3-2d-transforms/找到。

这涵盖了 2D 变换的基本知识。它们比它们的 3D 兄弟更广泛地实现,并提供了一个比旧方法(如绝对定位)更好的移动元素在屏幕上的方法。

阅读 CSS3 2D Transforms Module Level 3 的完整规范,请访问www.w3.org/TR/css3-2d-transforms/

提示

有关使用transform移动元素的好处,请参阅 Paul Irish 的一篇很棒的文章(www.paulirish.com/2012/why-moving-elements-with-translate-is-better-than-posabs-topleft/),其中提供了一些很好的数据。

此外,关于浏览器如何处理过渡和动画,以及变换为何如此有效的概述,我强烈推荐阅读以下博客文章:blogs.adobe.com/webplatform/2014/03/18/css-animations-and-transitions-performance/

CSS3 3D transformations

让我们看看我们的第一个示例。当我们悬停在元素上时翻转的元素。我在这里使用悬停来调用更改,因为这对于说明来说很简单,然而翻转动作也可以通过类更改(通过 JavaScript)或当元素获得焦点时轻松地启动。

我们将有两个这样的元素;一个是水平翻转元素,一个是垂直翻转元素。您可以在example_08-04中查看最终示例。图片无法完全传达这种技术,但想法是元素从绿色“面”翻转到红色“面”,并在透视的帮助下产生在 3D 空间中进行翻转的错觉。这是从绿色到红色过渡的一部分效果。

CSS3 3D transformations

提示

值得知道的是,虽然使用 top/left/bottom/right 值绝对定位元素是以像素为单位的,但变换可以在亚像素位置进行插值。

以下是翻转元素的标记:

<div class="flipper">
    <span class="flipper-object flipper-vertical">
        <span class="panel front">The Front</span>
        <span class="panel back">The Back</span>
    </span>
</div>

水平翻转元素的唯一区别是标记上的flipper-horizontal类,而不是flipper-vertical

由于大多数样式与美学有关,我们只会看一下样式中使翻转效果成为可能的基本要素。有关美学样式,请参考示例中的完整样式表。

首先,我们需要为.flipper-object设置一些透视,以便在其中进行翻转。为此,我们使用perspective属性。这需要一个长度,试图模拟观看者屏幕到元素 3D 空间边缘的距离。

如果您设置了一个低值,比如 20px 作为透视值,元素的 3D 空间将延伸到距离屏幕仅 20px 的地方;结果是非常明显的 3D 效果。另一方面,设置一个高值将意味着那个想象的 3D 空间的边缘将更远,因此产生一个不太明显的 3D 效果。

.flipper {
    perspective: 400px;
    position: relative;
}

我们将相对定位外部元素,以创建flipper-object在其中定位的上下文:

.flipper-object {
    position: absolute;
    transition: transform 1s;
    transform-style: preserve-3d;
}

除了将.flipper-object绝对定位在其最近的相对定位的父元素的左上角(绝对定位元素的默认位置)之外,我们还为变换设置了一个过渡。在 3D 方面,关键的是transform-styles: preserve-3d。这告诉浏览器,当我们变换这个元素时,我们希望任何子元素都保持 3D 效果。

如果我们没有在.flipper-object上设置preserve-3d,我们将永远看不到翻转元素的背面(红色部分)。您可以在www.w3.org/TR/2009/WD-css3-3d-transforms-20090320/上阅读此属性的规范。

我们翻转元素中的每个“面板”都需要定位在其容器的顶部,但我们也希望确保如果旋转了,我们不会看到它的“后面”(否则我们永远看不到绿色面板,因为它位于红色面板的“后面”)。为此,我们使用backface-visibility属性。我们将其设置为隐藏,以便元素的背面被隐藏:

.panel {
    top: 0;
    position: absolute;
    backface-visibility: hidden;
}

提示

我发现backface-visibility在一些浏览器中实际上有一些令人惊讶的副作用。它特别适用于改善旧版 Android 设备上固定位置元素的性能。有关更多信息以及它为什么会产生这种效果,请查看这篇文章:benfrain.com/easy-css-fix-fixed-positioning-android-2-2-2-3/和这篇文章:benfrain.com/improving-css-performance-fixed-position-elements/

接下来,我们希望默认情况下翻转我们的后面板(这样当我们翻转整个东西时,它实际上会处于正确的位置)。为此,我们应用了rotate变换:

.flipper-vertical .back {
    transform: rotateX(180deg);
}

.flipper-horizontal .back {
    transform: rotateY(180deg);
}

现在一切都就绪,我们要做的就是在悬停在外部元素上时翻转整个内部元素:

.flipper:hover .flipper-vertical {
    transform: rotateX(180deg);
}

.flipper:hover .flipper-horizontal {
    transform: rotateY(180deg);
}

您可以想象有无数种方式可以使用这些原则。如果您想知道带有一点透视效果的花哨导航效果或离屏菜单可能会是什么样子,我强烈建议您访问 Codrops:tympanus.net/Development/PerspectivePageViewNavigation/index.html

提示

阅读有关 CSS Transforms Module Level 1 的最新 W3C 发展情况:dev.w3.org/csswg/css-transforms/

transform3d 属性

除了使用透视,我还发现transform3d值非常有用。通过单个属性和值,这允许您在 X(左/右)、Y(上/下)和 Z(前/后)轴上移动元素。让我们修改我们的最后一个示例,利用translate3d变换。您可以在example_08-06中查看此示例。

除了用一点填充设置元素外,我们的上一个示例的唯一变化可以在这里看到:

.flipper:hover .flipper-vertical {
    transform: rotateX(180deg) translate3d(0, 0, -120px);
    animation: pulse 1s 1s infinite alternate both;
}

.flipper:hover .flipper-horizontal {
    transform: rotateY(180deg) translate3d(0, 0, 120px);
    animation: pulse 1s 1s infinite alternate both;
}

我们仍然应用变换,但这次,除了我们的旋转之外,我们还添加了translate3d。您可以传递给translate3d的逗号分隔的“参数”的语法是x轴移动、y轴移动和z轴移动。

在我们的两个示例中,我没有在xy轴(左右和上下)移动元素,而是向您移动或远离您。

如果您查看顶部示例,您会看到它在底部按钮后面翻转,并最终离屏幕更近 120 像素(负值实际上将其向您拉回)。

transform3d 属性

另一方面,底部按钮水平翻转,最终距离您 120 像素。

transform3d 属性

提示

您可以在www.w3.org/TR/css3-3d-transforms/阅读translate3d的规范。

使用渐进增强的转换

我发现transform3d最有用的地方是在屏幕上滑动面板,特别是“离屏”导航模式。如果你打开example_08-07,你会看到我创建了一个基本的、逐步增强的离屏模式。

每当您使用 JavaScript 和现代 CSS 功能(如变换)创建交互时,考虑从您想要支持的最低可能设备的角度是有意义的。那么那两个没有 JavaScript 的人怎么办(是的,那些家伙),或者如果 JavaScript 加载或执行时出现问题怎么办?如果某人的设备不支持变换(例如 Opera Mini)怎么办?别担心,通过一点努力,可以确保每种情况下都有一个可用的界面。

在构建这种界面模式时,我发现从最低级别的功能开始,并从那里进行增强是最有用的。因此,首先确定如果没有 JavaScript 可用,某人会看到什么。毕竟,如果显示菜单的方法依赖于 JavaScript,将菜单停放在屏幕外是没有用的。在这种情况下,我们依赖于标记来将导航区域放在正常的文档流中。最坏的情况是,无论视口宽度如何,他们只需滚动页面并点击链接即可:

使用渐进增强的变换

如果 JavaScript 可用,对于较小的屏幕,我们将菜单“拉”到左侧。当单击菜单按钮时,我们在body标签上添加一个类(使用 JavaScript),并使用这个类作为钩子,通过 CSS 将导航移回视图中。

使用渐进增强的变换

对于较大的视口,我们隐藏菜单按钮,仅将导航定位到左侧,并移动主要内容以适应。

使用渐进增强的变换

然后我们逐步增强导航显示/隐藏效果。这就是像 Modernizr 这样的工具真正发挥作用的地方;通过向 HTML 标签添加类,我们可以用作样式钩子(Modernizr 在第五章中有更详细的讨论,“CSS3 – 选择器、排版、颜色模式和新功能”)。

首先,对于只支持 translate 变换的浏览器(例如旧版 Android),简单的translateX

.js .csstransforms .navigation-menu {
    left: auto;
    transform: translateX(-200px);
}

对于支持translate3d的浏览器,我们使用translate3d。在支持的情况下,这将表现得更好,因为大多数设备上的图形处理器会卸载它:

.js .csstransforms3d .navigation-menu {
    left: auto;
    transform: translate3d(-200px, 0, 0);
}

采用渐进增强方法可以确保尽可能广泛的受众能够从您的设计中获得可用的体验。记住,用户不需要视觉一致性,但他们可能会欣赏功能一致性。

使用 CSS3 进行动画制作

如果你曾经使用过 Flash、Final Cut Pro 或 After Effects 等应用程序,那么在使用 CSS3 动画时,你会立即获得优势。CSS3 采用了时间轴应用程序中找到的动画关键帧约定。

动画广泛实现;在 Firefox 5+、Chrome、Safari 4+、Android(所有版本)、iOS(所有版本)和 Internet Explorer 10+中都受支持。CSS3 动画有两个组成部分;首先是keyframes声明,然后在animation属性中使用该keyframes声明。让我们来看看。

在之前的示例中,我们对结合变换和过渡的元素进行了简单的翻转效果。让我们将本章学到的所有技术结合起来,并为之前的示例添加动画。在下一个示例中,example_08-05,让我们在元素翻转后添加一个脉动动画效果。

首先,我们将创建一个keyframes at-rule:

@keyframes pulse {
  100% {
    text-shadow: 0 0 5px #bbb;
    box-shadow: 0 0 3px 4px #bbb;
  }
}

如您所见,在编写@keyframes来定义新的keyframes at-rule 后,我们为这个特定的动画命名(在这种情况下是 pulse)。

通常最好使用代表动画功能的名称,而不是你打算在哪里使用动画的名称,因为单个@keyframes规则可以在项目中使用多次。

在这里,我们使用了一个单一的关键帧选择器:100%。但是,在keyframes规则中,您可以设置尽可能多的关键帧选择器(定义为百分比点)。将这些想象成时间轴上的点。例如,在 10%处,使背景变蓝,在 30%处,使背景变紫,在 60%处,使元素半透明。您需要多少就设置多少。还有关键字 from,相当于 0%,to,相当于 100%。您可以这样使用它们:

@keyframes pulse {
  to {
    text-shadow: 0 0 5px #bbb;
    box-shadow: 0 0 3px 4px #bbb;
  }
}

但是要注意,WebKit 浏览器(iOS,Safari)并不总是对 from 和 to 值(更喜欢 0%和 100%)很友好,所以我建议坚持使用百分比关键帧选择器。

在这里,您会注意到我们没有费心定义起点。那是因为起点是每个属性已经处于的状态。这是规范的一部分,解释了这一点:www.w3.org/TR/css3-animations/

注意

如果未指定0%from关键帧,则用户代理将使用正在动画化的属性的计算值构造0%关键帧。如果未指定100%to关键帧,则用户代理将使用正在动画化的属性的计算值构造100%关键帧。如果关键帧选择器指定负百分比值或高于100%的值,则将忽略该关键帧。

在这个keyframes at-rule 中,我们在 100%处添加了 text-shadow 和 box-shadow。然后,我们可以期望keyframes应用到元素时,将文本阴影和框阴影动画到定义的程度。但是动画持续多久?我们如何使其重复,反转,以及其他可能性,我希望有答案?这就是我们实际应用keyframes动画的方法:

.flipper:hover .flipper-horizontal {
    transform: rotateY(180deg);
    animation: pulse 1s 1s infinite alternate both;
}

这里的animation属性被用作多个与动画相关的属性的速记。在这个例子中,我们实际上是按顺序声明了要使用的keyframes声明的名称(pulse),animation-duration(1 秒),动画开始前的延迟(1 秒,以便我们的按钮首先翻转的时间),动画将运行的次数(无限次),动画的方向(交替,所以它先沿着一条路线动画,然后返回另一条路线),以及我们希望animation-fill-mode保留在keyframes中定义的值,无论是向前还是向后(两者都是)。

速记属性实际上可以接受所有七个动画属性。除了前面示例中使用的属性之外,还可以指定animation-play-state。这可以设置为 running 或 paused,以有效地播放和暂停动画。当然,您不需要使用速记属性;有时分别设置每个属性可能更有意义(并且在将来重新访问代码时可能会有所帮助)。以下是各个属性以及在适当的情况下,用管道符号分隔的备用值:

.animation-properties {
    animation-name: warning;
    animation-duration: 1.5s;
    animation-timing-function: ease-in-out;
    animation-iteration-count: infinite;
    animation-play-state: running | paused;
    animation-delay: 0s;
    animation-fill-mode: none | forwards | backwards | both;
    animation-direction: normal | reverse | alternate | alternate-reverse;
}

注意

您可以在www.w3.org/TR/css3-animations/上阅读每个这些动画属性的完整定义。

如前所述,可以简单地在其他元素上重用已声明的keyframes,并且具有完全不同的设置:

.flipper:hover .flipper-vertical {
    transform: rotateX(180deg);
    animation: pulse 2s 1s cubic-bezier(0.68, -0.55, 0.265, 1.55) 5 alternate both;
}

在这里,pulse动画将持续 2 秒,并使用 ease-in-out-back 时间函数(定义为三次贝塞尔曲线)。它在两个方向上各运行五次。这个声明已经应用到示例文件中垂直翻转的元素上。

这只是使用 CSS 动画的一个非常简单的例子。几乎任何东西都可以成为关键帧,可能性非常广泛。阅读有关 CSS3 动画的最新发展,访问dev.w3.org/csswg/css3-animations/

animation-fill-mode 属性

animation-fill-mode属性值得特别一提。考虑一个动画,从黄色背景开始,经过 3 秒动画到红色背景。你可以在example_08-08中查看。

我们这样应用动画:

.background-change {
  animation: fillBg 3s;
  height: 200px;
  width: 400px;
  border: 1px solid #ccc;
}

@keyframes fillBg {
  0% {
    background-color: yellow;
  }
  100% {
    background-color: red;
  }
}

然而,一旦动画完成,div的背景将返回到原来的状态。这是因为默认情况下,“动画之外发生的事情,留在动画之外”!为了覆盖这种行为,我们有animation-fill-mode属性。在这种情况下,我们可以应用这样的属性:

animation-fill-mode: forwards;

这使得项目保留了动画结束时应用的任何值。在我们的例子中,div将保留动画结束时的红色背景颜色。有关animation-fill-mode属性的更多信息,请参阅:www.w3.org/TR/css3-animations/#animation-fill-mode-property

animation-fill-mode 属性

总结

填写多本书来覆盖 CSS 变换、过渡和动画的可能性是完全可能的。然而,希望通过本章的涉水,你能够掌握基础知识并运用它们。最终,通过拥抱 CSS 的这些新特性和技术,目标是使响应式设计比以往更加精简且丰富,而不是使用 JavaScript 来实现一些更花哨的美学增强。

在本章中,我们学习了 CSS3 过渡是什么以及如何编写它们。我们掌握了像 ease 和 linear 这样的时间函数,然后使用它们创建简单但有趣的效果。然后我们学习了所有关于scaleskew这样的 2D 变换,以及如何与过渡一起使用它们。我们还简要介绍了 3D 变换,然后学习了 CSS 动画的强大和相对简单。你最好相信我们的 CSS3 技能正在增长!

然而,如果有一个网站设计领域,我尽量避免的,那就是制作表单。我不知道为什么,我总是觉得制作它们是一项乏味且令人沮丧的任务。当我得知 HTML5 和 CSS3 可以比以往更容易地构建、样式化甚至验证(是的,验证!)整个表单过程时,我感到非常高兴。在下一章中,我想与你分享这些知识。

第九章:使用 HTML5 和 CSS3 征服表单

在 HTML5 之前,添加诸如日期选择器、占位文本和范围滑块到表单中总是需要 JavaScript。同样,我们无法轻松地告诉用户我们希望他们在某些输入字段中输入什么,例如,我们是希望用户输入电话号码、电子邮件地址还是 URL。好消息是,HTML5 在很大程度上解决了这些常见问题。

本章有两个主要目标。首先,了解 HTML5 表单功能,其次,了解如何使用最新的 CSS 功能为多个设备更简单地布局表单。

在本章中,我们将学习如何:

  • 轻松地在相关的表单输入字段中添加占位文本

  • 在必要时禁用表单字段的自动完成

  • 在提交之前设置某些字段为必填项

  • 指定不同的输入类型,如电子邮件、电话号码和 URL

  • 为了方便选择数值,创建数字范围滑块

  • 将日期和颜色选择器放入表单中

  • 学习如何使用正则表达式来定义允许的表单值

  • 如何使用 Flexbox 样式化表单

HTML5 表单

我认为理解 HTML5 表单的最简单方法是通过一个示例表单逐步进行。从最好的日间电视示例中,我有一个之前制作的。需要一个小的介绍。

两个事实:首先,我喜欢电影。其次,我对什么是一部好电影,什么不是有很强烈的意见。

每年奥斯卡提名公布时,我总是忍不住觉得奥斯卡学院选错了电影。因此,我们将从一个 HTML5 表单开始,让影迷们发泄对奥斯卡提名持续不公的不满。

它由几个fieldset元素组成,在其中我们包括了大量的 HTML5 表单输入类型和属性。除了标准的表单输入字段和文本区域,我们还有一个数字微调器、一个范围滑块,以及许多字段的占位文本。

在 Chrome 中没有应用样式的情况下是这样的:

HTML5 表单

如果我们“聚焦”在第一个字段上并开始输入文本,占位文本将被移除。如果我们在不输入任何内容的情况下失去焦点(再次点击输入框外部),占位文本将重新出现。如果我们提交表单(没有输入任何内容),则会发生以下情况:

HTML5 表单

令人振奋的消息是,所有这些用户界面元素,包括前面提到的滑块、占位文本和微调器,以及输入验证,都是由浏览器通过 HTML5 原生处理的,而无需 JavaScript。现在,表单验证并不完全跨浏览器兼容,但我们很快就会解决这个问题。首先,让我们了解所有与表单相关的 HTML5 的新功能,以及使所有这些成为可能的机制。一旦我们了解了所有的机制,我们就可以开始着手进行样式设计。

理解 HTML5 表单的组成部分

我们的 HTML5 动力表单中有很多内容,让我们来分解一下。表单的三个部分都包裹在一个带有标题的fieldset中:

<fieldset>
<legend>About the offending film (part 1 of 3)</legend>
<div>
  <label for="film">The film in question?</label>
  <input id="film" name="film" type="text" placeholder="e.g. King Kong" required>
</div>

从前面的代码片段中可以看到,表单的每个输入元素也都包裹在一个带有与每个输入相关联的标签的div中(如果我们也想的话,我们也可以用标签元素包装输入)。到目前为止,一切都很正常。然而,在这个第一个输入中,我们刚刚遇到了我们的第一个 HTML5 表单功能。在常见的 ID、名称和类型属性之后,我们有placeholder

占位文本

placeholder属性看起来是这样的:

placeholder="e.g. King Kong"

表单字段内的占位文本是一个如此常见的需求,以至于创建 HTML5 的人们决定它应该成为 HTML 的一个标准特性。只需在输入中包含placeholder属性,该值将默认显示,直到字段获得焦点。当失去焦点时,如果没有输入值,它将重新显示占位文本。

样式化占位文本

您可以使用:placeholder-shown伪选择器样式化placeholder属性。请注意,此选择器经历了许多迭代,因此请确保您已设置前缀工具,以提供已实现版本的回退选择器。

input:placeholder-shown {
  color: #333;
}

在上一个代码片段中的placeholder属性之后,下一个 HTML5 表单功能是required属性。

必需的

required属性看起来像这样:

required

在支持 HTML5 的浏览器中,通过在input元素内添加布尔值(意味着您只需包含属性或不包含属性),可以指示需要输入值的required属性。如果在不包含必要信息的字段提交表单,则应显示警告消息。显示的消息对于使用的浏览器和输入类型都是特定的(在内容和样式上)。

我们已经看到了required字段在 Chrome 中的浏览器消息是什么样子。以下截图显示了 Firefox 中相同的消息:

必需的

required值可以与许多输入类型一起使用,以确保输入值。需要注意的例外是rangecolorbuttonhidden输入类型,因为它们几乎总是具有默认值。

自动聚焦

HTML5 的autofocus属性允许表单已经聚焦在一个字段上,准备好接受用户输入。以下代码是一个在div中添加了autofocus属性的input字段的示例:

<div>
  <label for="search">Search the site...</label>
  <input id="search" name="search" type="search" placeholder="Wyatt Earp" autofocus>
</div>

在使用此属性时要小心。如果多个字段都添加了autofocus属性,则可能会在多个浏览器中引起混乱。例如,如果多个字段都添加了autofocus,在 Safari 中,具有autofocus属性的最后一个字段在页面加载时会聚焦。然而,Firefox 和 Chrome 在第一个autofocus字段被选中时会做相反的操作。

还值得考虑的是,一些用户在加载网页后会使用空格键快速跳过内容。在一个具有自动聚焦输入字段的表单页面上,它会阻止这种功能;相反,它会在聚焦的输入字段中添加一个空格。很容易看出这可能会成为用户的挫折之源。

如果使用autofocus属性,请确保它在表单中只使用一次,并确保您了解使用空格键滚动的用户的影响。

自动完成

默认情况下,大多数浏览器通过自动填充表单字段的值来帮助用户输入。虽然用户可以在浏览器中打开或关闭此偏好设置,但现在我们还可以指示浏览器在我们不希望表单或字段允许自动完成时。这不仅对于敏感数据(例如银行账号)有用,而且还可以确保用户注意并手动输入内容。例如,对于我填写的许多表单,如果需要电话号码,我会输入一个“欺骗”电话号码。我知道我不是唯一这样做的人(难道不是每个人都这样吗?),但我可以通过在相关输入字段上将autocomplete属性设置为关闭来确保用户不输入自动完成的欺骗号码。以下是一个将autocomplete属性设置为off的字段的代码示例:

<div>
  <label for="tel">Telephone (so we can berate you if you're wrong)</label>
  <input id="tel" name="tel" type="tel" placeholder="1-234-546758" autocomplete="off" required>
</div>

我们还可以通过在表单本身上使用属性来设置整个表单(但不是字段集)不自动完成。以下是一个代码示例:

<form id="redemption" method="post" autocomplete="off">

列表和相关的 datalist 元素

list属性和相关的datalist元素允许在用户开始输入字段中的值后向用户呈现多个选择。以下是一个使用list属性的代码示例,其中包含一个相关的datalist,全部包装在一个div中:

<div>
  <label for="awardWon">Award Won</label>
  <input id="awardWon" name="awardWon" type="text" list="awards">
  <datalist id="awards">
    <select>
      <option value="Best Picture"></option>
      <option value="Best Director"></option>
      <option value="Best Adapted Screenplay"></option>
      <option value="Best Original Screenplay"></option>
    </select>
  </datalist>
</div>

list属性(awards)中给出的值是datalist的 ID。这样做可以将datalist与输入字段关联起来。虽然在<select>元素中包装选项并不是严格必要的,但在为尚未实现该功能的浏览器应用 polyfill 时会有所帮助。

注意

令人惊讶的是,到 2015 年中期,iOS、Safari 或 Android 4.4 及以下仍然不支持datalist元素(caniuse.com/

您可以在www.w3.org/TR/html5/forms.html上阅读datalist的规范。

虽然input字段似乎只是一个普通的文本输入字段,但在输入字段时,支持的浏览器下方会出现一个选择框,其中包含来自datalist的匹配结果。在下面的截图中,我们可以看到列表的效果(Firefox)。在这种情况下,由于Bdatalist中的所有选项中都存在,所有值都会显示给用户选择:

列表和相关的 datalist 元素

然而,当输入D时,只有匹配的建议会出现,如下面的截图所示:

列表和相关的 datalist 元素

listdatalist不会阻止用户在输入框中输入不同的文本,但它们确实提供了另一种通过 HTML5 标记添加常见功能和用户增强的好方法。

HTML5 输入类型

HTML5 添加了许多额外的输入类型,其中包括其他功能,使我们能够限制用户输入的数据,而无需额外的 JavaScript 代码。这些新输入类型最令人欣慰的是,默认情况下,如果浏览器不支持该功能,它们会退化为标准的文本输入框。此外,还有很多很好的 polyfill 可用于使旧版浏览器跟上步伐,我们很快会看到。与此同时,让我们来看看这些新的 HTML5 输入类型以及它们提供的好处。

email

您可以像这样将输入设置为email类型:

type="email"

支持的浏览器将期望用户输入与电子邮件地址的语法匹配。在下面的代码示例中,type="email"requiredplaceholder一起使用:

<div>
  <label for="email">Your Email address</label>
  <input id="email" name="email" type="email" placeholder="dwight.schultz@gmail.com" required>
</div>

当与 required 一起使用时,提交不符合规范的输入将生成警告消息:

email

此外,许多触摸屏设备(例如 Android、iPhone 等)会根据此输入类型改变输入显示。下面的截图显示了 iPad 上type="email"的输入屏幕的外观。请注意,软键盘已添加@符号,以便轻松完成电子邮件地址:

email

数字

您可以像这样将输入字段设置为数字类型:

type="number"

支持的浏览器期望在此输入数字。支持的浏览器还提供所谓的微调控件。这些是微小的用户界面元素,允许用户轻松点击上下来改变输入的值。以下是一个代码示例:

<div>
  <label for="yearOfCrime">Year Of Crime</label>
  <input id="yearOfCrime" name="yearOfCrime" type="number" min="1929" max="2015" required>
</div>

以下是在支持的浏览器(Chrome)中的外观截图:

number

如果不输入数字,不同浏览器的实现方式也不同。例如,Chrome 和 Firefox 在表单提交之前不会做任何操作,然后在字段上方弹出警告。另一方面,Safari 什么也不做,只是让表单被提交。Internet Explorer 11 在焦点离开字段时会清空字段。

最小和最大范围

在上一个代码示例中,我们还设置了允许的最小和最大范围,类似于以下代码:

type="number" min="1929" max="2015"

超出此范围的数字(应该)会得到特殊处理。

您可能不会感到惊讶,浏览器对minmax范围的实现是各不相同的。例如,Internet Explorer 11、Chrome 和 Firefox 会显示警告,而 Safari 则什么也不做。

更改步进增量

您可以使用step属性来改变各种输入类型的微调控件的步进增量(粒度)。例如,每次步进 10 个单位:

<input type="number" step="10">

url

您可以设置输入字段期望输入 URL,如下所示:

type="url"

正如您所期望的,url输入类型用于 URL 值。与telemail输入类型类似;它的行为几乎与标准文本输入完全相同。但是,一些浏览器在提交不正确的值时会向警告消息中添加特定信息。以下是包括placeholder属性的代码示例:

<div>
  <label for="web">Your Web address</label>
  <input id="web" name="web" type="url" placeholder="www.mysite.com">
</div>

以下截图显示了在 Chrome 中提交不正确输入的 URL 字段时会发生什么:

url

type="email"一样,触摸屏设备通常根据此输入类型修改输入显示。以下截图显示了 iPad 上type="url"屏幕的外观:

url

注意到.com键了吗?因为我们使用了 URL 输入类型,设备会为易于 URL 完成而呈现它们(在 iOS 上,如果您不是要去.com 网站,您可以长按一下以获得其他几个流行的顶级域名)。

tel

设置输入字段以期望电话号码,如下所示:

type="tel"

以下是一个更完整的示例:

<div>
  <label for="tel">Telephone (so we can berate you if you're wrong)</label>
  <input id="tel" name="tel" type="tel" placeholder="1-234-546758" autocomplete="off" required>
</div>

尽管在许多浏览器上期望数字格式,甚至是现代的 evergreen 浏览器,如 Internet Explorer 11、Chrome 和 Firefox,它们仅仅表现得像文本输入字段。当输入不正确的值时,它们在字段失去焦点或表单提交时未能提供合适的警告消息。

然而,更好的消息是,与emailurl输入类型一样,触摸屏设备通常会贴心地适应这种输入,通过修改输入显示来方便完成;这是在 iPad 上访问tel输入时的外观(运行 iOS 8.2):

tel

注意键盘区域中缺少字母字符?这使用户更快地以正确格式输入值。

提示

快速提示

如果在 iOS Safari 中使用tel输入时默认的蓝色电话号码颜色让您感到不适,您可以使用以下选择器进行修改:

a[href^=tel] { color: inherit; }

您可以将输入设置为搜索类型,如下所示:

type="search"

search输入类型的工作方式类似于标准文本输入。以下是一个例子:

<div>
  <label for="search">Search the site...</label>
  <input id="search" name="search" type="search" placeholder="Wyatt Earp">
</div>

然而,软件键盘(如移动设备上的键盘)通常提供更贴心的键盘。这是当search输入类型获得焦点时出现的 iOS 8.2 键盘:

search

pattern

您可以设置输入以期望某种模式输入,如下所示:

pattern=""

pattern属性允许您通过正则表达式指定应在给定输入字段中允许的数据的语法。

注意

了解正则表达式

如果您以前从未遇到过正则表达式,我建议从这里开始:en.wikipedia.org/wiki/Regular_expressions

正则表达式在许多编程语言中被用作匹配可能的字符串的手段。虽然一开始格式可能令人生畏,但它们非常强大和灵活。例如,您可以构建一个正则表达式来匹配密码格式,或选择某种样式的 CSS 类命名模式。为了帮助您构建自己的正则表达式模式并直观地了解它们的工作原理,我建议从像www.regexr.com/这样的基于浏览器的工具开始。

以下代码是一个例子:

<div>
  <label for="name">Your Name (first and last)</label>
  <input id="name" name="name" pattern="([a-zA-Z]{3,30}\s*)+[a-zA-Z]{3,30}" placeholder="Dwight Schultz" required>
</div>

我对这本书的承诺如此之深,我在互联网上搜索了大约 458 秒,找到了一个可以匹配名字和姓氏语法的正则表达式。通过在pattern属性中输入正则表达式值,支持的浏览器会期望匹配的输入语法。然后,当与required属性一起使用时,支持的浏览器会对不正确的输入进行以下处理。在这种情况下,我尝试在没有提供姓氏的情况下提交表单。

同样,浏览器的行为不同。Internet Explorer 11 要求正确输入字段,Safari、Firefox 和 Chrome 什么也不做(它们只是像标准文本输入一样行为)。

颜色

想要设置一个输入字段接收十六进制颜色值?您可以这样做:

type="color"

color输入类型在支持的浏览器中调用颜色选择器(目前仅限 Chrome 和 Firefox),允许用户选择十六进制颜色值。以下代码是一个例子:

<div>
  <label for="color">Your favorite color</label>
  <input id="color" name="color" type="color">
</div>

日期和时间输入

新的datetime输入类型的思路是为选择日期和时间提供一致的用户体验。如果你曾经在网上购买活动门票,很可能使用过某种日期选择器。这种功能几乎总是通过 JavaScript(通常是 jQuery UI 库)提供的,但希望能够仅通过 HTML5 标记实现这种常见需求。

日期

以下代码是一个例子:

<input id="date" type="date" name="date">

color输入类型类似,原生浏览器支持非常有限,在大多数浏览器上默认为标准文本输入框。Chrome 和 Opera 是唯一实现此功能的现代浏览器。这并不奇怪,因为它们都使用相同的引擎(如果您感兴趣,它被称为Blink)。

日期

有各种不同的与datetime相关的输入类型可用。以下是其他类型的简要概述。

月份

以下代码是一个例子:

<input id="month" type="month" name="month">

该界面允许用户选择单个月份,并提供年份和月份的输入,例如 2012-06。以下截图显示了它在浏览器中的外观:

月份

以下代码是一个例子:

<input id="week" type="week" name="week">

当使用week输入类型时,选择器允许用户在一年中选择单个星期,并以 2012-W47 格式提供输入。

以下截图显示了它在浏览器中的外观:

周

时间

以下代码是一个例子:

<input id="time" type="time" name="time">

time输入类型允许使用 24 小时制的值,例如 23:50。

它在支持的浏览器中显示为微调控件,但仅允许相关的时间值。

范围

range输入类型创建了一个滑块界面元素。以下是一个例子:

<input type="range" min="1" max="10" value="5">

以下截图显示了它在 Firefox 中的外观:

范围

默认范围是从 0 到 100。但是,在我们的示例中指定了minmax值,将其限制在 1 到 10 之间。

我在使用range输入类型时遇到的一个大问题是,当前值从不显示给用户。尽管范围滑块仅用于模糊的数字选择,但我经常希望在值发生变化时显示值。目前,使用 HTML5 没有办法做到这一点。但是,如果您绝对必须显示滑块的当前值,可以通过一些简单的 JavaScript 轻松实现。将前面的示例修改为以下代码:

<input id="howYouRateIt" name="howYouRateIt" type="range" min="1" max="10" value="5" onchange="showValue(this.value)"><span  id="range">5</span>

我们添加了两个东西,一个是onchange属性,另一个是 ID 为 range 的span元素。现在,我们将添加以下简短的 JavaScript 代码:

<script>
  function showValue(newValue)
  {
    document.getElementById("range").innerHTML=newValue;
  }
</script>

这只是获取范围滑块的当前值,并在具有 ID 为 range 的元素中显示它(我们的span标记)。然后,您可以使用任何您认为合适的 CSS 来更改值的外观。

HTML5 中还有一些其他与表单相关的新功能。您可以在www.w3.org/TR/html5/forms.html阅读完整规范。

如何为不支持的浏览器提供 polyfill

所有这些 HTML5 表单的花哨都很好。然而,似乎有两件事严重影响了我们使用它们的能力:支持浏览器实现功能的差异,以及如何处理根本不支持这些功能的浏览器。

如果您需要在较旧或不支持的浏览器中支持某些功能,请考虑使用 Webshims Lib,您可以在afarkas.github.com/webshim/demos/下载。这是由 Alexander Farkas 编写的一个 polyfill 库,可以加载表单 polyfills 以使不支持 HTML5 表单功能的浏览器处理。

提示

小心使用 polyfills

每当您使用 polyfill 脚本时,请务必仔细考虑。虽然它们非常方便,但会增加项目的负担。例如,Webshims 还需要 jQuery,因此如果您以前没有使用 jQuery,则需要另一个依赖项。除非在较旧的浏览器中使用 polyfill 是必不可少的,否则我会避免使用。

Webshims 的方便之处在于它只在需要时添加 polyfills。如果被支持这些 HTML5 功能的浏览器查看,它几乎不会添加任何内容。老旧的浏览器虽然需要加载更多代码(因为它们默认情况下功能较弱),但用户体验类似,尽管相关功能是通过 JavaScript 创建的。

但受益的不仅仅是较旧的浏览器。正如我们所见,许多现代浏览器并没有完全实现 HTML5 表单功能。将 Webshims lib 应用到页面上也可以填补它们功能上的任何空白。例如,Safari 在提交带有必填字段为空的 HTML5 表单时不提供任何警告。用户不会得到有关问题的任何反馈:这几乎不理想。将 Webshims lib 添加到页面后,在上述情况下会发生以下情况。

因此,当 Firefox 无法为type="number"属性提供微调器时,Webshims lib 提供了一个合适的、由 jQuery 支持的替代方案。简而言之,这是一个很棒的工具,所以让我们安装并连接这个美丽的小包,然后我们可以继续使用 HTML5 编写表单,放心地知道所有用户都将看到他们需要使用我们的表单(除了那两个使用 IE6 并关闭了 JavaScript 的人——你们知道自己是谁——现在停止吧!)。

首先下载 Webshims lib(github.com/aFarkas/webshim/downloads)并提取包。现在将js-webshim文件夹复制到网页的相关部分。为了简单起见,我将其复制到了网站根目录。

现在将以下代码添加到页面的相应部分:

<script src="img/jquery-2.1.3.min.js"></script>
<script src="img/polyfiller.js"></script>
<script>
  //request the features you need:
  webshim.polyfill('forms');
</script>

让我们一步一步来。首先,我们链接到本地的 jQuery 库(在www.jquery.com获取最新版本)和 Webshim 脚本:

<script src="img/jquery-2.1.3.min.js"></script>
<script src="img/polyfiller.js"></script>

最后,我告诉脚本加载所有需要的 polyfills:

<script>
  //request the features you need:
  webshim.polyfill('forms');
</script>

就是这样。现在,相关的 polyfill 会自动添加缺失的功能。太棒了!

用 CSS3 样式化 HTML5 表单

我们的表单现在在各种浏览器上都可以正常使用,现在我们需要使其在不同的视口尺寸下更具吸引力。现在,我不认为自己是一个设计师,但通过应用我们在前几章学到的一些技巧,我仍然认为我们可以改善表单的美观度。

注意

您可以在example_09-02中查看样式化的表单,并且请记住,如果您还没有示例代码,可以在rwd.education获取它。

在这个例子中,我还包括了两个版本的样式表:styles.css是包含供应商前缀的版本(通过 Autoprefixer 添加),styles-unprefixed.css是原始的 CSS。如果您想查看如何应用任何内容,后者可能更容易查看。

在小视口中应用了一些基本样式后,表单的外观如下:

用 CSS3 样式化 HTML5 表单

在较大的视口中是这样的:

用 CSS3 样式化 HTML5 表单

如果你看一下 CSS,你会看到我们在之前的章节中学到的许多技巧。例如,Flexbox(第三章,流动布局和响应式图片)已被用于创建元素的统一间距和灵活性;变换和过渡(第八章,过渡、变换和动画)使得焦点输入字段增大,准备/提交按钮在获得焦点时垂直翻转。盒阴影和渐变(第六章,CSS3 的惊人美学)被用来强调表单的不同区域。媒体查询(第二章,媒体查询-支持不同的视口)被用于在不同的视口尺寸下切换 Flexbox 方向,CSS Level 3 选择器(第五章,CSS3-选择器、排版、颜色模式和新特性)被用于选择器否定。

我们不会再详细介绍这些技术。相反,我们将专注于一些特殊之处。首先,如何在视觉上指示必填字段(并且额外加分指示已输入值),其次,如何在字段获得用户焦点时创建“填充”效果。

指示必填字段

我们可以仅使用 CSS 向用户指示必填输入字段。例如:

input:required {
  /* styles */
}

通过该选择器,我们可以为必填字段添加边框或轮廓,或在字段内部添加background-image。基本上没有限制!我们还可以使用特定的选择器来仅在输入字段获得焦点时,针对必填的输入字段进行定位。例如:

input:focus:required {
  /* styles */
}

然而,这将会应用样式到输入框本身。如果我们想要修改相关的label元素上的样式怎么办?我决定我想在标签旁边用一个小星号符号来表示必填字段。但这带来了一个问题。通常,CSS 只允许我们在元素的子元素、元素本身或者元素的一般或相邻兄弟元素上进行更改(当我说状态时,我指的是hoverfocusactivechecked等)。在下面的例子中,我使用了:hover,但这对基于触摸的设备显然是有问题的。

.item:hover .item-child {}

通过前面的选择器,当悬停在项目上时,样式将应用到item-child

.item:hover ~ .item-general-sibling {}

通过这个选择器,当悬停在项目上时,样式将应用到item-general-sibling,如果它与项目在同一 DOM 级别,并跟随在其后。

.item:hover + .item-adjacent-sibling {}

在这里,当悬停在项目上时,样式将应用到item-adjacent-sibling,如果它是项目的相邻兄弟元素(在 DOM 中紧跟在它后面)。

所以,回到我们的问题。如果我们有一个带有标签和字段的表单,标签在输入框上方(以便给我们所需的基本布局),这让我们有点困扰:

<div class="form-Input_Wrapper">
  <label for="film">The film in question?</label>
  <input id="film" name="film" type="text" placeholder="e.g. King Kong" required/>
</div>

在这种情况下,仅使用 CSS,没有办法根据输入是否必填来更改标签的样式(因为它在标记中位于标签之后)。我们可以在标记中切换这两个元素的顺序,但那样我们会得到标签在输入框下面的结果。

然而,Flexbox 让我们能够轻松地在元素的视觉顺序上进行反转(如果你还没有阅读过,请在第三章中了解更多相关内容,流动布局和响应式图片)。这使我们可以使用以下标记:

<div class="form-Input_Wrapper">
  <input id="film" name="film" type="text" placeholder="e.g. King Kong" required/>
  <label for="film">The film in question?</label>
</div>

然后只需将flex-direction: row-reverseflex-direction: column-reverse应用于父元素。这些声明可以颠倒子元素的视觉顺序,使标签在输入框上方(较小的视口)或左侧(较大的视口)显示所需的美学效果。现在我们可以开始实际提供一些必填字段的指示以及它们何时接收到输入。

由于我们修改过的标记,相邻兄弟选择器现在使这成为可能。

input:required + label:after { }

这个选择器基本上是说,对于跟随具有required属性的输入的每个标签,应用封闭的规则。以下是该部分的 CSS:

input:required + label:after {
  content: "*";
  font-size: 2.1em;
  position: relative;
  top: 6px;
  display: inline-flex;
  margin-left: .2ch;
  transition: color, 1s;
}

input:required:invalid + label:after {
  color: red;
}

input:required:valid + label:after {
  color: green;
}

然后,如果你专注于必填输入并输入相关值,星号的颜色会变成绿色。这是一个细微但有用的触摸。

注意

除了我们已经看过的所有选择器之外,还有更多的选择器(已实现和正在指定)。要获取最新的列表,请查看 Selectors Level 4 规范的最新编辑草案:dev.w3.org/csswg/selectors-4/

创建背景填充效果

在第六章中,使用 CSS3 创建令人惊叹的美学,我们学习了如何生成线性和径向渐变作为背景图像。遗憾的是,无法在两个背景图像之间进行过渡(这是有道理的,因为浏览器实际上将声明光栅化为图像)。但是,我们可以在关联属性的值之间进行过渡,例如background-positionbackground-size。我们将利用这一因素,在inputtextarea获得焦点时创建填充效果。

以下是添加到输入的属性和值:

input:not([type="range"]),
textarea {
  min-height: 30px;
  padding: 2px;
  font-size: 17px;
  border: 1px solid #ebebeb;
  outline: none;
  transition: transform .4s, box-shadow .4s, background-position .2s;
  background: radial-gradient(400px circle,  #fff 99%, transparent 99%), #f1f1f1;
  background-position: -400px 90px, 0 0;
  background-repeat: no-repeat, no-repeat;
  border-radius: 0;
  position: relative;
}

input:not([type="range"]):focus,
textarea:focus {
  background-position: 0 0, 0 0;
}

在第一个规则中,正在生成一个实心白色径向渐变,但位置偏移不在视图之外。radial-gradient之后的 HEX 值是背后的背景颜色,因此提供了默认颜色。当输入获得焦点时,radial-gradient的背景位置被设置回默认值,因为我们在设置了背景图像的过渡,所以我们得到了两者之间的漂亮过渡。结果是当输入获得焦点时,出现输入被不同颜色“填充”的外观。

注意

不同的浏览器在样式化本机 UI 的部分时都有自己的专有选择器和功能。Aurelius Wendelken 编制了一个令人印象深刻的选择器列表。我制作了自己的副本(或者在 Git 版本控制中称为“分支”),你可以在gist.github.com/benfrain/403d3d3a8e2b6198e395找到。

摘要

在本章中,我们学习了如何使用一系列新的 HTML5 表单属性。它们使我们能够使表单比以往任何时候都更易于使用,并且捕获的数据更相关。此外,我们可以在需要时使用 JavaScript polyfill 脚本来未来化这个新的标记,以便所有用户无论其浏览器的能力如何,都能体验相似的表单功能。

我们即将结束我们的响应式 HTML5 和 CSS3 之旅。虽然我们在一起的时间里涵盖了大量内容,但我意识到我永远无法传授你们遇到的每种情况的所有信息。因此,在最后一章中,我想以更高层次的方式来看待响应式网页设计,并尝试提供一些确切的最佳实践,以便让你的下一个/第一个响应式项目有一个良好的开端。

第十章:接近响应式网页设计

在我最喜欢的故事和电影中,通常会有一幕导师传授宝贵建议和一些神奇物品给英雄。你知道这些物品会很有用;只是不知道何时或如何使用。

好吧,我想在最后一章中扮演导师的角色(再加上我的头发已经变少,我也没有英雄的外表)。我希望你,我的优秀学徒,在你踏上响应式之旅之前,能再给我一点时间,让我在你出发之前提供一些最后的建议。

本章将是一半哲学思考和指导,一半无关的技巧和技术。我希望在你的响应式冒险中,这些技巧会在某个时候证明有用。以下是我们将要涵盖的内容:

  • 尽快在浏览器和真实设备上获取设计

  • 让设计决定断点

  • 拥抱渐进增强

  • 定义浏览器支持矩阵

  • 渐进增强的实践

  • 将 CSS 断点链接到 JavaScript

  • 避免在生产中使用 CSS 框架

  • 开发务实的解决方案

  • 编写尽可能简单的代码

  • 在视口之间隐藏、显示和加载内容

  • 让 CSS 承担(视觉)重任

  • 使用验证器和 linting 工具

  • 分析和测试网页性能(webpagetest.org

  • 采用更快更有效的技术

  • 留意下一个“大”事物

尽快在浏览器中获取设计

我做的响应式设计工作越多,我发现尽快在浏览器环境中启动和运行设计变得越来越重要。如果你既是设计师又是开发人员,那就简化了事情。一旦你对自己需要什么有了足够的视觉感觉,你就可以在浏览器中创建原型,并在浏览器环境中进一步发展这个想法。通过完全放弃高保真度的全页面模拟,可以更全面地采用这种方法。相反,考虑一些像样式瓷砖这样的东西——介于心情板和完整模拟之间。样式瓷砖的介绍(styletil.es/)将它们描述为:

“样式瓷砖是一种设计成果,包括字体、颜色和界面元素,用于传达网页视觉品牌的本质。”

我发现这种图形成果可以用于在利益相关者之间呈现和传达外观和感觉,而不必诉诸无休止的合成。

让设计决定断点

我想重申前几章提到的一点。让设计定义断点应该设置在哪里。有了浏览器中的设计,这个过程会变得更容易。你应该始终从最小的屏幕尺寸开始修改设计,然后随着视口尺寸的增加,你可以看到在需要引入断点之前设计能够工作到什么程度。

你还会发现以这种方式编写设计会更容易。首先为最小的视口编写 CSS,然后在媒体查询中添加对不同元素的任何更改。例如:

.rule {
  /* Smallest viewport size styles */
}

@media (min-width: 40em) {
  .rule {
    /* Medium viewport size changes */
  }
}

@media (min-width: 70em) {
  .rule {
    /* Larger viewport size changes */
  }
}

在真实设备上查看并使用设计

如果可以的话,开始建立一个旧设备(手机/平板电脑)的“设备实验室”来查看你的工作。拥有多种不同的设备非常有益。它不仅可以让你感受设计在不同设备上的实际效果,还可以在过程的早期暴露布局/渲染的特殊性。毕竟,没有人喜欢相信他们在某个环境中完成了项目,结果被告知它在那里无法正常工作。早期测试,经常测试!这并不需要花费很多钱。例如,你可以在 eBay 上购买旧手机和平板电脑,或者从朋友/亲戚那里购买,因为他们在升级时会卖掉旧设备。

注意

使用诸如 BrowserSync 之类的工具来同步你的工作

最近我使用的最大的省时工具之一是BrowserSync。一旦配置好,当你保存你的工作时,诸如 CSS 之类的任何更改都会被注入到浏览器中,而你无需不断地刷新屏幕。如果这还不够好,你在同一 WiFi 上的其他设备上的任何其他浏览器窗口也会刷新。这样就不用每次更改都要拿起每个测试设备并点击刷新了。它甚至还同步滚动和点击。强烈推荐:browsersync.io/

拥抱渐进增强

在之前的章节中,我们简要考虑了渐进增强的概念。这是一种我在实践中发现非常有用的开发方法,我认为值得重复。渐进增强的基本思想是,你从最低公共分母开始考虑所有你的前端代码(HTML、CSS、JavaScript)。然后,你逐渐增强代码以适应更有能力的设备和浏览器。这可能看起来很简单,而且确实如此,但如果你习惯了以相反的方式工作;设计最佳体验,然后想出一种方法使那个东西在较差的设备/浏览器上运行,你会发现渐进增强是一种更容易的方法。

想象一下一台性能低下、功能差的设备。没有 JavaScript,没有 Flexbox 支持,没有 CSS3/CSS4 支持。在这种情况下,你能做些什么来提供可用的体验?

最重要的是,你应该编写有意义的 HTML5 标记,准确描述内容。如果你正在构建基于文本和内容的网站,这将是一项更容易的任务。在这种情况下,专注于正确使用mainheaderfooterarticlesectionaside等元素。这不仅有助于你区分代码的不同部分,还将为你的用户提供更大的无障碍性,而且没有额外的成本。

如果你正在构建类似基于 Web 的应用程序或视觉 UI 组件(轮播、选项卡、手风琴等),你需要考虑如何将视觉模式提炼成可访问的标记。

良好标记的原因是它为所有用户提供了基本水平的体验。你在 HTML 中能实现的越多,你就越不需要在 CSS 和 JavaScript 中为了支持旧浏览器而做更多的工作。而且没有人,我真的是指没有人,喜欢编写支持旧浏览器的代码。

注意

关于这个主题的更多阅读和实用示例,我推荐以下两篇文章。它们提供了关于如何使用 HTML 和 CSS 的结构处理相当复杂的交互的深刻见解:

以这种方式开始思考绝不是一件简单的事情。然而,这种方法很可能会在你努力尽量少地支持不佳的浏览器时为你提供帮助。

现在,谈谈那些浏览器。

定义浏览器支持矩阵

提前了解一个 Web 项目需要支持的浏览器和设备可能对开发成功的响应式 Web 设计至关重要。我们已经考虑过为什么渐进增强在这方面如此有用;如果做得正确,这意味着你的网站的绝大部分甚至在最老的浏览器上也是功能性的。

然而,也可能有时候你需要从更高的先决条件开始你的体验。也许你正在开发一个 JavaScript 是必不可少的项目,这并不罕见。在这种情况下,你仍然可以逐渐增强。只是,你只是从不同的起点开始增强。

无论你的起点是什么,关键是要确定它是什么。然后,只有在这之后,你才能定义并达成对你打算支持的不同浏览器和设备的视觉和功能体验的一致意见。

功能平等,而不是美学平等

试图让任何网站在每个浏览器中看起来和工作起来都一样是不现实也不可取的。除了特定于某些浏览器的怪异行为外,还有一些基本的功能考虑。例如,我们必须考虑触摸屏上的按钮和链接的触摸目标,这在基于鼠标的设备上是不相关的。

因此,作为响应式网页开发人员,你的一部分工作是教育你需要向之人(老板、客户、股东)解释“支持旧版浏览器”并不意味着“在旧版浏览器中看起来一样”。我通常会说的一句话是,支持矩阵中的所有浏览器将获得任务平等,而不是视觉平等。这意味着如果你需要构建一个结账页面,所有用户都能通过结账并购买商品。对于使用更现代浏览器的用户可能会有视觉和交互效果,但所有用户都能完成核心任务。

选择要支持的浏览器

通常,当我们讨论要支持哪些浏览器时,我们在讨论需要往回看多远。根据情况,有几种可能性需要考虑。

如果是现有网站,查看访客统计数据(谷歌分析或类似工具)。有了一些数据,你可能可以进行一些粗略的计算。例如:如果支持浏览器 X 的成本小于支持浏览器 X 产生的价值,那就支持浏览器 X!

另外,要考虑统计数据中占比不到 10%的浏览器,要进一步考虑趋势。过去 3、6 和 12 个月的使用情况发生了什么变化?如果目前占比为 6%,并且这个值在过去 12 个月内减少了一半,你就有更有力的理由考虑排除该浏览器进行特定增强。

如果是一个新项目并且没有统计数据,我通常会选择“前两个”策略。这将是当前版本加上每个浏览器的前两个版本。例如,如果 Internet Explorer 12 是当前版本,那就考虑为该版本以及 IE10 和 IE11(前两个版本)提供增强。这个选择在“常青”浏览器上更容易,这个术语用于不断更新的浏览器,比如 Firefox 和 Chrome。

用户体验分层

在这一点上,让我们假设股东已经接受教育并且同意。让我们也假设你已经明确了想要为其添加增强体验的浏览器。现在我们可以开始对体验进行分层。我喜欢保持简单,所以在可能的情况下,我选择定义一个简单的“基础”层和一个更“增强”的层。

基础体验是站点的最小可行版本,而增强版本是最完整功能和美学上令人满意的版本。你可能需要在你的层中提供更多细化,例如根据浏览器功能分叉体验;例如对 Flexbox 的支持或对translate3d的支持。无论层是如何定义的,确保你定义它们以及你期望用每个层交付的内容。然后你可以着手编写这些层。

实际交付体验层

现在,Modernizr 提供了最强大的方式来根据设备能力增强和分叉体验。虽然这意味着在项目中添加一个 JavaScript 依赖,但我认为这是值得的。

记住,当编写 CSS 时,不在媒体查询之外的代码,也不需要 Modernizr 添加类的选择器应该构成我们的“基础”体验。

然后,借助 Modernizr,我们可以根据浏览器的能力提供更多增强的体验。如果你回头看example_08-07,你会看到这种思维方式和代码模式应用到了侧栏菜单模式。

将 CSS 断点链接到 JavaScript

通常,涉及任何交互的基于 Web 的东西都会涉及 JavaScript。当你开发一个响应式项目时,你可能会希望在不同的视口大小下做不同的事情。不仅在 CSS 中,还有在 JavaScript 中。

假设我们想在 CSS 中达到某个断点时调用某个 JavaScript 函数(记住,“断点”是用来定义响应式设计应该显著改变的点)。假设该断点是 47.5rem(带有 16px 根字体大小,相当于 760px),我们只想在那个大小运行函数。显而易见的解决方案是简单地测量屏幕宽度,并在值匹配您为 CSS 断点决定的相同值时调用函数。

JavaScript 始终会返回宽度的像素值,而不是 REM 值,所以这是第一个复杂的问题。然而,即使我们在 CSS 中将断点设置为像素值,这仍意味着在更改视口大小时需要更新和更改这些值的两个地方。

幸运的是,有一种更好的方法。我第一次接触到这种技术是在 Jeremy Keith 的网站上:adactio.com/journal/5429/

您可以在example_10-01中找到这个完整的代码。然而,基本思想是在 CSS 中插入一些可以被 JavaScript 轻松读取和理解的东西。

在 CSS 中考虑这一点:

@media (min-width: 20rem) {
    body::after {
        content: "Splus";
        font-size: 0;
    }
}
@media (min-width: 47.5rem) {
    body::after {
        content: "Mplus";
        font-size: 0;
    }
}
@media (min-width: 62.5rem) {
    body::after {
        content: "Lplus";
        font-size: 0;
    }
}

对于我们想要传达给 JavaScript 的每个断点,我们使用after伪元素(您也可以使用 before,两者都一样好),并将该伪元素的内容设置为我们断点的名称。在我们之前的例子中,我使用Splus表示小屏幕及以上,Mplus表示中等屏幕及以上,Lplus表示大屏幕及以上。您可以使用任何对您有意义的名称,并在有意义的时候更改值(不同的方向,不同的高度,不同的宽度等)。

提示

::before::after伪元素被插入到 DOM 中作为阴影 DOM 元素。::before伪元素被插入为其父元素的第一个子元素,而::after被插入为最后一个子元素。您可以在浏览器的开发者工具中确认这一点。

有了这个 CSS 设置,我们可以浏览 DOM 树并看到我们的::after伪元素。

将 CSS 断点链接到 JavaScript

然后在我们的 JavaScript 中,我们可以读取这个值。首先,我们将这个值赋给一个变量:

var size = window.getComputedStyle(document.body,':after').getPropertyValue('content');

然后一旦我们有了它,我们可以对它做些什么。为了证明这个概念,我做了一个简单的自调用函数(自调用意味着它在浏览器解析时立即执行),根据视口大小在页面加载时警报不同的消息:

;(function alertSize() {
    if (size.indexOf("Splus") !=-1) {
        alert('I will run functions for small screens');
    }
    if (size.indexOf("Mplus") !=-1) {
        alert('At medium sizes, a different function could run');
    }
    if (size.indexOf("Lplus") !=-1) {
        alert('Large screen here, different functions if needed');
    }
})();

我希望你在项目中做一些更有趣的事情,而不仅仅是警报一条消息,但我认为以这种方式解决问题会带来很大的好处。你再也不会担心你的 CSS 媒体查询和宽度相关的 JavaScript 函数再次不同步了。

避免在生产中使用 CSS 框架

有大量的免费框架可用,旨在帮助快速原型设计和构建响应式网站。最常见的两个例子是 Bootstrap (getbootstrap.com/)和 Foundation (foundation.zurb.com/)。虽然它们是很棒的项目,特别适合学习如何构建响应式视觉模式,但我认为它们在生产中应该避免使用。

我曾经和许多开发者交谈过,他们在所有项目中都使用这些框架之一,并对其进行修改以适应他们的需求。这种方法对于快速原型设计可能非常有利(例如,向客户说明某些交互),但我认为对于你打算推向生产的项目来说,这是错误的做法。

首先,从技术角度来看,使用框架很可能会导致项目代码比实际需要的更多。其次,从美学角度来看,由于这些框架的流行,你的项目很可能会看起来与无数其他项目非常相似。

最后,如果你只是将代码复制粘贴到你的项目中,并根据自己的需求进行调整,你很可能无法完全理解“底层”发生了什么。只有通过定义和解决你所面临的问题,你才能掌握你放入项目中的代码。

编写务实的解决方案

在前端网页开发中,“象牙塔式的理想主义”是我特别讨厌的一个问题。虽然我们应该始终努力以“正确的方式”去做事,但务实主义必须始终占上风。让我举个例子(完成的代码是example_10-02)。假设我们有一个按钮来打开一个侧边菜单。我们的自然倾向可能是这样标记它:

<button class="menu-toggle js-activate-off-canvas-menu">
    <span aria-label="site navigation">&#9776;</span> menu
</button>

简单明了。这是一个按钮,所以我们使用了button元素。我们在按钮上使用了两个不同的 HTML 类,一个用于 CSS 样式(menu-toggle),另一个作为 JavaScript 钩子(js-activate-off-canvas-menu)。此外,我们使用了aria-label属性(ARIA 在第四章中有更详细的介绍,“适应性 Web 设计的 HTML5”)来向屏幕阅读器传达span内字符的含义。在这个例子中,我们使用了 HTML 实体&#9776; 这是 Unicode 字符“天卦”。这里使用它仅仅是因为它看起来像经常用来表示菜单的“汉堡图标”。

提示

如果你想得到关于何时以及如何使用aria-label属性的可靠建议,我强烈推荐 Opera 开发者网站上 Heydon Pickering 的以下文章:dev.opera.com/articles/ux-accessibility-aria-label/

在这一点上,我们似乎处于良好状态。语义化,高度可访问的标记和用于分离关注点的类。太好了。让我们添加一些样式:

.menu-toggle {
    appearance: none;
    display: inline-flex;
    padding: 0 10px;
    font-size: 17px;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
    border: 1px solid #ebebeb;
    min-height: 44px;
    text-decoration: none;
    color: #777;
}

[aria-label="site navigation"] {
    margin-right: 1ch;
    font-size: 24px;
}

在 Firefox 中打开这个,我们看到:

编写务实的解决方案

并不是我们所希望的。在这种情况下,浏览器已经决定我们走得太远了;Firefox 根本不允许我们将按钮元素用作 Flex 容器。这对开发人员来说是一个非常真实的冲突。我们是选择正确的元素还是正确的美学?鉴于理想情况下,我们希望菜单的“汉堡图标”在左边,单词“菜单”在右边。

提示

你可以看到在之前的代码中我们使用了appearance属性。它用于移除表单元素的浏览器默认样式,并且它有一个悠久的历史。它曾经被 W3C 规定了一段时间,然后后来被取消,留下了 Mozilla 和 WebKit 浏览器中的供应商前缀版本的属性。幸运的是,它现在又回到了标准轨道上:dev.w3.org/csswg/css-ui-4/#appearance-switching

当链接变成按钮时

我不会撒谎。鉴于这个困境,我通常选择后者。然后我尽量弥补我使用错误元素的事实,选择次优元素并在可能的情况下更改 ARIA 角色。在这种情况下,虽然我们的菜单按钮显然不是一个链接(毕竟,它并没有把用户带到任何地方),但我将使用a标签。我决定这是次优选择——比其他任何元素更像一个按钮。通过使用链接,我们可以实现期望的美学。这是我会使用的标记。请注意,在a标签上添加的 ARIA 角色,以指示它作为按钮的角色(而不是默认的链接)对辅助技术进行辅助:

<a class="menu-toggle js-activate-off-canvas-menu" role="button">
    <span aria-label="site navigation">&#9776;</span> menu
</a>

这并不完美,但这是一个务实的解决方案。这是在 Firefox 中(如果你感兴趣,版本是 39.0a2)左边是button元素,右边是a标签:

当链接变成按钮时

当然,对于这个简单的例子,我们可以将显示从 flex 改为 block,并调整填充,直到达到我们想要的美学效果。或者,我们可以保留button元素,并嵌套另一个语义上没有意义的元素(span),并将其设置为 Flex 容器。无论你选择哪种方法,都会有权衡。

最终,我们需要尽可能合理地标记文档。在一端,有一些开发者只使用 div 和 span 进行标记,以确保浏览器不会产生不需要的样式。这样做的代价是元素没有固有的含义,因此也没有“免费”的可访问性。在另一端是标记纯粹主义者,他们只会使用他们认为正确的元素来标记内容,而不管视觉上可能会出现多大的偏差。中间有一个折中的地方,我觉得那是最明智和最有效的地方。

尽可能使用最简单的代码

沉迷于新技术带来的力量是很容易的。因此,要设法以最简单的方式解决响应式问题。例如,如果你需要为项目列表中的第五个项目设置样式,并且可以访问标记,不要像这样使用nth-child选择器:

.list-item:nth-child(5) {
    /* Styles */
}

如果你可以访问标记,通过为项目添加 HTML 类来简化生活:

<li class="list-item specific-class">Item</li>

然后使用这个简单的类来为项目设置样式:

.specific-class {
    /* Styles */
}

这不仅更容易理解,而且可以免费获得更广泛的支持(较旧版本的 Internet Explorer 不支持nth-child选择器)。

在不同的视口上隐藏、显示和加载内容

关于响应式网页设计的一个常见格言是:如果在较小的视口上屏幕上没有某些内容,那么在较大的视口上也不应该有。

这意味着用户应该能够在每个视口大小下实现相同的目标(购买产品,阅读文章,完成界面任务)。这是常识。毕竟,作为用户,我们都曾因为使用较小的屏幕而无法在网站上实现目标而感到沮丧。

这也意味着随着屏幕空间更加充裕,我们不应该感到有必要添加额外的东西来填充空间(例如小部件、广告或链接)。如果用户在较小的屏幕尺寸下可以不使用这些额外内容,他们在更大的屏幕上也会很好地完成任务。在较大的视口尺寸下显示额外内容也意味着要么在较小的视口尺寸下已经存在内容,只是被隐藏了(通常使用 CSS 中的display: none;),要么在特定的视口尺寸下加载内容(借助 JavaScript 的帮助)。简而言之:要么内容已经加载但不可见,要么内容可见但可能是多余的。

总的来说,我认为上面的格言是一个明智的建议。至少,它让设计师和开发人员更加彻底地质疑他们在屏幕上显示的内容。然而,在网页设计中,总会有例外情况。

尽可能地,我会避免为不同的视口加载新的标记,但偶尔这是必要的。我曾经在复杂的用户界面上工作,这些界面在较宽的视口上需要不同的标记和设计。

在这种情况下,JavaScript 被用来用另一种标记替换一部分标记。这并不是理想的情况,但却是最务实的。如果由于某种原因 JavaScript 失败,用户将得到最小的屏幕布局。他们可以实现相同的目标,只是布局对于完成手头的任务来说不是最佳的。

这些是您在编写越来越复杂的响应式网页设计时可能会面临的选择,您需要根据具体情况自行判断最佳选择是什么。但是,如果您使用display: none来切换标记的可见性以实现目标,这并不是一个致命的错误。

让 CSS 来(视觉上)承担重任

事实上,JavaScript 提供了网页上无法仅通过 CSS 实现的交互水平。然而,在可能的情况下,在视觉方面,我们仍然应该使用 CSS 来承担所有繁重的工作。在实际操作中,这意味着不要仅使用 JavaScript 来动画显示菜单(我在看你 jQuery showhide方法)。而是使用 JavaScript 对标记的相关部分进行简单的类更改。然后让该类更改触发 CSS 中显示/动画的菜单。

提示

为了获得最佳性能,在 HTML 中切换类时,请确保尽可能接近要影响的项目添加类。例如,如果您希望弹出框出现在另一个元素上方,请在最接近的共享父元素上添加类。这将确保为了获得最佳性能,只有页面的特定部分被标记为“脏”,浏览器不应该再次绘制页面的大片区域。要了解有关性能的出色免费课程,请查看 Paul Lewis 的“浏览器渲染优化”课程:www.udacity.com/course/browser-rendering-optimization--ud860

验证器和 linting 工具

一般来说,编写 HTML 和 CSS 相当宽容。您可以偶尔错误地嵌套一些内容,错过偶尔的引号或自闭合标记,并不总是注意到问题。尽管如此,几乎每周我都会用不正确的标记使自己困惑。有时是像意外输入错误字符这样的失误。其他时候是像将div嵌套在span中这样的低级错误(将span嵌入块级元素div是无效的标记,导致结果不可预测)。幸运的是,有很好的工具可以帮助解决问题。最坏的情况下,如果遇到奇怪的问题,请转到validator.w3.org/并将您的标记粘贴在那里。它将指出所有错误以及行号,帮助您轻松解决问题。

验证器和 linting 工具

更好的是,安装和配置 HTML、CSS 和 JavaScript 的“linting”工具。或者选择一个带有一定程度的合理检查的文本编辑器。然后,随着您的代码的进行,问题区域会在您的代码中标记出来。以下是 Microsoft 的“Code”编辑器中标记出的 CSS 中的简单拼写错误的示例:

验证器和 linting 工具

就像小丑一样,我笨拙地输入了widthh而不是width。编辑器已经发现了这个错误,并指出了我的错误,并提供了一些明智的替代方案。在可能的情况下,接受这些工具。您的时间有更好的用途,而不是在代码中追踪简单的语法错误。

性能

考虑响应式网页设计的性能与美学一样重要。然而,性能呈现出一种不断变化的目标。例如,浏览器更新和改进了它们处理资产的方式,发现了超越现有“最佳实践”的新技术,技术最终得到了足够的浏览器支持,从而变得适合广泛采用。列表还在继续。

然而,有一些基本的实施细节是相当稳固的(好吧,直到 HTTP2 普及,很快就会有更多)。这些是:

  1. 尽量减少资产数量(例如,如果可以将它们合并为一个文件,则不要加载 15 个 JavaScript 文件)。

  2. 尽量减少页面权重(如果可以将图像压缩到原始大小的一小部分,则应该这样做)。

  3. 推迟非必要的资产(如果可以推迟加载 CSS 和 JavaScript 直到页面呈现,可以大大增加感知加载时间)。

  4. 确保页面尽快可用(通常是完成所有前面步骤的副产品)。

有许多很棒的工具可用于测量和优化性能。我个人最喜欢的是webpagetest.org/。在最简单的情况下,您选择一个 URL 并单击开始测试。它会向您显示页面的完整分析,但更有用的是,它会显示页面加载时的“电影带”视图,让您可以集中精力更早地完成呈现页面。这是 BBC 主页的电影带视图的一个例子:

性能

在尝试优化性能时,确保在开始之前进行测量(否则,您不知道您的性能工作有多有效)。然后进行修改,测试和重复。

下一个大事件

使前端 Web 开发变得有趣的一件事是,事物变化迅速。总是有新东西要学习,网络社区总是在找到更好、更快和更有效地解决问题的方法。

例如,在撰写本书的这一版之前的三年,响应式图像(srcsetpicture元素在第三章中详细介绍)根本不存在。那时,我们不得不使用巧妙的第三方解决方案来为不同的视口尺寸提供更合适的图像。现在,这种常见的需求已经合理化为 W3C 标准,我们现在都可以使用和享受。

同样,不久之前,Flexbox 只是规范作者眼中的一个闪烁。即使规范发展了,实现起来仍然很困难,直到 Andrey Sitnik 和那些聪明的 Evil Martians 的人们(evilmartians.com/)创建了 Autoprefixer,我们随后能够相对轻松地跨浏览器使用它。

未来还将有更多令人兴奋的能力供我们理解和实施。例如,我们已经在第四章中提到了服务工作者,响应式 Web 设计的 HTML5,例如(www.w3.org/TR/service-workers/); 这是创建离线可用的基于 Web 的应用程序的更好方法。

还有“Web 组件”,由 Shadow DOM(w3c.github.io/webcomponents/spec/shadow/)、自定义元素(w3c.github.io/webcomponents/spec/custom/)和 HTML 导入(w3c.github.io/webcomponents/spec/imports/)等标准组成,这将使我们能够创建完全定制和可重用的组件。

然后还有其他即将推出的增强功能,例如 CSS Level 4 选择器(dev.w3.org/csswg/selectors-4/)和 CSS Level 4 媒体查询,我们在第二章中有详细介绍,媒体查询-支持不同的视口

最后,即将到来的另一个重大变化是 HTTP2。它承诺使我们当前的许多最佳实践变成坏实践。对于一个深入的入门指南,我建议阅读 Daniel Stenberg 的http2 explained(这是一个免费的 PDF)。或者,对于一个更轻松的摘要,阅读 Matt Wilcox 的优秀文章HTTP2 for front-end web developers(mattwilcox.net/web-development/http2-for-front-end-web-developers)。

摘要

在我们共同度过的时光即将结束之际,你们谦卑的作者希望已经介绍了你们开始构建下一个网站或网络应用所需的所有技术和工具。

我坚信,通过事先考虑并对现有工作流程、实践和技术进行一些修改,我们可以创建出快速、灵活、易于维护的响应式网页设计,无论使用何种设备访问,都能呈现出令人难以置信的效果。

我们在一起的时间里涵盖了大量的信息;技术、技术优化、规格、工作流程、工具等等。我不指望任何人一次阅读就能全部掌握。因此,下次当你需要记住这个或那个语法,或者想要复习我们涵盖的响应式相关主题之一时,我希望你能再次翻阅这些页面。我会在这里等着你。

在那之前,祝你在响应式网页设计的探索中好运。

希望能再次见到你。

posted @ 2024-05-24 11:08  绝不原创的飞龙  阅读(29)  评论(0编辑  收藏  举报