Диаграмма пончиков: как перевернуть перевернутые числа?

#javascript #d3.js #svg

#javascript #d3.js #svg

Вопрос:

Я пытаюсь выяснить логику, необходимую для определения того, следует ли поворачивать текст на моих ярлыках пончиков на 180 градусов. В принципе, если они отображаются вверх ногами, я хочу повернуть их на 180, чтобы улучшить читаемость. И в идеале это только для перевернутых текстовых меток, а не для тех, которые уже доступны для чтения. Тогда возникает вопрос, как количественно определить эти понятия для if логики. Я считаю, что r.startAngle в моем коде это уместилось бы здесь. Вот фрагмент.

 var margins = {top:20, left:50, bottom:20, right:20};

var width = 300;
                var arcSize = (6 * width / 100);
                var innerRadius = arcSize * 3;

                var data = [
                  {value: 43, marker: 90, label: "Cash", color: '#b8cce4', neg:false},
                  {value: 91, marker: 191, label: "Bonds", color: '#95b3d7', neg:false},
                  {value: 12, marker: 26, label: "Stocks", color: '#4f81b9', neg:false},
                  {value: 7, marker: 15, label: "Securities Funds", color: '#366092', neg:false},
                  {value: 62, marker: 130, label: "Other", color: '#a6a6a6', neg:false}
                ];

var svg = d3.select('body').append('svg').attr('width', width 100).attr('height', width 100);

var graphGroup = svg.append("g")
    .attr("transform", "translate("   margins.left   ","   margins.top   ")");
/*
    graphGroup.append('image')
        .attr('x',width/2-50)
        .attr('y',width/2-50)
        .attr('width',100)
        .attr('height',100)
        .attr("xlink:href", 'china-life.png');
*/

graphGroup.append('text')
    .attr('x',width/2-41)
    .attr('y',width/2 14)
    .style('font-size','42px')
    .text('5%');

var arcs = data.map(function (obj, i) {
    return d3.svg.arc().innerRadius(i * arcSize   innerRadius).outerRadius((i   1) * arcSize - (width / 100)   innerRadius);
});
var arcsGrey = data.map(function (obj, i) {
    return d3.svg.arc().innerRadius(i * arcSize   (innerRadius   ((arcSize / 2) - 2))).outerRadius((i   1) * arcSize - ((arcSize / 2))   (innerRadius));
});

var pieData = data.map(function (obj, i) {
    return [
        {value: obj.value * 0.75, arc: arcs[i], object: obj},
        {value: (100 - obj.value) * 0.75, arc: arcsGrey[i], object: obj},
        {value: 100 * 0.25, arc: arcs[i], object: obj}];
});

var pie = d3.layout.pie().sort(null).value(function (d) {
    return d.value;
});

var g = graphGroup.selectAll('g').data(pieData).enter()
    .append('g')
    .attr('transform', 'translate('   width / 2   ','   width / 2   ') rotate(180)');
var gText = graphGroup.selectAll('g.textClass').data([{}]).enter()
    .append('g')
    .classed('textClass', true)
    .attr('transform', 'translate('   width / 2   ','   width / 2   ') rotate(180)');


g.selectAll('path').data(function (d) {
    return pie(d);
}).enter().append('path')
    .attr('id', function (d, i) {
        if (i == 1) {
            return "Text"   d.data.object.label
        }
    })
    .attr('d', function (d) {
        return d.data.arc(d);
    }).attr('fill', function (d, i) {
      if (d.data.object.neg==false) {
        return i == 0 ? d.data.object.color : i == 1 ? '#D3D3D3' : 'none' ;
      } else {
        return i == 0 ? 'red' : i == 1 ? '#D3D3D3' : 'none' ;
      }}).attr('class','segments');

/*
g.selectAll('.segments').attr('fill', function(d,i) {
  return d.data.object.neg==true ? 'red' : 'none';
});
*/

