0

I have a big circle SVG element and four rect elements that are correctly rotated around the edges of it. I'm using

.attr('transform', function(d, i) {return 'rotate(' + 40 + ((i+1)*30) + ', 110, 155)'; })

to rotate these rectangles around the origin, where 110 and 155 are the center coordinates of the circle.

I want to add text off to the side of the rectangles and I don't want them to be rotated. Typically I would do something like

svg.selectAll('text')
  .data(data)
  .enter()
  .append('text')

Then I would use the same x and y coordinates specified for the rectangles for the text. However, because I'm using a transform rotate for the rectangles, I can't specify the same x and y attributes. I want the text to look like this:

enter image description here

The rectangles are rotated around the origin, but the text is not. I tried using each to add the text and have it appear next to the rectangle, but to no avail.

I've made a codepen to demonstrate. How can I get the text to appear next to the rectangles without being rotated?

Himmel
  • 3,629
  • 6
  • 40
  • 77
  • [This question](https://stackoverflow.com/questions/17226206/d3-js-how-to-rotate-text-on-a-path) may help. – Lars Kotthoff Jul 18 '15 at 21:12
  • I've gotten as far as applying the same transform that I did the `rect` to the `text`, but I'm trying to not have the text rotated at all. I can't figure out a way to 'unrotate' the text. Is there a way I can select all of the `rect` elements and append text to them? – Himmel Jul 18 '15 at 21:25
  • You can't append `text` to `rect` elements. It'll be easier to place the text in the same way as the rectangles and then rotate it back around a different origin IMHO. – Lars Kotthoff Jul 18 '15 at 21:27
  • See this [non-d3 solution](http://stackoverflow.com/questions/25089341/how-to-keep-text-orientation-unchanged-during-rotation-in-svg/25097447#25097447) which you can adapt to this coding. The [demo](http://output.jsbin.com/piwetu/1) – Alvin K. Jul 18 '15 at 21:30
  • Aren't the x and y positions of the text just `r * cos * angle` and `r * sin * angle` respectively? – Mark Jul 18 '15 at 21:35

2 Answers2

1

You could calculate your texts' coordinates based on the transformations you applied to the rect. This can easily be achieved by getting the transformation matrix of your rect and applying that matrix to the point you want your text to be positioned at. This will also work if you have more complex transformations or a combination of various transformations on the same element.

In your codepen this could be coded as:

box.each(function(d) {
    // Create an invisible, virtual point for the calculation
    var p = svg.node().createSVGPoint();

    // Set the point's coordinates to this rect's coordinates
    p.x = this.x.animVal.value + boxWidth / 2;  // +offset for centering on box
    p.y = this.y.animVal.value - 15;            // -offset for positioning over box

    // Transform point based on this rect's transformation matrix
    p = p.matrixTransform(this.getCTM());       

    // Append text to svg at calculated position
    svg.append('text').text('hi').attr('x', p.x).attr('y', p.y).attr('fill', 'black');
});

Note, that this appends the texts to the svg instead of to the rects which is not permissible as already noted by Lars Kotthoff. You might want to insert some grouping of texts and boxes if needed, without jeopardizing the basic ideas of this calculation.

Check the updated codepen for a basic working example.

altocumulus
  • 21,179
  • 13
  • 61
  • 84
0

Something like this?

var text = svg.selectAll('text')
  .data(data)
  .enter()
  .append("text")
  .text(function(d){
    return d;
  })
  .attr('fill', 'black')
  .attr('transform', function(d, i) {
    var a = (340 + (i*30)) * Math.PI/180;
    var x = 210 * Math.cos(a) + originX;
    var y = 210 * Math.sin(a) + originY;
    return 'translate('+x+','+y+')'; 
  });

Example here.

Mark
  • 106,305
  • 20
  • 172
  • 230