带有 CSS 区域和排除对象的杂志式网站版式

Christian Cantrell
Christian Cantrell

简介

网络是一个非常强大的文本平台,Adobe 在该领域拥有丰富的经验和专业知识。因此,当 Adobe 寻求帮助 Web 技术不断进步的方法时,进一步提升 Web 的文字功能似乎是一个显而易见的着手点。 在 Web 上,文本通常采用单列垂直方向。虽然可以让文本围绕图形流动,甚至可以使用 CSS 将文本格式设置为多列,但在 Web 上实现真正类似杂志的布局仍然非常困难。借助 CSS 区域CSS 排除对象,Adobe 正率先努力将桌面出版的强大功能引入到现代浏览器中。例如,在以下屏幕截图中,CSS 排除项用于沿着山峰轮廓让文本流动:

CSS 排除项实际应用示例
CSS 排除对象的实际运作示例

下方屏幕截图中的文档还使用了 CSS 排除项,以允许文本环绕图片中的形状,以及 CSS 区域,以将文本格式设置为列和环绕引号:

CSS 区域实际应用示例
CSS 地区示例

CSS 区域

在详细介绍 CSS 区域之前,我想先介绍一下如何在 Google Chrome 中启用区域。启用 CSS 区域后,您可以尝试本文中提及的一些示例,并创建自己的示例。

在 Google Chrome 中启用 CSS 区域

从 Chrome 20 版(确切版本为 20.0.1132.57)开始,CSS 区域可通过 chrome://flags 接口启用。如需启用 CSS 区域,请按以下步骤操作:

  1. 在 Chrome 中打开新的标签页或窗口。
  2. 在位置栏中输入 chrome://flags
  3. 使用在网页中查找功能(Ctrl/Command + F),然后搜索“实验性 Web 平台功能”部分。
  4. 点击启用链接。
  5. 点击底部的 Relaunch Now(立即重新启动)按钮。

如需详细了解 Chrome 的标志,请参阅我关于Chrome 标志的所有信息一文。

重新启动浏览器后,您就可以开始自由地使用 CSS 区域了。

CSS 区域概览

CSS 区域允许一块带有语义标记的文本自动流入“框”(目前是元素)。下图演示了文本(流)和框(文本流入的区域)的分离:

内容流入指定区域
内容流入指定的区域

我们来看看实际的 CSS 区域用例。除了是 Adobe 的开发者之外,我还是一名科幻作家。我经常在网上以 Creative Commons 许可发布自己的作品,为了让作品能够在尽可能多的设备和浏览器上正常运行,我经常使用类似以下极其简单的格式:

未设置样式的旧版 Human 项目示例
未设置样式的人工旧版项目示例

通过使用 CSS 区域,我能够打造视觉上更具吸引力且功能更强大的体验,因为它更易于浏览且更舒适地阅读:

显示地区的“人类遗产”项目
包含区域的人工旧版项目。

为了演示目的,我在此原型中添加了显示 CSS 区域的功能。以下屏幕截图显示了这些区域的排列方式,看起来就像是围绕中心的图形和引言内容排列的列:

显示地区的人类遗产项目
显示地区的“人体旧版”项目

您可以在此处试用此原型(以及查看源代码)。使用箭头键进行导航,按 Esc 键可显示地区。您还可以在此处查看早期原型。

创建命名流

让一段文字在不同区域中流动的 CSS 非常简单。以下代码段会将名为“article”的命名流程分配给 id 为“content”的 div,并将同一“article”命名流程分配给任何类为“region”的元素。这样一来,“content”元素中包含的文本就会自动流经任何类为“region”的元素。

<!DOCTYPE html>
<html>
<head>
    <style>
    #content {
        { % mixin flow-into: article; % }
    }

    .region {
        { % mixin flow-from: article; % }
        box-sizing: border-box;
        position: absolute;
        width: 200px;
        height: 200px;
        padding: 10px;
    }

    #box-a {
        border: 1px solid red;
        top: 10px;
        left: 10px;
    }

    #box-b {
        border: 1px solid green;
        top: 210px;
        left: 210px;
    }

    #box-c {
        border: 1px solid blue;
        top: 410px;
        left: 410px;
    }
    </style>
</head>
<body>
    <div id="box-a" class="region"></div>
    <div id="box-b" class="region"></div>
    <div id="box-c" class="region"></div>
    <div id="content">
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent eleifend dapibus felis, a consectetur nisl aliquam at. Aliquam quam augue, molestie a scelerisque nec, accumsan non metus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin cursus euismod nisi, a egestas sem rhoncus eget. Mauris non tortor arcu. Pellentesque in odio at leo volutpat consequat....
    </div>
</body>
</html>

结果如下所示:

上述代码的结果
上述代码的结果

