我正在参加「掘金·启航计划」
引入D3模块
- 引入整个D3模块。
<!-- D3模块 -->
<script src="https://d3js.org/d3.v7.min.js"></script>
数据
- 有的时候数据是在
.csv
文件中的,这里对.csv
文件数据进行模拟。
function getMockCsvData() {
return (
'date,price\n' +
'Jan 2000,1394.46\n' +
'Feb 2000,1366.42\n' +
'Mar 2000,1498.58\n' +
'Apr 2000,1452.43\n' +
'May 2000,1420.6\n' +
'Jun 2000,1454.6\n' +
'Jul 2000,1430.83\n' +
'Aug 2000,1517.68\n' +
'Sep 2000,1436.51\n' +
'Oct 2000,1429.4\n' +
'Nov 2000,1314.95\n' +
'Dec 2000,1320.28\n' +
'Jan 2001,1366.01\n' +
'Feb 2001,1239.94\n' +
'Mar 2001,1160.33\n' +
'Apr 2001,1249.46\n' +
'May 2001,1255.82\n' +
'Jun 2001,1224.38\n' +
'Jul 2001,1211.23\n' +
'Aug 2001,1133.58\n' +
'Sep 2001,1040.94\n' +
'Oct 2001,1059.78\n' +
'Nov 2001,1139.45\n' +
'Dec 2001,1148.08\n' +
'Jan 2002,1130.2\n' +
'Feb 2002,1106.73\n' +
'Mar 2002,1147.39\n' +
'Apr 2002,1076.92\n' +
'May 2002,1067.14\n' +
'Jun 2002,989.82\n' +
'Jul 2002,911.62\n' +
'Aug 2002,916.07\n' +
'Sep 2002,815.28\n' +
'Oct 2002,885.76\n' +
'Nov 2002,936.31\n' +
'Dec 2002,879.82\n' +
'Jan 2003,855.7\n' +
'Feb 2003,841.15\n' +
'Mar 2003,848.18\n' +
'Apr 2003,916.92\n' +
'May 2003,963.59\n' +
'Jun 2003,974.5\n' +
'Jul 2003,990.31\n' +
'Aug 2003,1008.01\n' +
'Sep 2003,995.97\n' +
'Oct 2003,1050.71\n' +
'Nov 2003,1058.2\n' +
'Dec 2003,1111.92\n' +
'Jan 2004,1131.13\n' +
'Feb 2004,1144.94\n' +
'Mar 2004,1126.21\n' +
'Apr 2004,1107.3\n' +
'May 2004,1120.68\n' +
'Jun 2004,1140.84\n' +
'Jul 2004,1101.72\n' +
'Aug 2004,1104.24\n' +
'Sep 2004,1114.58\n' +
'Oct 2004,1130.2\n' +
'Nov 2004,1173.82\n' +
'Dec 2004,1211.92\n' +
'Jan 2005,1181.27\n' +
'Feb 2005,1203.6\n' +
'Mar 2005,1180.59\n' +
'Apr 2005,1156.85\n' +
'May 2005,1191.5\n' +
'Jun 2005,1191.33\n' +
'Jul 2005,1234.18\n' +
'Aug 2005,1220.33\n' +
'Sep 2005,1228.81\n' +
'Oct 2005,1207.01\n' +
'Nov 2005,1249.48\n' +
'Dec 2005,1248.29\n' +
'Jan 2006,1280.08\n' +
'Feb 2006,1280.66\n' +
'Mar 2006,1294.87\n' +
'Apr 2006,1310.61\n' +
'May 2006,1270.09\n' +
'Jun 2006,1270.2\n' +
'Jul 2006,1276.66\n' +
'Aug 2006,1303.82\n' +
'Sep 2006,1335.85\n' +
'Oct 2006,1377.94\n' +
'Nov 2006,1400.63\n' +
'Dec 2006,1418.3\n' +
'Jan 2007,1438.24\n' +
'Feb 2007,1406.82\n' +
'Mar 2007,1420.86\n' +
'Apr 2007,1482.37\n' +
'May 2007,1530.62\n' +
'Jun 2007,1503.35\n' +
'Jul 2007,1455.27\n' +
'Aug 2007,1473.99\n' +
'Sep 2007,1526.75\n' +
'Oct 2007,1549.38\n' +
'Nov 2007,1481.14\n' +
'Dec 2007,1468.36\n' +
'Jan 2008,1378.55\n' +
'Feb 2008,1330.63\n' +
'Mar 2008,1322.7\n' +
'Apr 2008,1385.59\n' +
'May 2008,1400.38\n' +
'Jun 2008,1280\n' +
'Jul 2008,1267.38\n' +
'Aug 2008,1282.83\n' +
'Sep 2008,1166.36\n' +
'Oct 2008,968.75\n' +
'Nov 2008,896.24\n' +
'Dec 2008,903.25\n' +
'Jan 2009,825.88\n' +
'Feb 2009,735.09\n' +
'Mar 2009,797.87\n' +
'Apr 2009,872.81\n' +
'May 2009,919.14\n' +
'Jun 2009,919.32\n' +
'Jul 2009,987.48\n' +
'Aug 2009,1020.62\n' +
'Sep 2009,1057.08\n' +
'Oct 2009,1036.19\n' +
'Nov 2009,1095.63\n' +
'Dec 2009,1115.1\n' +
'Jan 2010,1073.87\n' +
'Feb 2010,1104.49\n' +
'Mar 2010,1140.45'
)
}
// timeParse() 解析和格式化时间 -- %b月 %Y年
const parseDate = d3.timeParse('%b %Y')
function type(d) {
d.date = parseDate(d.date)
d.price = +d.price
return d
}
// csvParse() 解析给定的CSV字符串,返回一个对象数组。 type() 额外处理每个对象的方法
const data = d3.csvParse(getMockCsvData(), type)
d3.timeParse('%b %Y')
时间格式化。%b
:月份、%Y
:以世纪作为十进制数的年份。d3.csvParse()
解析给定的CSV字符串,返回一个对象数组。
- 模拟CSV格式数据,使用d3的
API
转换为js
数组。
添加画布
// 全局画布变量
const margin = { top: 50, right: 50, bottom: 125, left: 50 }
const margin2 = { top: 410, right: 50, bottom: 50, left: 50 }
const width = 960 - margin.left - margin.right
const height = 500 - margin.top - margin.bottom
const height2 = 500 - margin2.top - margin2.bottom
const svg = d3
.select('.d3Chart')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
比例尺和配置信息
// 比例尺
const x = d3.scaleTime().range([0, width])
const x2 = d3.scaleTime().range([0, width])
const y = d3.scaleLinear().range([height, 0])
const y2 = d3.scaleLinear().range([height2, 0])
- 创建时间的线性比例尺。
- 创建定量的线性比例尺。
// 坐标轴
const xAxis = d3.axisBottom(x)
const xAxis2 = d3.axisBottom(x2)
const yAxis = d3.axisLeft(y)
- 创建坐标轴绘制函数。
// 为比例尺添加输入域。
x.domain(
d3.extent(
data.map(function (d) {
return d.date
})
)
)
y.domain([
0,
d3.max(
data.map(function (d) {
return d.price
})
)
])
x2.domain(x.domain())
y2.domain(y.domain())
- 为比例尺添加默认输入域。
// 沿x轴创建画笔
const brush = d3
.brushX()
.extent([
[0, 0],
[width, height2]
]) // 设置 画笔范围
.on('brush', brushed)
function brushed(brush) {}
- 创建二维画笔。图表的移动根据画笔选中区域进行变化。
// 区域绘制
const area = d3
.area()
.x(function (d) {
return x(d.date)
})
.y0(height)
.y1(function (d) {
return y(d.price)
})
const area2 = d3
.area()
.x(function (d) {
return x2(d.date)
})
.y0(height2)
.y1(function (d) {
return y2(d.price)
})
d3.area()
创建面积生成器。
- 创建面积生成器,这里的图表绘制的区域折线图。如果只需要折线,就使用线生成器。
绘制图表和画笔操作
绘制折线图表
const chartSvg = svg
.append('g')
.attr('class', 'focus')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
// 绘制坐标轴
chartSvg
.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(-1,' + height + ')')
.call(xAxis)
chartSvg.append('g').attr('transform', 'translate(-1,0)').attr('class', 'y axis').call(yAxis)
// 绘制区域
chartSvg.append('path').datum(data).attr('class', 'area').attr('fill', 'steelblue').attr('d', area)
- 创建图表绘制组,使用坐标轴创建函数。
- 使用面积生成器,绘制折线。
绘制画笔区域
const context = svg
.append('g')
.attr('class', 'context')
.attr('transform', 'translate(' + margin2.left + ',' + margin2.top + ')')
context
.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height2 + ')')
.call(xAxis2)
// 折线区域
context.append('path').datum(data).attr('class', 'area').attr('fill', 'steelblue').attr('d', area2)
// 添加画笔
context
.append('g')
.attr('class', 'x brush')
.call(brush)
.selectAll('rect')
.attr('y', -6)
.attr('height', height2 + 7)
- 绘制画笔区域的图表,在图表上添加画笔功能。
// 设置裁剪
svg.append('defs').append('clipPath').attr('id', 'clip').append('rect').attr('width', width).attr('height', height)
chartSvg.select('.area').attr('clip-path', 'url(#clip)')
- 创建一个裁剪区域,设置给折线区域。
/*
* 画笔 回调
*/
function brushed(brush) {
// x2.invert 计算与给定范围值对应的域值。
const selection = brush.selection
x.domain(selection.map(x2.invert))
chartSvg.select('.area').attr('d', area)
chartSvg.select('.x.axis').call(xAxis)
}
- 根据画笔选中的区域,获取比例尺对应的数组值。
- 跟新图表的X轴比例尺,重新绘制X轴。实现平移效果
总结
本节使用的是裁剪的方式实现。每次变换都不改变原始数据,只修改x轴的比例尺,来重新绘制图形和x坐标轴。折线区域图会完整的绘制,使用裁剪的方式只加载图表范围的区域,就实现了平移效果。