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!
source to share
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>
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.
source to share
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 ' ' + d;
} else {
return d + ' '
}
});
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.
source to share