简体   繁体   English

D3.js围绕中心点旋转轴标签

[英]D3.js rotate axis labels around the middle point

I am working with D3.js now and even though I found very similar cases I am not really able to put them together and move on. 我现在正在使用D3.js,即使我发现了非常相似的案例,也无法将它们放在一起继续前进。

I have something similar as a radar chart and I would like to append to each ax I create (number of axes is not fixed could be 4, but also 40) text, which I already have, but rotate the text around center point and turn them as soon as they reach 180 degrees, actually 0 degrees. 我有一个类似于雷达图的东西,我想附加到我创建的每个轴上(轴数不是固定的,可以是4,也可以是40)我已经拥有的文本,但是围绕中心点旋转文本并旋转一旦达到180度(实际上是0度)

The result should look like this: 结果应如下所示:

在此处输入图片说明

What I have now is this: 我现在所拥有的是:

在此处输入图片说明

I only know how to reach this within arc , which is also nicely shown here , but I did not really figure it out for my case. 我只知道如何在arc内达到这一点,在这里也很好地展示了这一点 ,但是我并没有为我的情况弄清楚。

This is my code snippet, where I append those text criteria: 这是我的代码段,在其中添加了这些文本条件:

//Write criterias
      axis.append("text")
        .attr("class","labels")
        .attr("font-size","12px")
        .attr("font-family","Montserrat")
        .attr("text-anchor","middle") 
        .attr("fill","white")
        .attr("x",function (d, i) {
          return radius * Math.cos(angleSlice * i - Math.PI/2)*options.circles.labelFactor;
        })
        .attr("y",function (d, i) {
          return radius * Math.sin(angleSlice * i - Math.PI/2)*options.circles.labelFactor;
        })
        .text(function (d) {
          return d;
        });

EDIT: 编辑:

Here is my fiddle: https://jsfiddle.net/fsb47ndf/ 这是我的小提琴: https : //jsfiddle.net/fsb47ndf/

Thank you for any advise 谢谢你的建议

Some people find it difficult to rotate an SVG element, because the rotate function of the transform attribute rotates the element around the origin (0,0), not around its center: 某些人发现很难旋转SVG元素,因为transform属性的rotate函数将元素绕原点(0,0)而不是其中心旋转:

