You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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对象,直接用==比较的是对象引用,而非实际的时间值,所以永远不会匹配,导致目标刻度无法被移除。

修复代码

将时间比较逻辑替换为以下两种方式之一:

  1. 使用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")
    // ... 网格线属性保持不变
  1. 使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 08:14:54