Reputation: 72
Based on my old Problem: C3js horizontal bar chart labels cut off and sizing issue I am now trying to calculate the neccessary padding for y-axis based on the text width using canvas context textwidth.
const colors = ['#0065A3', '#767670', '#D73648', '#7FB2CE', '#00345B'];
const data= [
['veryveryveryveryverylongdatalabel01', 439034],
['veryveryveryveryverylongdatalabel02', 413664],
['veryveryveryveryverylongdatalabel03', 351376],
['veryveryveryveryverylongdatalabel04', 349932],
['veryveryveryveryverylongdatalabel05', 316490],
['veryveryveryveryverylongdatalabel06', 315039],
['veryveryveryveryverylongdatalabel07', 285908],
['veryveryveryveryverylongdatalabel08', 285681],
['veryveryveryveryverylongdatalabel09', 285215],
['veryveryveryveryverylongdatalabel10', 203248],
['veryveryveryveryverylongdatalabel11', 200508],
['veryveryveryveryverylongdatalabel12', 195508],
['veryveryveryveryverylongdatalabel13', 195058],
['veryveryveryveryverylongdatalabel14', 193508],
['veryveryveryveryverylongdatalabel15', 185508],
['veryveryveryveryverylongdatalabel16', 180508],
['veryveryveryveryverylongdatalabel17', 177508]
];
let totalDataValue = 0
data.forEach(function(d){
totalDataValue+=d[1];
});
let maxLabelWidth = 0;
/**
* Measures the rendered width of arbitrary text given the font size and font face
* @param {string} text The text to measure
* @param {number} fontSize The font size in pixels
* @param {string} fontFace The font face ("Arial", "Helvetica", etc.)
* @returns {number} The width of the text
**/
function getWidth(text, font) {
const canvas = document.createElement('canvas'),
context = canvas.getContext('2d');
context.font = font;
return context.measureText(text).width;
}
//TODO get font fpr c3-text from CSS
const font = '10px sans-serif'
data.forEach(function(d){
//formatted string
const label = d[0] + ": " + d3.format(",.0f")(d[1]) + " ["+d3.format(".2%")(d[1]/totalDataValue)+"]";
maxLabelWidth = Math.max(maxLabelWidth, getWidth(label, font));
});
var C3Styles = null;
const chart1 = c3.generate({
bindto: d3.select('#chart1'),
data: {
columns: data,
type: 'bar',
labels: {format : function(v, id) {return id + ": " + d3.format(",.0f")(v) + " ["+d3.format(".2%")(v/totalDataValue)+"]"; }}
},
bar: {
width: { ratio: 1}
},
legend: {
show: false,
},
tooltip: {
show: true,
format: {
value: function(value) {
return d3.format(",.0f")(value);
}
}
},
zoom: {
enabled: true
},
axis: {
x: {
show:false,
type:'category',
categories: ['value1']
},
y: {
show:false,
padding : {
top: Math.ceil(maxLabelWidth)
}
},
rotated: true
}
});
#chart1 {
width: 600px;
height: 400px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/c3/0.7.15/c3.min.css" rel="stylesheet" />
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.7.15/c3.min.js"></script>
<div id="chart1" class "c3">
</div>
There are two remaining Problems: First, the calculated width of the text is slightly lower than the width of the SVG text element although there is no spacing, padding etc. How do I get the width of the SVG element based on the text length?
Second, even if the value would be calculated correctly the calculated width of the text seems to be much to low to be used for axis.y.padding.top value. What am I missing?
Upvotes: 0
Views: 354
Reputation: 72
I found a solution for this: Do not use y-axis padding but instead global padding and turn off clip-path.
padding : {
right: Math.ceil(maxLabelDataWidth)
},
and
d3.select(chart1.element).select("." + c3.chart.internal.fn.CLASS.chart).attr("clip-path", null);
const colors = ['#0065A3', '#767670', '#D73648', '#7FB2CE', '#00345B'];
const data= [
['veryveryveryveryverylongdatalabel01', 439034],
['veryveryveryveryverylongdatalabel02', 413664],
['veryveryveryveryverylongdatalabel03', 351376],
['veryveryveryveryverylongdatalabel04', 349932],
['veryveryveryveryverylongdatalabel05', 316490],
['veryveryveryveryverylongdatalabel06', 315039],
['veryveryveryveryverylongdatalabel07', 285908],
['veryveryveryveryverylongdatalabel08', 285681],
['veryveryveryveryverylongdatalabel09', 285215],
['veryveryveryveryverylongdatalabel10', 203248],
['veryveryveryveryverylongdatalabel11', 200508],
['veryveryveryveryverylongdatalabel12', 195508],
['veryveryveryveryverylongdatalabel13', 195058],
['veryveryveryveryverylongdatalabel14', 193508],
['veryveryveryveryverylongdatalabel15', 185508],
['veryveryveryveryverylongdatalabel16', 180508],
['veryveryveryveryverylongdatalabel17', 177508]
];
let totalDataValue = 0
data.forEach(function(d){
totalDataValue+=d[1];
});
let maxLabelDataWidth = 0;
/**
* Measures the rendered width of arbitrary text given the font size and font face
* @param {string} text The text to measure
* @param {number} fontSize The font size in pixels
* @param {string} fontFace The font face ("Arial", "Helvetica", etc.)
* @returns {number} The width of the text
**/
function getWidth(text, font) {
const canvas = document.createElement('canvas'),
context = canvas.getContext('2d');
context.font = font;
return context.measureText(text).width;
}
//TODO get font fpr c3-text from CSS
const font = '10px sans-serif'
data.forEach(function(d){
//formatted string
const label = d3.format(",.0f")(d[1]) + " ["+d3.format(".2%")(d[1]/totalDataValue)+"]";
maxLabelDataWidth = Math.max(maxLabelDataWidth, getWidth(label, font));
});
var C3Styles = null;
const chart1 = c3.generate({
bindto: d3.select('#chart1'),
data: {
columns: data,
type: 'bar',
labels: {format : function(v, id) {return id + ": " + d3.format(",.0f")(v) + " ["+d3.format(".2%")(v/totalDataValue)+"]"; }}
},
bar: {
width: { ratio: 1}
},
legend: {
show: false,
},
tooltip: {
show: true,
format: {
value: function(value) {
return d3.format(",.0f")(value);
}
}
},
zoom: {
enabled: true
},
padding : {
right: Math.ceil(maxLabelDataWidth)
},
axis: {
x: {
show:false,
type:'category',
categories: ['value1']
},
y: {
show:false,
},
rotated: true
}
});
d3.select(chart1.element).select("." + c3.chart.internal.fn.CLASS.chart).attr("clip-path", null);
#chart1 {
width: 600px;
height: 400px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/c3/0.7.20/c3.min.css" rel="stylesheet" />
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.7.20/c3.min.js"></script>
<div id="chart1" class "c3">
</div>
Upvotes: 0