请注意,“content”div 中的文本不了解其呈现方式。换句话说,即使在流经不同区域时,其语义也可以保持完整。此外,由于区域只是元素,因此它们的定位和大小与任何其他元素一样,都是使用 CSS 进行的,因此与自适应设计原则完全兼容。将元素指定为命名流的一部分,只是意味着指定的文本会自动流经这些元素。

CSS 对象模型

CSS 对象模型 (CSSOM) 定义了用于处理 CSS 的 JavaScript API。下面列出了与 CSS 地区相关的新 API:

  • document.webkitGetNamedFlows():用于返回文档中可用的命名流程集合的函数。
  • document.webkitGetNamedFlows().namedItem("article"):用于返回对特定命名流的引用的函数。该参数对应于指定为 flow-intofrom-from CSS 属性值的名称。如需获取对上述代码段中指定的命名流程的引用,您需要传入字符串“article”。
  • WebKitNamedFlow:命名 Floe 的对象表示法,具有以下属性和函数:
    • firstEmptyRegionIndex:一个整数值,指向与命名流关联的第一个空白区域的索引。请参阅下文中的 getRegions(),了解如何获取地区集合。
    • name:包含数据流名称的字符串值。
    • overset:布尔值属性,其特征如下:
      • false,如果命名流的内容适用于关联的区域
      • true - 如果内容不适合,并且需要更多区域才能包含所有内容。
    • getContent():一个函数,用于返回一个包含对流入命名流的节点的引用的集合。
    • getRegions():一个函数,用于返回一个集合,其中包含对包含命名流程内容的区域的引用。
    • getRegionsByContentNode(node):一个函数,用于返回指向包含指定节点的区域的引用。这对于查找包含命名锚点等内容的区域特别有用。
  • webkitregionoversetchange 事件。每当关联内容的布局因任何原因(添加或移除内容、字号发生变化、区域形状发生变化等)而发生变化,导致区域的 webkitRegionOverset 属性发生变化时,系统都会在 WebkitNamedFlow 上触发此事件。此事件对于监听粗略的布局更改非常有用。这表示发生了重要的事情,布局可能需要注意,例如:需要更多区域、某些区域可能为空等。
  • webkitregionfragmentchange 事件。在编写本文时尚未实现。每当关联内容的布局因任何原因而发生变化时,都会在 WebkitNamedFlow 上触发此事件,与 webkitregionoversetchange 类似,但无论 webkitRegionOverset 属性是否发生任何变化。此事件对于监听不一定会影响命名流程的整个布局的精细布局更改非常有用,例如:内容从一个区域移至另一个区域,但总体内容仍适用于所有区域。
  • Element.webkitRegionOverset:元素在分配 flow-from CSS 属性后会变为区域。这些元素具有 webkitRegionOverset 属性,如果它们是命名流的一部分,则该属性会指示流中的内容是否超出相应区域。webkitRegionOverset 的可能值如下:
    • 如果内容超出相应区域的容量,则会出现“溢出”
    • 如果内容在区域结束前停止,则为“fit”
    • 如果内容尚未面向相应地区发布,则为“empty”

CSSOM 的主要用途之一是监听 webkitregionoversetchange 事件并动态添加或移除区域,以适应不同的文本量。例如,如果要设置格式的文本量不可预测(可能是用户生成的),如果浏览器窗口的大小发生变化,或者字体大小发生变化,则可能需要添加或移除区域,以适应流程的变化。此外,如果您想将内容整理成页面,则需要一种机制来动态修改 DOM 和区域。

以下 JavaScript 代码段演示了如何使用 CSSOM 视图按需要动态添加区域。请注意,为简单起见,此示例不处理移除区域或定义区域的大小和位置;它仅作演示之用。

var flow = document.webkitGetNamedFlows().namedItem("article")
flow.addEventListener("webkitregionoversetchange", onLayoutUpdate);

function onLayoutUpdate(event) {
    var flow = event.target;
    
    // The content does not fit
    if (flow.overset === true) {
    addRegion();
    } else {
    regionLayoutComplete();
    }
}

function addRegion() {
    var region = document.createElement("div");
    region.style = "flow-from: article";
    document.body.appendChild(region);
}

function regionLayoutComplete() {
    // Finish up your layout.
}

如需查看更多演示,请访问 CSS 地区示例页面

CSS 页面模板

利用 CSSOM 可能是实现分页和响应式布局等功能最强大、最灵活的方式,但 Adobe 在文本和桌面出版工具方面拥有丰富的经验,因此深知设计师和开发者也希望能够更轻松地获得相对通用的分页功能。因此,我们正在研究一项名为 CSS 页面模板的提案,该提案允许完全以声明方式定义分页行为。

