JS D3带时间刻度图表如何将X轴数字替换为数组内日期
D3折线图X轴替换为日期的解决方案
核心问题
报错和显示异常主要由以下几个错误导致:
- X轴使用了仅支持数字的线性比例尺
scaleLinear,日期类型数据需要使用D3提供的时间比例尺scaleTime - 日期重复解析:已经在遍历
xdata的循环里把日期字符串转成了Date对象,后续生成dataset时再次调用parseDate解析已转换的Date对象,会得到null值导致日期失效 - Y轴范围配置错误:原Y轴
range([height, max_value])不符合SVG坐标规则,会导致数值显示偏移 - 折线、圆点、标签的X坐标仍然使用索引计算,没有绑定实际日期值
修改步骤
- 替换X轴线性比例尺为时间比例尺,定义域取数据集的最小、最大日期值
- 移除
dataset生成时的重复日期解析逻辑 - 修正Y轴范围配置为
range([height, 0]),匹配SVG坐标从上到下增大的规则 - 将折线生成器、圆点、标签的X坐标取值改为从日期字段获取
- 自定义X轴日期显示格式,避免默认格式过于复杂
完整修正代码
<!DOCTYPE html> <meta charset="utf-8"> <body> </body> <!-- 加载d3库 --> <script src="https://d3js.org/d3.v5.min.js"></script> <script> // 边距配置 var margin = {top: 200, right: 300, bottom: 400, left: 300} , width = window.innerWidth - margin.left - margin.right , height = window.innerHeight - margin.top - margin.bottom; var parseDate = d3.timeParse("%Y-%m-%d"); // 定义日期格式化工具,用于X轴显示 var formatDate = d3.timeFormat("%Y-%m-%d"); // 原始数据 var xdata = [ ["2021-08-01",(d3.randomUniform(100)()).toFixed(1)], ["2021-08-02",(d3.randomUniform(100)()).toFixed(1)], ["2021-08-03",(d3.randomUniform(100)()).toFixed(1)], ["2021-08-04",(d3.randomUniform(100)()).toFixed(1)], ["2021-08-05",(d3.randomUniform(100)()).toFixed(1)], ["2021-08-06",(d3.randomUniform(100)()).toFixed(1)], ["2021-08-07",(d3.randomUniform(100)()).toFixed(1)], ["2021-08-08",(d3.randomUniform(100)()).toFixed(1)], ["2021-08-09",(d3.randomUniform(100)()).toFixed(1)], ["2021-08-10",(d3.randomUniform(100)()).toFixed(1)], ["2021-08-11",(d3.randomUniform(100)()).toFixed(1)] ]; var n = xdata.length; var max_value = 0; var col1 = 0; for (let i = 0; i < n; i++) { xdata[i][0] = parseDate(xdata[i][0]); col1 = parseFloat(xdata[i][1]) if (col1 > max_value) max_value = col1 } // 生成数据集,移除重复日期解析 var dataset = xdata.map(function(d) { return { x: d[0], y: parseFloat(d[1]) }; }); // X轴改用时间比例尺 var xScale = d3.scaleTime() .domain(d3.extent(dataset, d => d.x)) // 输入为日期范围 .range([0, width]); // 输出为像素范围 // 修正Y轴range配置 var yScale = d3.scaleLinear() .domain([0, max_value]) // 输入为数值范围 .range([height, 0]); // 输出匹配SVG坐标规则 // 修改线生成器X坐标取值 var line = d3.line() .x(function(d) { return xScale(d.x); }) // 从日期字段取X值 .y(function(d) { return yScale(d.y); }) .curve(d3.curveMonotoneX) // 挂载SVG容器 var svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // 渲染X轴,新增日期格式化配置 svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(d3.axisBottom(xScale).tickFormat(formatDate)); // 渲染Y轴 svg.append("g") .attr("class", "y axis") .call(d3.axisLeft(yScale)); // 渲染折线 svg.append("path") .datum(dataset) .attr("fill", "none") .attr("stroke", "steelblue") .attr("stroke-width", 3) .attr("d", line); // 渲染数据点 svg.selectAll(".dot") .data(dataset) .enter().append("circle") .style("fill","steelblue") .attr("cx", function(d) { return xScale(d.x) }) .attr("cy", function(d) { return yScale(d.y) }) .attr("r", 5); // 渲染数值标签 svg.selectAll(".text") .data(dataset) .enter().append("text") .style("fill", "red") .attr("x", function(d) { return xScale(d.x) - 15 }) .attr("y", function(d) { return yScale(d.y) - 10 }) .text(function(d) { return d.y }); </script>
运行修正后的代码即可看到X轴正常显示日期,折线、圆点、数值标签位置完全匹配。
内容的提问来源于stack exchange,提问作者K Ufo
相关产品推荐
相关产品推荐

