Wanderer
Wanderer

Reputation: 592

SVG Translate Group positioning on window resize with d3

I am seeing some strange behavior with transforms when using the d3 enter, update pattern. I create a group (svg:g) of text (svg:text) items. When I initially create the groups using .enter.append('g').attr('transform',function(d){...translate...} the items are relative to the top of the SVG. However, when I update the groups after a window resize the groups are relative to their outermost container.

My Data is a nested set of object (d3.nest)

var myData=[{
    key:f,
    values:[{
      group:f,
      text:'foo'
    }]
  },
  {
    key:b,
    values:[{
      group:'b',
      text:'bar'
    },{
      group:'b',
      text:'baz'
    }]
  }
.....
]

My enter/update pattern is:

// EDIT Some JS to add shapes first
var shapeCont = d3.select('#shapesAtTheTop');
var shapes = shapeCont.selectAll('rect')
  .data([50,50]);
shapes.exit().remove();
shapes.enter().append('rect')
  .attr('width',function(d){return window.innerWidth*(d/100)})
  .attr('x', function(d,i){return i*window.innerWidth*(d/100)})
  .attr('y',0)
  .attr('height',95);
// END EDIT
// Now add the text
var textgroupLeft = window.width/50, textGrouptop = 100;
function translateGroup(datum, i){
  var x = textGroupLeft * i;
  var y = textGroupTop;
  return 'translate('+x+' '+y+')';
}
var textContainer = d3.select('g.textContainer');
var txtg = textContainer.selectAll('g').data(myData,function(d){return d.key;});
// UPDATE
txtg.attr('transform',translateGroup);
// REMOVE
txtg.exit().remove();
// NEW
txtg.enter().append('g')
  .classed('textGroup',true')
  .attr('transform',transelateGroup)

var txt = d3.selectAll('g.textGroup').selectAll('text')
  .data(function(d){retrun d.values;},
    function(dataum){return JSON.stringify(dataum);})
// REMOVE
.....
// UPDATE
.....
// NEW
.....

My HTML/SVG is:

<svg>
  <g id="shapesAtTop">
  ..... add polygons with positions in the first 100px high ....
  </g>
  <g id="textBelow" class="textContainer">
  </g>
</svg>

Is it switching coordinate systems? If So Why?

Upvotes: 1

Views: 891

Answers (1)

John
John

Reputation: 1341

It is difficult to see where you are going wrong. You can fix this issue by explicitly defining a translation on the #textBelow group in your SVG either in the HTML or programmatically using D3. Once this group has the correct vertical position, it is easier to translate any nested objects.

Your code to determine the translations to apply is hard to follow. I would advise making use of a d3 BandScale to handle the mapping from the index of the object you are transforming to a value in pixels.

The snippet below demonstrates both of these using D3 v4. If you need to use a previous version of D3 then the name of the scale is different and you have to be more careful with selection.enter() as you don't get the selection.merge() method.

var data = [{key:'a'}, {key:'b'}]
var width = 500 //px

update(data,width)
setTimeout(function (){update(data,200)}, 2000)

function update(data, width) {
  //create a band scale to assist with layout
  var scale = d3
    .scaleBand()
    .domain(d3.range(data.length))
    .range([0, width])
    // padding ratio of total width
    .padding(0.1);

  // append/modify shapes
  var shapeCont = d3.select("#shapesAtTop");
  var shapes = shapeCont.selectAll("rect").data(data);
  // remove any unused nodes
  shapes.exit().remove();
  // enter
  shapes.enter()
    // add new rectangles
    .append("rect")
    // merge selection with any existing nodes
    .merge(shapes)
    // set all rect attributes
    .attr("x", function(d, i) {
      return scale(i);
    })
    .attr("y", 0)
    .attr("height", 95)
    .attr("width", scale.bandwidth());

  //append/modify text group elements
  var textContainer = d3.select('#textBelow');
  var txtg = textContainer.selectAll('g')
    .data(data, function(d) {
      return d? d.key : this.id
    });
  // remove any unused nodes
  txtg.exit().remove()
  // enter
  txtg.enter()
    .append('g')
    .classed('textGroup', true)
    // add some text to new elements
    .call( function (g) { g.append('text').text('I am text')})
    // merge exisiting and new
    .merge(txtg)
    .attr('transform', function(d, i) {
      return 'translate(' + scale(i) + ')'
    })
}
svg {
  width: 100%;
  height: 500px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script>
<svg>
  <g id="shapesAtTop">
  </g>
  <g id="textBelow" class="textContainer" transform="translate(0,120)">
  </g>
</svg>

Upvotes: 1

Related Questions