score:0

checkout chartjs-top-round-bar a usefull

you just need to

import 'chartjs-top-round-bar';

...

new chart('mychart', 
{ 
    options: { 
        barroundness: 0.3
    }
}

score:1

got this working in angular 11 and charts.js version 2.9.4 (might work in other versions too)

add this at the very end of your .component.ts file after export class { .. }

chart['elements'].rectangle.prototype.draw = function() {
    
    let ctx = this._chart.ctx;
    let view = this._view;

    //////////////////// edit this to change how rounded the edges are /////////////////////
                                    let borderradius = 10;


    let left = view.x - view.width / 2;
    let right = view.x + view.width / 2;
    let top = view.y;
    let bottom = view.base;

    ctx.beginpath();
    ctx.fillstyle = view.backgroundcolor;
    ctx.strokestyle = view.bordercolor;
    ctx.linewidth = view.borderwidth;

    let barcorners = [
        [left, bottom],
        [left, top],
        [right, top],
        [right, bottom]
    ];

    //start in bottom-left
    ctx.moveto(barcorners[0][0], barcorners[0][1]);

    for (let i = 1; i < 4; i++) {
        let x = barcorners[1][0];
        let y = barcorners[1][1];
        let width = barcorners[2][0] - barcorners[1][0];
        let height = barcorners[0][1] - barcorners[1][1];

        
        //fix radius being too big for small values
        if(borderradius > width/2){
            borderradius = width/2;
        }
        if(borderradius > height/2){
            borderradius = height/2;
        }



        // draw the lines that will be filled - replacing lineto with quadraticcurveto causes more edges to become rounded
        ctx.moveto(x + borderradius, y);
        ctx.lineto(x + width - borderradius, y);
        ctx.quadraticcurveto(x + width, y, x + width, y + borderradius);
        ctx.lineto(x + width, y + height - borderradius);
        ctx.lineto(x + width, y + height, x + width - borderradius, y + height);
        ctx.lineto(x + borderradius, y + height);
        ctx.lineto(x, y + height, x, y + height - borderradius);
        ctx.lineto(x, y + borderradius);
        ctx.quadraticcurveto(x, y, x + borderradius, y);

    }
    //fill the lines
    ctx.fill();
}; 

example ngoninit method:

ngoninit() {
console.log("asdf entering getchart");
this.canvas = document.getelementbyid('numberofsessionschart');
this.ctx = this.canvas.getcontext('2d');

this.mychart = new chart(this.ctx, {
  type: 'bar',
  data: {
    labels: ['jan 20', 'feb 20', 'mar 20', 'apr 20', 'may 20', 'jun 20', 'jul 20', 'aug 20', 'sept 20'],
    datasets: [{
      label: 'vvals',
      backgroundcolor: 'blue',
      data: [0, 50, 20, 30, 40, 50, 60, 70, 80, 90, 100],
    }]
  },
  options: {
    devicepixelratio: 2.2,
    //tooltips are the things that appear when you hover over the data that show the counts.
    //for some reason we can't choose where the tooltip is oriented.
    tooltips: {
        enabled: false
    },
    onclick: (e) => {
        console.log("asdf entering on click");  
    },
    
    legend: {
        display: true,
        position: 'right',
        reverse: true,
        labels: {
            fontcolor: 'black',
            fontsize: 15,
            padding: 20,
            usepointstyle: true,
            //width of circles in legend
            boxwidth: 9
        }
    },
    scales: {
        
        xaxes: [{  
            ticks: {
                padding: 10,
                fontsize: 13,
                fontfamily: 'roboto',
                fontcolor: 'black',
                beginatzero: true
            },
               gridlines: {
                tickmarklength: 0,
                color: '#9da0a2',
                drawonchartarea: false,
               },
        }],
        yaxes: [{ 
            ticks: {
                padding: 10,
                fontsize: 13,
                fontfamily: 'roboto',
                fontcolor: 'black',
                beginatzero: true,
                precision:0
            },
               gridlines: {
                tickmarklength: 0,
                color: '#9da0a2',
                drawonchartarea: false,
               },
        }],
      },
    responsive: false,
}
});



    }
}

score:2

also worth a mention is this solution which takes you 30 seconds to implement:

https://github.com/jedtrow/chart.js-rounded-bar-charts

