本章涵盖
- 了解 D3.js 的作用及其背后的理念
- 认识与 D3 结合使用来创建数据可视化的工具
- 使用代码创建可缩放矢量图形 (SVG) 并为其设计样式
- 了解数据可视化最佳实践如何支持您作为 D3 开发人员的旅程
D3.js 是网络上几乎所有最具创新性和令人兴奋的信息可视化的幕后黑手。D3 代表数据驱动文档,它是一个品牌名称,也是多年来以某种形式提供的一类应用程序。我们可以使用这个库来构建各种数据驱动的项目,从简单的条形图到动态地图,再到复杂的空间和时间探索。当您希望在数据可视化方面获得完全的创意和技术自由时,无论您是构建用于研究的交互式原型、顶级科技公司广泛且完全响应的数据仪表板,还是揭示数据故事的长篇文章,D3 都是您的首选工具当用户滚动时。
什么是D3.js
D3 是一个开源 JavaScript 库,由 Mike Bostock 于 年创建,用于为 Web 生成动态和交互式数据可视化。尽管过去几年推出了许多新的数据可视化库,但它们通常在底层使用 D3。这是因为 D3 与 JavaScript 一样,非常灵活且强大。
对可通过网络访问的数据可视化的需求
D3.js 的创建是为了满足对可通过网络访问的复杂数据可视化的迫切需求。假设您的公司正在使用商业智能工具,但它没有显示您的团队所需的数据模式。您必须构建一个自定义仪表板,根据您的特定领域量身定制,准确显示客户的行为方式。该仪表板需要快速、交互式且可在整个组织内共享。D3 将是此类项目的自然选择。
或者想象一下,您被雇用来实现一个网页,该网页以可视化方式展示 LGBTQ+ 群体(女同性恋、男同性恋、双性恋、跨性别者、酷儿等)的权利在过去几十年和全世界的演变情况。此页面包含许多随着用户滚动而变化的创意可视化效果。它们通过鼠标事件显示更多信息并适应屏幕大小。D3 将是构建此类项目的首选工具。
Mike Bostock 最初创建 D3 是为了利用新兴的 Web 标准,正如他所说,“避免了专有表示并提供了非凡的灵活性,暴露了 CSS3、HTML5 和 SVG 等 Web 标准的全部功能”(http: // d3js.org)。D3.js 版本 7 是这个流行库的最新版本,它通过模块化 D3 的各个部分来延续这一趋势,使其与 ECMAScript 模块(一种基于 JavaScript 的脚本语言)和现代应用程序开发完全兼容。
D3.js 使开发人员不仅能够制作丰富的交互式应用程序,而且能够制作样式和服务类似于传统 Web 内容的应用程序。这使得它们更可移植,更适合增长,并且更容易由其他团队成员可能不知道 D3 的具体语法的大型团体维护。
图 D3 开发人员可以访问各种数据表示形式,地图就是一个例子。这是由Christophe Viau创建的数字高程模型 (DEM) 地图。
Bostock 决定广泛处理数据并创建一个能够像图表一样简单、像网络一样简单、像列表一样简单地呈现地图的库,这也意味着开发人员不需要了解以下内容的抽象和语法:一个用于地图的库,另一个用于动态文本内容的库,还有另一个用于传统图形的库。相反,用于运行交互式网络可视化的代码接近于纯 JavaScript,并且也类似于表示 D3 地图上的动态点的代码。方法是相同的,但数据也可以是相同的,以一种方式制定用于网络的节点和链路,而以另一种方式制定用于地图上的地理空间表示。
D3 不仅可以创建复杂多样的图形,还可以嵌入用户期望的高水平交互性,这对于现代 Web 开发至关重要。使用 D3,每个图表的每个元素(从旋转的地球仪到饼图的切片)都以相同的方式进行交互。由于 D3 是由精通数据可视化实践的人编写的,因此它包含数据可视化和 Web 开发中标准的交互式组件和行为。
图 交互性是 D3 的核心。在此网络可视化中,鼠标交互揭示了不同组织之间的关系以及特定于所选节点的信息(
https://amdufour.github.io/organizations-against-polization)。
什么时候使用D3.js?
数据可视化领域正在蓬勃发展,可用于生成数据绑定图形的工具数量在过去十年中呈爆炸式增长。我们拥有 Excel(数据可视化的常用入口)和 Power BI(用于构建仪表板的 Microsoft 解决方案)等商业智能工具。另一方面,更有经验的数据科学家通常会转向 R 的 ggplot2 或 Python 的 matplotlib。
基于浏览器的点击式工具(例如 Tableau、Flourish、DataWrapper、RAWGraphs 和 Google 图表)也占据了主导地位,允许用最少的技术知识创建令人惊叹的作品。
最后,HighCharts、Chart.js 和 D3.js 等 JavaScript 库专门用于开发基于 Web 的交互式可视化。
而且这个列表远非详尽无遗......
那么,D3 在数据可视化工具的海洋中处于什么位置呢?我们何时以及如何使用它?我们可以说,虽然 D3 完全可以构建此处列出的数据可视化库提供的任何图表,但它通常不是构建简单的传统图表或探索阶段(我们调查哪种类型的可视化是)的首选选项。最适合代表我们的数据。构建 D3 项目需要时间,而 D3 在复杂、交互式和定制的项目中真正表现出色。数据可视化不仅仅是折线图和散点图!虽然上面提到的工具通常专注于预定义的图表,但 D3 允许我们将数据绑定到任何图形元素,并通过以独特的方式组合这些视觉元素来打破常规。
图 D3 具有 SVG 和画布绘图功能,允许开发人员构建自定义可视化效果,例如Elijah Meeks的乐谱表示形式。
以下是我们如何在数据可视化项目范围内使用 D3 的示例。首先,我们从预先存在的数据集或手动收集的数据开始。在开始数据分析过程之前,我们通常会花费大量时间清理、格式化和准备数据。Python 和 R 等数据科学工具在这方面功能强大,可以帮助我们识别隐藏在数据中的故事。Excel 还可以完成简单的数据整理和数据分析工作,并且需要较少的技术背景。我们甚至可以使用 JavaScript 和 D3 进行基本数据探索,因为它们提供了我们将在本书后面讨论的统计方法。
一旦数据分析开始,通常会创建一些原型来帮助完善我们的故事。Tableau 和 RawGraphs 等工具使我们能够快速生成此类图表。这是非常重要的一步,在此阶段创建的可视化通常并不花哨或精致。我们不想在原型设计阶段过于执着于我们的想法,花费大量时间。我们可能会发现自己必须“杀死我们的宝贝”并重新开始几次,直到我们找到最适合我们想要讲述的故事的可视化效果。网络图可能是一个例外,直接跳到 D3 对于这些项目通常是有意义的。
最后,一旦我们知道要创建的可视化类型,就该卷起袖子,对其进行编码,并使用 D3 对其进行完善。如今,编码步骤通常发生在单页应用程序 (SPA) 中,使用 React 或 Svelte 等框架。
图 使用 D3 构建的自定义可视化的另一个示例,其中形状与每首歌曲的不同属性(例如持续时间、流派和节奏)成比例(
https://amdufour.github.io/spotify-hits)。
D3.js 的工作原理
您可能已经尝试过 D3,并发现它并不容易上手。也许那是因为您希望它是一个简单的图表库。一个恰当的例子是创建条形图,我们将在第 2 章和第 3 章中进行此操作。D3 没有一个函数来创建条形图。相反,它有一个将
图 使用 Highcharts 与 D3.js 生成的条形图。Highcharts 的代码更简单、更短,但 D3.js 更通用。
在图 中,您可以看到我们通常如何使用 D3 进行数据可视化编码的地图。我们从一个数据集(通常是 CSV 或 JSON 文件)开始,然后使用 d3-fetch 模块将此数据集加载到我们的项目中。我们通常需要执行一些操作来格式化数据。例如,我们确保数字和日期的格式正确。如果我们之前没有这样做,我们可能还想询问我们的数据集以找到其主要特征。例如,提前知道其最大值和最小值通常很有帮助。然后我们准备开始构建可视化,为此我们将结合我们将在本书中学习的不同 D3 函数。最后,我们通过监听鼠标事件来添加交互性,允许用户过滤数据或放大可视化。
图 如何使用 D3.js 实现数据可视化
D3 生态系统 - 入门所需了解的内容
D3.js 从来不会单独使用,而是我们结合起来创建丰富的 Web 界面的技术和工具生态系统的一部分。与任何网页一样,D3 项目是在 DOM(文档对象模型)内构建的,并利用 HTML5 的强大功能。尽管 D3 可以创建和操作传统的 HTML 元素,例如分区 ( 鉴于 D3 是一个 JavaScript 库,我们自然倾向于将 D3 方法与本机 JavaScript 函数结合起来来访问和操作数据。D3 现在完全支持 JavaScript 的 ECMAScript 或 ES6 修订版以及大多数最新更新。D3 还作为模块提供,可以集成到我们构建 Web 项目所用的最新框架和库中。使用这些模块通常是首选方法,因为它不会污染我们应用程序的全局范围。 在本节中,我们将简要讨论这些技术及其在 D3 生态系统中的作用。由于 SVG 知识是理解 D3 的基础,因此我们将花时间更详细地解释您开始构建可视化所需理解的基础知识。如果您已经熟悉 HTML、SVG 元素、CSS、JavaScript 和 JavaScript 模块,请随意浏览或跳至第 节。 与 GIF 动画和框架成为网络动态内容顶峰的时代相比,我们已经走过了很长一段路。在图 中,您可以看到为什么 GIF 从未在强大的基于 Web 的数据可视化中流行起来。GIF 与设计用于使用 VML(矢量标记语言)的 infoviz 库一样,对于早期浏览器来说是必需的,但 D3 是为不再需要向后兼容性的现代浏览器而设计的。 图 世纪 年代的一些例子,比如dpgraph.com,仍然存在,让我们想起动画 GIF 无处不在的时代。 当您登陆网页时,要加载的第一个文件是超文本标记语言或 HTML 文件,如下例所示。浏览器解析 HTML 文件以构建文档对象模型或 DOM,这是用于 Web 文档的编程接口。我们经常将其称为 DOM 树,因为它由一组嵌套元素(也称为节点或标签)组成。在我们的示例中,和 the元素是父元素的子元素。同样,标签是、the和标签的父标签。标题也是该元素的同级元素。当您加载网页时,您在屏幕上看到的是标记中包含的元素。 I am a paragraph. I am another paragraph. 在 DOM 中,每个元素的三类信息定义了其行为和外观:样式、属性和特性。样式决定颜色、大小、边框、不透明度等。属性包括类、id 和交互行为,尽管某些属性也可以确定外观,具体取决于您正在处理的元素类型。对于 SVG 元素,属性用于设置不同形状的位置、大小和比例。属性通常指的是状态,例如复选框的“checked”属性,如果该框被选中,则该属性为 true;如果该框未被选中,则该属性为 false。尽管术语“属性”和“属性”经常互换使用,但它们是两个不同的东西。呈现 DOM 时,属性显示为初始状态。属性是元素的当前状态,并且可以随着用户与界面交互而改变。在第2章中,我们将讨论用于生成或修改HTML和SVG元素的样式和属性的D3方法。 DOM 还决定元素在屏幕上的绘制顺序,子元素在父元素之后和内部绘制。尽管 CSS 属性z-index使我们能够部分控制传统 HTML 元素绘制到屏幕上的顺序,但 SVG 元素严格遵循它们在 DOM 中出现的顺序。根据画家的模型,之后绘制的内容出现在之前绘制的内容之上。 可扩展矢量图形 (SVG) 的引入确实改变了网络的面貌。几年之内,SVG 图形成为主要的 Web 开发工具。光栅图形(PNG 和 JPG)由微小的像素组成,当我们放大得太近时,这些像素就会变得可见,而矢量图形则是通过数学和几何图形构建的。它们在任何尺寸和任何屏幕分辨率下都能保持清晰的外观。SVG 图形的另一个显着优势是它们可以直接注入 DOM,允许开发人员操纵其元素并为其设置动画,并使屏幕阅读器可以访问它们。如果构建正确,SVG 也具有高性能,其文件大小仅为等效光栅图像的一小部分。 当使用 D3 创建数据可视化时,我们通常将 SVG 形状注入 DOM 并修改其属性以生成组成可视化的视觉元素。了解 SVG 的工作原理、主要 SVG 形状及其表示属性对于大多数 D3 项目至关重要。 本书的每一章都包含旨在支持您的学习体验的代码练习。我们强烈建议您“做”这本书,而不仅仅是“读”这本书,这意味着在阅读章节时完成练习。通过这种方式,您将保留更多信息,并很快就能构建自己的 D3 项目! 对于每个练习和项目,您都可以访问现成的代码文件。您可以在本书的 Github 存储库( 从 Github 存储库下载代码文件 每一章都有自己的文件夹,其中包含一个或多个练习,按照每章中的部分编号。练习包括一个start文件夹,其中包含入门所需的所有文件。您将在文件夹中找到练习的完整解决方案end。当您完成一章的各个部分时,您可以继续在上一部分使用的文件中进行编码,或者使用专用于该部分的文件夹重新开始。两种选择都会导致相同的结果。 让我们开始探索矢量图形。转到本书提供的代码文件。找到end中的文件夹 图 我们将在本节中构建的基本 SVG 形状图库。 您正在查看的 SVG 图形包含创建 D3 可视化时最常使用的形状:线条、矩形、圆形、椭圆形、路径和文本。 使用 D3 时,您通常会告诉库应将哪些形状附加到 DOM。您还负责了解需要计算哪些表示属性才能使形状具有您正在寻找的尺寸、颜色和位置。在下面的练习中,您将编写创建图 中每个 SVG 元素的代码。我们将此练习称为SVG 形状图库。之后,您将了解入门所需的所有 SVG 基础知识。 在您选择的代码编辑器中打开练习文件夹index.html中的文件。我们推荐VS Code,这是一个免费、易于使用的代码编辑器,并且具有多种功能,对前端开发很有帮助。startSVG_Shapes_Gallery 正如您所看到的,index.html是一个简单的 HTML 文件。如果您在浏览器中打开此文件(右键单击该文件并在“打开方式”菜单中选择浏览器),您将只会看到一个空白页面。这是因为该元素为空。在接下来的小节中,我们将向此元素添加 SVG 形状。 清单 .a SVG 形状库练习的起始 HTML 文件 以下部分将介绍多个 SVG 元素及其属性。作为开发人员,我们在构建项目、使用我们不熟悉的 SVG 元素或寻找 JavaScript 函数来执行特定操作时严重依赖在线资源。在前端开发中,MDN Web Docs ( 在 SVG 图形的世界中,容器是在其上绘制所有内容的白板。每个 SVG 形状都嵌套在 打开浏览器的检查器工具(在浏览器窗口中右键单击并选择“检查”)。在检查器窗口中,您将看到组成页面的 DOM。找到容器,也称为 SVG 节点。当您在检查器中将鼠标移到它上面时,SVG 元素会在页面上突出显示。您可以在图 中看到此效果。 图 在 DOM 树中选择并在视口中突出显示的 SVG 节点 默认情况下,浏览器为 SVG 容器提供宽度300px和高度。150px但我们也可以使用属性来分配这些值。属性用于提供有关 HTML 元素的附加信息。对于内联 SVG,我们主要使用属性来设置组成 SVG 图形的元素和形状的大小和位置。 例如,我们可以设置SVG 容器的宽度和高度属性。返回到文本编辑器,并将 awidth和 aheight属性添加到 SVG 容器。将它们的值设置为和并保存文件。 在浏览器中重新加载项目并在检查工具中找到 SVG 节点。请注意,宽度和高度属性现在显示在 SVG 容器的括号内。如果将鼠标移到检查工具 DOM 树中的 SVG 节点上,您还会看到视口中的 SVG 容器现在的大小为 像素 x 像素。 图 SVG节点采用其属性指定的大小 为了帮助我们查看 SVG 容器,而不必从检查器中突出显示它,让我们给它一个边框。向 SVG 容器添加样式属性并插入 CSS 边框属性。在下一个代码片段中,我们使用 border 速记属性创建一个宽度为 1px 的黑色实心边框。 保存文件,重新加载页面并确认 SVG 容器周围有边框。现在,调整浏览器窗口的大小,直到它小于 SVG 容器。您将观察到 SVG 容器保持固定的宽度和高度,并且不适应浏览器窗口的大小。让我们尝试使 SVG 容器具有响应能力。 之前,我们将 SVG 属性设置为绝对值 (和),浏览器将它们解释为以像素为单位的测量值 (900px和300px)。但我们也可以使用百分比。在文本编辑器中,将宽度属性更改为相对值“ %”,保存文件并重新加载页面。 再次调整浏览器大小,并注意 SVG 如何获取可用的完整宽度并保持 像素的固定高度。这更好,但我们失去了原来的纵横比。 为了使内联 SVG 具有响应能力,我们可以使用viewBox 属性。在代码编辑器中,从 SVG 容器中删除width和属性,并将它们替换为属性。给它一个值。heightviewBox"" 再次调整浏览器窗口的大小。你注意到了什么?SVG 容器现在可以适应任何屏幕尺寸,同时保持其纵横比为 :。我们有一个响应式 SVG! 正如您所注意到的,viewBox 属性由四个值的列表组成。前两个数字指定 viewBox 坐标系的原点(x 和 y)。在本书中,我们将始终使用,但很高兴知道这些值可用于更改 SVG 容器的哪一部分在屏幕上可见。viewBox 属性的最后两个数字是其宽度和高度。它们定义 SVG 的纵横比,并确保其完美缩放以适合任何容器而不变形。 安装在容器内是这里的关键。到目前为止,我们的内联 SVG 的容器是 HTML元素,它通常会扩展以适应浏览器的视口。如果视口变得非常大,SVG 也会变得非常大。通常,我们希望 SVG 具有最大宽度,以便它不会大于页面上的其余内容。为此,请将 SVG 容器包装在宽度为 %、最大宽度为 1200px 的 div 内。为简单起见,我们将这些属性设置为内联样式,但在实际项目中,这些属性将来自 CSS 文件。请注意,我们还添加了值边距“ 0 auto”,以使 SVG 在页面上水平居中。 尝试再次调整浏览器的大小,看看我们的 SVG 如何优雅地适应任何屏幕尺寸,同时尊重其容器的最大宽度。该策略有助于将 D3 可视化注入响应式网页,我们将在本书中使用它。 既然我们知道了如何使内联 SVG 具有响应性,那么解决 SVG 形状在 SVG 容器内的定位方式就很重要了。SVG 容器就像一张白纸,我们可以在上面绘制矢量形状。矢量形状是根据基本几何原理定义的,并参考 SVG 容器的坐标系进行定位。 SVG 坐标系与笛卡尔坐标系类似。其 2D 平面使用两个垂直轴来确定元素的位置,称为 x 和 y。这两个轴源自SVG 容器的左上角,如图 所示。意思是y轴的正方向是从上到下。记住这一点可以让你避免一些头痛! 图 SVG容器的坐标系和元素的位置 为了在 SVG 容器内定位元素,我们从左上角的原点开始向右移动。这将为我们提供元素的水平 (x) 位置。对于垂直 (y) 位置,我们从顶部开始向下移动。这些位置由每个 SVG 形状的表示属性定义。 现在,我们将了解您在构建 D3 项目时经常遇到的 SVG 形状。我们还将讨论它们的主要表现属性。这里的目标绝不是编写 SVG 提供的所有形状和功能的综合指南,而是涵盖支持您的 D3 之旅的基础知识。 出色的艺术家可以用矢量图形绘制任何东西,但您可能不会因为您是一名艺术家而关注 D3。相反,您正在处理图形并考虑更务实的目标。从这个角度来看,理解几何基元(也称为图形基元)的概念至关重要。几何基元是简单的形状,例如点、线、圆和矩形。这些形状可以组合成更复杂的图形,特别方便直观地显示信息。 基元对于理解您在现实世界中看到的复杂信息可视化也很有用。树形布局,就像我们将在第 章中构建的那样,当您意识到它们只是圆形和直线时,它们就不那么令人生畏了。当您将交互式时间线视为矩形和点的集合时,它们更容易理解和创建。即使是主要以多边形、点和线形式出现的地理数据,当您将其分解为最基本的图形结构时,也不会那么混乱。 线条元素可能是所有 SVG 形状中最简单的。它获取两个点的位置,设置为属性,并在它们之间绘制一条直线。返回到该index.html文件,并在 SVG 容器内添加一个 图 在SVG容器的坐标系中定位线元素 如果您保存并重新加载项目,您的线条将不可见,您可能想知道发生了什么。为了使 SVG 线条在屏幕上可见,我们还需要设置其描边属性,该属性控制线条的颜色。border 属性的值与 CSS color 属性类似。它可以是颜色名称 ( black, blue, ...)、RGB 颜色 ( rgb,0,0)) 或十六进制值 ( #)。向您的线条添加笔划属性,并为其指定您选择的颜色(我们使用黑色)。现在它应该在屏幕上可见。 如果我们想设置线条的宽度,我们可以使用描边宽度属性。此属性接受绝对数字(转换为像素)或相对值 (%)。例如,以下行的 a 为stroke-width3px。如果stroke-width未声明该属性,浏览器将应用默认值 1px。 打开浏览器的检查器工具并找到 SVG 节点及其包含的行。双击其中一个属性,更改其值并观察新值如何修改线的起点或终点。花时间尝试不同的值,以确认您了解属性x1、y1、x2和如何y2影响线条的位置和长度。 -现在,为属性赋予 值x1。你看到线的起点是如何消失的吗?落在 SVG viewBox 之外的任何形状或形状部分在屏幕上都不可见。不过,该元素仍然存在于 DOM 中。我们可以访问和操纵它。如果 SVG 中的某个元素不可见,并且您不知道为什么首先要检查它是否在 SVG viewBox 之外!请记住,您始终可以通过使用开发人员工具检查 DOM 来找到它。正如我们之前所做的那样,如果将鼠标移到检查器工具中的元素上,即使它位于 SVG viewBox 之外,它也会在视口中突出显示。 图 SVG 线在 SVG 容器外部时部分隐藏 为了提高效率,大多数 SVG 元素只需要一个自闭合标签(我们使用 顾名思义,矩形元素 在我们的示例中,矩形的左上角位于SVG 容器原点的260px右侧和下方。25px它的宽度为120px,高度为60px。与其他位置属性一样,我们可以使用百分比而不是绝对数字来设置它们的值。例如,如果我们将该width属性设置为%,则矩形将扩展到 SVG 容器宽度的一半。 图 在 SVG 容器的坐标系中定位矩形并调整其大小 您可能已经注意到我们的矩形充满了黑色。默认情况下,浏览器对大多数 SVG 形状应用黑色填充。我们可以通过设置fill属性并为其指定任何 CSS 颜色来更改该颜色。如果我们想给矩形添加边框,我们添加一个描边属性。图 显示了一些示例。请注意,如果不声明属性,则矩形周围不会绘制边框stroke。另外,在最后一个矩形中,属性fill-opacity和border-opacity用于使fill和stroke半透明。与 CSS 中一样,不透明度可以设置为绝对值 ( ) 或百分比 (%)。与填充和描边相关的所有属性也可以从 CSS 文件设置或修改。 图 应用于矩形 SVG 形状的不同样式属性 如果您希望矩形具有圆角,则只需添加rx和ry属性,分别是水平和垂直角半径。这些属性接受绝对值(以像素为单位)和相对值(百分比)。例如,下面矩形的每个角的半径都是 20px。将此矩形添加到您的形状库中。 此时,您可能想知道 SVG 中是否有一个可以绘制正方形的元素。我们不需要一个!在 SVG 中,我们 作为参考,我们的形状库中现在有三种类型的 SVG 矩形:经典矩形、圆角矩形和正方形。为了好玩,我们给了它们颜色#6ba5d7并玩弄它们stroke和fill属性。请注意,只有笔划在正方形上可见,因为其fill属性值为transparent或none。您的矩形应该类似于图 中的矩形,除非您更改了它们的属性(我们鼓励您这样做)! 当您尝试在可视化中对齐形状时需要记住的一点是,笔划是均匀地绘制在 SVG 形状的内部和外部边界上的。如下图所示,如果矩形的width属性为 40px,则应用 ofstroke-width会1在视觉上向矩形的左侧添加 0.5px,向右侧添加 0.5px(而不是像我们本能地认为的那样,向每边添加 1px) ),实际总宽度为 41px。如果stroke-width是2,它会在每边添加 1px,依此类推。 笔划宽度对 SVG 形状实际宽度的影响 圆形形状经常用于数据可视化。它们自然地吸引眼球,并使可视化感觉更加友好和有趣。我们使用 图 在 SVG 容器的坐标系中定位圆和椭圆并调整其大小 您还可以使用圆形的填充和描边属性。为了生成图 中的效果,我们使用了透明填充和 3px 的描边,颜色为#81c21c。 类似地, SVG路径是迄今为止所有 SVG 元素中最灵活的。它们在 D3 中广泛用于绘制几乎所有无法用迄今为止讨论的形状基元之一(直线、矩形、圆形和椭圆形)表示的复杂形状和曲线。 d我们通过声明其属性(代表“draw”)来指示浏览器如何绘制路径。该d属性包含一个命令列表,从开始绘制路径的位置到要使用的曲线类型,直到指定我们是否希望路径成为闭合形状。例如,将以下路径元素添加到您的库中。在此示例中,d属性以M680 开头,表示“移动到坐标(, )”。然后我们从当前点 (, ) 到字母 C 后面的第三个坐标 () 指定的端点绘制一条三次贝塞尔曲线。三次贝塞尔曲线需要控制点,即字母 C 后面、起点和终点之间的坐标((, ) 和 (, ))。这些控制点定义了曲线的陡峭程度。然后我们有字母 S,它代表“停止”。它的工作原理与字母 C 类似,只不过它通向曲线的端点。这里最后一条曲线的起点是(),终点是(, ),控制点是(, )。曲线可以由一个或两个控制点定义。 图 一个简单的 SVG 路径及其控制点 要深入了解 SVG 路径,请参阅 MDN 的教程: 手动编写d属性对于简单的形状是可行的,但随着形状变得复杂而变得乏味。幸运的是,D3 具有强大的形状生成器,我们将在第 4 章中讨论。 关于路径要记住的另一件重要的事情是浏览器将以黑色填充它们,除非我们将它们的fill属性设置为noneor transparent。即使路径未闭合(如我们的示例中所示),情况也是如此。 。这对于可访问性来说是一个很大的优势。 由于数据可视化通常包含多个标签,因此有必要了解如何使用 到目前为止讨论的 SVG 形状使用自闭合标签 ( 保存文件并重新加载页面。您可能希望文本出现在 SVG 容器的左上角,但实际上却看不到......这是为什么?默认情况下,SVG 文本的位置是参考其基线计算的,由属性控制dominant-baseline。如果文本基线的坐标是(0, 0),您可以在图 中看到实际文本如何最终出现在 SVG 容器之外。由于位于 SVG 容器外部的任何元素都是不可见的,因此我们看不到文本。 图 位于 SVG 容器外部的文本 使用 SVG 文本时要考虑的另一点是文本的流动方式。常规 HTML 元素按照控制内容流的特定规则放置在页面上。如果您将一堆元素插入页面中,它们会自然地堆叠在一起,并且它们的内容将重排,以便它永远不会超出其容器。SVG 文本根本不流动,每个 SVG 元素必须单独定位。一种方法是设置它们的x和y属性。如果我们使用这些属性将文本放置在 处(, ),标签“line”将出现在形状库中 SVG 线的下方。 为了练习,创建一个新的文本元素,将标签“矩形”放置在矩形和正方形下方。 到目前为止,我们已经使用x和y属性来声明文本元素的左下角。但是如果我们想设置文本中点的位置怎么办?我们可以通过使用属性text-anchor并为其指定值来做到这一点middle。例如,我们可以使用此属性将圆形的文本标签居中。 图 text-anchor属性影响SVG文本的对齐方式。它的默认值是“开始”。为了根据中间对齐文本元素,我们应用“middle”的 text-andchor 属性。类似地,为了根据文本的结尾对齐文本,我们应用“end”的 text-andchor 属性。 最后为椭圆添加一个标签,为路径元素添加另一个标签。默认情况下,SVG 文本为黑色。您可以使用属性更改其颜色fill。 我们将在本节中讨论的最后一个 SVG 元素是组元素。group 或 如果我们希望正方形和“rect”标签一起显示并在 SVG 容器中作为一个整体移动,我们可以将它们放置在一个 包含正方形及其标签的组现在显示在 SVG 容器的左上角。我们可以将这个组及其包含的所有元素移动到 SVG 容器中任何我们想要的位置,同时保持正方形与其标签之间的对齐。 在 SVG 容器中移动组是通过转换属性完成的。变换属性比目前讨论的属性有点吓人,但与 CSS 变换属性相同。它采用一个变换(平移、旋转、缩放等)或一堆变换作为值。为了移动一个组,我们使用translate(x, y)变换。如果我们想要将 #如果我们对组应用填充属性, 最后重新加载页面并观察组内的标签如何继承组的颜色和字体,而保留在该组之外的标签则保持其原始外观。这种将共享属性应用于组元素的技术非常方便,可以帮助您将 DRY(不要重复自己)编码原则应用到您的工作中。当您需要更新这些属性时,它也会让您的生活更轻松。 恭喜您完成了本书的第一个练习!您可以在清单 .b 和编码文件的末尾文件夹中找到形状库的完整代码。当您构建第一个 D3 项目时,请使用此练习作为参考。 清单 .b 用于 SVG 形状图库练习的完整 HTML 文件 现在轮到你了!创建如下图所示的 SVG 图形。您可以在本章代码文件start内的文件夹中工作。02_SVG_exercise以下是一些指导原则: · 创建一个宽度和高度均为 像素的响应式 SVG 容器(当屏幕上有足够的空间时)。 · 绘制一个宽度和高度均为 像素的正方形。将其置于 SVG 容器的中心,并为其提供透明填充和 5px 黑色描边。 · 在 SVG 容器的中心添加一个半径为 100px 的圆。将其填充属性设置为 CSS 颜色名称“plum”。 · 绘制两条对角黑线,笔划为 5 像素。一个从正方形的左上角到右下角。另一个从正方形的右上角到左下角。 · 添加文本“SVG 太棒了!” 位于正方形上方并将其置于 SVG 容器的中心。为文本指定以下样式属性:字体大小为 18px,字体系列为 sans-serif。 我们鼓励您构建此 SVG 图形来强化本节中讨论的概念。 02_SVG_exercise / end您可以在附录 D 的 D. 节和本章代码文件的文件夹中找到解决方案。我们鼓励您尝试自己完成它。 我们已经提到过,我们通常使用 SVG 元素构建 D3 项目。有时,我们可能需要从大型数据集创建复杂的可视化效果,而传统的 SVG 方法可能会产生性能问题。请务必记住,对于数据可视化中的每个图形细节,D3 都会将一个或多个 SVG 元素附加到 DOM。一个典型的例子是由数千个节点和链接组成的大型网络可视化。这些可能会让你的浏览器喘不过气来……尽管浏览器可以轻松处理的对象数量随着性能的提高而不断增加,但普遍接受的经验法则是,如果满足以下条件,我们应该考虑使用画布而不是 SVG:可视化包含 多个元素。 Canvas 是一种客户端绘图 API,它使用脚本(通常是 JavaScript)来创建视觉效果和动画。它不会将 XML 元素添加到 DOM,这在从大型数据集构建可视化时可以显着提高性能。 Canvas 还允许您使用 WebGL API 来创建 3D 对象。尽管学习 WebGL 超出了本书的范围,但为 Web 创建 3D 数据可视化是可能的。目前主要用于实验项目。在第 章中,我们将介绍如何使用画布构建可视化并讨论其优点和缺点。 CSS 代表层叠样式表,是一种描述 DOM 元素如何在屏幕上显示及其外观的语言。从页面的整体网格布局到文本使用的字体系列,再到散点图中圆圈的颜色,CSS 可以将普通的 HTML 文件变成令人惊叹的网页。在 D3 项目中,我们通常使用内联样式或通过外部样式表应用 CSS 样式。 内联样式应用于具有该style属性的元素,如以下示例所示。该style属性可以在传统的 HTML 或 SVG 元素上使用,D3 有一个方便的方法来设置或修改该属性,我们将在第 2 章中讨论。 内联样式仅影响应用它们的元素。如果我们想要将相同的设计传播到多个元素,我们需要将相同的style属性应用于每个元素(或将所有元素包装在一起的 SVG 组)。它当然有效,但不是最有效的方法。 另一方面,外部 CSS 样式表非常适合全局应用样式。一种策略是要求 D3 将相同的类名添加到多个元素。然后,我们使用此类名称作为外部样式表中的选择器,并将相同的样式属性应用于目标元素组,如以下示例所示。这种方法效率更高,尤其是在维护大型项目时。它还遵循关注点分离原则,即我们将由 JavaScript 控制的行为与由 CSS 监管的样式分开。请注意,CSS 预处理器(例如 SASS 和 LESS)是此处描述的外部样式表方法的一部分。 在 CSS 样式表中: 请记住,内联样式优先于从外部样式表应用的样式。在任何前端开发项目中,规划 CSS 样式的架构并考虑级联顺序非常重要。 D3 是一个 JavaScript 库。它在 JavaScript 现有核心功能的基础上添加了新方法。这意味着在使用 D3 时,具有一点 JavaScript 经验会很有帮助。这也意味着,在构建 D3 项目时,您可以访问所有现有的 JavaScript 功能。 在本节中,我们将解释 D3 项目中广泛使用的两个 JavaScript 主题:方法链和对象操作。 如果您在网络上搜索 D3 项目的示例,您会发现在同一选择上会依次调用方法。这种技术就是我们所说的方法链,有助于保持代码简洁和可读。 我们可以将方法链视为汽车装配线。假设我们编写了运行这样一条装配线的脚本。正如您在下面的示例中看到的,我们首先声明一个car创建新Car()对象的变量。然后我们调用函数putOnHood(),将引擎盖放在汽车顶部,然后我们继续调用将放置车轮、轮胎和灯的函数。每个连续的调用都会添加一个元素到Car()对象,并且,一旦执行了所有方法,汽车就有了引擎盖、车轮、轮胎和车灯。每个方法都会将更新后的汽车对象传递给下一个方法,从而“链接”。请注意,每个调用都用点分隔,并且调用方法的顺序很重要。在我们的汽车装配线示例中,我们需要先安装车轮,然后才能将轮胎安装到车轮上。 现在让我们看看如何在 D3 中使用方法链接。想象一下,我们想要从 DOM 中获取所有 div 并在每个 div 中添加一个段落元素。段落元素应具有类属性my-class并包含文本“Wow”。然后,我们要在每个段落中插入一个 span 元素,并将文本“Even More Wow”以粗体显示。如果没有方法链接,我们需要将每个操作存储到一个常量中,然后在执行下一个操作时调用该常量,如下所示。光是看着就已经很累了…… 由于方法链接,相同的示例变得更加简洁。 在 D3 中,断行(JavaScript 会忽略这一点)以及缩进链接方法是很常见的。这使得代码更容易阅读,并且缩进可以帮助我们看到我们正在处理哪个元素。 不要担心理解前面的代码示例的作用,尽管您完全可以从不同方法的名称中猜出它!目前,我们只希望您熟悉如何在 JavaScript 中链接方法。我们将在第 2 章中介绍 D3 特定的术语。 D3 都是关于数据的,而数据通常被构造为 JavaScript 对象。了解这些对象的构造以及如何访问和操作它们包含的数据将为您构建可视化提供巨大帮助。 我们首先讨论简单数组,它是元素列表。在与数据相关的项目中,数组通常是数字或字符串的有序列表。 数组中的每个元素都有一个数字位置,称为索引,数组中第一个元素的索引为 0。 数组具有长度属性,对于非稀疏数组,该属性指定它们包含的元素数量。由于数组是零索引的,因此数组中最后一个元素的索引对应于数组长度减一。 我们还可以使用方法来确定数组是否包含特定值includes()。true如果数组中的元素之一与作为参数传递的值完全对应,则此方法返回。否则,它返回false。 然而,大多数数据集并不是简单的数字或字符串列表,它们的每个数据点通常由多个属性组成。让我们想象一个虚构机构的员工数据库,如表 所示。该表包含四列:每个员工的 ID、姓名和职位,以及该员工是否在 D3 工作。 ID 姓名 位置 与_d3一起工作 1 佐伊 数据分析师 错误的 2 詹姆士 前端开发人员 真的 3 爱丽丝 全栈开发人员 真的 4 休伯特 设计师 错误的 数据集中的每一行或数据点都可以由 JavaScript 对象表示,如下所示row1。 我们可以使用点符号轻松访问对象中每个属性的值。 我们还可以使用括号表示法访问这些值。如果属性名称包含空格等特殊字符,或者如果我们之前将属性名称保存在常量或变量中,那么括号表示法会很方便。 在现实生活中,数据集通常被格式化为对象数组。例如,如果我们使用 D3 加载表 中包含的数据集(正如我们将在第 3 章中学习的那样),我们将获得以下对象数组,可以将其保存在名为 的常量中data。 data我们可以使用循环遍历数组中的每个元素或数据点。更具体地说,JavaScript forEach循环非常方便且易于编写和阅读。迭代数据集的一个常见用例是数据整理。当我们加载外部 CSV 文件时,数字通常被格式化为字符串。让我们以data数组为例,将属性的值id从字符串转换为数字。 在下面的示例中,数组迭代器使d我们能够访问每个对象。使用点符号,我们id使用运算符将?每个值转换为数字+。 JavaScript 提供了许多数组迭代器方法,可以帮助我们与数据交互,甚至在需要时重塑数据。假设我们想要将数据集中的每个员工定位到可视化上。创建一个仅包含员工姓名的简单数组可能会派上用场,为此我们将使用map()方法。 同样,如果我们只想隔离使用 D3 的员工,我们可以使用这些filter()方法。 最后,我们可以通过该方法找到id为3的员工find()。请注意,该find()方法在找到它要查找的值后停止迭代。我们只能在搜索单个数据点时使用此方法。 本节讨论的方法远未涵盖 JavaScript 提供的所有数组和对象操作技术。但在处理数据时,您可能会不断回想起它们。每当您需要找到另一种方法来访问或操作数据时,MDN Web 文档 ( JavaScript 在过去十年中发生了重大变化。现代 JavaScript 的两个最重要的趋势是 Node.js 的兴起和 JavaScript 框架作为大多数项目标准的建立。 对于 D3 项目,我们想了解的主要 Node 技术是 NPM,即 Node Package Manager。NPM 允许您安装“模块”或小型 JavaScript 代码库以在应用程序中使用。您不必包含,
),但我们主要使用 SVG 图形或在画布(从脚本渲染位图图像的 HTML 元素)内生成可视化效果。然后,我们还可以使用旧的 CSS 样式表,它可以增强 D3 项目并使其设计更易于维护,尤其是在广泛的团队中。
HTML 和 DOM
I am a title
SVG——可缩放矢量图形
如何访问代码文件
https://github.com/d3js-in-action-third-edition/code-files)上找到它们。如果您熟悉 Git,则可以将存储库克隆到您的计算机上。您还可以下载压缩文件。
chapter_01/SVG_Shapes_Gallery并右键单击该文件index.html。在菜单中,转到打开方式并选择浏览器。我们建议使用 Chrome 或 Firefox,因为它们具有出色的检查器工具。该文件将在新的浏览器选项卡中打开,并且将出现您在图 中看到的矢量图形。您还可以在 Github 托管项目 (
https://d3js-in-action-third-edition.github.io/svg-shapes-gallery ) 上查看这些 SVG 形状。
哪里可以找到更多信息
https://developer.mozilla.org/ ) 始终是可靠且全面的资源。它包含 HTML 元素及其属性、CSS 属性和 JavaScript 函数的易于理解且通常可编辑的示例。响应式 svg 容器
svg坐标系
数据可视化技巧:几何基元
线
笔记
长方形
图 三种SVG矩形
SVG笔画的位置
圆和椭圆
路径
https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths。文本
分组元素
[...]
练习:创建 SVG 图形
画布和webGL
CSS
.my-class {
font-size: 16px;
font-family: serif;
}
In the DOM:
JavaScript
方法链接
let car = new Car().putOnHood().putOnWheels().putOnTires().putOnLights();
const mySelection = d3.selectAll("div");
const myParagraphs = mySelection.append("p");
const myParagraphsWithAClass = myParagraphs.attr("class", "my-class");
const myParagraphsWithText = myParagraphsWithAClass.text("Wow");
const mySpans = myParagraphsWithText.append("span");
const mySpansWithText = mySpans.text("Even More Wow")
const myBoldSpans = mySpansWithText.style("font-weight", "");
d3.selectAll("div").append("p").attr("class", "my-class").text("Wow")
? .append("span").text("Even More Wow").style("font-weight", "");
d3.selectAll("div")
.append("p")
.attr("class", "my-class")
.text("Wow")
.append("span")
.text("Even More Wow")
.style("font-weight", "");
数组和对象操作
const arrayOfNumbers = [, , 9, , ];
const arrayOfStrings = ["blue", "red", "yellow", "orange"];
arrayOfNumbers[0] // =>
arrayOfStrings[2] // => "yellow"
arrayOfNumbers.length; // => 5
arrayOfStrings[arrayOfStrings.length - 1] // => "orange"
arrayOfNumbers.includes(9) // => true
arrayOfStrings.includes("pink") // => false
arrayOfStrings.includes("ellow") // => false
表 包含员工及其职位的小型数据集
const row1 = {
id:"1",
name:"Zoe",
position:"Data analyst",
works_with_d3:false
};
row1.name // => "Zoe"
row1.works_with_d3 // => false
row1["position"] // => "Data analyst"
const myProperty = "works_with_d3";
row1[myProperty] // => false
const data = [
{id:"1", name:"Zoe", position:"Data analyst", works_with_d3:false},
{id:"2", name:"James", position:"Frontend developer", works_with_d3:true},
{id:"3", name:"Alice", position:"Fullstack developer", works_with_d3:true},
{id:"4", name:"Hubert", position:"Designer", works_with_d3:false}
];
data.forEach(d => {
d.id = +d.id;
});
data.map(d => d.name); // => ["Zoe", "James", "Alice", "Hubert"]
data.filter(d => d.works_with_d3);
// => [
{id:2, name:"James", position:"Frontend developer", works_with_d3:true},
{id:4, name:"Hubert", position:"Designer", works_with_d3:true}
];
data.find(d => d.id === 3);
// => {id:"3", name:"Alice", position:"Fullstack developer", works_with_d3:true}
https://developer.mozilla.org/ ) 始终是包含大量示例的可靠参考。 Node 和 JavaScript 框架