graphGroup.selectAll('g').each(function (d, index) {
    var el = d3.select(this);
    var path = el.selectAll('path').each(function (r, i) {
        if (i === 1) {
            var centroid = r.data.arc.centroid({
                startAngle: r.startAngle   0.05,
                endAngle: r.startAngle   0.001   0.05
            });
            var lableObj = r.data.object;
            g.append('text')
                .attr('font-size', ((5 * width) / 100))
                .attr('dominant-baseline', 'central')
                /*.attr('transform', "translate("   centroid[0]   ","   (centroid[1]   10)   ") rotate("   (180 / Math.PI * r.startAngle   7)   ")")
                 .attr('alignment-baseline', 'middle')*/
                .append("textPath")
                .attr("textLength", function (d, i) {
                    return 0;
                })
                .attr("xlink:href", "#Text"   r.data.object.label)
                .attr("startOffset", '5')
                .attr('font-weight','normal')
                .attr("dy", '-3em')
                .text(function(d) {
                  if (lableObj.neg==true) {
                    return '-' lableObj.marker;
                  } else {
                    return lableObj.marker ;
                  }
                  });
        }
        if (i === 0) {
            var centroidText = r.data.arc.centroid({
                startAngle: r.startAngle,
                endAngle: r.startAngle
            });
            var lableObj = r.data.object;
            /*gText.append('text')
                .attr('font-size', ((5 * width) / 100))
                .text(lableObj.label)
                .attr('transform', "translate("   (centroidText[0] - ((1.5 * width) / 100))   ","   (centroidText[1]   ") rotate("   (180)   ")"))
                .attr('dominant-baseline', 'central');*/
        }
    });
}); 
 <script src="http://d3js.org/d3.v3.min.js"></script> 
     if (i === 0 amp;amp; r.startAngle>180){
        var centroidText = r.data.arc.centroid({
            startAngle: r.startAngle,
            endAngle: r.startAngle)
        } else {
        var centroidText = r.data.arc.centroid({
            startAngle: r.startAngle*Math.pi,
            endAngle: r.startAngle*Math.pi)
        var lableObj = r.data.object; }
 

К сожалению, это приводит к неожиданному «)» после ошибки консоли списка.

Во фрагменте кода числа: 15 и 26 «перевернуты» и должны быть перевернуты.

Вопрос

Как мне перевернуть текстовые метки на диаграмме пончиков, чтобы улучшить читаемость? Есть ли встроенные средства или это единственный способ обновить мою (пока неудачную) логику?

Комментарии:

1. Это unexpected ")" связано с отсутствием } в конце endAngle: r.startAngle*Math.pi)

2. @DBS странно, ошибка сохраняется, она довольно сложная, ее трудно отлаживать.

Ответ №1:

Это не обычные текстовые элементы. Это <textPath> элементы.

Поэтому их вращение не является правильным решением. Что вам нужно сделать, так это получить длину пути…

 var thisLength = this.getTotalLength();
 

… и перемещение текстов, используя startOffset , в конец пути, вот так:

 .attr("startOffset", function(){
    return index === 2 || index === 3 ? thisLength - 22 : 5
})
 

Обратите внимание на тот факт, что здесь я просто заставляю второй и третий элементы (магические числа, как 22 и для заполнения) менять свои позиции: в вашем реальном коде вам нужно будет исправить функцию, чтобы найти элементы, которые startOffset необходимо изменить, в соответствии с базовой тригонометрией.

Вот ваш код с этим изменением:

 var margins = {
  top: 20,
  left: 50,
  bottom: 20,
  right: 20
};

var width = 300;
var arcSize = (6 * width / 100);
var innerRadius = arcSize * 3;

var data = [{
    value: 43,
    marker: 90,
    label: "Cash",
    color: '#b8cce4',
    neg: false
  },
  {
    value: 91,
    marker: 191,
    label: "Bonds",
    color: '#95b3d7',
    neg: false
  },
  {
    value: 12,
    marker: 26,
    label: "Stocks",
    color: '#4f81b9',
    neg: false
  },
  {
    value: 7,
    marker: 15,
    label: "Securities Funds",
    color: '#366092',
    neg: false
  },
  {
    value: 62,
    marker: 130,
    label: "Other",
    color: '#a6a6a6',
    neg: false
  }
];

var svg = d3.select('body').append('svg').attr('width', width   100).attr('height', width   100);

var graphGroup = svg.append("g")
  .attr("transform", "translate("   margins.left   ","   margins.top   ")");
