计算 C3js 水平条形图的填充
Calculate padding for C3js horizontal bar charts
根据我的老问题: 我现在尝试使用 canvas 上下文文本宽度根据文本宽度计算 y 轴的必要填充。
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>
还有两个问题:
首先,虽然没有间距,填充等,但计算出的文本宽度略低于SVG文本元素的宽度。如何根据文本长度获取SVG元素的宽度?
其次,即使可以正确计算该值,计算出的文本宽度似乎也太低,无法用于 axis.y.padding.top 值。我错过了什么?
我找到了一个解决方案:不要使用 y 轴填充,而是使用全局填充并关闭 clip-path。
padding : {
right: Math.ceil(maxLabelDataWidth)
},
和
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>
根据我的老问题:
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>
还有两个问题: 首先,虽然没有间距,填充等,但计算出的文本宽度略低于SVG文本元素的宽度。如何根据文本长度获取SVG元素的宽度?
其次,即使可以正确计算该值,计算出的文本宽度似乎也太低,无法用于 axis.y.padding.top 值。我错过了什么?
我找到了一个解决方案:不要使用 y 轴填充,而是使用全局填充并关闭 clip-path。
padding : {
right: Math.ceil(maxLabelDataWidth)
},
和
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>