【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》全新第三版封面

《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 容器内的定位情况

【图 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 添加到每个分组元素里的矩形元素示意图

【图 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:

图 3.28 配置了分段比例尺并添加间隙后的条形图效果

接下来能可以正式添加标签了!再次调用选择集 barAndLabel,将 SVG 文本元素分别添加进去。由于各标签需要展示每个对应的技术名称,因此需要再链式调用一次 text() 方法。该方法只接受一个参数:文本元素要显示的文本内容。本例则需要根据每个绑定的数据项动态设置对应的文本内容:

barAndLabel.append("text").text(d => d.technology);

内容设置好后,再用 xy 属性给每个标签定位。先来看水平方向,各标签末端要同矩形条的起始位置对齐。矩形条从 100px 开始,于是可以把文本元素放在大约 96px 的位置,与矩形条保持 4px 的间距。然后令其 text-anchor 属性(attribute)的值为 end,实现标签右对齐。这样 x 属性值就代表了各标签的末端位置,如图 3.32 所示:

图 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-familyfont-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 加注了技术标签的条形图效果

【图 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 计数标签的定位计算示意图

【图 3.34 计数标签的定位计算示意图】

接着,再给技术标签设置 font-familyfont-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

【图 3.35 最终实现的在线版 D3 条形图效果,详见:http://mng.bz/mjor】

恭喜您完成了本章的学习——知识点着实很密集!如果还没有掌握讲过的所有概念,也不必过于担心。后续章节还将继续提到这些概念,相信很快就能融会贯通 。

译注

实测时发现,左边标签的字号取 11px 时部分标签显示不全,调整为 10px 正常。相应的得票数标签也最好该小些,设为 8px 比较合适(相关源码已同步上传到 CSDN 下载资源):

补图 1 本地实测并重新调整字号后的 D3 条形图效果

【补图 1 本地实测并重新调整字号后的 D3 条形图效果】

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

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

相关文章

深度学习:基于MindSpore实现ResNet50中药分拣

ResNet基本介绍 ResNet(Residual Network)是一种深度神经网络架构,由微软研究院的Kaiming He等人在2015年提出,并且在ILSVRC 2015竞赛中取得了很好的成绩。ResNet主要解决了随着网络深度增加而出现的退化问题,即当网络…

vulnhub-digitalworld.local DEVELOPMENT靶机

vulnhub:digitalworld.local: DEVELOPMENT ~ VulnHub 导入靶机,放在kali同网段,扫描 靶机在192.168.114.129,扫描端口 开了几个端口,8080端口有网页,访问 说是让访问html_pages 似乎把页面都写出来了&…

Unity网络开发基础 —— 实践小项目

概述 接Unity网络开发基础 导入基础知识中的代码 需求分析 手动写Handler类 手动书写消息池 using GamePlayer; using System; using System.Collections; using System.Collections.Generic; using UnityEngine;/// <summary> /// 消息池中 主要是用于 注册 ID和消息类…

JavaEE之多线程进阶-面试问题

一.常见的锁策略 锁策略不是指某一个具体的锁&#xff0c;所有的锁都可以往这些锁策略中套 1.悲观锁与乐观锁 预测所冲突的概率是否高&#xff0c;悲观锁为预测锁冲突的概率较高&#xff0c;乐观锁为预测锁冲突的概率更低。 2.重量级锁和轻量级锁 从加锁的开销角度判断&am…

ssm教师办公管理系统的设计与实现+jsp

系统包含&#xff1a;源码论文 所用技术&#xff1a;SpringBootVueSSMMybatisMysql 免费提供给大家参考或者学习&#xff0c;获取源码请私聊我 需要定制请私聊 目 录 目 录 III 1 绪论 1 1.1 研究背景 1 1.2 目的和意义 1 1.3 论文结构安排 2 2 相关技术 3 2.1 JSP技…

大模型存储选型 JuiceFS 在关键环节性能详解

从去年开始&#xff0c;LLM大语言模型领域发展迅速、如 LLaMA、ChatGLM、Baichuan、Qwen 和 yi-model 等基础模型&#xff08;Foundation Models&#xff09;的数量显著增加。众多企业也开始基于这些基础模型做 post-training 的相关工作&#xff0c;以开发特定垂直领域的模型实…

一键生成二维码的源码系统 电脑+手机版自适应代码 带完整的安装代码包以及搭建部署教程

系统概述 一键生成二维码的源码系统是一款集二维码生成、管理和应用于一体的综合性平台。它采用先进的技术和算法&#xff0c;能够快速、准确地生成各种类型的二维码&#xff0c;包括文本、链接、图片等。同时&#xff0c;该系统还具备高度的灵活性和可扩展性&#xff0c;能够…

基于matlab变频器控制交流电机调速系统的设计与仿真(毕业论文)

目录 摘要 I ABSTRACT II 绪论 1 1交流调速技术发展概况 2 1.1电力电子器件 3 1.2变流技术 3 1.3变频调速的控制方式 4 1.4MATLAB/Simulink仿真介绍 4 2逆变电路的建模与仿真 5 2.1绝缘栅双极型晶体管 6 2.2三相桥式逆变电路的基本原理 6 2.3正弦脉冲宽度调制&#xff08;SPWM&…

