【D3.js in Action 3 精译_035】4.1 D3 中的坐标轴的创建(下篇):坐标轴与轴标签的具体实现

当前内容所在位置(可进入专栏查看其他译好的章节内容)

  • 第一部分 D3.js 基础知识
    • 第一章 D3.js 简介(已完结)
      • 1.1 何为 D3.js?
      • 1.2 D3 生态系统——入门须知
      • 1.3 数据可视化最佳实践(上)
      • 1.3 数据可视化最佳实践(下)
      • 1.4 本章小结
    • 第二章 DOM 的操作方法(已完结)
      • 2.1 第一个 D3 可视化图表
      • 2.2 环境准备
      • 2.3 用 D3 选中页面元素
      • 2.4 向选择集添加元素
      • 2.5 用 D3 设置与修改元素属性
      • 2.6 用 D3 设置与修改元素样式
      • 2.7 本章小结
    • 第三章 数据的处理(已完结)
      • 3.1 理解数据
      • 3.2 准备数据
      • 3.3 将数据绑定到 DOM 元素
        • 3.3.1 利用数据给 DOM 属性动态赋值
      • 3.4 让数据适应屏幕
        • 3.4.1 比例尺简介(上篇)
        • 3.4.2 线性比例尺(中篇)
          • 3.4.2.1 基于 Mocha 测试 D3 线性比例尺(DIY 实战)
        • 3.4.3 分段比例尺(下篇)
          • 3.4.3.1 使用 Observable 在线绘制 D3 条形图(DIY 实战)
      • 3.5 加注图表标签(上篇)
        • 3.5.1 人物专访:Krisztina Szűcs(下篇)
      • 3.6 本章小结
    • 第四章 直线、曲线与弧线的绘制 ✔️
      • 4.1 坐标轴的创建(上篇)
        • 4.1.1 D3 中的边距约定(中篇)
        • 4.1.2 坐标轴的生成(中篇)
          • 4.1.2.1 比例尺的声明(中篇)
          • 4.1.2.2 坐标轴的添加(下篇) ✔️
          • 4.1.2.3 轴标签的添加(下篇) ✔️
      • 4.2 D3 折线图的绘制(精译中 ⏳)

文章目录

      • 4.1.2 坐标轴的生成 Generating axes
        • 1 比例尺的声明 Declaring the scales
        • 2 坐标轴的添加 Appending the axes ✔️
        • 3 轴标签的添加 Adding axis labels ✔️

《D3.js in Action》全新第三版封面

《D3.js in Action》全新第三版封面

译者按
上一篇我们完成了折线图比例尺的定义,它们是为本篇将要实现的坐标轴做铺垫。D3 坐标轴的相关知识点是 D3 基础知识中的重中之重,是后续定制各种可视化效果的前提。让我们跟着作者的思路,一口气拿下这块高地!

4.1.2 坐标轴的生成 Generating axes

(详见本专栏 【第 034 篇】)

1 比例尺的声明 Declaring the scales

(详见本专栏 【第 034 篇】)

2 坐标轴的添加 Appending the axes ✔️

两个比例尺的初始化完毕后,接下来添加坐标轴。D3 有四个坐标轴生成器:axisTop()axisRight()axisBottom()axisLeft(),分别用于创建顶部、右侧、底部及左侧的坐标轴。它们都归属于 d3-axis 模块。

上一节提过,坐标轴生成器函数接受一个比例尺作为参数。例如创建折线图的底部坐标轴,就该调用 axisBottom() 生成器,并将比例尺 xScale 作为参数传入,因为该比例尺负责分配底部坐标轴上的数据。然后将结果赋给常量 bottomAxis

const bottomAxis = d3.axisBottom(xScale);

坐标轴生成器负责将构成坐标轴的各要素组装到一起,为了让这些要素渲染到屏幕上,还需要调用 D3 选择集上的 call() 方法。在下列代码片段中,注意观察选择集 innerChart 的用法:在调用坐标轴生成器前我们先添加了一个类名为 axis-x 的分组元素,以便后续给坐标轴定位并设计样式。

const bottomAxis = d3.axisBottom(xScale);
innerChart
  .append("g")
    .attr("class", "axis-x")
    .call(bottomAxis);

