上一篇介绍了 D3.js 的基础,这篇会边绘製图表边介绍绘製的方法,我们会使用到 SVG,还不熟悉 SVG 也可以参考下面的文章哦
SVG - 基础图形与样式
SVG - 进阶功能介绍
方法
因为 D3 实在有太多方法了,这边我们只介绍比例尺与座标轴的绘製,如果想了解所有方法可以参考这里
比例尺
绘製图表时,资料常常有大有小,而比例尺就是将资料等比例转换到某个幅度内
d3.min()
:取阵列最小值d3.max()
:取阵列最大值d3.scaleLinear()
:建立一个比例尺domain()
:定义资料範围range()
:定义图表资料範围rangeRound()
:同 range
,但会将结果四捨五入clamp(true)
:将超过範围的值设为最大值 or 最小值,预设为 false
unknown
:将 undefined
或 NaN
转为指定数值ticks
:将图表资料分为数个区间const dataset = [0, 33, 65, 199.9, 454, 887.5, 1000]const min = d3.min(dataset) // 0const max = d3.max(dataset) // 1000const scale = d3 .scaleLinear() .domain([min, max]) .range([0, 100])console.log(scale(99)) // 9.9,若使用 rangeRound 则为 10console.log(scale(500)) // 50console.log(scale(1001)) // 0console.log(scale(-1)) // 0console.log(scale(NaN)) // 母汤乱输入
座标轴
座标轴可以让资料与图表更容易检视,另外也需要用到上面说到的比例尺~
d3.axisTop
:建立上方座标轴d3.axisRight
:建立右方座标轴d3.axisBottom
:建立下方座标轴d3.axisLeft
:建立左方座标轴ticks
:指定资料的区间数量,预设为 10tickValues
:显示特定座标值,需传入阵列tickSize
:调整刻线长度tickPadding
:调整刻线与数值的距离tickFormat
:可传入函式自订数值格式const dataset = [0, 33, 69, 59, 100]const chart = d3 .select('.chart') .attr('width', 400) .attr('height', 400); const scale = d3 .scaleLinear() .domain([0, d3.max(dataset)]) .range([0, 100])const axis = d3 .axisBottom(scale) .ticks(5) .tickSize(20) .tickPadding(20) .tickFormat(item => item + '分');chart.append('g').call(axis)// axis(chart.append('g'))// 上面的方法与 call 效果相同,皆是将坐标轴绘製出来
图表种类与绘製
上面介绍了许多基本技巧,接下来就要使用上面学到的来画各式各样的图啰,html
如下
<body> <svg class="chart"></svg></body>
长条图
// 以下七胖团员的朋友人数,当然是我掰的XD const sevenFat = [ { name: 'HowHow', friend: 3 }, { name: '蔡 Brother', friend: 13 }, { name: '阿嘎', friend: 25 }, { name: '马叔叔', friend: 8 }, { name: 'RJ', friend: 10 }]// 设定 svg 的宽高const chart = d3 .select('.chart') .attr('width', 500) .attr('height', 500);// 选取所有的 g 群组const group = chart .selectAll('g') .data(sevenFat) .enter() .append('g');// 取比例尺const scale = d3 .scaleLinear() .domain([0, d3.max(sevenFat.map(item => item.friend))]) .range([0, 300]);// 绘製长条图group .append('rect') .attr('x', 100) .attr('y', (item, i) => i * 25) .attr('width', item => scale(item.friend)) .attr('height', 25 - 2) .attr('fill', 'steelblue');// 显示名字group .append('text') .attr('x', 0) .attr('y', (item, i) => i * 25 + 18) .style('fill', 'black') .style('font-size', '18px') .style('font-weight', 'bold') .text(item => item.name);// 显示朋友数量group .append('text') .attr('x', (item, i) => 95 + scale(item.friend)) .attr('y', (item, i) => i * 25 + 18) .attr('text-anchor', 'end') .style('fill', 'black') .style('font-size', '18px') .style('font-weight', 'bold') .text(item => item.friend); // 显示坐标轴const axis = d3 .axisBottom(scale) .ticks(5) .tickFormat(item => item + '人'); chart .append('g') .call(axis) .attr('transform', 'translate(100,125)');
散布图
// 以下小弟打靶成绩,中心点为 (50, 50) const shot = [ { x: 10, y: 96 }, { x: 90, y: 13 }, { x: 77, y: 89 }, { x: 25, y: 35 }, { x: 13, y: 6 }];// 设定 svg 的宽高const chart = d3 .select('.chart') .attr('width', 500) .attr('height', 500);// 选取所有的 g 群组const group = chart .selectAll('g') .data(shot) .enter() .append('g');// 取 X 轴比例尺const scaleX = d3 .scaleLinear() .domain([0, 100]) .range([0, 300]);// 显示 X 坐标轴const axisX = d3.axisBottom(scaleX);chart .append('g') .call(axisX) .attr('transform', 'translate(30,330)');// 取 Y 轴比例尺const scaleY = d3 .scaleLinear() .domain([100, 0]) .range([0, 300]);// 显示 Y 坐标轴const axisY = d3.axisLeft(scaleY);chart .append('g') .call(axisY) .attr('transform', 'translate(30,30)');// 绘製座标点group .append('circle') .attr('cx', item => scaleX(item.x)) .attr('cy', item => scaleY(item.y)) .attr('r', '2px') .attr('height', 25 - 2) .attr('fill', 'red') .attr('transform', 'translate(30,30)');// 显示座标group .append('text') .attr('x', item => scaleX(item.x)) .attr('y', item => scaleY(item.y)) .style('fill', 'black') .style('font-size', '10px') .style('font-weight', 'bold') .text(item => `(${item.x},${item.y})`) .attr('transform', 'translate(35,25)');
折线图
折线图就要多介绍到一些绘製 path
的方法啦~
d3.line
:定义一个线段x
:传入一个涵式,定义 x 座标y
:传入一个涵式,定义 y 座标curve
:将路径更改为曲线,详细可参考这里// 前公司每个月产品的产量const report = [ { month: 1, quantity: 8561 }, { month: 2, quantity: 9347 }, { month: 3, quantity: 10335 }, { month: 4, quantity: 9900 }, { month: 5, quantity: 12012 }, { month: 6, quantity: 10300 }, { month: 7, quantity: 13020 }, { month: 8, quantity: 15300 }, { month: 9, quantity: 17210 }, { month: 10, quantity: 13400 }, { month: 11, quantity: 11059 }, { month: 12, quantity: 9900 }];// 设定 svg 的宽高const chart = d3 .select('.chart') .attr('width', 500) .attr('height', 500);// 选取所有的 g 群组const group = chart .selectAll('g') .data(report) .enter() .append('g');// 取 X 轴比例尺const scaleX = d3 .scaleLinear() .domain([0, d3.max(report.map(item => item.month))]) .range([0, 300]);// 显示 X 坐标轴const axisX = d3.axisBottom(scaleX);chart .append('g') .call(axisX) .attr('transform', 'translate(50,330)');// 取 Y 轴比例尺const scaleY = d3 .scaleLinear() .domain([d3.max(report.map(item => item.quantity)), 0]) .range([0, 300]);// 显示 Y 坐标轴const axisY = d3.axisLeft(scaleY);chart .append('g') .call(axisY) .attr('transform', 'translate(50,30)');// 定义线段const line = d3 .line() .x(item => scaleX(item.month)) .y(item => scaleY(item.quantity)) .curve(d3.curveBasis);// 将线段绘製出来group .append('path') .attr('d', line(report)) .attr('transform', 'translate(50,30)') .attr('stroke', 'black') .attr('stroke-width', 1) .attr('fill', 'none');
结语
这篇画了三种基本的图表,分别为长条图、散布图与折线图,简单的使用了 D3 提供的方法来绘製,其实画起来并不难,D3 帮我们处理掉许多底层的部分,接下来只要够了解 SVG 就能够轻易的画出漂亮的图了,那就祝大家画图表愉快拉~