ksav
ksav

Reputation: 20830

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?

Upvotes: 7

Views: 16648

Answers (2)

ksav
ksav

Reputation: 20830

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

Upvotes: 3

Justin Wilson
Justin Wilson

Reputation: 340

This may work. To shows years, and instead of showing the name of the month, shows just a tick line, without the text:

var timeAxis = d3.svg.axis()
        .scale(timeScale)
        .orient('bottom')
        .ticks(d3.time.years, 1)//should display 1 year intervals
        .tickFormat(d3.time.format('%Y'))//%Y-for year boundaries, such as 2011
        .tickSubdivide(12);//subdivide 12 months in a year

Resources:

  1. d3.js major minor tick style (http://bl.ocks.org/vjpgo/4689130)
  2. scale.ticks and scale.tickFormat() (https://github.com/mbostock/d3/wiki/Time-Intervals#year)

Sample code that shows a tick for every two hours, and formats it to only show hour and AM/PM:

var timeAxis = d3.svg.axis()
        .scale(timeScale)
        .orient('bottom')
        .ticks(d3.time.hours, 2)
        .tickFormat(d3.time.format('%I%p'));

Upvotes: 8

Related Questions