score:53

Accepted answer

I ended up with one of the Lars Kotthoff's advices.

Every time when I call(axis) I also adjust text labels.
Here is simplified code:

function renderAxis() {
    axisContainer
        .transition().duration(300)
        .call(axis)                  // draw the standart d3 axis
        .call(adjustTextLabels);     // adjusts text labels on the axis 
}

function adjustTextLabels(selection) {
    selection.selectAll('.major text')
        .attr('transform', 'translate(' + daysToPixels(1) / 2 + ',0)');
}

// calculate the width of the days in the timeScale
function daysToPixels(days, timeScale) {
    var d1 = new Date();
    timeScale || (timeScale = Global.timeScale);
    return timeScale(d3.time.day.offset(d1, days)) - timeScale(d1);
}

Update:
BTW, here is a calendar demo with I ended up: http://bl.ocks.org/oluckyman/6199145

enter image description here

score:0

I often do this by stacking multiple axes, each with a custom .tickFormat().

If I'm placing labels in between dates, I'll often do something like this:

@timeDaysAxisLabels = d3.svg.axis()
    .scale(@timescale)
    .orient('bottom')
    .ticks(d3.time.hour.utc, 12)  # I want ticks at noon, easiest to just get them ever 12 hours
    .tickFormat((d) =>
        # only draw labels at noon, between the date boundaries
        if d.getUTCHours() == 12
            # draw the label!
            formatter = d3.time.format.utc('%a %d %b')  # "Mon 12 Apr"
            return formatter(d)
        else
            # not noon, don't draw anything
            return null)
    .tickSize(0)
    .tickPadding(30)

I'll also create a separate axis with no labels at all, and a non-zero .tickSize() to actually draw ticks, but this block above positions date labels in the center of the "column".

score:0

        svg.append("g")
          .attr("class", "axis axis-years")
          .attr("transform", "translate(0," + (height + 1) + ")")
          .call(xAxis)
        .selectAll("text")

     .attr("x", "-1.8em")
     .attr("y", ".00em")
     .attr("transform", function (d) {
         return "rotate(-90)"});

score:1

Already a few good replies but just to add one more. Note the use of text-anchor.

Same idea: After your call, select the text, reposition.

.call(xAxis)                
.selectAll(".tick text")
.style("text-anchor", "start")
.attr("x", axisTextAdjust)

score:2

You can do this by using axis.tickSize(major[[,minor],end]) and .tickSubdivide(). Your ticks are set to line up with the major ticks, but if you set the height of these ticks to 0, and set some height for minor ticks, and specify that there is one minor tick between each pair of major ticks, you will end up with tick labels between your ticks. Your code would look like this:

var myAxis = d3.svg.axis()
    .ticks(15)
    .tickSubdivide(1)
    .tickSize(0, 6, 0);

Note that you need to explicitly set an end size. If you only provide two numbers, they will be interpreted as major and end and minor will default to 0.

Here's a fiddle.

score:3

You might want to consider using D3FC, which has a drop-in replacement for the D3 axis component that supports this feature.

Here's an example which substitutes the D3 axis d3.axisBottom, for the D3FC equivalent fc.axisBottom:

const axis = fc.axisBottom(linear)
  .tickCenterLabel(true);

The tickCenterLabel centres the axis labels as requested.

Here's what the axis looks like with tickCenterLabel = false:

enter image description here

And here with the tickCenterLabel = true:

enter image description here

Full disclosure - I'm a maintainer and contributor to D3FC

score:7

There is no easy (i.e. built-in) way of doing this, but you can still achieve it. There are a few options. The most straightforward one is probably to use the tickFormat function to specify a format with a suitable number of spaces in front/after the numbers. This would need to be hand-tuned for each application though.

Alternatively, you could select the label elements after they have been drawn and add a suitable transform attribute that shifts them accordingly. Again, this would have to be hand-tuned.

Your third option is to have two different axes, one for the ticks and one for the labels. The idea is that the axis that provides the ticks has no labels and the other one no ticks. You would need to set the tick values appropriately, but at least you wouldn't have to guess the right offset.


Related Query

More Query from same tag