Skip to content Skip to sidebar Skip to footer

How To Rotate Text Around Its Centroid (vertically Flip) In Svg / D3?

I have text objects labeling points that are evenly spaced around a circle. Thanks to this article, I am able to correctly position both the points and text objects but the labels

Solution 1:

If you want to reverse the labels for those on the left side of the circle. You can achieve different ways. One way is by modifying three attributes of the text as you append it:

.attr('x', function (d, i) { return nodes[0].x + 15; })  
.style("text-anchor", "start")
.attr("transform", function(d,i) {
  return"rotate(" + (d.angle * 180) / Math.PI + ", 225, 225)"
})

If you modify only some of these, you might not get the results you are looking for.

Modification of text-end

This is needed as your text will start away from the point you are defining, and as the text may have variable length, defining a start point will be more complex than necessary. For points you need to flip, you'll need to use:

.style("text-anchor", "end")

Modification of the transform and x

The text needs to be rotated 180 degrees so that it is right way up; however, if you modify this function to add 180 degrees to any text, then the text will appear on the wrong side of the display. So, you'll need to set x to a new value too, so that it appears on the correct side of the display:

.attr('x', function (d, i) { return nodes[0].x - 215; }) // radius * 2, add 15 for spacing off point

.attr("transform", function(d,i) {
  return"rotate(" + ((d.angle * 180) / Math.PI - 180) + ", 225, 225)"
})

All together, that looks like:

(function () {
        var paddding = 250;

        var createNodes = function () {

            var nodeData = [
                { id: 0, label: 'AAA' },
                { id: 1, label: 'BBB' },
                { id: 2, label: 'CCC' },
                { id: 3, label: 'DDD' },
                { id: 4, label: 'EEE' },
                { id: 5, label: 'FFF' },
                { id: 6, label: 'GGG' },
                { id: 7, label: 'HHH' }
            ];

            var radius = 100;
            var nodes = [],
                width = (radius * 2) + paddding,
                height = (radius * 2) + paddding,
                angle,
                x,
                y,
                i;

            var numNodes = nodeData.length;

            for (i = 0; i < numNodes; i++) {
                angle = (i / (numNodes / 2)) * Math.PI; 
                x = (radius * Math.cos(angle)) + (width / 2);
                y = (radius * Math.sin(angle)) + (width / 2);
                nodes.push({ 'id': i, 'x': x, 'y': y, 'label': nodeData[i].label, 'angle': angle });
            }
            return nodes;
        }

        var createSvg = function (radius, callback) {
            d3.selectAll('svg').remove();
            var svg = d3.select('#canvas').append('svg:svg')
                .attr('width', (radius * 2) + paddding)
                .attr('height', (radius * 2) + paddding);
            callback(svg);
        }

        var createElements = function (svg, nodes, elementRadius) {
            element = svg.selectAll('circle')
                .data(nodes)
                .enter().append('svg:circle')
                .attr('r', elementRadius)
                .attr('cx', function (d, i) { return d.x; })
                .attr('cy', function (d, i) { return d.y; });

            element = svg.selectAll('text')
                .data(nodes)
                .enter().append('svg:text')
                .text(function (d, i) { return d.label + " - " + d.angle.toFixed(2) + ", " + (d.angle*180/Math.PI); })
                .attr('x', function (d, i) { return nodes[0].x - 215; }) // radius * 2, add 15 for spacing off point
                .attr('y', function (d, i) { return nodes[0].y; })
                .attr("dy", ".35em")
                .style("alignment-baseline","middle")
                .style("text-anchor", "end")
                .attr("transform", function(d,i) {
                    return"rotate(" + ((d.angle * 180) / Math.PI - 180) + ", 225, 225)";})
                ;
        }

        var draw = function () {
            var radius = 100;
            var nodes = createNodes();

            createSvg(radius, function (svg) {
                createElements(svg, nodes, 10);
            });
        }

        $(document).ready(function () {
            draw();
        });
    })();
* {
        font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
        font-size: 13px;
    }
    circle {
        fill: steelblue;
        fill-opacity: .8;
    }

    circle:hover {
        fill: orange;
        fill-opacity: .8;
    }