/*
    graphGroup.append('image')
        .attr('x',width/2-50)
        .attr('y',width/2-50)
        .attr('width',100)
        .attr('height',100)
        .attr("xlink:href", 'china-life.png');
*/

graphGroup.append('text')
  .attr('x', width / 2 - 41)
  .attr('y', width / 2   14)
  .style('font-size', '42px')
  .text('5%');

var arcs = data.map(function(obj, i) {
  return d3.svg.arc().innerRadius(i * arcSize   innerRadius).outerRadius((i   1) * arcSize - (width / 100)   innerRadius);
});
var arcsGrey = data.map(function(obj, i) {
  return d3.svg.arc().innerRadius(i * arcSize   (innerRadius   ((arcSize / 2) - 2))).outerRadius((i   1) * arcSize - ((arcSize / 2))   (innerRadius));
});

var pieData = data.map(function(obj, i) {
  return [{
      value: obj.value * 0.75,
      arc: arcs[i],
      object: obj
    },
    {
      value: (100 - obj.value) * 0.75,
      arc: arcsGrey[i],
      object: obj
    },
    {
      value: 100 * 0.25,
      arc: arcs[i],
      object: obj
    }
  ];
});

var pie = d3.layout.pie().sort(null).value(function(d) {
  return d.value;
});

var g = graphGroup.selectAll('g').data(pieData).enter()
  .append('g')
  .attr('transform', 'translate('   width / 2   ','   width / 2   ') rotate(180)');
var gText = graphGroup.selectAll('g.textClass').data([{}]).enter()
  .append('g')
  .classed('textClass', true)
  .attr('transform', 'translate('   width / 2   ','   width / 2   ') rotate(180)');


g.selectAll('path').data(function(d) {
    return pie(d);
  }).enter().append('path')
  .attr('id', function(d, i) {
    if (i == 1) {
      return "Text"   d.data.object.label
    }
  })
  .attr('d', function(d) {
    return d.data.arc(d);
  }).attr('fill', function(d, i) {
    if (d.data.object.neg == false) {
      return i == 0 ? d.data.object.color : i == 1 ? '#D3D3D3' : 'none';
    } else {
      return i == 0 ? 'red' : i == 1 ? '#D3D3D3' : 'none';
    }
  }).attr('class', 'segments');

/*
g.selectAll('.segments').attr('fill', function(d,i) {
  return d.data.object.neg==true ? 'red' : 'none';
});
*/

graphGroup.selectAll('g').each(function(d, index) {
  var el = d3.select(this);
  var path = el.selectAll('path').each(function(r, i) {
    if (i === 1) {
      var centroid = r.data.arc.centroid({
        startAngle: r.startAngle   0.05,
        endAngle: r.startAngle   0.001   0.05
      });
      var lableObj = r.data.object;
      var thisLength = this.getTotalLength();
      g.append('text')
        .attr('font-size', ((5 * width) / 100))
        .attr('dominant-baseline', 'central')
        /*.attr('transform', "translate("   centroid[0]   ","   (centroid[1]   10)   ") rotate("   (180 / Math.PI * r.startAngle   7)   ")")
         .attr('alignment-baseline', 'middle')*/
        .append("textPath")
        .attr("textLength", function(d, i) {
          return 0;
        })
        .attr("xlink:href", "#Text"   r.data.object.label)
        .attr("startOffset", function() {
          return index === 2 || index === 3 ? thisLength - 22 : 5
        })
        .attr('font-weight', 'normal')
        .attr("dy", '-3em')
        .text(function(d) {
          if (lableObj.neg == true) {
            return '-'   lableObj.marker;
          } else {
            return lableObj.marker;
          }
        });
    }
    if (i === 0) {
      var centroidText = r.data.arc.centroid({
        startAngle: r.startAngle,
        endAngle: r.startAngle
      });
      var lableObj = r.data.object;
      /*gText.append('text')
          .attr('font-size', ((5 * width) / 100))
          .text(lableObj.label)
          .attr('transform', "translate("   (centroidText[0] - ((1.5 * width) / 100))   ","   (centroidText[1]   ") rotate("   (180)   ")"))
          .attr('dominant-baseline', 'central');*/
    }
  });
}); 
 <script src="http://d3js.org/d3.v3.min.js"></script>