Я думаю, что самый близкий результат, который вы можете получить с chart.js и закругленными границами, использует этот ответ: https://stackoverflow.com/a/43281198/10387396.
Но он не очень хорошо работает с отрицательными значениями.
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>
С Echarts это сделать немного проще.
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>
Если вам не нужны закругленные границы, а только стилизация отрицательных значений, вы можете отобразить свои данные, чтобы создать массив цветов в зависимости отего значение:
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';
Результат:
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>