<scripttype="text/javascript"src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script><scripttype="text/javascript"src="http://mbostock.github.com/d3/d3.v2.min.js"></script><divid="canvas"></div>

However, now the labels on the right are upside down. All that is left is to determine is whether a label falls on the right half or the left half and assign the appropriate attributes based on this.

Zero degrees points to the right, it is not the top of the diagram. Therefore, you need to ascertain if d.angle is less than 90 degrees (bottom right) or more than 270 degrees (top right), if so, your original code can be applied. If not, then you need to flip the label using the above code:

(function () {
        var paddding = 250;

        var createNodes = function () {

            var nodeData = [
                { id: 0, label: 'AAA' },
                { id: 1, label: 'BBB' },
                { id: 2, label: 'CCC' },
                { id: 3, label: 'DDD' },
                { id: 4, label: 'EEE' },
                { id: 5, label: 'FFF' },
                { id: 6, label: 'GGG' },
                { id: 7, label: 'HHH' }
            ];

            var radius = 100;
            var nodes = [],
                width = (radius * 2) + paddding,
                height = (radius * 2) + paddding,
                angle,
                x,
                y,
                i;

            var numNodes = nodeData.length;

            for (i = 0; i < numNodes; i++) {
                angle = (i / (numNodes / 2)) * Math.PI; 
                x = (radius * Math.cos(angle)) + (width / 2);
                y = (radius * Math.sin(angle)) + (width / 2);
                nodes.push({ 'id': i, 'x': x, 'y': y, 'label': nodeData[i].label, 'angle': angle });
            }
            return nodes;
        }

        var createSvg = function (radius, callback) {
            d3.selectAll('svg').remove();
            var svg = d3.select('#canvas').append('svg:svg')
                .attr('width', (radius * 2) + paddding)
                .attr('height', (radius * 2) + paddding);
            callback(svg);
        }

        var createElements = function (svg, nodes, elementRadius) {
            element = svg.selectAll('circle')
                .data(nodes)
                .enter().append('svg:circle')
                .attr('r', elementRadius)
                .attr('cx', function (d, i) { return d.x; })
                .attr('cy', function (d, i) { return d.y; });

            element = svg.selectAll('text')
                .data(nodes)
                .enter().append('svg:text')
                .text(function (d, i) { return d.label + " - " + d.angle.toFixed(2) + ", " + (d.angle*180/Math.PI); })
                .attr('x', function (d, i) { 
                   if (d.angle > Math.PI/2  && d.angle < 1.5 * Math.PI) {
                     return nodes[0].x - 215 } 
                   else {  
                     return  nodes[0].x + 15; 
                     }
                }) 
                .attr('y', function (d, i) { return nodes[0].y; })
                .attr("dy", ".35em")
                .style("alignment-baseline","middle")
                .style("text-anchor", function(d) { 
                  if (d.angle > Math.PI/2  && d.angle < 1.5 * Math.PI) {
                     return"end" 
                   }
                   else {
                     return"start";
                   }
                })
                .attr("transform", function(d,i) {
                  if (d.angle > Math.PI/2  && d.angle < 1.5 * Math.PI) {
                     return"rotate(" + ((d.angle * 180) / Math.PI - 180) + ", 225, 225)";
                   }
                   else {
                     return"rotate(" + ((d.angle * 180) / Math.PI) + ", 225, 225)" 
                   }
                 })
                ;
        }

        var draw = function () {
            var radius = 100;
            var nodes = createNodes();

            createSvg(radius, function (svg) {
                createElements(svg, nodes, 10);
            });
        }

        $(document).ready(function () {
            draw();
        });
    })();
* {
        font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
        font-size: 13px;
    }
    circle {
        fill: steelblue;
        fill-opacity: .8;
    }

    circle:hover {
        fill: orange;
        fill-opacity: .8;
    }
<scripttype="text/javascript"src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script><scripttype="text/javascript"src="http://mbostock.github.com/d3/d3.v2.min.js"></script><divid="canvas"></div>

Post a Comment for "How To Rotate Text Around Its Centroid (vertically Flip) In Svg / D3?"