If optional parameters and are not supplied, the rotate is about the origin of the current user coordinate system ( source ) 如果未提供可选参数,则旋转以当前用户坐标系的原点为准( source

Thus, an easy option is dropping the x and the y attributes of the texts, and positioning them using transform . 因此,一个简单的选择是删除文本的xy属性,并使用transform定位它们。 That way, we can easily calculate the rotation: 这样,我们可以轻松计算出旋转角度:

.attr("transform", function(d, i) {
    var rotate = angleSlice * i > Math.PI / 2 ?
        (angleSlice * i * 180 / Math.PI) - 270 :
        (angleSlice * i * 180 / Math.PI) - 90;
    return "translate(" + radius * Math.cos(angleSlice * i - Math.PI / 2) * options.circles.labelFactor +
        "," + radius * Math.sin(angleSlice * i - Math.PI / 2) * options.circles.labelFactor +
        ") rotate(" + rotate + ")"
})

Here is your code: 这是您的代码:

 data = [{ name: 'DATA1', value: 22, }, { name: 'DATA2', value: 50, }, { name: 'DATA3', value: 0, }, { name: 'DATA4', value: 24, }, { name: 'DATA5', value: 22, }, { name: 'DATA6', value: 30, }, { name: 'DATA7', value: 20, }, { name: 'DATA8', value: 41, }, { name: 'DATA9', value: 31, }, { name: 'DATA10', value: 30, }, { name: 'DATA11', value: 30, }, { name: 'DATA12', value: 30, }, { name: 'DATA13', value: 30, }, { name: 'DATA14', value: 30, }, ]; var options = { width: 600, height: 600, margins: { top: 100, right: 100, bottom: 100, left: 100 }, circles: { levels: 6, maxValue: 100, labelFactor: 1.15, opacity: 0.2, }, }; var allAxis = (data.map(function(i, j) { return i.name })), total = allAxis.length, radius = Math.min(options.width / 2, options.height / 2), angleSlice = Math.PI * 2 / total, Format = d3.format(''); var rScale = d3.scale.linear() .domain([0, options.circles.maxValue]) .range([50, radius]); var svg = d3.select("body").append("svg") .attr("width", options.width + options.margins.left + options.margins.right) .attr("height", options.height + options.margins.top + options.margins.bottom); var g = svg.append("g") .attr("transform", "translate(" + (options.width / 2 + options.margins.left) + "," + (options.height / 2 + options.margins.top) + ")"); var axisGrid = g.append("g") .attr("class", "axisWraper"); var axis = axisGrid.selectAll(".axis") .data(allAxis) .enter() .append("g") .attr("class", "axis") //append them lines axis.append("line") .attr("x1", 0) .attr("y1", 0) .attr("x2", function(d, i) { var tempX2 = radius * Math.cos(angleSlice * i - Math.PI / 2); return tempX2; }) .attr("y2", function(d, i) { var tempY = radius * Math.sin(angleSlice * i - Math.PI / 2); return tempY; }) .attr("class", "line") .attr("stroke", "black") .attr("fill", "none"); //Draw background circles axisGrid.selectAll(".levels") .data([6, 5, 4, 3, 2, 1]) .enter() .append("circle") .attr("class", "gridCircle") .attr("r", function(d, i) { return parseInt(radius / options.circles.levels * d, 10); }) .attr("stroke", "black") .attr("fill-opacity", options.circles.opacity); //Write data axis.append("text") .attr("class", "labels") .attr("font-size", "12px") .attr("font-family", "Montserrat") .attr("text-anchor", "middle") .attr("fill", "black") .attr("transform", function(d, i) { var rotate = angleSlice * i > Math.PI ? (angleSlice * i * 180 / Math.PI) - 270 : (angleSlice * i * 180 / Math.PI) - 90; return "translate(" + radius * Math.cos(angleSlice * i - Math.PI / 2) * options.circles.labelFactor + "," + radius * Math.sin(angleSlice * i - Math.PI / 2) * options.circles.labelFactor + ") rotate(" + rotate + ")" }) .text(function(d) { return d; }); 
 <script src="https://d3js.org/d3.v3.min.js"></script> 

Like already mentioned by Gerardo Furtado in his answer life can get easier if you ditch your x and y attributes in favor of doing all positioning and rotation via the transform attribute. 就像Gerardo Furtado在他的答案中已经提到的那样,如果放弃xy属性以支持通过transform属性进行所有定位和旋转,则生活会变得更加轻松。 However, you can take his approach even a step further by letting the browser do all the trigonometry. 但是,通过让浏览器完成所有的三角函数,您甚至可以使他的方法更进一步。 All you need to do is specify a list of appropriate transform definitions. 您需要做的就是指定适当的变换定义的列表。

.attr("transform", function(d, i) {
  var angleI = angleSlice  * i * 180 / Math.PI - 90;   // the angle to rotate the label
  var distance = radius * options.circles.labelFactor; // the distance from the center
  var flip = angleI > 90 ? 180 : 0;                    // 180 if label needs to be flipped

  return "rotate(" + angleI + ") translate(" + distance + ")" + "rotate(" + flip + ")");
  //       ^1.^                    ^2.^                           ^3.^
})

If you omit the x and y attributes, they will default to 0 , which means, that the texts will all start off at the origin. 如果省略xy属性,则它们的默认值将为0 ,这意味着文本将全部从原点开始。 From there it is easy to move and rotate them to their final position applying just three transformations: 从那里可以轻松地将它们移动并旋转到最终位置,只需执行以下三个转换即可:

  1. rotate the texts to the angle according to their position on the perimeter 根据文本在周长上的位置将文本rotate到角度
  2. translate the rotated texts outwards to their final position translate旋转的文本向外translate到最终位置
  3. Flip the texts on the left hand side of the circle using another rotate . 使用另一rotate按钮,翻转圆圈左侧的文本。

