2👍
I think that the closest you can get with chart.js and rounded borders is using this answer: https://stackoverflow.com/a/43281198/10387396.
But it doesn’t work pretty well with negative values.
Chart.helpers.drawRoundedTopRectangle = function(ctx, x, y, width, height, radius) {
ctx.beginPath();
ctx.moveTo(x + radius, y);
// top right corner
ctx.lineTo(x + width - radius, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
// bottom right corner
ctx.lineTo(x + width, y + height);
// bottom left corner
ctx.lineTo(x, y + height);
// top left
ctx.lineTo(x, y + radius);
ctx.quadraticCurveTo(x, y, x + radius, y);
ctx.closePath();
};
Chart.elements.RoundedTopRectangle = Chart.elements.Rectangle.extend({
draw: function() {
var ctx = this._chart.ctx;
var vm = this._view;
var left, right, top, bottom, signX, signY, borderSkipped;
var borderWidth = vm.borderWidth;
if (!vm.horizontal) {
// bar
left = vm.x - vm.width / 2;
right = vm.x + vm.width / 2;
top = vm.y;
bottom = vm.base;
signX = 1;
signY = bottom > top? 1: -1;
borderSkipped = vm.borderSkipped || 'bottom';
} else {
// horizontal bar
left = vm.base;
right = vm.x;
top = vm.y - vm.height / 2;
bottom = vm.y + vm.height / 2;
signX = right > left? 1: -1;
signY = 1;
borderSkipped = vm.borderSkipped || 'left';
}
// Canvas doesn't allow us to stroke inside the width so we can
// adjust the sizes to fit if we're setting a stroke on the line
if (borderWidth) {
// borderWidth shold be less than bar width and bar height.
var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom));
borderWidth = borderWidth > barSize? barSize: borderWidth;
var halfStroke = borderWidth / 2;
// Adjust borderWidth when bar top position is near vm.base(zero).
var borderLeft = left + (borderSkipped !== 'left'? halfStroke * signX: 0);
var borderRight = right + (borderSkipped !== 'right'? -halfStroke * signX: 0);
var borderTop = top + (borderSkipped !== 'top'? halfStroke * signY: 0);
var borderBottom = bottom + (borderSkipped !== 'bottom'? -halfStroke * signY: 0);
// not become a vertical line?
if (borderLeft !== borderRight) {
top = borderTop;
bottom = borderBottom;
}
// not become a horizontal line?
if (borderTop !== borderBottom) {
left = borderLeft;
right = borderRight;
}
}
// calculate the bar width and roundess
var barWidth = Math.abs(left - right);
var roundness = this._chart.config.options.barRoundness || 0.5;
var radius = barWidth * roundness * 0.5;
// keep track of the original top of the bar
var prevTop = top;
// move the top down so there is room to draw the rounded top
top = prevTop + radius;
var barRadius = top - prevTop;
ctx.beginPath();
ctx.fillStyle = vm.backgroundColor;
ctx.strokeStyle = vm.borderColor;
ctx.lineWidth = borderWidth;
// draw the rounded top rectangle
Chart.helpers.drawRoundedTopRectangle(ctx, left, (top - barRadius + 1), barWidth, bottom - prevTop, barRadius);
ctx.fill();
if (borderWidth) {
ctx.stroke();
}
// restore the original top value so tooltips and scales still work
top = prevTop;
},
});
Chart.defaults.roundedBar = Chart.helpers.clone(Chart.defaults.bar);
Chart.controllers.roundedBar = Chart.controllers.bar.extend({
dataElementType: Chart.elements.RoundedTopRectangle
});
//*********//
const d1= [65, 59, 80, 81,-40, 56, 55, 40 ];
const setColor = data => data < 0 ? '#ff4433': '#3377ee';
var data = {
labels: ["January", "February", "March", "April", "May", "June", "July", "August"],
datasets: [
{
label: "My First dataset",
backgroundColor: d1.map(setColor),
borderColor: d1.map(setColor),
data: d1
},
],
};
var options = {
scaleBeginAtZero: false,
responsive: true,
barRoundness: 1,
scales: {
yAxes: [{
ticks: {
suggestedMin: -70,
suggestedMax: 100
}
}]
}
};
const ctx = "myChart";
const chart = new Chart(ctx, { type: 'roundedBar', data, options });
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.min.js"></script>
<canvas id="myChart" width="250" height="100"></canvas>
With Echarts it’s a bit easier to do that.
const chart = echarts.init(document.getElementById('main'));
const d1 = [65, 59, 80, 81,-40, 56, 55, 40 ];
// specify chart configuration item and data
const option = {
tooltip: {},
legend: {
data:['Sales']
},
xAxis: {
data: ["January", "February", "March", "April", "May", "June", "July", "August"]
},
yAxis: {},
series: [{
name: 'Sales',
type: 'bar',
barWidth: 20,
data: d1.map(item => {
return {
value: item,
itemStyle: {
color: item > 0 ? '#2196F3' : '#F44336',
barBorderRadius: item > 0 ? [100, 100, 0 ,0]
: [0 ,0 ,100, 100]
}
}
}),
}]
};
// use configuration item and data specified to show chart
chart.setOption(option);
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/4.1.0/echarts.min.js"></script>
<div id="main" style="width: 100vw;height:400px;"></div>
If you don’t care about rounded borders, but only styling the negative values, you can map your data in order to create an array of colors depending on its value:
const chartData = [65, 59, 80, 81,-40, 56, 55, 40 ];
const data = {
labels: ["January", "February", "March", "April", "May", "June", "July", "August"],
datasets: [
{
label: "My First dataset",
backgroundColor: chartData.map(setColor),
borderColor: chartData.map(setColor),
data: chartData
},
],
};
// If data < 0 return a red color.
const setColor = data => data < 0 ? '#ff4433': '#3377ee';
Result:
const d1= [65, 59, 80, 81,-40, 56, 55, 40 ];
const setColor = data => data < 0 ? '#ff4433': '#3377ee';
var data = {
labels: ["January", "February", "March", "April", "May", "June", "July", "August"],
datasets: [
{
label: "My First dataset",
backgroundColor: d1.map(setColor),
borderColor: d1.map(setColor),
data: d1
},
],
};
var options = {
scaleBeginAtZero: false,
responsive: true,
scales: {
yAxes: [{
ticks: {
suggestedMin: -60,
suggestedMax: 100
}
}]
}
};
const ctx = "myChart";
const chart = new Chart(ctx, { type: 'bar', data, options });
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.min.js"></script>
<canvas id="myChart" width="250" height="100"></canvas>
0👍
For the rounded border you can use the new version 3 of the lib, it supports it natively.
For the red bars if they are negative, you can pass an array with all colours for background colour and for every index on which the data is negative change it to red. Should work
Example rounded corners: https://www.chartjs.org/samples/master/charts/bar/border-radius.html