Add svg icon always right behind text

So I'm working with D3.js and I have a radar chart with data that is represented by text placed inside a circle.

The texts are always of different lengths, and I would like to immediately after each of these texts add an icon in this order: chart, text label, trash can.

I already tried to calculate the length inside the attribute that displays the text, which gives me the length of each label correctly, but then I was unable to add the icon:

.attr("lenght", function() {
          var datalong = this.getComputedTextLength();
       })

      

Then I also tried adding the icon separately, which works, but I don't know how to get the length:

axis.append("svg:image")
        .attr("xlink:href","http://svgur.com/i/121.svg")
        .attr("y",-7)
        .attr("opacity",1)
        .attr("transform", function(d, i,e) {
          var angleI = angleSlice * i * 180 / Math.PI - 90; 
          var distance = radius * options.trash.trashFactor; 
          var flip = angleI > 90 ? 180 : 0; 
          return "rotate(" + angleI + ") translate(" + distance + ")" + "rotate(" + flip + ")"
        });

      

Then I tried to add an icon in the method where I write data (text) but the icon was not displayed. I have tried to group them and place them next to each other, but also without success.

I have a JSFiddle with some closest option I was able to get. I hope one can understand what I am trying to do, if not please refer to the fiddle and where you can easily understand what I am trying to achieve.

JSFiddle: https://jsfiddle.net/fsb47ndf/5/

Any help is greatly appreciated!

+3


source to share


2 answers


You can get the length of a text using this cumbersome math:

var textLength = d3.select(this.parentNode).select("text").node().getComputedTextLength();

      

Basically, this is what the code does:

  • d3.select(this.parentNode)

    : it picks the parent of the icon, then ...
  • select("text").node()

    : it selects the text that is a child of this parent, and finally ...
  • getComputedTextLength()

    : it gets the length of this text.

Then you use it to set the distance from the icons:

    var distance = angleI > 90 ? radius + textLength + 40 : radius + textLength + 30;

      



Here is your updated fiddle: https://jsfiddle.net/0L4xzmfo/

And here's the same code in a snippet:

 data = [{
   name: 'DATA11111',
   value: 22,
 }, {
   name: 'DATA2',
   value: 50,
 }, {
   name: 'DATA33333333',
   value: 0,
 }, {
   name: 'DATA444444',
   value: 24,
 }, {
   name: 'DATA55',
   value: 22,
 }, {
   name: 'DATA6666',
   value: 30,
 }, {
   name: 'DATA7',
   value: 20,
 }, {
   name: 'DATA8',
   value: 41,
 }, {
   name: 'DATA9',
   value: 31,
 }, {
   name: 'DATA10',
   value: 30,
 }, {
   name: 'DATA1121213213',
   value: 30,
 }, {
   name: 'DATA12',
   value: 30,
 }, {
   name: 'DATA1123123212313',
   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,
     dataFactor: 1.09,
     opacity: 0.2,
   },

   trash: {
     trashFactor: 1.32
   }

 };


 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", function(d, i) {
     var angleI = angleSlice * i * 180 / Math.PI - 90;
     return angleI > 90 ? "end" : "start"
   })
   .attr("dy", ".35em")
   .attr("fill", "black")
   .attr("transform", function(d, i) {
     var angleI = angleSlice * i * 180 / Math.PI - 90; // the angle to rotate the label
     var distance = radius * options.circles.dataFactor; // 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) {
     return d;
   });


 axis.append("svg:image")
   .attr("xlink:href", "http://svgur.com/i/121.svg")
   .attr("class", "trash")
   .attr("y", -7)
   .attr("text-anchor", "end")
   .attr("transform", function(d, i) {
     var textLength = d3.select(this.parentNode).select("text").node().getComputedTextLength();
     var angleI = angleSlice * i * 180 / Math.PI - 90; // the angle to rotate the label
     var distance = angleI > 90 ? radius + textLength + 40 : radius + textLength + 30; // 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 + ")"
   });
      

.trash {
  position: absolute;
}
      

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
      

Run code


PS: I'm using a magic number because I haven't looked at the code to find the exact complement of the text. Change this magic number accordingly.

+2


source


Here's another idea: use FontAwesome fonts and icons. This way you avoid adding svg: image.

.html(function (d, i) {
    var angleI = angleSlice * i * 180 / Math.PI - 90;
    if (angleI > 90) {
        return '&#xf014; ' + d;
    } else {
        return d + ' &#xf014;'
    }
});    

      



https://jsfiddle.net/fsb47ndf/25/

& # xf014 is the FA unicode for the trash symbol ( http://fontawesome.io/icon/trash-o/ ). Rotate the icon the same way you rotate the text.

+3


source







All Articles