download and put the .js file into your projects folder, load it and use
var options = { cornerradius: 20, };

to get rounded bars.

credits: https://github.com/jedtrow

score:3

solution by @jordanwillis doesn't work with chart.js version after 2.8.0.

to make it work just add following code before drawroundedtoprectangle definition

chart.helpers.merge(chart.defaults.global, {
    datasets: {
        roundedbar: {
            categorypercentage: 0.8,
            barpercentage: 0.9
        }
    }
});

score:8

the following only customizes the chart.elements.rectangle.prototype.draw - no need to create an entirely new chart type.

other pros:

  • it works well with both vertical & horizontal bar charts
  • it works well with both vertical & horizontal stacked bar charts - it only rounds the last box in the series

noted issue: because this only rounds the box in the last dataset, if the value of the datapoint in the last dataset is < either of the previous data points, the visual top-most box will not be rounded. however, if the last data point is negative and the lowest value, it will round that box on the bottom corners.

credit: original code belongs to https://github.com/uffo. code below and linked fiddle demonstrate increasing positive values in each dataset for each stack, and also modifies some of the default radius options.

/**customize the rectangle.prototype draw method**/
chart.elements.rectangle.prototype.draw = function() {
  var ctx = this._chart.ctx;
  var vm = this._view;
  var left, right, top, bottom, signx, signy, borderskipped, radius;
  var borderwidth = vm.borderwidth;

  // if radius is less than 0 or is large enough to cause drawing errors a max
  //      radius is imposed. if cornerradius is not defined set it to 0.
  var cornerradius = this._chart.config.options.cornerradius;
  var fullcornerradius = this._chart.config.options.fullcornerradius;
  var stackedrounded = this._chart.config.options.stackedrounded;
  var typeofchart = this._chart.config.type;

  if (cornerradius < 0) {
    cornerradius = 0;
  }
  if (typeof cornerradius == 'undefined') {
    cornerradius = 0;
  }
  if (typeof fullcornerradius == 'undefined') {
    fullcornerradius = false;
  }
  if (typeof stackedrounded == 'undefined') {
    stackedrounded = false;
  }

  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;
    }
  }

  ctx.beginpath();
  ctx.fillstyle = vm.backgroundcolor;
  ctx.strokestyle = vm.bordercolor;
  ctx.linewidth = borderwidth;

  // corner points, from bottom-left to bottom-right clockwise
  // | 1 2 |
  // | 0 3 |
  var corners = [
    [left, bottom],
    [left, top],
    [right, top],
    [right, bottom]
  ];

  // find first (starting) corner with fallback to 'bottom'
  var borders = ['bottom', 'left', 'top', 'right'];
  var startcorner = borders.indexof(borderskipped, 0);
  if (startcorner === -1) {
    startcorner = 0;
  }

  function cornerat(index) {
    return corners[(startcorner + index) % 4];
  }

  // draw rectangle from 'startcorner'
  var corner = cornerat(0);
  ctx.moveto(corner[0], corner[1]);


  var nextcornerid, nextcorner, width, height, x, y;
  for (var i = 1; i < 4; i++) {
    corner = cornerat(i);
    nextcornerid = i + 1;
    if (nextcornerid == 4) {
      nextcornerid = 0
    }

    nextcorner = cornerat(nextcornerid);

    width = corners[2][0] - corners[1][0];
    height = corners[0][1] - corners[1][1];
    x = corners[1][0];
    y = corners[1][1];

    var radius = cornerradius;
    // fix radius being too large
    if (radius > math.abs(height) / 2) {
      radius = math.floor(math.abs(height) / 2);
    }
    if (radius > math.abs(width) / 2) {
      radius = math.floor(math.abs(width) / 2);
    }

      var x_tl, x_tr, y_tl, y_tr, x_bl, x_br, y_bl, y_br;
      if (height < 0) {
        // negative values in a standard bar chart
        x_tl = x;
        x_tr = x + width;
        y_tl = y + height;
        y_tr = y + height;

        x_bl = x;
        x_br = x + width;
        y_bl = y;
        y_br = y;

        // draw
        ctx.moveto(x_bl + radius, y_bl);

        ctx.lineto(x_br - radius, y_br);

        // bottom right
        ctx.quadraticcurveto(x_br, y_br, x_br, y_br - radius);


        ctx.lineto(x_tr, y_tr + radius);

        // top right
        fullcornerradius ? ctx.quadraticcurveto(x_tr, y_tr, x_tr - radius, y_tr) : ctx.lineto(x_tr, y_tr, x_tr - radius, y_tr);


        ctx.lineto(x_tl + radius, y_tl);

        // top left
        fullcornerradius ? ctx.quadraticcurveto(x_tl, y_tl, x_tl, y_tl + radius) : ctx.lineto(x_tl, y_tl, x_tl, y_tl + radius);


        ctx.lineto(x_bl, y_bl - radius);

        //  bottom left
        ctx.quadraticcurveto(x_bl, y_bl, x_bl + radius, y_bl);

      } else if (width < 0) {
        // negative values in a horizontal bar chart
        x_tl = x + width;
        x_tr = x;
        y_tl = y;
        y_tr = y;

        x_bl = x + width;
        x_br = x;
        y_bl = y + height;
        y_br = y + height;

        // draw
        ctx.moveto(x_bl + radius, y_bl);

        ctx.lineto(x_br - radius, y_br);

        //  bottom right corner
        fullcornerradius ? ctx.quadraticcurveto(x_br, y_br, x_br, y_br - radius) : ctx.lineto(x_br, y_br, x_br, y_br - radius);

        ctx.lineto(x_tr, y_tr + radius);

        // top right corner
        fullcornerradius ? ctx.quadraticcurveto(x_tr, y_tr, x_tr - radius, y_tr) : ctx.lineto(x_tr, y_tr, x_tr - radius, y_tr);

        ctx.lineto(x_tl + radius, y_tl);

        // top left corner
        ctx.quadraticcurveto(x_tl, y_tl, x_tl, y_tl + radius);

        ctx.lineto(x_bl, y_bl - radius);

        //  bttom left corner
        ctx.quadraticcurveto(x_bl, y_bl, x_bl + radius, y_bl);

      } else {
      
          var lastvisible = 0;
        for (var findlast = 0, findlastto = this._chart.data.datasets.length; findlast < findlastto; findlast++) {
          if (!this._chart.getdatasetmeta(findlast).hidden) {
            lastvisible = findlast;
          }
        }
        var rounded = this._datasetindex === lastvisible;

        if (rounded) {
        //positive value
          ctx.moveto(x + radius, y);

          ctx.lineto(x + width - radius, y);

          // top right
          ctx.quadraticcurveto(x + width, y, x + width, y + radius);


          ctx.lineto(x + width, y + height - radius);

          // bottom right
          if (fullcornerradius || typeofchart == 'horizontalbar')
            ctx.quadraticcurveto(x + width, y + height, x + width - radius, y + height);
          else
            ctx.lineto(x + width, y + height, x + width - radius, y + height);


          ctx.lineto(x + radius, y + height);

          // bottom left
          if (fullcornerradius)
            ctx.quadraticcurveto(x, y + height, x, y + height - radius);
          else
            ctx.lineto(x, y + height, x, y + height - radius);


          ctx.lineto(x, y + radius);

          // top left
          if (fullcornerradius || typeofchart == 'bar')
            ctx.quadraticcurveto(x, y, x + radius, y);
          else
            ctx.lineto(x, y, x + radius, y);
        }else {
          ctx.moveto(x, y);
          ctx.lineto(x + width, y);
          ctx.lineto(x + width, y + height);
          ctx.lineto(x, y + height);
          ctx.lineto(x, y);
        }
      }
    
  }

  ctx.fill();
  if (borderwidth) {
    ctx.stroke();
  }
};