下面将介绍一个 CSS 页面模板的常见用例。以下代码段展示了如何使用 CSS 创建两个命名流程:“article-flow”和“timeline-flow”。此外,它还定义了一个名为“combined-articles”的第三个选择器,其中包含这两种流程。只需在“combined-articles”选择器中添加 overflow-style 属性,即可指示系统应沿 x 轴(即水平方向)自动分页显示内容:

<style>
    #article {
    { % mixin flow-into: article-flow; % }
    }

    #timeline {
    { % mixin flow-into: timeline-flow; % }
    }

    #combined-articles {
    overflow-style: paged-x;
    }
</style>

现在,我们已经定义了流程并指定了所需的溢出行为,接下来可以创建页面模板了:

@template {
    @slot left {
    width: 35%;
    float: left;
    { % mixin flow-from: article-flow; % }
    }

    @slot time {
    width: 25%;
    float: left;
    { % mixin flow-from: timeline-flow; % }
    }

    @slot right {
    width: 35%;
    float: left;
    { % mixin flow-from: article-flow; % }
    }
}

页面模板使用新的“at”语法进行定义。在上面的代码段中,我们定义了三个槽,每个槽对应于一列。“article-flow”中的文本将流经左侧和右侧的列,“timeline-flow”中的文本将填充中间的列。结果可能如下所示:

页面模板示例
页面模板示例

请注意,文章文本(左侧和右侧列中的文本)是英文,中间的时间轴是德文。此外,文档页面会横向滚动,而无需任何 JavaScript 代码。所有操作都是在 CSS 中以声明方式完成的。

CSS 页面模板仍处于提案阶段,但我们提供了一个原型,该原型使用 JavaScript“shim”(也称为 polyfill),以便您立即对其进行实验。

如需详细了解 CSS 区域的一般信息,请参阅 html.adobe.com 上的“CSS 区域”页面

CSS 排除项

为了实现真正类似于杂志的布局,仅仅能够让文本在区域中流动还不够。若要制作高质量且视觉上引人入胜的桌面出版物,关键要素之一就是让文本能够围绕不规则图形和形状流动,或者在其内部流动。CSS 排除对象将这种生产环境质量带到了 Web 上。

以下屏幕截图来自 CSS 排除项原型,显示了文本围绕与大型岩层轮廓相匹配的路径动态流动:

CSS 排除项实际应用示例
CSS 排除对象的实际运作示例

下一个屏幕截图展示了相反的情况:文本在不规则多边形内流动:

文本流入不规则多边形
文本流入不规则多边形

要想让文本在任意形状周围或内部流动,第一步是开发和优化所需的算法。Adobe 目前正在开发将直接贡献给 WebKit 的实现。优化这些算法后,它们将成为 CSS 排除对象的其余部分的基础。

如需详细了解 CSS 排除对象,请参阅 html.adobe.com 上的 CSS 排除对象页面;如需详细了解 Adobe 在 CSS 排除对象底层技术方面的工作,请参阅 Hans Muller 撰写的博文《Horizontal Box: Polygon Intersection for CSS Exclusions》(水平框:CSS 排除对象的多边形相交)。

CSS 区域和 CSS 排除对象的当前状态

我第一次公开谈论 CSS 区域和 CSS 排除对象是在 2011 年 Google I/O 大会的 Adobe 开发者专题讨论中。当时,我在我们自己的自定义原型浏览器中展示演示。观众的反响非常热烈,但当他们发现我展示的所有功能都尚未在任何主流浏览器中提供时,也明显感到失望。

今年(2012 年),我再次参加了 Google I/O 大会,这次我和同事 Vincent Hardy 以及 Google 的 Alex Danilo 一起担任了演讲者(您可以点击此处观看演讲)。仅仅一年后,WebKit 中就实现了约 80% 的 CSS 区域规范,并且最新版本的 Google Chrome 中也已实现了该规范(请注意,CSS 区域目前必须通过 chrome://flags 启用)。Chrome for Android 甚至已初步支持 CSS 区域:

Android 版 Chrome 中的地区
Android 版 Chrome 支持的国家/地区

此外,Internet Explorer 10 预览版中实现了 CSS 区域和 CSS 排除,并且目前已列入 Mozilla 的 2012 年 Firefox 路线图。Safari 的下一个主要版本应支持 CSS 区域规范的大部分内容,后续更新应包含其余内容。

以下是自 2011 年 4 月向 W3C 提交初始提案以来,我们在 CSS 区域和 CSS 排除范围方面取得的进展的详细时间表:

区域和排除对象进度
区域和排除范围进度

总结

Adobe 在文本、字体和桌面出版方面拥有丰富的经验,并通过 InDesign 等工具提供相关服务。虽然 Web 已经是一个非常强大的文本平台,但我们希望利用自己的知识和经验,进一步提升文本呈现效果。CSS 区域和 CSS 排除项可让内容保持语义结构,同时实现真正的杂志式布局,最终让 Web 变得更加富有表现力。