Author: arcsin1
time: 2020.04.25
(单条)x轴节点过多,即时间或者连续点太多了。
(多条)数据样本过多,导致折线堆积,难以聚焦到重点。(可以用交互动画解决) 典型列子
变量数值大多情况下在0
先看看折线图的样子(你心急,完整demo链接在最下面)
我分为5个部分:(目前只介绍svg篇,后续再介绍canvas篇) 1. 画出x轴 2. 画出y轴 3. 画出数据圆点和值label 4. 画出线条连线 5. 动画(后面补上) 复制代码
const data = [ { year: "1991", value: 3 }, { year: "1992", value: 4 }, { year: "1993", value: 3.5 }, { year: "1994", value: 5 }, { year: "1995", value: 4.9 }, { year: "1996", value: 6 }, { year: "1997", value: 7 }, { year: "1998", value: 9 }, { year: "1999", value: 13 } ]; 复制代码
注意点: svg的元素基本在g里面绘制,当做group
const width = 600; const height = 400; const svg = d3 .select("body") .append("svg") .attr("width", width) .attr("height", height); // 创建一个g 当后面元素的group容器,移到(30,30)的位置 // 定义上下左右边距给坐标轴文字距离 const m = { top: 30, right: 30, bottom: 30, left: 30 }; const g = svg.append("g").attr("transform", "translate(30, 30)"); // 实际我们图的高度宽度 const gW = width - m.left; const gH = height - m.top - m.bottom; 复制代码
关于比例尺参考 可视化D3专题系列(一)初见
创建scaleBand一般用在条形图时(也可以给折线图),考虑到每个条之间的填充,有助于确定条的几何形状。域被指定为值的数组(每个带一个值),范围被指定为带的最小和最大范围(例如条形图的总宽度)。
实际上,scaleBand会将范围划分为n个波段(其中n是域数组中值的数量),并考虑任何指定的填充来计算波段的位置和宽度。
var bandScale = d3.scaleBand() .domain(['Mon', 'Tue', 'Wed', 'Thu', 'Fri']) .range([0, 200]); bandScale('Mon'); // returns 0 bandScale('Tue'); // returns 40 bandScale('Fri'); // returns 160 // 可以使用.bandwidth()以下命令访问每个波段的宽度: bandScale.bandwidth(); // returns 40 复制代码
// 定义x坐标轴的比例尺,gW为x轴的宽度,关于scaleBand参考上一篇文章以及相关文章 // 这里我们会得到一个xScale.bandwidth()的距离,bandwidth()访问每个波段的宽度: const xScale = d3.scaleBand().range([0, gW]); // 定义好x轴定义域,画出x轴axisBottom,底部位置,year是值 xScale.domain(data.map(item => item.year)); g.append("g") .attr("transform", `translate(0, ${gH})`) .call(d3.axisBottom(xScale)) .attr("stroke", "red"); 复制代码
更详细比列尺介绍
// 定义y坐标轴的比例尺,gH为y轴的宽度 const yScale = d3.scaleLinear().range([gH, 0]); // 定义好y轴d定义域,画出y轴,y轴画在左边axisLeft,value是值 yScale.domain([0, d3.max(data, item => item.value)]); g.append("g") .call(d3.axisLeft(yScale)) .attr("stroke", "red"); 复制代码
要注意我们要把点和文字放在一个g里面,才能顺利画出来
// 先给点画上小圆圈和文字,创建一个文字和圆圈的group // join那句可以改为以前v4写法.enter().append('circle') const group1 = g .selectAll(".gruop-circle-text") .data(data) .join("g") .attr("class", "gruop-circle-text"); // 画出圆点即是圆圈,xScale.bandwidth()就用到了,bandwidth()访问每个波段的宽度, // xScale.bandwidth() / 2 我们把元素位置居中 group1 .selectAll("circle") .data(data) .join("circle") .attr("cx", d => { return xScale(d.year) + xScale.bandwidth() / 2; }) .attr("cy", d => { return yScale(d.value); }) .attr("r", 3) .attr("fill", "red"); // 绘制出文字 group1 .selectAll("text") .data(data) .join("text") .attr("x", d => { return xScale(d.year) + xScale.bandwidth() / 2; }) .attr("y", d => yScale(d.value) - 2) .text(d => d.value); 复制代码
d3 为我们提供更优质的line生成器 d3.line
更多关于线条生成参考
// 创建一个line的生成器 用d3.line,把所有点连起来 const line = d3 .line() .x(d => { // 这里是d3.scaleBand自带比例尺 return xScale(d.year) + xScale.bandwidth() / 2; }) .y(d => { return yScale(d.value); }) .curve(d3.curveCatmullRom); //这里有多种形态可以选择 复制代码
g.append("path") .attr("d", line(data)) .attr("fill", "none") .attr("stroke", "purple"); 复制代码
完整折线图
看到这里,其实发现绘制一个基本折线图就是 一个个积木搭建起来的,是不是很有趣。
下一章我们来讲讲多条折线图和各种动画效果。
周末愉快!