bongbang
bongbang

Reputation: 1712

D3 axis label rotate transition not smooth

In my transition, an axis rotates 90 degree and then the labels rotate in the opposition direction in order to remain upright. Below is a minimal example of what I want, except the transition is not as smooth as it could be. If you watch closely, you can see the labels shift (translate) up before rotating into place. How can I get rid of this shift? I've fiddled with rotate and translate to no avail.

(If you think this isn't too bad, I agree, but the shift is actually significantly more noticeable in my actual plot for some reason.)

Update. The culprit is the text-anchor property's getting switched back and forth between middle and start. Since these are discrete values, I can't think of a simple way to transition between them.

var width = 170;
var scale = d3.scaleLinear().domain([0, 5])
  .range([0, width]);

var axis = d3.axisBottom()
  .scale(scale)
  .ticks(6);

var graph = d3.select('svg').append('g')
  .attr('transform', 'translate(10,10)');

graph.append('g')
  .attr('transform', 'translate(0,' + width + ')')
  .call(axis);

var tickLabels = d3.selectAll('text');

var toggle = false;
d3.select('button').on('click', function() {
  toggle = !toggle;
  if (toggle) {
    graph.transition().duration(1000)
      // .attr('transform','rotate(-90)');
      .attr('transform', 'rotate(-90 ' + (width / 2 + 10) + ' ' + (width / 2 + 10) + ')');
    tickLabels.transition().duration(1500).delay(1000)
      .attr("y", 0)
      .attr("x", 9)
      .attr("dy", ".3em")
      .attr("transform", "rotate(90)")
      .style("text-anchor", "start");
  } else {
    graph.transition().duration(1000)
      .attr('transform', 'rotate(0) translate(10,10)');
    tickLabels.transition().duration(1500).delay(1000)
      .attr('y', 9)
      .attr('x', 0.5)
      .attr('dy', '0.71em')
      .attr('transform', 'rotate(0)')
      .style('text-anchor', null);
  }
});
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width='200' height='200'>
</svg>
<div>
  <button>Rotate</button>
</div>

Upvotes: 1

Views: 633

Answers (1)

bongbang
bongbang

Reputation: 1712

Found the solution, which is actually fairly simple. The key is to alter the x attribute to offset the text-anchor shift before rotating the labels. The result is actually quite nice.

var width = 170;
var scale = d3.scaleLinear().domain([0, 5])
  .range([0, width]);

var axis = d3.axisBottom()
  .scale(scale)
  .ticks(6);

var graph = d3.select('svg').append('g')
  .attr('transform', 'translate(10,10)');

graph.append('g')
  .attr('transform', 'translate(0,' + width + ')')
  .call(axis);

var tickLabels = d3.selectAll('text');

var toggle = false;
d3.select('button').on('click', function() {
  toggle = !toggle;
  if (toggle) {
    graph.transition().duration(1000)
      // .attr('transform','rotate(-90)');
      .attr('transform', 'rotate(-90 ' + (width / 2 + 10) + ' ' + (width / 2 + 10) + ')');
    tickLabels.transition().duration(0).delay(1000)
      .attr('x', -3)
      .style("text-anchor", "start")
      .transition().duration(1000)
      .attr("y", 0)
      .attr("x", 9)
      .attr("dy", ".3em")
      .attr("transform", "rotate(90)");
  } else {
    graph.transition().duration(1000)
      .attr('transform', 'rotate(0) translate(10,10)');
    tickLabels.transition().duration(0).delay(1000)
      .attr('x', 12)
      .style('text-anchor', null)
      .transition().duration(1000)
      .attr('y', 9)
      .attr('x', 0.5)
      .attr('dy', '0.71em')
      .attr('transform', 'rotate(0)');

  }
});
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width='200' height='200'>
</svg>
<div>
  <button>Rotate</button>
</div>

Upvotes: 1

Related Questions