Day18-D3 的 Axis( ) & ticks( ) 轴线与刻度

本篇大纲:轴线的组成、建立轴线的必备工具、绘制轴线的API、ticks 刻度、XY 轴范例

讲了好久终於进到轴线与刻度了!!!前面的章节看完比例尺之後,接下来就轮到绘制轴线啦~轴线也是图表中很重要的一部分,几乎所有图表都会有X轴跟Y轴
https://ithelp.ithome.com.tw/upload/images/20210930/201349304M7KzQ0UFg.jpg

今天我们就来讲讲要怎麽绘制轴线吧!

轴线的组成

首先,我们先来看看轴线是由哪些DOM元素组成的。图表的座标轴其实是一个复杂的结构,一条座标轴包含了

  • < path > 一条直线,标示轴线
  • < line > 一组沿着轴的刻度记号(ticks)
  • < text > 每个刻度记号的标签

https://ithelp.ithome.com.tw/upload/images/20210930/20134930Z48dxiOHMs.jpg

通常我们会使用 < g > 这个 svg 的结构标签把整条轴线的元素包在一起,之後对 < g > 元素进行一些操作(例如:移动的时候,就能将整条轴线一起移动。)

https://ithelp.ithome.com.tw/upload/images/20210930/20134930f2oAA94pLI.jpg

建立轴线的必备工具

了解轴线的组成结构後,我们来看看要建立轴线的两大必备工具

  • DOM 元素 (path、line、text 等等)
    我们必须要有一整组DOM元素,才能绘制出轴线

  • 比例尺
    除了 DOM 元素,也要搭配比例尺去计算出轴线的最大与最小值,才知道轴线要画多长、刻度要怎麽计算

有了这两个工具,我们就可以简单来建立一条轴线了,我们先画一条X轴试试看

// html
<div class="demo1"></div>

// js
d3.select('.demo1')
  .append('svg')
  .attr("width", 500)
  .attr("height", 200)
  .append('g')

// 设定比例尺
const scale = d3.scaleLinear()
                .domain([0, 100])
                .range([0, 500]);

// 建立轴线
let axis = d3.axisBottom(scale);
d3.select('svg g')
  .call(axis);

建立好的轴线长这样
https://ithelp.ithome.com.tw/upload/images/20210930/20134930TIyRGKKuzp.jpg

你一定会说:天啊这不是我要的轴线~这不是我要的轴线!为什麽会这样呢?那是因为 svg 都是从左上方的原点由上往下绘制,因此轴线长这样很正常。如果想要把轴线左右留点空间、移到下方的位置,我们就要 margintransform 来进行一些设定。

  • 设定 width、height、margin 变数
  • 设定把用 transform 移到 x = margin、y = 高度扣掉marigin*2的地方,再开始绘制轴线
  • 将输入域的范围设定成 svg 宽度扣掉 margin*2 (左右两边的margin),这样绘制出来的轴线左右才有距离
let width = 500, 
    height = 200,
    margin = 10;

// 轴线建立
d3.select('.demo1')
  .append('svg')
  .attr("width", width)
  .attr("height", height)
  .append('g')
  .attr('transform', `translate(${margin}, ${height-(margin*2)})`)

const scale = d3.scaleLinear()
                .domain([0, 100])
                .range([0, `${width-(margin*2)}`]);

let axis = d3.axisBottom(scale);
d3.select('svg g')
  .call(axis);

这样建立出来的轴线就是完美的X轴啦!
https://ithelp.ithome.com.tw/upload/images/20210930/20134930FAOcHXe1iZ.jpg

特别要注意的是,建立轴线时,我们会使用 selection.call( ) 这个方法来呼叫轴线的方法,那D3提供哪几种绘制轴线的方法呢? 我们一起来看一下

绘制轴线的API

一样先来看看官方文件~D3 提供了这些方法让我们能绘制轴线
https://ithelp.ithome.com.tw/upload/images/20210930/20134930QA4hPHxUUg.jpg

axisTop(scale)axisBottom(scale)axisRight(scale)axisLeft(scale)

这四个 API 是主要拿来建立轴线的方法,它们都是带入 scale比例尺 作为参数,接着根据带入的比例尺生成一个 axis generator,并包含预设的 ticks刻度设定

Scale 参数

轴线会因为带入的 scale比例尺 不同,而产生不同的刻度间距,例如:
https://ithelp.ithome.com.tw/upload/images/20210930/2013493098HkiQHN2l.jpg

如果不知道每种 scale 怎麽设定,欢迎看看上一篇的 scale 讲解

刻度位置

这四个 API 之所以又分成 Top、Bottom、Right、Left 四种,只是因为生成的 ticks 方向不同~

  • axisTop ⇒ ticks 刻度在轴线上方
    https://ithelp.ithome.com.tw/upload/images/20210930/20134930sP4qfH2IV1.jpg

  • axisBottom ⇒ ticks 刻度在轴线下方
    https://ithelp.ithome.com.tw/upload/images/20210930/20134930IfI4ejokWv.jpg

  • axisRight ⇒ ticks 刻度在轴线右方
    https://ithelp.ithome.com.tw/upload/images/20210930/20134930v2mBKgh3lM.jpg

  • axisLeft ⇒ ticks 刻度在轴线左方
    https://ithelp.ithome.com.tw/upload/images/20210930/20134930THGaEuSNd1.jpg

ticks 刻度

除了预设好的刻度之外,D3也提供了不少让我们能自订 ticks 刻度的 API
https://ithelp.ithome.com.tw/upload/images/20210930/20134930MbgqARks2r.jpg

我们能进行的设定包含:

  • 自订刻度的数量或数值
  • 自订刻度的呈现文字
  • 自订刻度的大小、长短、颜色

现在就让我挑几个常用的API来讲解吧!

axis.ticks( ) => 调整ticks数量

这个方法可以让我们自行调整刻度的数量。

let scale = d3.scaleLinear().domain([0, 100]).range([0, 500]);

let axis = d3.axisBottom(scale);

axis.ticks(20);

d3.select('svg g')
  .call(axis);

https://ithelp.ithome.com.tw/upload/images/20210930/20134930l0VhRrUyLa.jpg

但要注意的是,即使我们定义好想要的特定刻度数量,如果刻度的值并非完整的数值的话,D3会自行调整刻度数量,让 ticks value 是漂亮的数字 (例如以5为单位间隔),例如:

let scale = d3.scaleLinear().domain([0, 100]).range([0, 500]);

let axis = d3.axisBottom(scale);

axis.ticks(27); // 改成要27个ticks

d3.select('svg g')
  .call(axis);

出来的结果是一样的
https://ithelp.ithome.com.tw/upload/images/20210930/20134930rw30oo6oTy.jpg

axis.tickValues( ) => 指定想呈现的 ticks value

这个方法可以用来指定只想呈现哪些 tick values。这个方法超方便,如果我们想呈现某些特别奇怪的刻度,就可以用它来设定~

let scale = d3.scaleLinear().domain([0, 100]).range([0, 500]);

let axis = d3.axisBottom(scale);

axis.tickValues([0, 25, 53, 77, 81, 95]);

d3.select('svg g')
  .call(axis);

https://ithelp.ithome.com.tw/upload/images/20210930/20134930XLNjRvSMDv.jpg

axis.tickFormat( ) => 调整 tick label 的文字

这个也是非常好用的一个API,它可以协助我们调整想要呈现的刻度文字

let scale = d3.scaleLinear().domain([0, 100]).range([0, 500]);

let axis = d3.axisBottom(scale);

axis.tickFormat(function(d) {
		    return d + "元";
		  });

d3.select('svg g')
  .call(axis);

https://ithelp.ithome.com.tw/upload/images/20210930/20134930IsMVhfEznt.jpg

axis.tickSize( )axis.tickSizeInner( )axis.tickSizeOuter( )

再来介绍的这三种方法是用来设定刻度线的长度,D3的刻度线分为 inner 跟 outer 两种

  • inner ⇒ 轴线内部的刻度线
  • outer ⇒ 轴线最左右两边的起始点刻度线

https://ithelp.ithome.com.tw/upload/images/20210930/2013493018thUTZf3W.jpg

而这三种 API 分别能设定

  • tickSize( ) ⇒ 内部+外部刻度线长短一起设定
  • tickSizeInner( ) ⇒ 内部刻度线的长短
  • tickSizeOuter( ) ⇒ 最左右两侧刻度线的长短

预设的刻度线长度是6,但我们可以透过 tickSize 的设定来加长或缩短刻度线的长度,而且会自订把 tick value label 排到线段之後

let scale = d3.scaleLinear().domain([0, 100]).range([0, 500]);

let axis = d3.axisBottom(scale)
  .tickSize(30);

d3.select('svg g')
	.call(axis);

https://ithelp.ithome.com.tw/upload/images/20210930/20134930c1qdVLL03z.jpg

接着我们也可以进行一些漂亮的设定,像是使用css 跟 tickSize 搭配,去画出这种虚线段的轴线
https://ithelp.ithome.com.tw/upload/images/20210930/20134930dRbwmXX7AY.jpg

我们先建立轴线


// js
let scale = d3.scaleLinear().domain([0, 100]).range([0, 400]);
let axis = d3.axisRight(scale)
             .tickSize(400); // 把tick的尺寸设定成跟range的最大值一样,就能得到满版的轴线

d3.select('.demoDashed')
  .append('svg')
  .attr('width', '500')
  .attr('height', '400')
  .append('g')
  .attr('class', 'dashed')
  .attr('transform', 'translate(20, 20)')
  .call(axis);

接着再设定CSS,将轴线设定为隐藏,刻度线设定为dashed

// css
g.dashed path {
	display: none;
}

g.dashed line {
	stroke-dasharray: 1 1;
}

这样就完成拉~如果再用一样的方式设定X轴,就能得到棋盘状的格线罗!

再来如果我们想单独设定 tickSizeOuter 或 tickSizeInner 也可以,但就要一起设定 tickPadding,否则轴线就会变成这样

let scale = d3.scaleLinear().domain([0, 100]).range([0, 500]);

let axis = d3.axisBottom(scale)
  .tickSizeOuter(60);

d3.select('svg g')
	.call(axis);

https://ithelp.ithome.com.tw/upload/images/20210930/20134930P7nf6DsRqq.jpg

看到这张图,知道接下来的 tickPadding 是做什麽用了吗?

axis.tickPadding( ) ⇒ 调整刻度线与文字标签的距离

你没猜错!tickPadding( ) 就是用来调整刻度线跟文字标签的距离,预设的距离是3,但我们可以依照自己的需求去设定

let scale = d3.scaleLinear().domain([0, 100]).range([0, 500]);

let axis = d3.axisBottom(scale)
						 .tickPadding(60);

d3.select('svg g')
	.call(axis);

https://ithelp.ithome.com.tw/upload/images/20210930/20134930J0pbRM2bch.jpg

哇赛好不容易把轴线跟刻度的API 都大致讲完了,我们现在来用资料实际画完整的 XY 轴吧!


XY 轴范例

我们这次要建立的轴线是这样
https://ithelp.ithome.com.tw/upload/images/20210930/20134930hAv68GaXTp.jpg

由於有两条轴线,因此两条轴线都要分别设定要使用的 scale 跟 axis。我们先看到手上拥有的资料

// html
<div class="XY_Axis"></div>

// js
const svg = d3.select(".XY_Axis")
                .append("svg")
                .attr("width", 500)
                .attr("height", 200);

// data
const data = [{x:100, y:20}, {x:18, y:30}, {x:90, Y:250}]

一般来说我们都会拿到这种阵列包物件的资料,里面带有多笔数值。我们把要设定X轴的资料跟设定Y轴的资料分别整理出来

// map 资料集
  xData = data.map((i) => i.x); // 预期套资料後会得到 [100, 18, 90] 的阵列
  yData = data.map((i) => i.y); // 预期套资料後会得到 [20, 30, 250] 的阵列

接着我们先来设定 X 轴线

// 设定要给 X 轴用的scale 跟 axis
const xScale = d3.scaleLinear()
              .domain([0, d3.max(xData)])
              .range([20, 480]); // X 轴绘制的viewport起点终点(宽度)

const xAxis = d3.axisBottom(xScale)
              .tickFormat(function (d) {
                //调整标签样式
                return `${d} 元`;
              })

// 呼叫绘制x轴、调整x轴位置
const xAxisGroup = svg.append("g")
                    .call(xAxis)
                    .attr("transform", "translate(0,180)")

再来设定 Y 轴线

// 设定要给 Y 轴用的 scale 跟 axis
const yScale = d3.scaleLinear()
                .domain([0, d3.max(yData)])
                .range([20, 180]);  // Y 轴绘制的viewport 起点终点(高度)

const yAxis = d3.axisLeft(yScale)
                .ticks(5)

// 呼叫绘制y轴、调整y轴位置
const yAxisGroup = svg.append("g")
                      .call(yAxis)
                      .attr("transform", "translate(20,0)")

这时候座标轴已经画出来了,但你会发现Y轴的刻度跟我们平常看到的不一样,是颠倒的?
https://ithelp.ithome.com.tw/upload/images/20210930/20134930F34bJj62yk.jpg

这是因为轴线是从 svg 左上方的原点,由上往下绘制,所以当我们把 range 设定成 ([20, 180]) 时,Y轴线就是正常的从离原点20px的地方开始由上方往下建立。
https://ithelp.ithome.com.tw/upload/images/20210930/20134930FWsVCnCExh.jpg

为了要让Y轴的数值能颠倒过来,我们要把 range 的值也颠倒设定

const yScale = d3.scaleLinear()
                .domain([0, d3.max(yData)])
                .range([180, 20]);  // 颠倒

这样就完成啦!要注意的是,由於 X 跟 Y 轴的长度分别是看 svg 宽度跟 svg 高度,因此它们两个的 range 范围设定也会有所不同,不要设定错罗!
https://ithelp.ithome.com.tw/upload/images/20210930/20134930NB0v8K7nhz.jpg

终於完整介绍完座标轴跟刻度啦!!大家明天见~


Github Page 图表与 Github 程序码

最後一样附上本章的程序码与图表 GithubGithub Page,需要的人请自行取用~


<<:  信任与安全感

>>:  Material UI in React [ Day 29 ] Customization Component 自订组件 (part2)

C# 入门之开篇

为什么选择 C# 正如简介里面介绍的一样,作为一名运维人员,你应该懂一门开发语言,那么那么多语言中,...

Day14:今天来谈一下Microsoft Defender for Endpoint中的威胁与弱点管理

Microsoft Defender for Endpoint的威胁与弱点管理(TVM)功能 会根据...

新新新手阅读 Angular 文件 - Day03

学习内容 这一篇的内容,是纪录阅读官方文件 tutorial: A Hero Editor 的笔记,...

Debian 执行alien转换rpm报错解决

在debian下使用alien转换MegaRAID Storage Manager的RPM到deb过...

【Day 18】Shellcode 与他的快乐夥伴 (上) - Shellcode Loader

环境 Windows 10 21H1 Visual Studio 2019 前情提要 在【Day 0...