如何在SVG / D3中围绕其质心(垂直翻转)旋转文本?

时间:2017-07-14 14:16:59

标签: javascript d3.js svg

我有文字对象标记点围绕圆均匀分布。感谢this article,我能够正确定位点和文本对象,但圆圈左半球上的标签需要旋转180度(垂直翻转)才能更清晰。

enter image description here

我以为我可以将文本对象旋转到自己的原点,然后再将其旋转到圆圈周围的适当位置,但无法确定如何找到每个文本对象的中心位置。

如何围绕圆心的左半球旋转文本对象(角度> = PI / 2&& =< = PI * 1.5)?或者是否有一个更好的技术使用?

<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 个答案:

答案 0 :(得分:2)

如果要反转圆圈左侧的标签。你可以实现不同的方式。一种方法是在附加文本时修改文本的三个属性:

.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)"
})

如果仅修改其中一些,则可能无法获得所需的结果。

修改text-end

这是必需的,因为您的文本将从您定义的点开始,并且由于文本可能具有可变长度,因此定义起点将比必要更复杂。对于需要翻转的点数,您需要使用:

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

修改transformx

文本需要旋转180度才能正确向上;但是,如果您修改此功能以向任何文本添加180度,则文本将显示在显示屏的错误一侧。因此,您还需要将x设置为新值,以使其显示在显示的正确一侧:

.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)"
})

总之,看起来像:

&#13;
&#13;
(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();
        });
    })();
&#13;
* {
        font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
        font-size: 13px;
    }
    circle {
        fill: steelblue;
        fill-opacity: .8;
    }

    circle:hover {
        fill: orange;
        fill-opacity: .8;
    }
&#13;
<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>
&#13;
&#13;
&#13;

但是,现在右边的标签是颠倒的。剩下的就是确定标签是在右半部分还是在左半部分,并根据此分配适当的属性。

零度指向右侧,它不是图的顶部。因此,您需要确定d.angle是否小于90度(右下)或大于270度(右上角),如果是,则可以应用原始代码。如果没有,那么您需要使用上面的代码翻转标签:

&#13;
&#13;
(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();
        });
    })();
&#13;
* {
        font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
        font-size: 13px;
    }
    circle {
        fill: steelblue;
        fill-opacity: .8;
    }

    circle:hover {
        fill: orange;
        fill-opacity: .8;
    }
&#13;
<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>
&#13;
&#13;
&#13;