【D3.js in Action 3 精译_029】3.5 给 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 本章小结
文章目录
- 3.5 加注图表标签 Adding labels to a chart

《D3.js in Action》全新第三版封面
译者按
终于来到了本章代码量最为密集的最后一个小节了。作者真的是事无巨细,真正把大家当成零基础的数据可视化爱好者,竟然细致到 JS 的注释是怎么加的也要说明一下……不过这也算是本书的一大魅力吧。也多亏了这份执着和对细节把控的精益求精,新版才出现了这么多直观精美的配图。由于篇幅太长,本节最后的人物专访就放到下篇,本文为上篇,主要介绍条形图的最终实现。一起跟随作者实战起来吧!
3.5 加注图表标签 Adding labels to a chart
示例条形图就快做完了,但目前还不知道哪个矩形条对应哪个技术,也不知道矩形条的长度代表哪些票数。这些问题只要给图表加两组标签(label)就能解决:第一组列出技术名称,放在左侧;第二组则显示各矩形条对应的 count 票数值,分别放到各矩形条的最右端。
在基于 SVG 的可视化项目中,标签的制作可以通过 SVG 文本元素(text)实现:先将各矩形条分别与两个文本元素相结合,然后将其一同嵌入 SVG 的某个分组元素(group)内。根据第 1 章 1.2.2 节介绍的 SVG 分组元素的知识,这里可以用分组元素将多个子元素视为一个整体进行平移,以便于后续将绑定数据传递给它的后代元素。
接下来需要稍微重构一下代码。首先注释掉与矩形条元素相关的所有代码行,留待后用。在 JavaScript 中,单行注释以双斜杠(//)开头,而多行注释则以 /* 开始、以 */ 结束。
再回到处理数据绑定的那部分代码。此时应该让数据绑定到 SVG 的分组元素(g)上,而不是之前的矩形条上。然后将该选择集赋给一个常量 barAndLabel:
const barAndLabel = svg.selectAll("g").data(data).join("g");
为了让矩形条与标签元素同步移动,可以利用 transform 属性让各分组元素做垂直平移(vertical translation)。transform 变换属性(attribute)上的 translate 平移属性(property)接受两个参数:水平平移量,设为 0;以及垂直平移量,这里设为各矩形条的垂直坐标,由之前定义好的分段比例尺函数 yScale 计算得到:
const barAndLabel = svg.selectAll("g").data(data).join("g").attr("transform", d => `translate(0, ${yScale(d.technology)})`);
虽然 SVG 的分组元素没有可视化的图形表示,也不以占据某个有界空间的形式存在,但我们仍然可以将其想象成能够封装所有子元素的内容盒。借助 transform 属性,这些分组元素实现了垂直方向的均匀排布,如图 3.30 所示。各矩形条及其标签元素将相对于它们所在的父级分组元素进行定位:

【图 3.30 封装了矩形条与标签等后代元素的分组元素在 SVG 容器内的定位情况】
一切就绪后,就可以重新添加矩形条了。如下所示,调用选择集 barAndLabel,并将矩形元素添加进去:
const barAndLabel = svg.selectAll("g").data(data).join("g").attr("transform", d => `translate(0, ${yScale(d.technology)})`);barAndLabel.append("rect");
由于该选择集包含多个分组元素,D3 会分别给每个分组添加一个矩形元素。保存项目并使用检查工具进行查看,确认它们都已经添加到了 DOM 结构中,如图 3.31 所示:

【图 3.31 添加到每个分组元素里的矩形元素示意图】
现在可以取消刚才的注释,把它们用到新加的 rect 元素上。D3 数据绑定的一大好处,是绑定的数据会传递给分组内的所有后代元素。因此矩形条依然可以像之前那样拿到数据,唯一的区别是,矩形的 y 属性要设为 0,因为分组元素已经带着它完成了垂直平移:
barAndLabel.append("rect").attr("width", d => xScale(d.count)).attr("height", yScale.bandwidth()).attr("x", 100).attr("y", 0) // 矩形不用再做垂直平移,其定位相对于其父级分组元素的位置.attr("fill", d => d.technology === "D3.js" ? "yellowgreen":"skyblue");
这时就能看到各个矩形条了,效果和之前完全相同(详见图 3.28)。
译注
为方便查看新的条形图效果,我这里直接附上图 3.28:
接下来能可以正式添加标签了!再次调用选择集 barAndLabel,将 SVG 文本元素分别添加进去。由于各标签需要展示每个对应的技术名称,因此需要再链式调用一次 text() 方法。该方法只接受一个参数:文本元素要显示的文本内容。本例则需要根据每个绑定的数据项动态设置对应的文本内容:
barAndLabel.append("text").text(d => d.technology);
内容设置好后,再用 x 和 y 属性给每个标签定位。先来看水平方向,各标签末端要同矩形条的起始位置对齐。矩形条从 100px 开始,于是可以把文本元素放在大约 96px 的位置,与矩形条保持 4px 的间距。然后令其 text-anchor 属性(attribute)的值为 end,实现标签右对齐。这样 x 属性值就代表了各标签的末端位置,如图 3.32 所示:

【图 3.32 各技术标签的定位计算示意图】
再来看垂直方向。由于各标签的定位相对于所在的父级分组元素,只需稍向下平移即可与矩形条居中对齐。注意,SVG 文本元素的垂直定位是相对于它的基线(baseline)而言的。经反复试错与微调,最终给定的 y 值为 12 像素。位置的微调可以在浏览器的检查工具(inspector)里快速实现:
barAndLabel.append("text").text(d => d.technology).attr("x", 96).attr("y", 12).attr("text-anchor", "end");
最后,在根据各自的喜好,调用 style() 方法设置文本标签的 font-family 与 font-size 属性,分别确定字体及字号。本例使用的字体为 11 号无衬线字体,如图 3.33 所示:
barAndLabel.append("text").text(d => d.technology).attr("x", 96).attr("y", 12).attr("text-anchor", "end").style("font-family", "sans-serif").style("font-size", "11px");

【图 3.33 加注了技术标签的条形图效果】
接着,再在矩形条的另一端添加一组标签,显示该技术在问卷调查中的得票数,做法与添加技术名称标签类似。先调用 barAndLabel 选择集常量,然后在每个分组元素内添加一个文本元素,再通过链式调用的 text() 方法给每项技术指定相应的 count 值:
barAndLabel.append("text").text(d => d.count)
由于计数标签位于矩形条的末端,而矩形条的水平坐标可以通过 xScale 函数计算得到。再加上矩形条两边的间距(左边为预留的 100px,后边同样保持 4px 间隔),这样技术标签的 x 属性就能确定了。垂直方向,也令其下移 12px,如图 3.34 所示:
barAndLabel.append("text").text(d => d.count).attr("x", d => 100 + xScale(d.count) + 4).attr("y", 12)

【图 3.34 计数标签的定位计算示意图】
接着,再给技术标签设置 font-family 和 font-size 属性。注意,计数标签的字号为 9px,比技术名称的字号 11px 小一些,目的是为了让两组标签保持视觉上的层次感。较大的标签更吸引眼球,也便于让观众理解得票数是次于技术名称的样式设计。
barAndLabel.append("text").text(d => d.count).attr("x", d => 100 + xScale(d.count) + 4).attr("y", 12).style("font-family", "sans-serif").style("font-size", "9px");
最后一步,再在条形图左侧绘制一条垂直线,作为垂直方向的轴线。在以下代码片段中,我们将这条线段添加到 SVG 容器内。该线段的起点坐标 (x1, y1) 为 (100, 0),即 SVG 容器的顶部;终点坐标 (x2, y2) 则位于 (100, 700),即容器底部。再指定好线条的描边色,让轴线显示出来:
svg.append("line").attr("x1", 100).attr("y1", 0).attr("x2", 100).attr("y2", 700).attr("stroke", "black");
如果再把 SVG 容器的边框去掉,最终条形图的效果就应该如图 3.35 所示。该项目也托管到了 GitHub ,可以访问 http://mng.bz/mjor 进行访问。值得一提的是,本章给标签预留间距的做法并不常用。业内更通用的实现方案是遵守 D3 外边距约定(D3 margin convention),具体内容将在下一章进行介绍,后续章节也将按这种写法来进行讲解。

【图 3.35 最终实现的在线版 D3 条形图效果,详见:http://mng.bz/mjor】
恭喜您完成了本章的学习——知识点着实很密集!如果还没有掌握讲过的所有概念,也不必过于担心。后续章节还将继续提到这些概念,相信很快就能融会贯通 。
译注
实测时发现,左边标签的字号取
11px时部分标签显示不全,调整为10px正常。相应的得票数标签也最好该小些,设为8px比较合适(相关源码已同步上传到 CSDN 下载资源):
【补图 1 本地实测并重新调整字号后的 D3 条形图效果】
相关文章:
【D3.js in Action 3 精译_029】3.5 给 D3 条形图加注图表标签(上)
当前内容所在位置(可进入专栏查看其他译好的章节内容) 第一部分 D3.js 基础知识 第一章 D3.js 简介(已完结) 1.1 何为 D3.js?1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践(上)1.3 数据可…...
用python做一个简单的画板
一,画板的介绍 画板(Paint Board)是一个提供用户绘图、涂鸦和创作的平台。现代数字画板通常是由软件程序实现的,具有多种功能。以下是画板的一些主要特征和功能: 1. 基本绘图工具 画笔和铅笔:用户可以选…...
根据传入的文件流链接实现前端下载
后端传入一个下载的url,实现点击按钮,下载文件。 方式一: 通过window.open(“URL”, _blank) 方式 PS:会打开一个新的页面 import React from react;const DownloadButton () > {// window.open("URL", "_…...
大数据新视界 --大数据大厂之大数据环境下的零信任安全架构:构建可靠防护体系
💖💖💖亲爱的朋友们,热烈欢迎你们来到 青云交的博客!能与你们在此邂逅,我满心欢喜,深感无比荣幸。在这个瞬息万变的时代,我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…...
基于springboot的高校招生系统(含源码+sql+视频导入教程+文档+PPT)
👉文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于springboot的高校招生系统1拥有两种角色:管理员和用户 管理员:学生管理、专业管理、报名管理、录取通知管理、招生公告管理等 用户:登录注册、报…...
【C++设计模式】行为型模式:观察者模式
文章目录 行为型模式:观察者模式 行为型模式:观察者模式 观察者模式定义了一种一对多的依赖关系:它让一个主题(被观察者)对象关联多个观察者对象,并且当主题对象的状态发生变化时,它会主动通知…...
本篇5K,立志最细,FreeRtos中的信号量Semaphore教程详解!!!
前言:本篇教程,参考韦东山,开发文档,连接放在最后 目录 Semaphore基本概念 二值信号量(Binary Semaphore) 计数信号量(Couting Semaphore) 互斥信号量(Mutex&…...
【Postman】接口测试工具使用
干就完啦 Postman发送get请求案例1: Postman发送post请求案例2 Postman发送其他请求Postman测试实战 学习目标:能够使用Postman发送get/post/put/delete请求并获取响应结果 Postman发送get请求 首先postman是一款接口调试工具,支持win&…...
springboot 整合 rabbitMQ(1)
目录 一、MQ概述 二、MQ的优势和劣势 三、常见的MQ产品 RabbitMQ使用步骤 第一步:确保rabbitmq启动并且可以访问15672 第二步:导入依赖 第三步:配置 auto自动确认 manual手工确认(推荐使用!可以防止消息丢失&a…...
Appium Device Farm安装教程
环境要求:Appium version ≥ 2.4.X 安装appium npm install -g appium2.11.3 如果安装提示如下问题 npm error code EEXIST npm error syscall rename npm error path /Users/wan/.npm/_cacache/tmp/d5787519 npm error dest /Users/wan/.npm/_cacache/content-…...
异常、基类
异常 人生和世界总是充满着意外,争议、冲突和战争似乎是人类必然经历的过程。程序执行也有不满的时候。 不同编程语言的异常 Ada/Modula-3是早期引入异常处理的语言。 C语言没有标准意义的异常,使用goto或setjmp模拟错误或异常发生时的处理流程。 C/Java…...
VScode 自定义代码配色方案
vscode是一款高度自定义配置的编辑器, 我们来看看如何使用它自定义配色吧 首先自定义代码配色是什么呢? 看看我的代码界面 简而言之, 就是给你的代码的不同语义(类名, 函数名, 关键字, 变量)等设置不同的颜色, 使得代码的可读性变强. 其实很多主题已经给出了定制好的配色方案…...
MuMu模拟器12 KitsumeMask安装教程
这里是引用"> 在MuMu模拟器上安装KitsumeMask的时候遇到安装失败的情况。 一、下载APK安装包 如果你没有apk安装包可以通过下面的百度网盘进行下载 通过网盘分享的文件:KitsumeMask 链接: https://pan.baidu.com/s/1yeq3I6BsUD7J6uI-bnk-Vw?pwd=7n3v 提取码: 7n3v 二…...
Perforce静态分析工具2024.2新增功能:Helix QAC全新CI/CD集成支持、Klocwork分析引擎改进和安全增强
Perforce Helix QAC和Klocwork的最新版本对静态分析工具进行了重大改进,通过尽早修复错误、降低开发成本和加快发布速度,使开发团队实现左移。 本文中,我们将概述2024.2版本的新特性和新功能。 CI/CD和左移以实现持续合规性 现代软件开发实…...
太阳能电池特性及其应用
中南民族大学-通信工程2024-大学物理下实验 目录 代码实现结果显示 🛠工具使用 MarsCode(插件,集成在PyCharm); python编程(豆包AI智能体) 💻编程改进 此处是用「Matplotlib」来作图…...
日语学习零基础生活日语口语柯桥外语学校|股票用日语怎么说?
在日语中,“股票”可以说: • 株(かぶ) 这是最常用的表达方式,直接表示“股票”。 例如: 株を買う - 买股票 株を売る - 卖股票 • 株式(かぶしき) 这个词也是“股票”的意…...
第2关:寻找一个序列中的第K小的元素(即第k小元问题)
[TOC]寻找一个序列中的第K小的元素(即第k小元问题) 对于给定的含有n(n<100)元素的无序序列,求这个序列中第k(1≤k≤n)小的元素。 任务描述 本关任务:编写一个能计算数组中的第k小的元素的小程序。 相关…...
docker 搭建 vue3 + vite
vue3发布了,今天就分享一下我使用docker 搭建 vue3 vite 开发环境。至于为什么使用docker搭建,因为多版本可以快速切换,和本地环境避免冲突。好了话不多说我们开始吧。 1. 准备资料 Docker Desktop wsl2 ubuntu 下载地址 : https://www.docker.…...
【网易云音乐】--源代码分享
最近写了一个网易云音乐的音乐实现部分,是通过JavaScript和jQuery实现的,具体效果大家可以参照下面的视频 源代码分享 - git地址: 网易云音乐源代码 下面将着重讲解一下音乐实现部分 视频有点模糊,不好意思,在b站上添加视频的时候…...
股市大涨下的会展业创新者
近期,股市涨势强劲有力,各大指数普遍上扬,市场活力空前。与此同时,伴随全球经济逐步复苏及会展行业不断发展,上市展览公司机遇与挑战并存。国内外市场需求持续增长拓展了广阔发展空间,但同时行业竞争愈发激…...
日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...
区块链技术概述
区块链技术是一种去中心化、分布式账本技术,通过密码学、共识机制和智能合约等核心组件,实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点:数据存储在网络中的多个节点(计算机),而非…...
嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)
目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 编辑编辑 UDP的特征 socke函数 bind函数 recvfrom函数(接收函数) sendto函数(发送函数) 五、网络编程之 UDP 用…...

