Information Technology Information Technology - 3 months ago 21
Javascript Question

How to properly transpose X and Y axes using the D3 axis commands?

Original code can be found at: http://bl.ocks.org/Guerino1/be1a49bc4c4ad4d0f787a8e26ab2718e (refer to the HTML div labeled "Transition/Transpose X and Y Axes")

In the above example, I try to transpose the Y axis to X and the X axis to Y using the following D3 code...

function draw50( xAxisData, yAxisData, selectString )
{

var w = 400;
var h = 400;
var marginLeft = 10;
var marginRight = w - 10;
var marginTop = 20;
var marginBottom = h - 20;

var lineData = [];
var pt1 = {x: 0, y: 0};
lineData.push(pt1);
var pt2 = {x: 0, y: h};
lineData.push(pt2);
var pt3 = {x: w, y: h};
lineData.push(pt3);
var pt4 = {x: w, y: 0};
lineData.push(pt4);
var pt5 = {x: 0, y: 0};
lineData.push(pt5);

var lineFunction = d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate("linear");

var canvas = d3.select(selectString).append("svg")
.attr("height", h)
.attr("width", w)

// Put a border around the canvas for visual effects
canvas.append("path")
.attr("d", lineFunction(lineData))
.attr("stroke", "blue")
.attr("stroke-width", 4)
.attr("fill", "none");

// InnerCanvas is the offset canvas, that is offset away
// from the margins, using the transform/translate for the
// entire canvas, instead of just for individual axis.
var innerCanvas = canvas.append("g")
.attr("transform", "translate(60,10)");

// Setup y axis : Range is the length of the line
// NOTE: A value of "1" for rangeRoundBands allows points
// to be centered over the ordinal text markers
var yAxisScale = d3.scale.ordinal().domain(yAxisData).rangeRoundBands([marginBottom-20, marginTop], 1);
var yAxis = d3.svg.axis().scale(yAxisScale).orient("left");
var yAxisGroup = innerCanvas.call(yAxis);

// Setup x axis : Range is the length of the line
// NOTE: A value of "1" for rangeRoundBands allows points
// to be centered over the ordinal text markers
var xAxisScale = d3.scale.ordinal().domain(xAxisData).rangeRoundBands([0, marginRight], 1);
var xAxis = d3.svg.axis().scale(xAxisScale).orient("bottom");
var xAxisGroup = innerCanvas.append("g")
.attr("transform", "translate(0,354)")
.call(xAxis);

// Transitions X axis.
xAxisScale = d3.scale.ordinal().domain(yAxisData).rangeRoundBands([0, marginRight], 1);
xAxis = d3.svg.axis().scale(xAxisScale).orient("bottom");
xAxisGroup.transition()
.duration(3000)
.delay(500)
.attr("fill", "red")
.attr("transform", "translate(0,354)")
.call(xAxis);

// Transitions Y axis.
yAxisScale = d3.scale.ordinal().domain(xAxisData).rangeRoundBands([marginBottom-20, marginTop], 1);
yAxis = d3.svg.axis().scale(yAxisScale).orient("left");
yAxisGroup.transition()
.duration(3000)
.delay(500)
.attr("fill", "red")
.call(yAxis);

};


If I comment out the last bit of code that attempts to transition the Y axis (to the X value set), the X axis transition to Y seems to work fine. However, when I uncomment and include that last set of code, both axes do not transition properly.

NOTE: I want to transition, both, the text and the line/path.

What's the right way to do this?

Thanks for any help you can offer.

Answer

If I understand correctly, you want to swap the xAxis for the yAxis, is that right? In that case, you have to do all the changes you want in the scales and the axis and, after applying the changes, transitioning the axis:

d3.transition(svg)
    .select(".x.axis")//this class was previously applied
    .transition()
    .duration(2000)
    .call(xAxis);

Check this snippet:

var width = 400, height = 400, flag = true;
var svg = d3.select("#svgdiv")
  .append("svg")
  .attr("width", width)
  .attr("height", height);

var xScale = d3.scale.ordinal()
  .domain("ABCDEFGHIJ".split(""))
  .rangeBands([30, width - 20]);

var yScale = d3.scale.linear()
  .domain([0, 10])
  .range([height - 20, 20]);

var xAxis = d3.svg.axis()
  .scale(xScale)
  .orient("bottom");

var yAxis = d3.svg.axis()
  .scale(yScale)
  .orient("left");

svg.append("g")
  .attr("class", "x axis")
  .attr("transform", `translate(0,${height-20})`)
  .call(xAxis);

svg.append("g")
  .attr("class", "y axis")
  .attr("transform", "translate(30, 0)")
  .call(yAxis);

//here is the code for transposing the axes:

d3.select("#myButton").on("click", function(){

  if(flag){
    yAxis.orient("bottom");
    xAxis.orient("left");
xScale.domain("ABCDEFGHIJ".split(""))
  .rangeBands([width - 20, 30]);
yScale.domain([0, 10])
  .range([20, height - 20]);
  } else {
    yAxis.orient("left");
    xAxis.orient("bottom");
xScale.domain("ABCDEFGHIJ".split(""))
  .rangeBands([30, width - 20]);

yScale.domain([0, 10])
  .range([height - 20, 20]);
  };
  
  d3.transition(svg)
    .select(".x.axis")
    .transition()
    .duration(2000)
    .attr("transform", function(){
      if(flag){
      return "translate(30, 0)"
      } else { return `translate(0,${height-20})`}})
    .call(xAxis);

  d3.transition(svg)
    .select(".y.axis")
    .transition()
    .duration(2000)
    .attr("transform", function(){
      if(!flag){
      return "translate(30, 0)"
    } else { return `translate(10,${height-20})`}})
    .call(yAxis);
  
    flag = !flag;
  
});
.axis path,
.axis line {
  fill: none;
  stroke: #aaa;
  shape-rendering: crispEdges;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<button id="myButton">Swap</button>
<div id="svgdiv"></div>

Comments