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

news2024/11/26 2:59:00

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

  • 第一部分 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 完成设置后的坐标轴及轴标签效果图】

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uniapp使用navigator标签不支持flex布局

前言 今天使用uniapp开发时&#xff0c;选用navigator作为flex布局容器&#xff0c;内部元素水平排列&#xff0c;可是发现微信小程序生效&#xff0c;网页端不生效。 微信小程序效果如下&#xff1a; 网页端效果如下&#xff1a; 源代码如下&#xff1a; <template>&l…

园区高能耗企业 水-电-气-热-油采集系统-能源管理系统

能源管理系统能源管理系统源码能源管理平台能源管理系统&#xff08;EMS&#xff09;能源监测能源管控能源系统能源监控能源预测&#xff0c;适用于高能耗企业能源企业 一、介绍 基于SpringCloud的能管管理系统-能源管理平台源码-能源在线监测平台-双碳平台源码-SpringCloud全…

Vxe UI vue vxe-table 虚拟树表格的使用,流畅的渲染万级数据树结构表格

Vxe UI vue vxe-table 虚拟树表格的使用&#xff0c;流畅的渲染万级数据树结构表格 代码 普通树表格&#xff0c;一般存数据库里都是平级数据&#xff0c;vxe-table 的树渲染这就非常友好了&#xff0c;只有带有父子id关联的数组&#xff0c;就可以自动渲染树表格。 <te…

LeetCode--买卖股票的最佳时机含冷冻期--动态规划

一、题目解析 二、算法原理 我们可以使用dp[i]来表示第i天买卖股票所获得的最大利润。由题可得我们只能持有一支股票&#xff0c;并且在卖出后有冷冻期的限制&#xff0c;因此我们会有三种不同的状态&#xff1a; 我们目前持有一支股票&#xff0c;对应的「累计最大收益」记为…

ONLYOFFICE 文档8.2版本已发布:PDF 协作编辑、改进界面、性能优化等更新

ONLYOFFICE 在线编辑器最新版本已经发布&#xff0c;其中包含30多个新功能和500多个错误修复。阅读本文了解所有更新。 关于 ONLYOFFICE 文档 ONLYOFFICE 是一个开源项目&#xff0c;专注于高级和安全的文档处理。坐拥全球超过 1500 万用户&#xff0c;ONLYOFFICE 是在线办公领…

基于Java+SpringBoot+Vue的水果购物网站的设计与实现

基于JavaSpringBootVue的水果购物网站的设计与实现 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末附源码下载链接&#x1…

【C++基础编程】一、初识C++

文章目录 1、编程语言是什么2、进制3、第一个C程序4、注释 一、初识C 1、编程语言是什么 我们编写程序&#xff0c;就是希望与计算机进行交流&#xff0c;让计算机帮助我们实现我们期望的效果。从这点出发&#xff0c;其实和人与人之间的沟通交流是一样的。两个人如果需要正常…

8.MySQL复合查询

目录 复合查询基本查询回顾多表查询 - 笛卡尔积自连接子查询单行子查询多行子查询多列子查询在from中使用子查询 合并查询unionunion all 表的内连和外连内连接外连接左外连接右外连接 复合查询 前面我们讲解的mysql表的查询都是对一张表进行查询&#xff0c;在实际开发中这远远…

正点原子阿尔法ARM开发板-IMX6ULL(十)——用CRT完成串口验证与DDR3/RGBLCD简述

文章目录 一、串口实验1.1 bsp_uart.c 二、DDR3三、关于RGBLCD 小唠叨&#xff1a; 我发现我学习效率上&#xff0c;看文本信息时候&#xff0c;获取信息是很快的&#xff0c;可能10分钟看个pdf比看30分钟的视频&#xff0c;效率来的更高一点。比如学python&#xff0c;应该多看…

整流器滤波电路

一、整流器滤波电路概述 整流器滤波电路的主要功能是将交流电&#xff08;AC&#xff09;转换为直流电&#xff08;DC&#xff09;&#xff0c;并通过滤波器减少波动以输出稳定的直流电。其工作原理主要分为两个部分&#xff1a;整流部分和滤波部分。 二、整流电路 整流电路是…

