D3.js围绕中点

时间:2017-03-03 14:22:15

标签: javascript d3.js

我现在正在与D3.js合作,即使我发现非常相似的情况,我也无法将它们放在一起继续前进。

我有类似雷达图的东西,我想附加到我创建的每个斧头(轴的数量不固定可能是4,但也是40)文本,我已经拥有,但是围绕中心旋转文本一旦它们达到180度,就转向它们,实际上是0度。

结果应如下所示:

enter image description here

我现在拥有的是:

enter image description here

我只知道如何在arc内达到此目标,这也很好地显示here,但我并没有真正理解我的情况。

这是我的代码段,我附加了这些文字标准:

//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;
        });

修改

这是我的小提琴:https://jsfiddle.net/fsb47ndf/

感谢您的任何建议

3 个答案:

答案 0 :(得分:2)

有些人发现旋转SVG元素很困难,因为rotate属性的transform函数会围绕原点(0,0)旋转元素,而不是围绕其中心:

  

如果未提供可选参数,则旋转大约是当前用户坐标系的原点(source

因此,一个简单的选项是删除文本的xy属性,并使用transform定位它们。这样,我们可以轻松计算轮换:

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

这是你的代码:



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>
&#13;
&#13;
&#13;

答案 1 :(得分:1)

就像Gerardo Furtado已经在他的answer生活中提到的那样,如果你放弃xy属性,转而通过{{1}进行所有定位和轮换,可以变得更轻松}属性。但是,通过让浏览器执行所有三角函数,您可以更进一步。您需要做的就是指定适当的转换定义列表。

transform

如果省略xy属性,它们将默认为.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.^ }) ,这意味着文本将全部从原点开始。从那里开始,只需三次转换即可轻松移动并旋转到最终位置:

    根据他们在周边的位置
  1. 0文本到角度
  2. rotate将旋转的文字向外移至最终位置
  3. 使用另一个translate翻转圆圈左侧的文字。
  4. 请查看以下代码片段以了解正常工作:

    &#13;
    &#13;
    rotate
    &#13;
    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;
    });
    &#13;
    &#13;
    &#13;

答案 2 :(得分:0)

您可以使用这样的内容来旋转所有标签。您可能必须根据您的需要调整定位和旋转角度。

var angle = 180;

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