D3基础03 - 比例尺与坐标轴
我们可以利用D3提供的比例尺、坐标轴以及绘图功能,实现一个简单的柱状图。并且通过增加鼠标事件、transition动画和缓动动画来优化柱状图的用户体验,接下来我们逐步看一下如何实现柱状图。
比例尺
D3的比例尺功能可以让我们自由的建立起一个坐标系统,并设置每一个坐标轴的分量大小,同时可以让坐标轴的刻度与画布坐标轴的像素点对应起来。
坐标轴的刻度可以分成两种:
- 类目型坐标轴:坐标轴是由不连续的元素组成,如
[张三,李四,王五]
- 数值型坐标轴:坐标轴是由连续的数值组成,如
[0,1,2]
,这里指的是数组,不是数学上的区间,这一点需要区分开,后续以中括号出现的数值对都是数组。
因此比例尺规则也就与之对应的有两种:
scaleBand
类目轴型比例尺规则scaleLinear
数值轴型比例尺规则
利用比例尺建立x坐标轴
- x轴是类目型坐标轴
- 首先需要获取x轴数据的长度并将每个数据的索引存储起来
- 然后设置x坐标轴起始和结束的坐标轴像素位置
- 然后利用比例尺将数据与坐标轴刻度对应起来
- 基于x轴比例尺建立x轴坐标生成器
- 调用生成器绘制x轴,并将数据填充进去。
废话不多说,我们还是用代码说话:
/*===========1-必备数据===========*/
/*categories 类目数据*/
const categories = ['html', 'css', 'js'];
/*===========2-建立容器对象===========*/
/*获取main 容器*/
const main = d3.select('#main')
/*声明绘图框尺寸
width 宽度,600
height 高度,600
*/
const width = 600
const height = 600
/*建立svg 对象
* svg 画布尺寸100%充满容器对象
* 绘图框尺寸按照600设置
* */
const svg = main.append('svg')
.attr('version', 1.2)
.attr('xmlns', 'http://www.w3.org/2000/svg')
.attr('width', '100%')
.attr('height', '100%')
.attr('viewBox', `0 0 ${width} ${height}`)
/*===========3-建立基础数据===========*/
/*计算类目数量 len*/
const len = categories.length
/*用range()方法,基于类目数量,获取x轴的在图表坐标系中的数据 xChartData,如[0,1,2]*/
const xChartData = d3.range(len)
console.log('xChartData', xChartData);
/*x轴在像素坐标内的起始点和结束点 xPixelRange,左右各偏移50*/
const xPixelRange = [50, width - 50]
/*===========4-建立x 轴比例尺 xScale===========*/
/*
* 用scaleBand()方法建立分段比例尺 xScale
* 用domain()方法在比例尺中写入图表数据xChartData
* 用rangeRound()方法在比例尺中写入像素数据,即像素的起始位和结束位xPixelRange
* */
const xScale = d3.scaleBand()
.domain(xChartData)
.rangeRound(xPixelRange)
console.log('xScale', xScale)
/*===========5-建立x 轴对象===========*/
/*基于比例尺xScale,用axisBottom()方法创建刻度朝下的坐标轴生成器 xAxisGenerator*/
const xAxisGenerator = d3.axisBottom(xScale)
/*利用坐标轴生成器绘制坐标轴
* 在svg中append 加入g 对象
* 用transform 属性中的translate设置x轴的y位置
* 用call()方法调用xAxisGenerator轴生成器,生成坐标轴
* 用selectAll()方法选择所有的text文本
* 用text()方法将图表数据设置为类目数据
* 用attr()方法设置字体大小
* */
svg.append('g')
.attr('transform', `translate(0,${height-50})`)
.call(xAxisGenerator)
.selectAll('text')
.text(d => categories[d])
.style('font-size', '12px')
利用比例尺建立y坐标轴
虽然y轴是数值型,但是整体思路与x轴的绘制一致,唯一需要注意的一点是:像素坐标轴的y轴方向和我们绘制的y轴方向相反,因此在建立比例尺时要注意对应关系。
/*===========1-必备数据===========*/
/*数据源source:两个系列的数据*/
const source = [
//html css js
[30, 20, 40], //学习人数
[40, 30, 50] //就业人数
]
/*===========1-建立容器对象===========*/
/*获取main 容器*/
const main = d3.select('#main')
/*声明绘图框尺寸
width 宽度,600
height 高度,600
*/
const width = 600
const height = 600
/*建立svg 对象
* svg 画布尺寸100%充满容器对象
* 绘图框尺寸按照600设置
* */
const svg = main.append('svg')
.attr('version', 1.2)
.attr('xmlns', 'http://www.w3.org/2000/svg')
.attr('width', '100%')
.attr('height', '100%')
.attr('viewBox', `0 0 ${width} ${height}`)
/*===========3-建立基础数据===========*/
/*计算数据源中所有数据的极值 maxY
* 用js原生方法flat()展开数据源,再通过max()方法取极值
* */
const maxY = Math.max(...source.flat())
console.log('maxY', maxY);
/*声明y轴在图表坐标系中的数据起点和结束点 yChartRange*/
const yChartRange = [0, maxY]
/*声明y轴在像素坐标系中的数据起点和结束点 yPixelRange*/
const yPixelRange = [height - 50, 50]
/*===========4-建立y 轴比例尺 yScale===========*/
/*
* 用scaleLinear()方法建立线性比例尺 yScale
* 用domain()方法在比例尺中写入图表数据yChartRange
* range()方法在比例尺中写入像素数据,即像素的起始位和结束位yPixelRange
* */
const yScale = d3.scaleLinear()
.domain(yChartRange)
.range(yPixelRange)
/*===========5-建立y 轴对象===========*/
/*基于比例尺yScale,用axisLeft()方法创建刻度朝左的坐标轴生成器 yAxisGenerator*/
const yAxisGenerator = d3.axisLeft(yScale)
/*利用坐标轴生成器生成坐标轴
* 在svg中append 加入g 对象
* 用transform 属性中的translate设置y轴的x位置
* 用call()方法调用xAxisGenerator轴生成器,生成坐标轴
* 用style()方法设置字体大小
* */
svg.append('g')
.attr('transform', 'translate(50 0)')
.call(yAxisGenerator)
.style('font-size', '12px')
绘制图表
上面我们已经成功建立起了坐标系,接下来离成功就只有一步了:绘图。
- 初始化绘图相关数据
- 获取到x轴每个类目的像素宽度(HTML、CSS、JS)
- 获取到系列的长度(就业人数、学习人数)
- 两个相除,得到每个类目下每个系列所占的x轴像素宽度。
- 构建绘图区域
g
——只是搭建了绘图区域框架,到这一步是还没有图形绘制出来- 创建好绘图区域集合,并将类目数据绑定到每个集合
- 设置未来会填充到
g
内的每一个柱状体x轴坐标起始像素位置 - 设置填充颜色
- 构建矩形集合
rect
——只是创建了柱状体集合和对象,但是没有设置属性- 创建好柱状体集合,并将系列数据绑定到每个合集
- 在每个
g
元素内批量穿件rect
元素
- 设置每个柱状体的属性
/*===========6-建立绘图区相关的基础数据===========*/
/*-----绘图区相关的基础数据-----*/
/*用x轴比例尺xScale的bandwidth()方法获取x轴上一个类目的像素宽xBandW*/
const xBandW = xScale.bandwidth()
console.log('xBandW', xBandW);
/*获取系列的数量n*/
const n = source.length
/*用类目宽除以系列数,得到一个类目中每个系列元素的宽,即列宽colW*/
const colW = xBandW / n
console.log('colW', colW);
/*计算调色盘颜色数量colorLen*/
const colorLen = color.length
/*===========7-架构绘图区===========*/
/*在svg中建立系列集合seriesObjs,在系列集合中建立系列对象
* 在svg中append 加入g 对象
* selectAll() 选择所有g元素,此处重点不在选择,而是建立一个选择集对象
* 用data() 方法将具备系列信息的数据源source绑定到系列集合中
* 用join() 基于数据源批量创建g元素,一个g代表一个系列,之后每个g元素里都会放入三个不同类目的柱状体
* 用transform 属性中的translate设置系列的x像素位——列宽乘以系列索引
* 基于系列索引,从调色盘中取色,然后将其作为一个系列中所有图形的填充色
* */
const seriesObjs = svg.append('g')
.selectAll('g')
.data(source)
.join('g')
.attr('transform', (seriesData, seriesInd) => {
const seriesX = colW * seriesInd
return `translate(${seriesX},0)`
})
.attr('fill', (seriesData, seriesInd) => color[seriesInd % colorLen])
/*在系列集合中建立柱状体集合rects
* 用系列集合seriesObjs 的selectAll()方法选择所有的rect元素,用于建立选择集对象
* 用data()方法将之前绑定在每个系列集合中的数据绑定到柱状体集合中
* 用join()基于每个系列的数据数据批量创建rect元素
* 用classed() 方法为其添加item属性
* */
const rects = seriesObjs.selectAll('rect')
.data(seriesData => seriesData)
.join('rect')
.classed('item', true)
console.log('rects', rects);
/*=8-用attr()方法设置每个柱状体的x、y位置和width、height 尺寸=*/
/*
* 设置柱状体的x像素位
* 从回调参数中获取柱状体在当前系列中的索引rectInd,系列索引 seriesInd
* 基于柱状体在当前系列中的索引rectInd,用x轴比例尺xScale()获取柱状体在当前系列中的x像素位
* 设置柱状体像素宽width为列宽colW
* 设置柱状体的y像素位
* 从回调参数中解构柱状体数据rectData
* 基于柱状体数据rectData,用y轴比例尺yScale()获取柱状体的y像素位
* 设置柱状体的像素像素高
* 从回调参数中解构柱状体数据rectData
* 让y轴上刻度为0的像素位,减去刻度为柱状图实际数据的像素位,即为柱状图的像素高
* */
rects.attr('x', (rectData, rectInd) => xScale(rectInd))
.attr('width', colW)
.attr('y', rectData => yScale(rectData))
.attr('height', rectData => yScale(0) - yScale(rectData))
到这里,就成功建立起一个直观的柱状图,后续还可以用过添加transition动画、缓动动画、鼠标事件让这个柱状图用户体验更好、更炫酷(这个留到出差回来之后再写)
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!