AnaTraf | 网络性能监控与TCP响应时延:保障高效运维的核心要素

http://www.anatraf.com 网络作为业务运行的核心&#xff0c;直接影响着业务的连续性和用户体验。为了确保网络的高效性和稳定性&#xff0c;网络性能监控成为IT运维工作中的重要一环。TCP响应时延则是衡量网络性能的重要指标之一。本文将探讨如何通过网络性能监控和优化TCP响…

win10 有线网络变自带的wifi热点

① 首先确定自己的台式机或者笔记本带是否有无线网卡 win10查看无线网开 自带的wifi热点打不开——解决办法 ②win 搜索 “”移动热点“” 1: 打开热点 通过wlan 2: 编辑 热点名称密码即可 完成&#xff0c;有线网络变私人热点 手机 其他设备连接即可使用

图像分割-DeepLab

DeepLabV3源码链接&#xff1a;https://github.com/bubbliiiing/deeplabv3-plus-pytorch&#xff08;打不开私信我获取&#xff09; 一、简介 一般的模型如Unet一般用于医学领域&#xff0c;小目标&#xff0c;如细胞分割。 为了增大感受野&#xff08;从而更好的获得全局特征…

Linux 基础io_理解文件系统_软硬链接_动静态库

一.磁盘 1.磁盘物理结构 盘片 磁盘可以有多个磁片&#xff0c;每个磁片有两个盘面&#xff0c;每个盘面都对应一个磁头&#xff0c;都可以存储数据。 磁道 扇区 磁道是指在盘面上&#xff0c;由磁头读写的数据环形轨道。每个磁道都是由一圈圈的圆形区域组成&#xff0c;数据…

操作系统期末|考研复习知识点汇总 - 持续更新

本文将根据个人学习进度对b站王道408课程以及题目考察的知识点进行整合&#xff0c;视频中详细的导图将会直接复用&#xff0c;并且将会对一些重点知识进行扩展以及一些思维导图的补充&#xff0c;目前第三章内容正在整理中…… 一&#xff1a;计算机系统概述 1.1操作系统概念…

解锁PDF权限密码

目录 背景: 定义与功能&#xff1a; 过程&#xff1a; 主要功能&#xff1a; 使用方式&#xff1a; 使用限制&#xff1a; 注意事项&#xff1a; 总结&#xff1a; 背景: 前段时间自己设置了PDF文件的许可口令&#xff0c;忘了口令导致自己无法编辑内容等&#xff0c;这…

7、Nodes.js包管理工具

四、包管理工具 4.1 npm(Node Package Manager) Node.js官方内置的包管理工具。 命令行下打以下命令&#xff1a; npm -v如果返回版本号&#xff0c;则说明npm可以正常使用 4.1.1npm初始化 #在包所在目录下执行以下命令 npm init #正常初始化&#xff0c;手动…

docker基础使用创建固定硬盘大小为40G的虚拟机

在docker中创建的服务器&#xff0c;匹配出容器id&#xff0c;服务器ip&#xff0c;服务器核数&#xff0c;服务器内存&#xff0c;服务器硬盘空间 for i in $(docker ps | grep -aiE web | awk {print $1});do echo $i; docker inspect $i|grep -aiE ipaddr|tail -1|grep -ai…

Spring Boot 依赖注入为 null 问题

目录 问题 省流 代码复现 TestService TestAspect TestController 源码分析 AbstractAutoProxyCreator CglibAopProxy Enhancer 问题 工作中&#xff0c;在负责的模块里使用 DubboService 注解注册了一个 dubbo 接口&#xff0c;给定时任务模块去调用。在自我调试阶…

使用Bert+BiLSTM+CRF训练 NER任务

使用的数据集在这里E-Commercial NER Dataset / 电商NER数据集_数据集-阿里云天池 针对面向电商的命名实体识别研究&#xff0c;我们通过爬取搜集了淘宝商品文本的标题&#xff0c;并标注了4大类&#xff0c;9小类的实体类别。具体类型及实体数量如下 针对面向电商的命名实体…