【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 完成设置后的坐标轴及轴标签效果图】

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

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

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

相关文章

若依前后分离版集成积木报表嵌入菜单

今天主要是分享积木报表如何嵌入若依项目菜单,上一篇文件已经分享过若依集成积木报表,这里就不再多费口舌了,有需要的朋友可以移步上一篇文章查看:若依前后分离版集成积木报表-CSDN博客 1、首先是后端配置: 1.1…

【进阶OpenCV】 (11)--DNN板块--实现风格迁移

文章目录 DNN板块一、DNN特点二、DNN函数流程三、实现风格迁移1. 图像预处理2. 加载星空模型3. 输出处理 总结 DNN板块 DNN模块是 OpenCV 中专门用来实现 DNN(Deep Neural Networks,深度神经网络) 模块的相关功能,其作用是载入别的深度学习框架(如 TensorFlow、Caf…

KANO模型,用户需求与产品设计的必备技能!

在竞争日益激烈的市场中,了解客户需求并提供符合客户期望的产品和服务变得尤为重要。那么有没有一种方法可以作为参考呢?今天小编就带大家了解一下KANO模型。KANO模型(Kano Model)作为一种帮助企业识别和优先处理客户需求的工具&a…

Django操作数据库

Django操作数据库 1、ORM框架2、ORM-创建数据库3、ORM-连接数据库4、ORM-操作表4.1、类创建表4.2、修改表结构4.2.1、删除表结构4.2.2、新增表结构 5、ORM-增删改查5.1、新增数据5.2、删除数据5.3、查询数据5.4、更新数据 1、ORM框架 Django开发操作数据库很简单,内…

项目实战:Qt+OpenCV仿射变换工具v1.1.0(支持打开图片、输出棋盘角点、调整偏移点、导出变换后的图等等)

若该文为原创文章,转载请注明出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/143105881 长沙红胖子Qt(长沙创微智科)博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、Op…

如何解决JMeter响应数据乱码?

问题: 解决: 1、找到JMeter安装目录下的bin目录 2、 在bin目录下,打开" jmeter.properties "文件 3、搜索"sampleresult.default.encoding" 4、改成"sampleresult.default.encodingUTF-8",去掉前面…

数字图像处理:图像分割应用

数字图像处理:图像分割应用 图像分割是图像处理中的一个关键步骤,其目的是将图像分成具有不同特征的区域,以便进一步的分析和处理。 1.1 阈值分割法 阈值分割法(Thresholding)是一种基于图像灰度级或颜色的分割方法&…

图片写入GPS经纬高信息

近期项目中需要往java平台传输图片,直接使用QNetworkAccessManager和QHttpMultipart类即可,其他博文中有分享。 主要是平台接口对所传输图片有要求:需要包含GPS信息(经度、纬度、高度)。 Qt无法直接实现,…

教育平台的创新设计:Spring Boot实现

3系统分析 3.1可行性分析 通过对本信息化在线教学平台实行的目的初步调查和分析,提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本信息化在线教学平台采用Spring Boot框架,JA…

【D3.js in Action 3 精译_037】4.1 DIY 实战:D3 源码分析之——d3.timeFormat() 函数

当前内容所在位置(可进入专栏查看其他译好的章节内容) 第一部分 D3.js 基础知识 第一章 D3.js 简介(已完结) 1.1 何为 D3.js?1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践(上)1.3 数据可…

Axure重要元件三——中继器修改数据

亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢! 课程主题:中继器修改数据 主要内容:显示编辑内容、表格赋值、修改数据 应用场景:更新行、表单数据行修改 案例展示: 正文…

前端算法合集-2(含面试题-美团一面)

主要考察的就是数组扁平化,由浅入深吧 ①利用tostring()和split() let arr [1, 2, 3, [4, 5, 6], [7, 8, [9, 10, 11], 12], [13, 14, 15]] let newarrarr.toString().split(,) let numarrnewarr.map((item)>{itemNumber(item)return item }) console.log(numarr) ②利用…

AndroidStudio移动开发:使用Service播放音乐【步骤】

目录 一、创建新application 二、准备音乐文件 三、创建项目和布局(如果需要交互界面的话,这里简单假设一个基本布局) 四、创建MusicService类 五、在MainActivity中启动和控制Service(也可以另外创建MusicActivity类&#x…

MoeCTF 2024 ---Misc方向WP

安全杂项 signin 题目描述: xdsec的小伙伴们和参赛者来上课,碰巧这一天签到系统坏了,作为老师的你,要帮他们 教师代签。 特殊提醒:luo同学今天好像在宿舍打游戏,不想来上课,这是严重的缺勤行为…

【数据分享】全国人口-人口年龄结构和抚养比(1990-2021年)

数据介绍 一级标题指标名称单位指标解释人口年末总人口万人年末人口数指每年12月31日24时的人口数。年度统计的全国人口总数内未包括香港、澳门特别行政区和台湾省以及海外华侨人数。1981年及以前人口数据为户籍统计数;1982、1990、2000、2010、2020年数据为当年人口…

实操上手TinyEngine低代码引擎插件化开发

1.背景介绍 1.1 TinyEngine 低代码引擎简介 低代码开发是近些年非常热门的一种开发方式,用户可以通过可视化的方式,简单拖拽,不写代码或者编写少量代码,类似搭积木一样搭建业务应用。 TinyEngine是一个强大的低代码引擎&#x…

Redis 常用指令详解

Redis是一款开源的、高性能的键值对存储数据库,常用于缓存、会话存储以及其他需要快速访问的数据场景。本文将介绍Redis的一些常用指令,并通过代码示例进行说明。 一、连接操作指令 1. 连接 Redis 服务器 ./redis-cli -h 127.0.0.1 -p 63792. 认证&a…

计算广告第三版pdf

需要该书pdf版本的同学点赞,私信我:

Spark_入库时报错ORA-00001 unique constraint violated 解决办法

首先可能是数据入重复了 检查一下看看是否入库前删除了分区的数据,可能是重复数据入库的问题,如果不是这个那么继续排查。 入库的数据有问题,检测方法 如果报主键冲突了,则group by 一下id,date,然后select 的时候加一…

飞睿智能超宽带UWB音频传输模块,超低延迟数据传输,实时音频声音更纯净

在信息爆炸的时代,音频传输技术正以未有的速度发展,创新我们进入一个全新的听觉世界。今天,我们要探讨的,就是这场技术创新中的一颗璀璨明星——飞睿智能超宽带(UWB)音频传输模块。它以其独特的优势&#x…