然后在浏览器中查看生成的坐标轴效果。默认情况下,D3 坐标轴会出现在选择集的原点位置,本例中即为内部图表的左上角,如图 4.6 所示。此时可以通过平移坐标轴所在的 SVG 分组元素将其移动到图表底部。顺便强调一下:在分组元素上设置的样式变换会被旗下所有子元素继承。以下代码片段中,包含坐标轴元素的分组元素向下平移,平移量为内部图表的高度值:

const bottomAxis = d3.axisBottom(xScale);
innerChart
  .append("g")
    .attr("class", "axis-x")
    .attr("transform", `translate(0, ${innerHeight})`)
    .call(bottomAxis);

图 4.6 默认情况下,D3 坐标轴会在选择集的原点位置生成,即内部图表的左上角位置。需要通过平移操作定位到目标位置。

【图 4.6 默认情况下,D3 坐标轴会在选择集的原点位置生成,即内部图表的左上角位置。需要通过平移操作定位到目标位置。】

另一个需要调整的是坐标轴上的标签格式。默认情况下,D3 会根据定义域自动设置轴标签格式,在屏幕上渲染出相应的小时、天数、月份及年份标签。但默认格式未必是我们想要的效果。为此,D3 提供了多种方式来更改标签格式。

先来看一下,x 轴已经有了二月到十二月的标签,唯独没有一月份的。考虑到您所在的时区,数据集中的最早日期未必就是一月一日的零点,这样 D3 就无法将其视为首月的起点;又由于数据集不是动态的,硬编码一个变量 firstDate 代表最早日期不失为一个合理的解决方案。它可以通过 JavaScriptDate() 构造函数实现。

在以下代码片段中,firstDate 的值改为了一个 Date() 日期,并指定了年份(2021)、月份(00,月份索引值从零开始)以及日期(01),同时指定了小时、分钟及秒数(0, 0, 0):

const firstDate = new Date(2021, 00, 01, 0, 0, 0);
const lastDate = d3.max(data, d => d.date);
const xScale = d3.scaleTime()
  .domain([firstDate, lastDate])
  .range([0, innerWidth]);

保存项目后,会看到 1 月 1 日的位置有了一个轴标签;但该标签只显示了 2021 年,如图 4.7 所示。这没不能算错,因为 Fri Jan 01 2021 00:00:00 对应的就是 2021 年年初位置,只是我们想换成一个显示月份的标签。

图 4.7 默认情况下,D3 会自动调整轴标签上的时间格式。本例中 2021 年 1 月 1 日为一年的起点。这本身并没有错,但就可读性而言不是很理想。

【图 4.7 默认情况下,D3 会自动调整轴标签上的时间格式。本例中 2021 年 1 月 1 日为一年的起点。这本身并没有错,但就可读性而言不是很理想。】

还可以使用 d3-axis 模块下的 axis.tickFormat() 方法来设置轴标签的格式。刻度线(Ticks) 是上述坐标轴上的短竖线。它们通常伴随刻度标签一同显示,但也可以不显示。