/**chart data**/
var data = {
  labels: ["red", "blue", "yellow", "green", "purple", "orange"],
  datasets: [{
    label: 'data 0',
    data: [12, 19, 3, 5, 2, 3],
    backgroundcolor: [
      'rgba(255, 99, 132, 1)',
      'rgba(54, 162, 235, 1)',
      'rgba(255, 206, 86, 1)',
      'rgba(75, 192, 192, 1)',
      'rgba(153, 102, 255, 1)',
      'rgba(255, 159, 64, 1)'
    ],
    borderwidth: 0
  }, {
    label: 'data 1',
    data: [20, 24, 10, 15, 12, 13],
    backgroundcolor: [
      'rgba(255, 159, 64, 1)',
      'rgba(255, 99, 132, 1)',
      'rgba(255, 206, 86, 1)',

      'rgba(54, 162, 235, 1)',
      'rgba(153, 102, 255, 1)',
      'rgba(75, 192, 192, 1)'

    ],
    borderwidth: 0
  }, {
    label: 'data 2',
    data: [20, 30, 30, 20, 14, 20],
    backgroundcolor: [
      'rgba(75, 192, 192, 1)',
      'rgba(255, 159, 64, 1)',
      'rgba(255, 99, 132, 1)',
      'rgba(255, 206, 86, 1)',

      'rgba(54, 162, 235, 1)',
      'rgba(153, 102, 255, 1)'


    ],
    borderwidth: 0
  }]
};

