How to rotate text around its centroid (vertically

2019-08-12 20:05发布

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 on the left hemisphere of the circle need to be rotated 180 degrees (flipped vertically) to be more legible.

enter image description here

I thought I could rotate the text object about its own origin before rotating it to the appropriate position around the circle but was unable to determine how to locate the center position of each text object.

How can I rotate text objects about their center for those on the left hemisphere of the circle (angle>= PI/2 && angle<=PI*1.5)? Or is there a better technique to use?

<style type="text/css">
    * {
        font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
        font-size: 13px;
    }
    circle {
        fill: steelblue;
        fill-opacity: .8;
    }

    circle:hover {
        fill: orange;
        fill-opacity: .8;
    }
</style>

<div id="canvas"></div>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.v2.min.js"></script>
<script type="text/javascript">
    (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 + 15; }) // 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", "start")
                .attr("transform", function(d,i) {
                    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();
        });
    })();
</script>

1条回答
爷的心禁止访问
2楼-- · 2019-08-12 20:50

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;
    }
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.v2.min.js"></script>
<div id="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;
    }
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.v2.min.js"></script>
<div id="canvas"></div>

查看更多
登录 后发表回答