假设我们想要的刻度标签格式为月份的缩写形式。在 D3 中可以使用 d3-time-format 模块下的 d3.timeFormat() 方法来设置格式。该方法接受一个格式字符串作为参数,月份名称的缩写对应格式字符串为 %b。这些格式的完整列表可以在该模块的官方文档中进行查看(译注:详见官方文档:https://d3js.org/d3-time-format#locale_format)。

以下代码片段通过底部坐标轴的选择集链式调用了 tickFormat() 方法,并将时间格式作为参数传入,最终效果如图 4.8 所示。

const bottomAxis = d3.axisBottom(xScale)
  .tickFormat(d3.timeFormat("%b"));

图 4.8 将底部轴的标签格式设为月份缩写形式的效果图

【图 4.8 将底部轴的标签格式设为月份缩写形式的效果图】

这样就设置好了日期标签的格式,每个标签都是各月的第一天,效果还不错。还可以再进一步,将标签放在两个刻度之间来提高可读性,表示每月是从前一个时间刻度延伸至后一个时间刻度。

要调整刻度标签的位置,首先得选中它们。打开浏览器的检查工具(Inspector),仔细查看 D3 生成的 SVG 坐标轴元素,会看到一个类名为 domainpath 元素。它在定义域的上方绘制了一条水平线段。该路径元素还包含两个外部刻度线,即图形两端的短竖线,如图 4.9 所示。而坐标轴的每个刻度与刻度标签则由一条短竖线和文本元素构成,并统一放在一个类名为 tick 的 SVG 分组元素内。这些 SVG 分组元素通过沿坐标轴方向的平移量来确定各刻度线与标签文本的方位。坐标轴生成器所创建的 SVG 元素标签及样式类均由 D3 的公共 API 接口控制。我们可以利用这些接口来自定义坐标轴的外观。

图 4.9 构成坐标轴的 SVG 元素示意图

【图 4.9 构成坐标轴的 SVG 元素示意图】

了解了坐标轴的结构,就可以通过选择器 ".axis-x text" 选中 x 轴的所有标签,即 axis-x 样式类下的所有文本元素。然后进行如下调整:利用其 y 属性将文本元素下移 10px,以进一步提高可读性;再将其 font-family 设置为之前用过的 Roboto,因为 D3 会默认将 axisfont-family 改为 sans-serif,从而阻断了文本标签对项目根节点的字体样式的继承。最后再将字号增大到 14px

出于分离关注点原则(separation of concerns)的考虑,以下示例代码中的最后两个样式最好通过 CSS 样式表来设置。但这里使用 D3 来简化问题:

d3.selectAll(".axis-x text")
  .attr("y", "10px")
  .style("font-family", "Roboto, sans-serif")
  .style("font-size", "14px");

为了将月份标签在对应的刻度间居中显示,这里需要调整 x 的属性值。由于每个月的天数不尽相同(28 到 31 天不等),我们需要找出各标签当月第一天与下月第一天的中间位置。同时需要注意,D3 已经在 g.asix-x 分组元素上将 text-anchor 属性自动设为了 "middle"

由于 D3 绑定到各标签的日期数据对应该月第一天,以下代码片段中,我们利用 JavaScriptgetMonth() 方法获取到当前月份。该方法返回一个介于 0 到 11 之间的整数值,分别代表一到十二月。然后将月份加一并通过 Date() 构造出一个新的 JavaScript 日期值。我们在第三章学过,回调函数里的第一个参数,通常命名为 d,代表绑定数据集中的每一个数据项,类似于遍历数据时用到的 forEach() 方法。

最后,再用 xScale 求出本月一号与下个月一号之间的中间距离。完成后的坐标轴效果将如图 4.10 所示(第 2 ~ 6 行):

d3.selectAll(".axis-x text")
  .attr("x", d => {
    const currentMonth = d;
    const nextMonth = new Date(2021, currentMonth.getMonth() + 1, 1);
    return (xScale(nextMonth) - xScale(currentMonth)) / 2;
  })
  .attr("y", "10px")
  .style("font-family", "Roboto, sans-serif")
  .style("font-size", "14px");

图 4.10 让月份标签居中显示的 x 坐标轴效果图

【图 4.10 让月份标签居中显示的 x 坐标轴效果图】

以上代码涉及很多操作,想必已经让您对自定义 D3 坐标轴的不同方法有了一个大致的了解。

接着再来为折线图添加 y 坐标轴,它的实现要简单得多。由于 y 轴靠左显示,这里用到的坐标轴生成器为 d3.axisLeft(),同时将 yScale 比例尺作为参数传入,并将结果赋给一个常量 leftAxis

const leftAxis = d3.axisLeft(yScale);

类似地,想要将坐标轴添加到内部绘图区,需要在内部图表的选择集上添加一个分组元素,并指定一个样式类 axis-y:(第 2 ~ 4 行)

const leftAxis = d3.axisLeft(yScale);
innerChart
  .append("g")
  .attr("class", "axis-y")
  .call(leftAxis);

保存设置后将看到 y 轴已经定位好了无需平移,如图 4.11 所示。剩下的工作就是设置标签的字体并增大字号。以下代码片段先选中样式类 axis-y 下的所有 SVG 文本元素,然后通过 x 属性令其向左稍作平移,以便提高可读性;之后再分别设置其 font-familyfont-size 属性:

d3.selectAll(".axis-y text")
  .attr("x", "-5px")
  .style("font-family", "Roboto, sans-serif")
  .style("font-size", "14px");

图 4.11 完成设置后的 x 坐标轴与 y 坐标轴效果图

【图 4.11 完成设置后的 x 坐标轴与 y 坐标轴效果图】

您可能也注意到了两个轴标签在设置 font-familyfont-size 时存在重复代码。在学习阶段,这样写并不算什么大问题;要是在专业项目环境下,则应当尽量避免像这样的代码冗余。除了前面提过的使用 CSS 样式表来统一设置外,还可以使用组合选择器,如以下代码所示(第 1 行):

d3.selectAll(".axis-x text, .axis-y text")
  .style("font-family", "Roboto, sans-serif")
  .style("font-size", "14px");
3 轴标签的添加 Adding axis labels ✔️

实现了折线图的坐标轴后,还有一件事需要完成——添加轴标签——它可以帮助读者更好地理解我们的图表。x 轴上的刻度标签不言而喻,但 y 轴则不然:虽然读者知道它们的取值范围在 0 到 90 之间,但可能并不知道它们的含义是什么。

这时就需要通过设置轴标签来解决这个问题。在 D3 项目中,标签就是 SVG 文本元素。因此只需将文本元素添加到 SVG 容器中即可。轴标签的内容就设为 "Temperature (°F)",然后将其垂直坐标指定到距离 SVG 容器原点 20px 的位置:

svg
  .append("text")
  .text("Temperature (°F)")
  .attr("y", 20);

大功告成!您本地的折线图效果此时应该如图 4.12 所示。

图 4.12 完成设置后的坐标轴及轴标签效果图

【图 4.12 完成设置后的坐标轴及轴标签效果图】

下一节我们将实现折线图的绘制。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/894181.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Vue.js设计与实现】第三篇第9章:渲染器-简单Diff算法-阅读笔记

文章目录 9.1 减少 DOM 操作的性能开销9.2 DOM 复用与 key 的作用9.3 找到需要移动的元素9.4 如何移动元素9.5 添加新元素9.6 移除不存在的元素 系列目录:【Vue.js设计与实现】阅读笔记目录 当新旧vnode 的子节点都是一组节点时,为了以最小的性能…

《深度学习》OpenCV库、Dlib库 人脸检测 案例解析

目录 一、Dlib库 1、什么是Dlib库 2、OpenCV优缺点 1)优点 2)缺点 3、Dlib库优缺点 1)优点 2)缺点 4、安装Dlib库 二、案例实现 1、对图片进行人脸识别 运行结果: 2、使用摄像头或对视频检测人脸 运行结…