六西格玛设计DFSS方法论在消费级无人机设计中的应用——张驰咨询

本文基于六西格玛设计方法论&#xff0c;对消费级无人机的设计流程进行系统化研究&#xff0c;探讨如何通过六西格玛设计的理念、工具和方法提升无人机产品的设计质量和市场竞争力。文章从市场定位、客户需求分析出发&#xff0c;深入到关键KPI指标的制定&#xff0c;并逐步阐述…

【数字孪生智慧园区物联网平台建设】智慧园区整体解决方案和集成方案(PPT+Word+实现)

数字孪生智慧园区物联网平台建设 1. 安防监控 2. 消防系统 3. 巡更系统 4. 红外线系统 5. 车辆识别 6. 人流管理 7. 消防机房 8. 能耗管理 9. 配电室 10. 智能集成 软件全套资料部分文档清单&#xff1a; 工作安排任务书&#xff0c;可行性分析报告&#xff0c;立项申请审批表&…

【华为HCIP实战课程十】OSPF网络DR和BDR实战讲解,网络工程师

一、DR与BDR的基础介绍 点到点同步LSA成本小 多点接入网络同步LSA成本大,需要DR/BDR 由于MA网络中,任意两台路由器都需要传递路由信息,网络中有n台路由器,则需要建立n*(n-1)/2个邻接关系。任何一台路由器的路由变化都会导致多次传递,浪费了带宽资源,DR和BDR应运而生!…

uibot发送邮件:自动化邮件发送教程详解!

uibot发送邮件的操作指南&#xff1f;uibot发送邮件的两种方式&#xff1f; 在现代办公环境中&#xff0c;自动化流程的引入极大地提高了工作效率。uibot发送邮件功能成为了许多企业和个人实现邮件自动化发送的首选工具。AokSend将详细介绍如何使用uibot发送邮件。 uibot发送…

使用Pytorch写简单线性回归

文章目录 Pytorch一、Pytorch 介绍二、概念三、应用于简单线性回归 1.代码框架2.引用3.继续模型(1)要定义一个模型&#xff0c;需要继承nn.Module&#xff1a;(2)如果函数的参数不具体指定&#xff0c;那么就需要在__init__函数中添加未指定的变量&#xff1a; 2.定义数据3.实例…

IP地址类型选择指南:动态IP、静态IP还是数据中心IP?

你是否曾经困惑于如何选择最适合业务需求的IP地址类型&#xff1f;面对动态IP、静态IP和数据中心IP这三种选择&#xff0c;你是否了解它们各自对你的跨境在线业务可能产生的深远影响&#xff1f; 在跨境电商领域&#xff0c;选择合适的IP类型对于业务的成功至关重要。动态IP、…

gitee开源商城diygw-mall

DIYGW可视化开源商城系统。所的界面布局显示都通过低代码可视化开发工具生成源码实现。支持集成微信小程序支付。 DIYGW可视化开源商城系统是一款基于thinkphp8 framework、 element plus admin、uniapp开发而成的前后端分离系统。 开源商城项目源码地址&#xff1a;diygw商城…

Java中String类的常见操作Api

目录 String类的常见操作 1).int indexOf (char 字符) 2).int lastIndexOf(char 字符) 3).int indexOf(String 字符串) 4).int lastIndexOf(String 字符串) 5).char charAt(int 索引) 6).Boolean endWith(String 字符串) 7).int length() 8).boolean equals(T 比较对象) 9).b…

区块链积分系统:重塑支付安全与商业创新的未来

在当今社会&#xff0c;数字化浪潮席卷全球&#xff0c;支付安全与风险管理议题日益凸显。随着交易频次与规模的不断扩大&#xff0c;传统支付体系正面临前所未有的效率、合规性和安全挑战。 区块链技术&#xff0c;凭借其去中心化、高透明度以及数据不可篡改的特性&#xff0c…

SSH 公钥认证:从gitlab clone项目repo到本地

这篇文章的分割线以下文字内容由 ChatGPT 生成&#xff08;我稍微做了一些文字上的调整和截图的补充&#xff09;&#xff0c;我review并实践后觉得内容没有什么问题&#xff0c;由此和大家分享。 假如你想通过 git clone git10.12.5.19:your_project.git 命令将 git 服务器上…

简单的maven nexus私服学习

简单的maven nexus私服学习 1.需求 我们现在使用的maven私服是之前同事搭建的&#xff0c;是在公司的一台windows电脑上面&#xff0c;如果出问题会比较难搞&#xff0c;所以现在想将私服迁移到我们公司的测试服务器上&#xff0c;此处简单了解一下私服的一些配置记录一下&am…

多线程(二):Thread类常见的属性和方法

目录 1、run & start 2、Thread类常见的属性和方法 2.1 构造方法 2.2 属性 3、后台进程 & 前台进程 4、setDaemon 5、isAlive 6、终止一个线程 6.1 变量捕获 6.2 currentThread & isInterrupted & interrupt 1、run & start 在多线程&#xff08…