/**chart options - radius options are here**/
var options = {
	//border radius; default: 0; if a negative value is passed, it will overwrite to 0;
  cornerradius: 10, 
  //default: false; if true, this would round all corners of final box;
  fullcornerradius: false, 
  //default: false; if true, this rounds each box in the stack instead of only final box;
  stackedrounded: false,
	elements: {
    point: {
      radius: 25,
      hoverradius: 35,
      pointstyle: 'rectrounded',

    }
  },
  scales: {
    yaxes: [{
      ticks: {
        beginatzero: true
      },
      stacked: true,
      radius: 25
    }],
    xaxes: [{
      ticks: {
        beginatzero: true
      },
      stacked: true,

    }]
  }
};

/**generate chart**/
var ctxbar = document.getelementbyid("mychart");
var mybarchart = new chart(ctxbar, {
  type: 'bar',
  data: data,
  options: options
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/chart.js/2.7.1/chart.js"></script>
<canvas id="mychart" height="300" width="800"></canvas>


fiddle: https://jsfiddle.net/adammoisa/v0dthnyr/7/

score:22

the code that you were trying to use is actually for chart.js v1 and, as you discovered, does not work for chart.js v2 (which is almost a full chart.js re-write).

to achieve the same results in chart.js v2, you need to extend chart.elements.rectangle and overwrite it's draw method in order to paint the rounded top. there is already a chart.js helper method that will draw a rounded rectangle (chart.helpers.drawroundedrectangle), so we will modify it slightly and create a new helper method that will only draw a rounded top (instead of all sides).

// draws a rectangle with a rounded top
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;
  },
});

next, you will also have to extend the bar chart controller (chart.controllers.bar) and overwrite dataelementtype to use the new "rounded rectangle" for the chart instead of a regular rectangle.

chart.defaults.roundedbar = chart.helpers.clone(chart.defaults.bar);

chart.controllers.roundedbar = chart.controllers.bar.extend({
  dataelementtype: chart.elements.roundedtoprectangle
});

lastly, we will modify the chart's config to use the new chart type created above and add a new options property called barroundness to control how round the top is (0 is flat, 1 is a semi-circle).

var ctx = document.getelementbyid("canvas").getcontext("2d");
var mybar = new chart(ctx, {
  type: 'roundedbar',
  data: {
    labels: ["car", "bike", "walking"],
    datasets: [{
      label: 'students',
      backgroundcolor: chartcolors.blue,
      data: [
        randomscalingfactor(), 
        randomscalingfactor(), 
        randomscalingfactor(), 
      ]
    }, {
      label: 'teachers',
      backgroundcolor: chartcolors.red,
      data: [
        randomscalingfactor(), 
        randomscalingfactor(), 
        randomscalingfactor(), 
      ]
    }, {
      label: 'visitors',
      backgroundcolor: chartcolors.green,
      data: [
        randomscalingfactor(), 
        randomscalingfactor(), 
        randomscalingfactor(), 
      ]
    }]
  },
  options: {
    responsive: true,
    barroundness: 1,
    title: {
      display: true,
      text: "chart.js - bar chart with rounded tops (drawroundedtoprectangle method)"
    },
  }
});

you can see a full working example at this codepen.

also, in case you want a slightly different "rounded top" look, here is another codepen that uses a different approach to drawing the top (a single quadratic curve).


Related Query

More Query from same tag