安装和简单使用Milvus

安装和简单使用Milvus 1 介绍 Milvus是国产的高性能分布式向量数据库。 # Milvus官网 https://milvus.io/# 安装文档 https://milvus.io/docs/install-overview.md# Python的对应关系和接口文档 https://milvus.io/api-reference/pymilvus/v2.4.x/About.md2 安装Milvus 2.1…

flutter assets配置加载本地图片报错

首选列出我在照着网上说的设置assets怎么搞都报错,错误如下,搞的我想骂娘。 flutter: uses-material-design: true assets: - assets/images 后来找到了下面这个教程,才终于解决,就是要在后面加一个"/" 。 flutter这个…

北京大学与长安汽车联合发布TEOcc: 时域增强的多模态占据预测

北京大学与长安汽车联合发布TEOcc: 时域增强的多模态占据预测 Abstract 作为一种新颖的3D场景表示,语义占据(semantic occupancy)在自动驾驶领域引起了广泛关注。然而,现有的占据预测方法主要集中于设计更好的占据表示形式&…

scala 抽象类

理解抽象类 抽象的定义 定义一个抽象类 :abstract class A {} idea实例 抽象类重写 idea实例 练习 1.abstract2.错3.abstract class A{}4.对

保姆级Pinpoint(APM)实战教程

什么是Pinpoint Pinpoint是由韩国NAVER公司开发并开源的一款应用程序管理工具,主要针对大规模分布式系统进行性能监控和故障诊断。通过跟踪分布式应用程序之间的事务,帮助分析系统的整体结构以及其中的组件是如何相互连接的。 与其对标的还有Twitter的Zi…

软件模拟I2C和硬件直接驱动I2C读取TCA95系列I2C转IO芯片分析

