基于Datetime对象的D3.js坐标轴刻度移除失效问题求助
问题:移除X轴与xmax重合的刻度避免和箭头重叠失败
我希望在X轴最后一个刻度等于xmax时移除该刻度,避免和坐标轴箭头头部重叠,但以下代码无法生效:
g.selectAll("g.xaxis g.tick") .each((d,i,n) => { if (xmax == d) { console.log("equal datetime") } if ( i == 0 || d == xmax) { d3.select(n[i]).remove(); } })
完整代码如下:
draw_line() function draw_line() { /* d3.csv("save.csv").then(function(data) { plot_line(data) }); */ var csvtext = `date,close 1-May-12,58.13 30-Apr-12,53.98 27-Apr-12,67.00 26-Apr-12,89.70 25-Apr-12,99.00 24-Apr-12,130.28 23-Apr-12,166.70 20-Apr-12,234.98 19-Apr-12,345.44 18-Apr-12,443.34 17-Apr-12,543.70 16-Apr-12,580.13 13-Apr-12,605.23 12-Apr-12,622.77 11-Apr-12,626.20 10-Apr-12,628.44 9-Apr-12,636.23 5-Apr-12,633.68 4-Apr-12,624.31 3-Apr-12,629.32 2-Apr-12,618.63 30-Mar-12,599.55 29-Mar-12,609.86 28-Mar-12,617.62 27-Mar-12,614.48` var data = d3.csvParse(csvtext) plot_line(data) function plot_line(data) { var xname = 'date' var yname = 'close' var parseTime = d3.timeParse("%d-%b-%y"); data.forEach(function(d) { d[xname] = parseTime(d[xname]); d[yname] = +d[yname]; }); var width = 400 var height = 200 var margin = {top:20,left:50,right:20,bottom:30} var fontsize = 14 var xScale = d3.scaleTime().range([0, width]); var yScale = d3.scaleLinear().range([height, 0]); var valueline = d3.line() .x(function(d) { return xScale(d[xname]); }) .y(function(d) { return yScale(d[yname]); }); var svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) svg.append('defs') .append('marker') .attr('id','arrowhead') .attr('markerUnits','strokeWidth') .attr('markerWidth',6) .attr('markerHeight',6) .attr('viewBox','0 0 6 6') .attr('refX',6) .attr('refY',3) .attr('orient','auto') .append('path') .attr('d','M1,3,L1,1 L5,3 L1,5 z') .attr('stroke','black') .attr('fill','black') .attr('stroke-miterlimit', 10) var g = svg.append("g") .attr("transform",`translate(${margin.left},${margin.top})`); xScale.domain(d3.extent(data,function(d) {return d[xname];})); yScale.domain([0,d3.max(data,function(d) {return d[yname];})]); var line = g.append("path") .data([data]) .attr("class", "line") .attr("d", valueline) .attr('fill','none') .attr('stroke','steelblue') .attr('stroke-width',2) var xaxis = g.append("g") .attr('class','xaxis') .attr("transform", `translate(${0},${height})`) .call(d3.axisBottom(xScale) //.tickFormat((d,i) => null) //.tickSizeInner(8) //.tickSize(18) .tickSizeOuter(0) ); var yaxis = g.append("g") .attr('class','yaxis') .call(d3.axisLeft(yScale) //.tickFormat((d,i) => null) //.tickSizeInner(8) .tickSizeOuter(0) ); [xaxis,yaxis].forEach(axis => { axis.select("path") .attr("marker-end", "url(#arrowhead)"); }) xaxis.selectAll("text") .attr("text-anchor", "end") .attr("dx", "-.8em") .attr("dy", ".15em") .attr("transform", "rotate(-25)") .attr('font-size',fontsize) yaxis.selectAll('text') .attr('font-size',fontsize) var xmax = d3.max(data,d => d[xname]) const xnum = xaxis.selectAll(".tick").size(); // add axis grid lines g.selectAll("g.xaxis g.tick") .each((d,i,n) => { if (xmax == d) { console.log("equal datetime") } if ( i == 0 || d == xmax) { d3.select(n[i]).remove(); } }) .append("line") .attr("class", "gridline") .attr("x1", 0) .attr("y1", -height) .attr("x2", 0) .attr("y2", 0) .attr("stroke", "#9ca5aecf") .attr('stroke-width',1) .attr("stroke-dasharray","4") // make it dashed var ymax = d3.max(data,d => d[yname]) const ynum = yaxis.selectAll(".tick").size(); g.selectAll('g.yaxis g.tick') .each((d,i,n) => { if ( i == 0 || d == ymax) { d3.select(n[i]).remove(); } }) .append("line") .attr("class", "gridline") .attr("x1", 0) .attr("y1", 0) .attr("x2", width) .attr("y2", 0) .attr("stroke", "#9ca5aecf") .attr('stroke-width',1) .attr("stroke-dasharray","4") // make it dashed } }
<script src="https://unpkg.com/d3@7.0.4/dist/d3.min.js"></script>
解决方案
核心问题
代码无效的关键原因是时间对象的比较方式错误:xmax和tick绑定的d都是Date对象,直接用==比较的是对象引用,而非实际的时间值,所以永远不会匹配,导致目标刻度无法被移除。
修复代码
将时间比较逻辑替换为以下两种方式之一:
- 使用
Date.getTime()获取时间戳进行比较:
g.selectAll("g.xaxis g.tick") .each((d,i,n) => { // 用时间戳比较时间值是否相等 if ( i == 0 || d.getTime() === xmax.getTime()) { d3.select(n[i]).remove(); } }) .append("line") // ... 网格线属性保持不变
- 使用D3内置的时间比较方法
d3.timeEqual(适用于D3 v7及以上版本):
g.selectAll("g.xaxis g.tick") .each((d,i,n) => { // 用D3方法直接比较时间是否相等 if ( i == 0 || d3.timeEqual(d, xmax)) { d3.select(n[i]).remove(); } }) .append("line") // ... 网格线属性保持不变
额外优化
如果你的需求就是移除X轴最后一个刻度(无论是否严格等于xmax),可以直接选择最后一个tick元素,无需遍历比较:
// 移除X轴第一个和最后一个刻度 xaxis.selectAll(".tick").filter((d,i,n) => i === 0 || i === n.length -1).remove();
内容的提问来源于stack exchange,提问作者lucky1928
相关产品推荐
相关产品推荐

