我正在使用最新的chartjs。如何在条形图中实现这种倒置的边框半径?
const myData = {
labels: ["1 Jan", "2 Jan", "3 Jan", "4 Jan", "5 Jan", "6 Jan", "7 Jan", "8 Jan"],
datasets: [{
label: 'Employee',
backgroundColor: background_1,
data: [12, 59, 5, 56, 58, 12, 59, 87, 45],
borderRadius: 100,
barPercentage: 0.5, // Adjust this value to change the width of the bars
categoryPercentage: 0.3,
borderColor: 'white',
borderWidth: 1
}, {
label: 'Engineer',
backgroundColor: background_2,
data: [12, 59, 5, 56, 58, 12, 59, 85, 23],
borderRadius: 100,
barPercentage: 0.5, // Adjust this value to change the width of the bars
categoryPercentage: 0.3,
borderColor: 'white',
borderWidth: 1,
borderSkipped: 'middle'
}]
}
这是我当前的数据。
chart.js的堆积条形图已经 做大部分事情。
创建所描述的圆形边框的第一步, 就是将
borderRadius
设置为一个对象,所以内部的bars
也有圆角,例如,
borderRadius: {topLeft: 100, topRight: 100, bottomLeft: 100, bottomRight: 100}
而不是 borderRadius: 100
,
请参阅 文档中有关堆叠图表的 borderRadius 提示。
然后我们必须将第二个栏延伸到第一个栏下方(或 相同符号和堆栈的前一个柱下方的第 n 个柱),所以你得到 第二根条形底部的“负半径”效果。
afterDatasetUpdate
处理程序。
必须保留前一柱的信息, 区分正域和负域,并建立索引 通过堆栈 ID。所有这些都是细节,最好看 代码。
const pluginStackRounded = {// inline plugin, no id
afterDatasetUpdate(chart, context){
const plugin = this;
if(chart.width && chart.height){
const barElements = context.meta.data,// or chart.getDatasetMeta(context.index).data;
stackId = context.meta.stack,
values = context.meta._parsed;
if(context.index === 0){
plugin._maxExtension = {};
}
if(!plugin._maxExtension[stackId]){
plugin._maxExtension[stackId] = {
positive: Array(values.length).fill(null),
negative: Array(values.length).fill(null)
};
}
barElements.forEach(
(barElement, i) => {
const barWidth = barElement.$animations?.width?._active
&& barElement.$animations?.width._to || barElement.width,
barBase = barElement.$animations?.base?._active &&
barElement.$animations.base._to || barElement.base,
barY = barElement.$animations?.base?._active &&
barElement.$animations.y._to || barElement.y,
isNegative = barY > barBase; //values[i].y < 0;
const maxExtension =
plugin._maxExtension[stackId][isNegative ? "negative" : "positive"][i];
if(maxExtension !== null){
if(barElement.options.$shared){
barElement.options = Object.assign({$shared: false}, barElement.options);
barElement.options.borderRadius = Object.assign({},
barElement.options.borderRadius);
}
if(!isNegative){
barElement.options.borderRadius.bottomLeft = 1;
barElement.options.borderRadius.bottomRight = 1;
}
else{
barElement.options.borderRadius.topLeft = 1
barElement.options.borderRadius.topRight = 1
}
if(barElement.$animations?.base?._active){
barElement.$animations.base._to = isNegative ?
Math.max(barBase - barWidth / 2, maxExtension) :
Math.min(barBase + barWidth / 2, maxExtension);
}
else{
barElement.base = isNegative ?
Math.max(barElement.base - barWidth, maxExtension) :
Math.min(barElement.base + barWidth, maxExtension);
}
}
// update maxExtension for the next dataset
const barHeight = barElement.$animations?.height?._active &&
barElement.$animations.height._to || barElement.height;
if(isNegative){
plugin._maxExtension[stackId].negative[i] = barBase + barHeight/2;
}
else{
plugin._maxExtension[stackId].positive[i] = barBase - barHeight/2;
}
}
);
}
}
};
const myData = {
labels: ["1 Jan", "2 Jan", "3 Jan", "4 Jan", "5 Jan", "6 Jan", "7 Jan", "8 Jan", "9 Jan"],
datasets: [
{
label: 'Employee',
backgroundColor: '#6fd',
data: [12, 59, 5, 56, 58, 12, 59, 87, 45],
stack: "A"
},
{
label: 'Engineer',
backgroundColor: '#fd6',
data: [12, 59, 5, 56, 58, 12, 59, 85, 23],
stack: "A"
}
]
};
const config = {
type: 'bar',
data: myData,
plugins:[pluginStackRounded],
options: {
responsive: true,
datasets: {
bar: {
barPercentage: 0.25,
borderRadius: {topLeft: 100, topRight: 100, bottomLeft: 100, bottomRight: 100},
borderSkipped: false,
borderColor: 'white',
borderWidth: 1
}
},
scales:{
y:{
ticks: {
stepSize: 20 // for easy verification on the grid
}
}
},
plugins: {
legend: {
position: 'top',
}
}
},
};
new Chart(document.querySelector('#chart1'), config);
<div style="min-height: 60vh">
<canvas id="chart1">
</canvas>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.0/chart.umd.js" integrity="sha512-6HrPqAvK+lZElIZ4mZ64fyxIBTsaX5zAFZg2V/2WT+iKPrFzTzvx6QAsLW2OaLwobhMYBog/+bvmIEEGXi0p1w==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>