问题描述: 软件读取I2C转IO信号跳变,低电平时能读到高电平信号,高电平时能读到低电平信号,正确信号和错误信号的比值约10:1。 原因分析: I2C芯片的驱动底层采用了软件模拟实现,没有防错机制,…

MongoDB 8.0已全面可用

全球广受欢迎的文档型数据库MongoDB目前最新最强的版本,在易用性、企业级安全性、 弹性、可用性等方面均有大幅提升,适用于各种应用程序。 MongoDB 8.0的优化使整体吞吐量提高了32%,时间序列数据聚合的处理速度提高了200%以上。MongoDB 8.0的…

SSD |(七)FTL详解(中)

文章目录 📚垃圾回收🐇垃圾回收原理🐇写放大🐇垃圾回收实现🐇垃圾回收时机 📚解除映射关系📚磨损均衡 📚垃圾回收 🐇垃圾回收原理 ✋设定一个迷你SSD空间: 假…

OpenAi推出ChatGPT客户端

10 月 18 日,继苹果 macOS 版之后,OpenAI 为微软 Windows 用户推出了 ChatGPT 应用桌面客户端。目前这款应用正在测试,ChatGPT Plus / Enterprise / Team / Edu 版本的付费用户可以在微软应用商店中下载使用。 这款应用实质上是网页版 ChatGP…

Part1_MCP4017T-502E/LT型数字变阻器使用方法

MCP4017T-502E/LT是Microchip(微芯)公司的一款SC70封装且具备7位单I2C™数字端口与易失性存储器数字电位器,通过数字接口来控制电位器的阻值大小,可用于需要精确调整电压分压比、信号增益控制等应用场景。相比传统的机械电位器&am…

【Java】多线程 Start() 与 run() (简洁实操)

Java系列文章目录 补充内容 Windows通过SSH连接Linux 第一章 Linux基本命令的学习与Linux历史 文章目录 Java系列文章目录一、前言二、学习内容:三、问题描述start() 方法run() 方法 四、解决方案:4.1 重复调用 .run()4.2 重复调用 start()4.3 正常调用…

初识Linux · 重定向和缓冲区

目录 前言: 预备知识 缓冲区 重定向 前言: 其实有了文件2的预备知识,我们已经初步了解了文件描述符fd是什么,底层是如何运作的了,那么本文,我们通过文件描述符对重定向和缓冲区有一个更深层次的理解&a…

鸿蒙开发案例:推箱子

推箱子游戏(Sokoban)的实现。游戏由多个单元格组成,每个单元格可以是透明的、墙或可移动的区域。游戏使用Cell类定义单元格的状态,如类型(透明、墙、可移动区域)、圆角大小及坐标偏移。而MyPosition类则用于…

三菱PLC如何实现数据排序的分析?

一、分析 将D100到D104中的据从小到大排序结果存在D100到D104中,如D100到D104中存入100,34,27,45,22这5个数据,编写一个子程序,只到通过调用这个子程序就可以实现这5个数据的排序。当然简单的方…

iOS IPA上传到App Store Connect的三种方案详解

引言 在iOS应用开发中,完成开发后的重要一步就是将IPA文件上传到App Store Connect以便进行测试或发布到App Store。无论是使用Xcode进行原生开发,还是通过uni-app、Flutter等跨平台工具生成的IPA文件,上传到App Store的流程都是类似的。苹果…

衡石分析平台系统分析人员手册-应用模版

应用模板​ 应用模板使分析成果能被快速复用,节省应用创作成本,提升应用创作效率。此外应用模板实现了应用在不同环境上快速迁移。 支持应用复制功能 用户可以从现有的分析成果关联到新的分析需求并快速完成修改。 支持应用导出为模板功能 实现多个用户…

数论的第二舞——卡特兰数

当然了,虽然主角是卡特兰数,但是我们该学的数论还是不能落下的,首先先来介绍一个开胃小菜线性筛 1.积性函数: 2.线性筛 线性筛的筛选素数的时间复杂度更低,可以达到O(n)的时间复杂度 将每一轮进行筛选的数 n 表示…

Threejs 实现3D 地图(02)创建3d 地图

"d3": "^7.9.0", "three": "^0.169.0", "vue": "^3.5.10" 地图数据来源&#xff1a; DataV.GeoAtlas地理小工具系列 <script setup> import {onMounted, ref} from vue import * as THREE from three im…