Have a look at the following snippet for a working demo: 请查看以下片段,以获取有效的演示:

 data = [{ name: 'DATA1', value: 22, }, { name: 'DATA2', value: 50, }, { name: 'DATA3', value: 0, }, { name: 'DATA4', value: 24, }, { name: 'DATA5', value: 22, }, { name: 'DATA6', value: 30, }, { name: 'DATA7', value: 20, }, { name: 'DATA8', value: 41, }, { name: 'DATA9', value: 31, }, { name: 'DATA10', value: 30, }, { name: 'DATA11', value: 30, }, { name: 'DATA12', value: 30, }, { name: 'DATA13', value: 30, }, { name: 'DATA14', value: 30, }, ]; var options = { width: 600, height: 600, margins: { top: 100, right: 100, bottom: 100, left: 100 }, circles: { levels: 6, maxValue: 100, labelFactor: 1.15, opacity: 0.2, }, }; var allAxis = (data.map(function(i, j) { return i.name })), total = allAxis.length, radius = Math.min(options.width / 2, options.height / 2), angleSlice = Math.PI * 2 / total, Format = d3.format(''); var rScale = d3.scale.linear() .domain([0, options.circles.maxValue]) .range([50, radius]); var svg = d3.select("body").append("svg") .attr("width", options.width + options.margins.left + options.margins.right) .attr("height", options.height + options.margins.top + options.margins.bottom); var g = svg.append("g") .attr("transform", "translate(" + (options.width / 2 + options.margins.left) + "," + (options.height / 2 + options.margins.top) + ")"); var axisGrid = g.append("g") .attr("class", "axisWraper"); var axis = axisGrid.selectAll(".axis") .data(allAxis) .enter() .append("g") .attr("class", "axis") //append them lines axis.append("line") .attr("x1", 0) .attr("y1", 0) .attr("x2", function(d, i) { var tempX2 = radius * Math.cos(angleSlice * i - Math.PI / 2); return tempX2; }) .attr("y2", function(d, i) { var tempY = radius * Math.sin(angleSlice * i - Math.PI / 2); return tempY; }) .attr("class", "line") .attr("stroke", "black") .attr("fill", "none"); //Draw background circles axisGrid.selectAll(".levels") .data([6, 5, 4, 3, 2, 1]) .enter() .append("circle") .attr("class", "gridCircle") .attr("r", function(d, i) { return parseInt(radius / options.circles.levels * d, 10); }) .attr("stroke", "black") .attr("fill-opacity", options.circles.opacity); //Write data axis.append("text") .attr("class", "labels") .attr("font-size", "12px") .attr("font-family", "Montserrat") .attr("text-anchor", "middle") .attr("fill", "black") .attr("dy", ".35em") .attr("transform", function(d, i) { var angleI = angleSlice * i * 180 / Math.PI - 90; // the angle to rotate the label var distance = radius * options.circles.labelFactor; // the distance from the center var flip = angleI > 90 ? 180 : 0; // 180 if label needs to be flipped return "rotate(" + angleI + ") translate(" + distance + ")" + "rotate(" + flip + ")" }) .text(function(d) { console.log(d); return d; }); 
 <script src="https://d3js.org/d3.v3.js"></script> 

You can use something like this to rotate all the labels. 您可以使用类似的方法来旋转所有标签。 You probably have to adjust the positioning and rotation angle based on exactly how you want it. 您可能必须根据实际需要调整位置和旋转角度。

var angle = 180;

svg.selectAll(".labels")
   .attr("transform", "translate(300,0) rotate("+angle+")");

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM