Search code examples
javascriptd3.jsscaleaxis

D3.js time scale tick marks - Years and months only - Custom time format


I'm trying to make a zoomable chart with time scale on the x axis.

The default behaviour with an xScale like so:

var x = d3.time.scale()
  .domain([getDate(minDate), getDate(maxDate)])
  .range([0, width]);

and an xAxis like so:

var xAxis = d3.svg.axis()
  .scale(x);

is almost what I'm after.

But I'd like to have a custom Time Format that shows years, and instead of showing the name of the month, shows just a tick line, without the text (and maybe add a class to these ticks), while still retaining the default behaviour of the axis on zoom.

Any help would be much appreciated.

See JSfiddle here

Edit: I imagine that Lars' answer here would be a good approach, so long as it could be applied to a zoomable chart. Any fresh thoughts?


Solution

  • I ended up writing a small function to check the zoom level and set the axis' amount of ticks accordingly.

    function calcTickAmount() {
      if (d3.event.scale > 15) {
        return 3
      } else {
        return 10;
      }
    }
    

    Then this function would be called within the update function

    function draw() {
      xAxis.ticks(calcTickAmount());
      axes.call(xAxis);
    }
    

    // Config SVG
    var width = 500,
      height = 300,
      minDate = '1860',
      maxDate = '1958';
    
    // Draw SVG element
    var svgSelect = d3.select('div.tl').append('svg');
    
    // Translate SVG G to accomodate margin
    var translateGroupSelect = svgSelect
      .attr('width', width)
      .attr('height', height)
      .append('g');
    
    // Define d3 xScale
    var x = d3.time.scale()
      .domain([new Date(minDate), new Date(maxDate)])
      .range([0, width]);
    
    // Define main d3 xAxis
    var xAxis = d3.svg.axis()
      .scale(x)
      .ticks(10);
    
    // Draw axes
    var axes = translateGroupSelect.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + 0 + ")")
      .call(xAxis);
    
    // Define zoom
    var zoom = d3.behavior.zoom()
      .x(x)
      .scaleExtent([1, 32])
      .center([width / 2, height / 2])
      .size([width, height])
      .on("zoom", draw);
    
    
    // Apply zoom behavior to SVG element
    svgSelect.call(zoom);
    
    
    // Repetitive drawing stuff on every zoom event
    function draw() {
      xAxis.ticks(calcTickAmount());
      axes.call(xAxis);
    }
    
    function calcTickAmount() {
      if (d3.event.scale > 15) {
        return 3
      } else {
        return 10;
      }
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
    <div class="tl">
    </div>

    Updated Fiddle