[Chartjs]-Customizing Chart.js

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

Leave a comment